Add support for enabling/disabling ports for gNMI devices
This change also includes:
- Refactoring of gNMI protocol+driver to take advantage of the recent
changes to the gRPC protocol subsystem (e.g. no more locking, start RPC
with timeouts, etc.).
- Fixed Stratum driver to work after GeneralDeviceProvider refactoring
- Updated bmv2.py to generate ChassisConfig for stratum_bmv2
- Fixed portstate command to use the same port name as in the store
Change-Id: I0dad3bc73e4b6d907b5cf6b7b9a2852943226be7
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/AbstractGnmiHandlerBehaviour.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/AbstractGnmiHandlerBehaviour.java
index 4ce9f18..3774bfb 100644
--- a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/AbstractGnmiHandlerBehaviour.java
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/AbstractGnmiHandlerBehaviour.java
@@ -17,7 +17,6 @@
package org.onosproject.drivers.gnmi;
import com.google.common.base.Strings;
-import io.grpc.StatusRuntimeException;
import org.onosproject.gnmi.api.GnmiClient;
import org.onosproject.gnmi.api.GnmiClientKey;
import org.onosproject.gnmi.api.GnmiController;
@@ -32,20 +31,12 @@
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
/**
* Abstract implementation of a behaviour handler for a gNMI device.
*/
public class AbstractGnmiHandlerBehaviour extends AbstractHandlerBehaviour {
- // Default timeout in seconds for device operations.
- private static final String DEVICE_REQ_TIMEOUT = "deviceRequestTimeout";
- private static final int DEFAULT_DEVICE_REQ_TIMEOUT = 60;
-
protected final Logger log = LoggerFactory.getLogger(getClass());
protected DeviceId deviceId;
protected DeviceService deviceService;
@@ -95,58 +86,4 @@
return null;
}
}
-
- /**
- * Returns the device request timeout driver property, or a default value
- * if the property is not present or cannot be parsed.
- *
- * @return timeout value
- */
- private int getDeviceRequestTimeout() {
- final String timeout = handler().driver()
- .getProperty(DEVICE_REQ_TIMEOUT);
- if (timeout == null) {
- return DEFAULT_DEVICE_REQ_TIMEOUT;
- } else {
- try {
- return Integer.parseInt(timeout);
- } catch (NumberFormatException e) {
- log.error("{} driver property '{}' is not a number, using default value {}",
- DEVICE_REQ_TIMEOUT, timeout, DEFAULT_DEVICE_REQ_TIMEOUT);
- return DEFAULT_DEVICE_REQ_TIMEOUT;
- }
- }
- }
-
- /**
- * Convenience method to get the result of a completable future while
- * setting a timeout and checking for exceptions.
- *
- * @param future completable future
- * @param opDescription operation description to use in log messages. Should
- * be a sentence starting with a verb ending in -ing,
- * e.g. "reading...", "writing...", etc.
- * @param defaultValue value to return if operation fails
- * @param <U> type of returned value
- * @return future result or default value
- */
- <U> U getFutureWithDeadline(CompletableFuture<U> future, String opDescription,
- U defaultValue) {
- try {
- return future.get(getDeviceRequestTimeout(), TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- log.error("Exception while {} on {}", opDescription, deviceId);
- } catch (ExecutionException e) {
- final Throwable cause = e.getCause();
- if (cause instanceof StatusRuntimeException) {
- final StatusRuntimeException grpcError = (StatusRuntimeException) cause;
- log.warn("Error while {} on {}: {}", opDescription, deviceId, grpcError.getMessage());
- } else {
- log.error("Exception while {} on {}", opDescription, deviceId, e.getCause());
- }
- } catch (TimeoutException e) {
- log.error("Operation TIMEOUT while {} on {}", opDescription, deviceId);
- }
- return defaultValue;
- }
}
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiDeviceDescriptionDiscovery.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiDeviceDescriptionDiscovery.java
index e56f2f3..ce134ad 100644
--- a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiDeviceDescriptionDiscovery.java
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiDeviceDescriptionDiscovery.java
@@ -18,11 +18,16 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Futures;
import gnmi.Gnmi;
import gnmi.Gnmi.GetRequest;
import gnmi.Gnmi.GetResponse;
+import org.onlab.packet.ChassisId;
+import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
@@ -49,11 +54,24 @@
private static final Logger log = LoggerFactory
.getLogger(OpenConfigGnmiDeviceDescriptionDiscovery.class);
- private static final String LAST_CHANGE = "last-change";
+ private static final String LAST_CHANGE = "last-changed";
+
+ private static final String UNKNOWN = "unknown";
@Override
public DeviceDescription discoverDeviceDetails() {
- return null;
+ return new DefaultDeviceDescription(
+ data().deviceId().uri(),
+ Device.Type.SWITCH,
+ data().driver().manufacturer(),
+ data().driver().hwVersion(),
+ data().driver().swVersion(),
+ UNKNOWN,
+ new ChassisId(),
+ true,
+ DefaultAnnotations.builder()
+ .set(AnnotationKeys.PROTOCOL, "gNMI")
+ .build());
}
@Override
@@ -63,9 +81,7 @@
}
log.debug("Discovering port details on device {}", handler().data().deviceId());
- final GetResponse response = getFutureWithDeadline(
- client.get(buildPortStateRequest()),
- "getting port details", GetResponse.getDefaultInstance());
+ final GetResponse response = Futures.getUnchecked(client.get(buildPortStateRequest()));
final Map<String, DefaultPortDescription.Builder> ports = Maps.newHashMap();
final Map<String, DefaultAnnotations.Builder> annotations = Maps.newHashMap();
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java
new file mode 100644
index 0000000..4f050b0
--- /dev/null
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortAdminBehaviour.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.gnmi;
+
+import gnmi.Gnmi;
+import org.onosproject.gnmi.api.GnmiClient;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PortAdmin;
+
+import java.util.concurrent.CompletableFuture;
+
+import static java.util.concurrent.CompletableFuture.completedFuture;
+
+/**
+ * Implementation of PortAdmin for gNMI devices with OpenConfig support.
+ */
+public class OpenConfigGnmiPortAdminBehaviour
+ extends AbstractGnmiHandlerBehaviour
+ implements PortAdmin {
+
+ @Override
+ public CompletableFuture<Boolean> enable(PortNumber number) {
+ doEnable(number, true);
+ // Always returning true is OK assuming this is used only by the
+ // GeneralDeviceProvider, which ignores the return value and instead
+ // waits for a gNMI Update over the Subscribe RPC.
+ return completedFuture(true);
+ }
+
+ @Override
+ public CompletableFuture<Boolean> disable(PortNumber number) {
+ doEnable(number, false);
+ return completedFuture(true);
+ }
+
+ @Override
+ public CompletableFuture<Boolean> isEnabled(PortNumber number) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ private void doEnable(PortNumber portNumber, boolean enabled) {
+ if (portNumber.isLogical()) {
+ log.warn("Cannot update port status for logical port {} on {}",
+ portNumber, deviceId);
+ return;
+ }
+ final GnmiClient client = getClientByKey();
+ if (client == null) {
+ log.warn("Cannot update ports on {}, missing gNMI client", deviceId);
+ return;
+ }
+ final Gnmi.Path path = Gnmi.Path.newBuilder()
+ .addElem(Gnmi.PathElem.newBuilder().setName("interfaces").build())
+ .addElem(Gnmi.PathElem.newBuilder().setName("interface")
+ .putKey("name", portNumber.name()).build())
+ .addElem(Gnmi.PathElem.newBuilder().setName("config").build())
+ .addElem(Gnmi.PathElem.newBuilder().setName("enabled").build())
+ .build();
+ final Gnmi.TypedValue value = Gnmi.TypedValue.newBuilder()
+ .setBoolVal(enabled)
+ .build();
+ final Gnmi.SetRequest request = Gnmi.SetRequest.newBuilder()
+ .addUpdate(Gnmi.Update.newBuilder()
+ .setPath(path)
+ .setVal(value)
+ .build())
+ .build();
+
+ // Async submit request and forget about it. In case of errors, the
+ // client will log them. In case of success, we should receive a gNMI
+ // Update over the Subscribe RPC with the new oper status.
+ client.set(request);
+ }
+}
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortStatisticsDiscovery.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortStatisticsDiscovery.java
index a9481a4..a41144e 100644
--- a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortStatisticsDiscovery.java
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/OpenConfigGnmiPortStatisticsDiscovery.java
@@ -17,6 +17,7 @@
package org.onosproject.drivers.gnmi;
import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.Futures;
import gnmi.Gnmi;
import gnmi.Gnmi.GetRequest;
import gnmi.Gnmi.GetResponse;
@@ -45,7 +46,7 @@
private static final Map<Pair<DeviceId, PortNumber>, Long> PORT_START_TIMES =
Maps.newConcurrentMap();
- private static final String LAST_CHANGE = "last-change";
+ private static final String LAST_CHANGE = "last-changed";
@Override
public Collection<PortStatistics> discoverPortStatistics() {
@@ -67,10 +68,7 @@
ifacePortNumberMapping.put(portName, port.number());
});
- GetResponse getResponse =
- getFutureWithDeadline(client.get(getRequest.build()),
- "getting port counters",
- GetResponse.getDefaultInstance());
+ GetResponse getResponse = Futures.getUnchecked(client.get(getRequest.build()));
Map<String, Long> inPkts = Maps.newHashMap();
Map<String, Long> outPkts = Maps.newHashMap();
diff --git a/drivers/gnmi/src/main/resources/gnmi-drivers.xml b/drivers/gnmi/src/main/resources/gnmi-drivers.xml
index 9af162f..0928284 100644
--- a/drivers/gnmi/src/main/resources/gnmi-drivers.xml
+++ b/drivers/gnmi/src/main/resources/gnmi-drivers.xml
@@ -22,6 +22,8 @@
impl="org.onosproject.drivers.gnmi.GnmiHandshaker"/>
<behaviour api="org.onosproject.net.device.PortStatisticsDiscovery"
impl="org.onosproject.drivers.gnmi.OpenConfigGnmiPortStatisticsDiscovery"/>
+ <behaviour api="org.onosproject.net.behaviour.PortAdmin"
+ impl="org.onosproject.drivers.gnmi.OpenConfigGnmiPortAdminBehaviour"/>
</driver>
</drivers>
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/AbstractStratumBehaviour.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/AbstractStratumBehaviour.java
new file mode 100644
index 0000000..b7c56e9
--- /dev/null
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/AbstractStratumBehaviour.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.stratum;
+
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.HandlerBehaviour;
+
+/**
+ * Abstract implementation of a driver behaviour for Stratum devices that
+ * provides access to protocol-specific implementations of the same behavior.
+ *
+ * @param <B> type of behaviour
+ */
+public abstract class AbstractStratumBehaviour<B extends HandlerBehaviour>
+ extends AbstractHandlerBehaviour {
+
+ protected B p4runtime;
+ protected B gnmi;
+
+ public AbstractStratumBehaviour(B p4runtime, B gnmi) {
+ this.p4runtime = p4runtime;
+ this.gnmi = gnmi;
+ }
+
+ @Override
+ public void setHandler(DriverHandler handler) {
+ super.setHandler(handler);
+ p4runtime.setHandler(handler);
+ gnmi.setHandler(handler);
+ }
+
+ @Override
+ public void setData(DriverData data) {
+ super.setData(data);
+ p4runtime.setData(data);
+ gnmi.setData(data);
+ }
+}
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumDeviceDescriptionDiscovery.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumDeviceDescriptionDiscovery.java
new file mode 100644
index 0000000..4a65e26
--- /dev/null
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumDeviceDescriptionDiscovery.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.stratum;
+
+import org.onosproject.drivers.gnmi.OpenConfigGnmiDeviceDescriptionDiscovery;
+import org.onosproject.drivers.p4runtime.P4RuntimeDeviceDescriptionDiscovery;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.PortDescription;
+
+import java.util.List;
+
+import static java.lang.String.format;
+
+/**
+ * Implementation of DeviceDescriptionDiscovery for Stratum devices.
+ */
+public class StratumDeviceDescriptionDiscovery
+ extends AbstractStratumBehaviour<DeviceDescriptionDiscovery>
+ implements DeviceDescriptionDiscovery {
+
+ private static final String UNKNOWN = "unknown";
+
+
+ public StratumDeviceDescriptionDiscovery() {
+ super(new P4RuntimeDeviceDescriptionDiscovery(),
+ new OpenConfigGnmiDeviceDescriptionDiscovery());
+ }
+
+ @Override
+ public DeviceDescription discoverDeviceDetails() {
+ final DeviceDescription p4Descr = p4runtime.discoverDeviceDetails();
+ final DeviceDescription gnmiDescr = gnmi.discoverDeviceDetails();
+ return new DefaultDeviceDescription(
+ data().deviceId().uri(),
+ Device.Type.SWITCH,
+ data().driver().manufacturer(),
+ data().driver().hwVersion(),
+ data().driver().swVersion(),
+ UNKNOWN,
+ p4Descr.chassisId(),
+ // Availability is mandated by P4Runtime.
+ p4Descr.isDefaultAvailable(),
+ DefaultAnnotations.builder()
+ .set(AnnotationKeys.PROTOCOL, format(
+ "%s, %s",
+ p4Descr.annotations().value(AnnotationKeys.PROTOCOL),
+ gnmiDescr.annotations().value(AnnotationKeys.PROTOCOL)))
+ .build());
+ }
+
+ @Override
+ public List<PortDescription> discoverPortDetails() {
+ return gnmi.discoverPortDetails();
+ }
+}
diff --git a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumHandshaker.java b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumHandshaker.java
index 8762af0..8ce68d7 100644
--- a/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumHandshaker.java
+++ b/drivers/stratum/src/main/java/org/onosproject/drivers/stratum/StratumHandshaker.java
@@ -21,9 +21,6 @@
import org.onosproject.net.MastershipRole;
import org.onosproject.net.device.DeviceAgentListener;
import org.onosproject.net.device.DeviceHandshaker;
-import org.onosproject.net.driver.AbstractHandlerBehaviour;
-import org.onosproject.net.driver.DriverData;
-import org.onosproject.net.driver.DriverHandler;
import org.onosproject.net.provider.ProviderId;
import java.util.concurrent.CompletableFuture;
@@ -31,33 +28,20 @@
/**
* Implementation of DeviceHandshaker for Stratum device.
*/
-public class StratumHandshaker extends AbstractHandlerBehaviour implements DeviceHandshaker {
-
- private P4RuntimeHandshaker p4runtime;
- private GnmiHandshaker gnmi;
+public class StratumHandshaker
+ extends AbstractStratumBehaviour<DeviceHandshaker>
+ implements DeviceHandshaker {
public StratumHandshaker() {
- p4runtime = new P4RuntimeHandshaker();
- gnmi = new GnmiHandshaker();
- }
-
- @Override
- public void setHandler(DriverHandler handler) {
- super.setHandler(handler);
- p4runtime.setHandler(handler);
- gnmi.setHandler(handler);
- }
-
- @Override
- public void setData(DriverData data) {
- super.setData(data);
- p4runtime.setData(data);
- gnmi.setData(data);
+ super(new P4RuntimeHandshaker(), new GnmiHandshaker());
}
@Override
public boolean isReachable() {
- return p4runtime.isReachable() && gnmi.isReachable();
+ // Reachability is mainly used for mastership contests and it's a
+ // prerequisite for availability. We can probably live without gNMI and
+ // gNOI, but we will always need P4Runtime.
+ return p4runtime.isReachable();
}
@Override
diff --git a/drivers/stratum/src/main/resources/stratum-drivers.xml b/drivers/stratum/src/main/resources/stratum-drivers.xml
index fa0c0c4..f854d5f 100644
--- a/drivers/stratum/src/main/resources/stratum-drivers.xml
+++ b/drivers/stratum/src/main/resources/stratum-drivers.xml
@@ -19,6 +19,8 @@
hwVersion="master" swVersion="Stratum" extends="p4runtime,gnmi">
<behaviour api="org.onosproject.net.device.DeviceHandshaker"
impl="org.onosproject.drivers.stratum.StratumHandshaker"/>
+ <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+ impl="org.onosproject.drivers.stratum.StratumDeviceDescriptionDiscovery"/>
</driver>
<driver name="stratum-dummy" manufacturer="Open Networking Foundation"