Various changes in BMv2 driver and provider modules (onos1.6 cherry-pick)
Driver notable changes:
- Implemented new behaviors, removed deprecated ones
- Removed flow rule translator classes (now under protocol module)
- Improved FlowRuleProgrammable: now it uses BMv2TableEntryService
to lookup/bind flow rules with BMv2 table entries, retrieves flow
statistics, better exception handling when adding/replacing/removing
table entries.
- Improved PacketProgrammable: better exception handling and logging
Provider notable changes:
- Bmv2DeviceProvider: detects and notifies device configuration
changes and reboots to Bmv2DeviceContextService, added support for
periodic polling of port statistics
- Bmv2PacketProvider: implemented workaround for OutboundPackets with
flood treatment
Change-Id: I79b756b533d4afb6b70025a137b2e811fd42a4e8
diff --git a/drivers/bmv2/features.xml b/drivers/bmv2/features.xml
index c78c28b..7e986c2 100644
--- a/drivers/bmv2/features.xml
+++ b/drivers/bmv2/features.xml
@@ -20,6 +20,6 @@
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
- <bundle>mvn:${project.groupId}/onos-bmv2-protocol/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-bmv2-protocol-api/${project.version}</bundle>
</feature>
</features>
diff --git a/drivers/bmv2/pom.xml b/drivers/bmv2/pom.xml
index c0bc9ec..60b20b3 100644
--- a/drivers/bmv2/pom.xml
+++ b/drivers/bmv2/pom.xml
@@ -22,11 +22,11 @@
<artifactId>onos-drivers-general</artifactId>
<groupId>org.onosproject</groupId>
<version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-drivers-bmv2</artifactId>
- <version>1.7.0-SNAPSHOT</version>
<packaging>bundle</packaging>
@@ -36,7 +36,7 @@
<onos.app.name>org.onosproject.drivers.bmv2</onos.app.name>
<onos.app.origin>ON.Lab</onos.app.origin>
<onos.app.category>Drivers</onos.app.category>
- <onos.app.title>BMv2 Device Drivers</onos.app.title>
+ <onos.app.title>BMv2 Drivers</onos.app.title>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.requires>
org.onosproject.bmv2
@@ -46,7 +46,7 @@
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-bmv2-protocol</artifactId>
+ <artifactId>onos-bmv2-protocol-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DeviceDescriptionDiscovery.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DeviceDescriptionDiscovery.java
new file mode 100644
index 0000000..2ed71e2
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DeviceDescriptionDiscovery.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.bmv2;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.onlab.packet.ChassisId;
+import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
+import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
+import org.onosproject.bmv2.api.service.Bmv2Controller;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+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;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.util.List;
+
+import static org.onosproject.bmv2.api.runtime.Bmv2Device.*;
+import static org.onosproject.net.Device.Type.SWITCH;
+
+/**
+ * Implementation of the device description discovery behaviour for BMv2.
+ */
+public class Bmv2DeviceDescriptionDiscovery extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
+
+ private static final String JSON_CONFIG_MD5 = "bmv2JsonConfigMd5";
+ private static final String PROCESS_INSTANCE_ID = "bmv2ProcessInstanceId";
+
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+ private Bmv2Controller controller;
+
+ private boolean init() {
+ controller = handler().get(Bmv2Controller.class);
+ if (controller == null) {
+ log.warn("Failed to get a BMv2 controller");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public DeviceDescription discoverDeviceDetails() {
+
+ if (!init()) {
+ return null;
+ }
+
+ DeviceId deviceId = handler().data().deviceId();
+
+ Bmv2DeviceAgent deviceAgent;
+ try {
+ deviceAgent = controller.getAgent(deviceId);
+ } catch (Bmv2RuntimeException e) {
+ log.error("Failed to connect to Bmv2 device", e);
+ return null;
+ }
+
+ DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder();
+
+ try {
+ String md5 = deviceAgent.getJsonConfigMd5();
+ BigInteger i = new BigInteger(1, md5.getBytes());
+ annotationsBuilder.set(JSON_CONFIG_MD5, String.format("%1$032X", i).toLowerCase());
+ } catch (Bmv2RuntimeException e) {
+ log.warn("Unable to dump JSON configuration from {}: {}", deviceId, e.explain());
+ }
+ try {
+ int instanceId = deviceAgent.getProcessInstanceId();
+ annotationsBuilder.set(PROCESS_INSTANCE_ID, String.valueOf(instanceId));
+ } catch (Bmv2RuntimeException e) {
+ log.warn("Unable to get process instance ID from {}: {}", deviceId, e.explain());
+ }
+
+ annotationsBuilder.set(AnnotationKeys.PROTOCOL, PROTOCOL);
+
+ return new DefaultDeviceDescription(deviceId.uri(),
+ SWITCH,
+ MANUFACTURER,
+ HW_VERSION,
+ SW_VERSION,
+ SERIAL_NUMBER,
+ new ChassisId(),
+ annotationsBuilder.build());
+ }
+
+ @Override
+ public List<PortDescription> discoverPortDetails() {
+
+ if (!init()) {
+ return null;
+ }
+
+ DeviceId deviceId = handler().data().deviceId();
+
+ Bmv2DeviceAgent deviceAgent;
+ try {
+ deviceAgent = controller.getAgent(deviceId);
+ } catch (Bmv2RuntimeException e) {
+ log.error("Failed to connect to Bmv2 device", e);
+ return null;
+ }
+
+ List<PortDescription> portDescriptions = Lists.newArrayList();
+
+ try {
+ deviceAgent.getPortsInfo().forEach(p -> {
+ PortNumber portNumber = PortNumber.portNumber((long) p.number(), p.ifaceName());
+ portDescriptions.add(new DefaultPortDescription(portNumber, p.isUp(), DefaultAnnotations.EMPTY));
+ });
+ } catch (Bmv2RuntimeException e) {
+ log.error("Unable to get port descriptions of {}: {}", deviceId, e);
+ }
+
+ return ImmutableList.copyOf(portDescriptions);
+ }
+}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DriversLoader.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DriversLoader.java
index 89ed1bd..e6a7a7c 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DriversLoader.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DriversLoader.java
@@ -20,7 +20,7 @@
import org.onosproject.net.driver.AbstractDriverLoader;
/**
- * Loader for barefoot drivers from specific xml.
+ * Loader for BMv2 drivers from xml file.
*/
@Component(immediate = true)
public class Bmv2DriversLoader extends AbstractDriverLoader {
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2ExtensionSelectorResolver.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2ExtensionSelectorResolver.java
new file mode 100644
index 0000000..28e93a7
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2ExtensionSelectorResolver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.bmv2;
+
+import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
+import org.onosproject.net.behaviour.ExtensionSelectorResolver;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.criteria.ExtensionSelector;
+import org.onosproject.net.flow.criteria.ExtensionSelectorType;
+
+import java.util.Collections;
+
+import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS;
+
+/**
+ * Implementation of the extension selector resolver behaviour for BMv2.
+ */
+public class Bmv2ExtensionSelectorResolver extends AbstractHandlerBehaviour implements ExtensionSelectorResolver {
+
+ @Override
+ public ExtensionSelector getExtensionSelector(ExtensionSelectorType type) {
+ if (type.equals(BMV2_MATCH_PARAMS.type())) {
+ return new Bmv2ExtensionSelector(Collections.emptyMap());
+ }
+
+ return null;
+ }
+}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2ExtensionTreatmentResolver.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2ExtensionTreatmentResolver.java
new file mode 100644
index 0000000..00a22bb
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2ExtensionTreatmentResolver.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.bmv2;
+
+import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
+
+/**
+ * Implementation of the extension treatment resolver behavior for BMv2.
+ */
+public class Bmv2ExtensionTreatmentResolver extends AbstractHandlerBehaviour implements ExtensionTreatmentResolver {
+
+ @Override
+ public ExtensionTreatment getExtensionInstruction(ExtensionTreatmentType type) {
+ if (type.equals(BMV2_ACTION.type())) {
+ return new Bmv2ExtensionTreatment(null);
+ }
+ return null;
+ }
+}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
index 7d78fe3..01d812f 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
@@ -16,28 +16,25 @@
package org.onosproject.drivers.bmv2;
-import com.eclipsesource.json.Json;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.commons.lang3.tuple.Triple;
-import org.onosproject.bmv2.api.model.Bmv2Model;
-import org.onosproject.bmv2.api.runtime.Bmv2Client;
+import org.onosproject.bmv2.api.context.Bmv2Configuration;
+import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
+import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
+import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslatorException;
+import org.onosproject.bmv2.api.context.Bmv2Interpreter;
+import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
+import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
+import org.onosproject.bmv2.api.runtime.Bmv2ParsedTableEntry;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
-import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
-import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
-import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
-import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslatorException;
-import org.onosproject.drivers.bmv2.translators.Bmv2SimpleTranslatorConfig;
-import org.onosproject.net.Device;
+import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
+import org.onosproject.bmv2.api.service.Bmv2Controller;
+import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
+import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.DefaultFlowEntry;
import org.onosproject.net.flow.FlowEntry;
@@ -50,97 +47,130 @@
import java.util.Collections;
import java.util.Date;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
+import static org.onosproject.bmv2.api.runtime.Bmv2RuntimeException.Code.*;
import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
/**
- * Flow rule programmable device behaviour implementation for BMv2.
+ * Implementation of the flow rule programmable behaviour for BMv2.
*/
-public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
- implements FlowRuleProgrammable {
+public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
- private static final Logger LOG =
- LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class);
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
- // There's no Bmv2 client method to poll flow entries from the device. Use a local store.
- // FIXME: this information should be distributed across instances of the cluster.
- private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, TimestampedFlowRule>>
- ENTRIES_MAP = Maps.newConcurrentMap();
+ // Needed to synchronize operations over the same table entry.
+ private static final ConcurrentMap<Bmv2TableEntryReference, Boolean> ENTRY_LOCKS = Maps.newConcurrentMap();
- // Cache model objects instead of parsing the JSON each time.
- private static final LoadingCache<String, Bmv2Model> MODEL_CACHE = CacheBuilder.newBuilder()
- .expireAfterAccess(60, TimeUnit.SECONDS)
- .build(new CacheLoader<String, Bmv2Model>() {
- @Override
- public Bmv2Model load(String jsonString) throws Exception {
- // Expensive call.
- return Bmv2Model.parse(Json.parse(jsonString).asObject());
- }
- });
+ private Bmv2Controller controller;
+ private Bmv2TableEntryService tableEntryService;
+ private Bmv2DeviceContextService contextService;
+
+ private boolean init() {
+ controller = handler().get(Bmv2Controller.class);
+ tableEntryService = handler().get(Bmv2TableEntryService.class);
+ contextService = handler().get(Bmv2DeviceContextService.class);
+ if (controller == null) {
+ log.warn("Failed to get a BMv2 controller");
+ return false;
+ }
+ if (tableEntryService == null) {
+ log.warn("Failed to get a BMv2 table entry service");
+ return false;
+ }
+ if (contextService == null) {
+ log.warn("Failed to get a BMv2 device context service");
+ return false;
+ }
+ return true;
+ }
@Override
public Collection<FlowEntry> getFlowEntries() {
- DeviceId deviceId = handler().data().deviceId();
-
- Bmv2Client deviceClient;
- try {
- deviceClient = Bmv2ThriftClient.of(deviceId);
- } catch (Bmv2RuntimeException e) {
- LOG.error("Failed to connect to Bmv2 device", e);
+ if (!init()) {
return Collections.emptyList();
}
- Bmv2Model model = getTranslator(deviceId).config().model();
+ DeviceId deviceId = handler().data().deviceId();
+
+ Bmv2DeviceAgent deviceAgent;
+ try {
+ deviceAgent = controller.getAgent(deviceId);
+ } catch (Bmv2RuntimeException e) {
+ log.error("Failed to get BMv2 device agent: {}", e.explain());
+ return Collections.emptyList();
+ }
+
+ Bmv2DeviceContext context = contextService.getContext(deviceId);
+ if (context == null) {
+ log.warn("Unable to get device context for {}", deviceId);
+ }
+
+ Bmv2Interpreter interpreter = context.interpreter();
+ Bmv2Configuration configuration = context.configuration();
List<FlowEntry> entryList = Lists.newArrayList();
- model.tables().forEach(table -> {
- // For each table declared in the model for this device, do:
- try {
- // Bmv2 doesn't support proper polling for table entries, but only a string based table dump.
- // The trick here is to first dump the entry ids currently installed in the device for a given table,
- // and then filter ENTRIES_MAP based on the retrieved values.
- Set<Long> installedEntryIds = Sets.newHashSet(deviceClient.getInstalledEntryIds(table.name()));
- ENTRIES_MAP.forEach((key, value) -> {
- if (key.getLeft() == deviceId && key.getMiddle() == table.name()
- && value != null) {
- long entryId = value.getKey();
- // Filter entries_map for this device and table.
- if (installedEntryIds.contains(entryId)) {
- // Entry is installed.
- long bytes = 0L;
- long packets = 0L;
- if (table.hasCounters()) {
- // Read counter values from device.
- try {
- Pair<Long, Long> counterValue = deviceClient.readTableEntryCounter(table.name(),
- entryId);
- bytes = counterValue.getLeft();
- packets = counterValue.getRight();
- } catch (Bmv2RuntimeException e) {
- LOG.warn("Unable to get counter values for entry {} of table {} of device {}: {}",
- entryId, table.name(), deviceId, e.toString());
- }
- }
- TimestampedFlowRule tsRule = value.getRight();
- FlowEntry entry = new DefaultFlowEntry(tsRule.rule(), ADDED,
- tsRule.lifeInSeconds(), packets, bytes);
- entryList.add(entry);
- } else {
- // No such entry on device, can remove from local store.
- ENTRIES_MAP.remove(key);
+ configuration.tables().forEach(table -> {
+ // For each table in the configuration AND exposed by the interpreter.
+ if (!interpreter.tableIdMap().inverse().containsKey(table.name())) {
+ return;
+ }
+
+ // Bmv2 doesn't support proper polling for table entries, but only a string based table dump.
+ // The trick here is to first dump the entries currently installed in the device for a given table,
+ // and then query a service for the corresponding, previously applied, flow rule.
+ List<Bmv2ParsedTableEntry> installedEntries = tableEntryService.getTableEntries(deviceId, table.name());
+ installedEntries.forEach(parsedEntry -> {
+ Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId,
+ table.name(),
+ parsedEntry.matchKey());
+ ENTRY_LOCKS.compute(entryRef, (key, value) -> {
+
+ Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookupEntryReference(entryRef);
+
+ if (frWrapper == null) {
+ log.warn("missing reference from table entry service, BUG? " +
+ "deviceId={}, tableName={}, matchKey={}",
+ deviceId, table.name(), entryRef.matchKey());
+ return null;
+ }
+
+ long remoteEntryId = parsedEntry.entryId();
+ long localEntryId = frWrapper.entryId();
+
+ if (remoteEntryId != localEntryId) {
+ log.warn("getFlowEntries(): inconsistent entry id! BUG? Updating it... remote={}, local={}",
+ remoteEntryId, localEntryId);
+ frWrapper = new Bmv2FlowRuleWrapper(frWrapper.rule(), remoteEntryId,
+ frWrapper.creationDate());
+ tableEntryService.bindEntryReference(entryRef, frWrapper);
+ }
+
+ long bytes = 0L;
+ long packets = 0L;
+
+ if (table.hasCounters()) {
+ // Read counter values from device.
+ try {
+ Pair<Long, Long> counterValue = deviceAgent.readTableEntryCounter(table.name(),
+ remoteEntryId);
+ bytes = counterValue.getLeft();
+ packets = counterValue.getRight();
+ } catch (Bmv2RuntimeException e) {
+ log.warn("Unable to get counters for entry {}/{} of device {}: {}",
+ table.name(), remoteEntryId, deviceId, e.explain());
}
}
+
+ FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
+ packets, bytes);
+ entryList.add(entry);
+ return true;
});
- } catch (Bmv2RuntimeException e) {
- LOG.error("Unable to get flow entries for table {} of device {}: {}",
- table.name(), deviceId, e.toString());
- }
+
+ });
});
return Collections.unmodifiableCollection(entryList);
@@ -160,17 +190,27 @@
private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
- DeviceId deviceId = handler().data().deviceId();
-
- Bmv2Client deviceClient;
- try {
- deviceClient = Bmv2ThriftClient.of(deviceId);
- } catch (Bmv2RuntimeException e) {
- LOG.error("Failed to connect to Bmv2 device", e);
+ if (!init()) {
return Collections.emptyList();
}
- Bmv2FlowRuleTranslator translator = getTranslator(deviceId);
+ DeviceId deviceId = handler().data().deviceId();
+
+ Bmv2DeviceAgent deviceAgent;
+ try {
+ deviceAgent = controller.getAgent(deviceId);
+ } catch (Bmv2RuntimeException e) {
+ log.error("Failed to get BMv2 device agent: {}", e.explain());
+ return Collections.emptyList();
+ }
+
+ Bmv2DeviceContext context = contextService.getContext(deviceId);
+ if (context == null) {
+ log.error("Unable to get device context for {}", deviceId);
+ return Collections.emptyList();
+ }
+
+ Bmv2FlowRuleTranslator translator = tableEntryService.getFlowRuleTranslator();
List<FlowRule> processedFlowRules = Lists.newArrayList();
@@ -179,120 +219,114 @@
Bmv2TableEntry bmv2Entry;
try {
- bmv2Entry = translator.translate(rule);
+ bmv2Entry = translator.translate(rule, context);
} catch (Bmv2FlowRuleTranslatorException e) {
- LOG.error("Unable to translate flow rule: {}", e.getMessage());
+ log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
continue;
}
String tableName = bmv2Entry.tableName();
- Triple<DeviceId, String, Bmv2MatchKey> entryKey = Triple.of(deviceId, tableName, bmv2Entry.matchKey());
+ Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey());
/*
From here on threads are synchronized over entryKey, i.e. serialize operations
over the same matchKey of a specific table and device.
*/
- ENTRIES_MAP.compute(entryKey, (key, value) -> {
+ ENTRY_LOCKS.compute(entryRef, (key, value) -> {
+ // Get from store
+ Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookupEntryReference(entryRef);
try {
if (operation == Operation.APPLY) {
// Apply entry
long entryId;
- if (value != null) {
+ if (frWrapper != null) {
// Existing entry.
- entryId = value.getKey();
- try {
- // Tentatively delete entry before re-adding.
- // It might not exist on device due to inconsistencies.
- deviceClient.deleteTableEntry(bmv2Entry.tableName(), entryId);
- value = null;
- } catch (Bmv2RuntimeException e) {
- // Silently drop exception as we can probably fix this by re-adding the entry.
- }
+ entryId = frWrapper.entryId();
+ // Tentatively delete entry before re-adding.
+ // It might not exist on device due to inconsistencies.
+ silentlyRemove(deviceAgent, entryRef.tableName(), entryId);
}
// Add entry.
- entryId = deviceClient.addTableEntry(bmv2Entry);
- value = Pair.of(entryId, new TimestampedFlowRule(rule));
+ entryId = doAddEntry(deviceAgent, bmv2Entry);
+ frWrapper = new Bmv2FlowRuleWrapper(rule, entryId, new Date());
} else {
// Remove entry
- if (value == null) {
+ if (frWrapper == null) {
// Entry not found in map, how come?
- LOG.debug("Trying to remove entry, but entry ID not found: " + entryKey);
+ forceRemove(deviceAgent, entryRef.tableName(), entryRef.matchKey());
} else {
- deviceClient.deleteTableEntry(tableName, value.getKey());
- value = null;
+ long entryId = frWrapper.entryId();
+ doRemove(deviceAgent, entryRef.tableName(), entryId, entryRef.matchKey());
}
+ frWrapper = null;
}
// If here, no exceptions... things went well :)
processedFlowRules.add(rule);
} catch (Bmv2RuntimeException e) {
- LOG.warn("Unable to {} flow rule: {}", operation.name().toLowerCase(), e.toString());
+ log.warn("Unable to {} flow rule: {}", operation.name(), e.explain());
}
- return value;
+ // Update binding in table entry service.
+ if (frWrapper != null) {
+ tableEntryService.bindEntryReference(entryRef, frWrapper);
+ return true;
+ } else {
+ tableEntryService.unbindEntryReference(entryRef);
+ return null;
+ }
});
}
return processedFlowRules;
}
- /**
- * Gets the appropriate flow rule translator based on the device running configuration.
- *
- * @param deviceId a device id
- * @return a flow rule translator
- */
- private Bmv2FlowRuleTranslator getTranslator(DeviceId deviceId) {
-
- DeviceService deviceService = handler().get(DeviceService.class);
- if (deviceService == null) {
- LOG.error("Unable to get device service");
- return null;
- }
-
- Device device = deviceService.getDevice(deviceId);
- if (device == null) {
- LOG.error("Unable to get device {}", deviceId);
- return null;
- }
-
- String jsonString = device.annotations().value("bmv2JsonConfigValue");
- if (jsonString == null) {
- LOG.error("Unable to read bmv2 JSON config from device {}", deviceId);
- return null;
- }
-
- Bmv2Model model;
+ private long doAddEntry(Bmv2DeviceAgent agent, Bmv2TableEntry entry) throws Bmv2RuntimeException {
try {
- model = MODEL_CACHE.get(jsonString);
- } catch (ExecutionException e) {
- LOG.error("Unable to parse bmv2 JSON config for device {}:", deviceId, e.getCause());
- return null;
+ return agent.addTableEntry(entry);
+ } catch (Bmv2RuntimeException e) {
+ if (e.getCode() != TABLE_DUPLICATE_ENTRY) {
+ forceRemove(agent, entry.tableName(), entry.matchKey());
+ return agent.addTableEntry(entry);
+ } else {
+ throw e;
+ }
}
+ }
- // TODO: get translator config dynamically.
- // Now it's hardcoded, selection should be based on the device bmv2 model.
- Bmv2FlowRuleTranslator.TranslatorConfig translatorConfig = new Bmv2SimpleTranslatorConfig(model);
- return new Bmv2DefaultFlowRuleTranslator(translatorConfig);
+ private void doRemove(Bmv2DeviceAgent agent, String tableName, long entryId, Bmv2MatchKey matchKey)
+ throws Bmv2RuntimeException {
+ try {
+ agent.deleteTableEntry(tableName, entryId);
+ } catch (Bmv2RuntimeException e) {
+ if (e.getCode() == TABLE_INVALID_HANDLE || e.getCode() == TABLE_EXPIRED_HANDLE) {
+ // entry is not there with the declared ID, try with a forced remove.
+ forceRemove(agent, tableName, matchKey);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ private void forceRemove(Bmv2DeviceAgent agent, String tableName, Bmv2MatchKey matchKey)
+ throws Bmv2RuntimeException {
+ // Find the entryID (expensive call!)
+ for (Bmv2ParsedTableEntry pEntry : tableEntryService.getTableEntries(agent.deviceId(), tableName)) {
+ if (pEntry.matchKey().equals(matchKey)) {
+ // Remove entry and drop exceptions.
+ silentlyRemove(agent, tableName, pEntry.entryId());
+ break;
+ }
+ }
+ }
+
+ private void silentlyRemove(Bmv2DeviceAgent agent, String tableName, long entryId) {
+ try {
+ agent.deleteTableEntry(tableName, entryId);
+ } catch (Bmv2RuntimeException e) {
+ // do nothing
+ }
}
private enum Operation {
APPLY, REMOVE
}
-
- private class TimestampedFlowRule {
- private final FlowRule rule;
- private final Date addedDate;
-
- public TimestampedFlowRule(FlowRule rule) {
- this.rule = rule;
- this.addedDate = new Date();
- }
-
- public FlowRule rule() {
- return rule;
- }
-
- public long lifeInSeconds() {
- return (new Date().getTime() - addedDate.getTime()) / 1000;
- }
- }
}
\ No newline at end of file
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java
index 24add74..7131475 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java
@@ -17,56 +17,59 @@
package org.onosproject.drivers.bmv2;
import org.onlab.util.ImmutableByteSequence;
-import org.onosproject.bmv2.api.runtime.Bmv2Client;
+import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
-import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
+import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.net.DeviceId;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketProgrammable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.List;
+
import static java.lang.Math.toIntExact;
-import static org.onosproject.net.PortNumber.FLOOD;
+import static java.util.stream.Collectors.toList;
import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
+import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
/**
- * Packet programmable device behaviour implementation for BMv2.
+ * Implementation of the packet programmable behaviour for BMv2.
*/
public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable {
- private static final Logger LOG =
- LoggerFactory.getLogger(Bmv2PacketProgrammable.class);
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public void emit(OutboundPacket packet) {
TrafficTreatment treatment = packet.treatment();
- treatment.allInstructions().forEach(inst -> {
- if (inst.type().equals(OUTPUT)) {
- Instructions.OutputInstruction outInst = (Instructions.OutputInstruction) inst;
- if (outInst.port().isLogical()) {
- if (outInst.port() == FLOOD) {
- // TODO: implement flood
- LOG.info("Flood not implemented", outInst);
- }
- LOG.info("Output on logical port not supported: {}", outInst);
- } else {
- try {
- long longPort = outInst.port().toLong();
- int portNumber = toIntExact(longPort);
- send(portNumber, packet);
- } catch (ArithmeticException e) {
- LOG.error("Port number overflow! Cannot send packet on port {} (long), as the bmv2" +
- " device only accepts int port values.");
- }
- }
+ // BMv2 supports only OUTPUT instructions.
+ List<OutputInstruction> outInstructions = treatment.allInstructions()
+ .stream()
+ .filter(i -> i.type().equals(OUTPUT))
+ .map(i -> (OutputInstruction) i)
+ .collect(toList());
+
+ if (treatment.allInstructions().size() != outInstructions.size()) {
+ // There are other instructions that are not of type OUTPUT
+ log.warn("Dropping emit request, treatment nor supported: {}", treatment);
+ return;
+ }
+
+ outInstructions.forEach(outInst -> {
+ if (outInst.port().isLogical()) {
+ log.warn("Dropping emit request, logical port not supported: {}", outInst.port());
} else {
- LOG.info("Instruction type not supported: {}", inst.type().name());
+ try {
+ int portNumber = toIntExact(outInst.port().toLong());
+ send(portNumber, packet);
+ } catch (ArithmeticException e) {
+ log.error("Dropping emit request, port number too big: {}", outInst.port().toLong());
+ }
}
});
}
@@ -75,19 +78,25 @@
DeviceId deviceId = handler().data().deviceId();
- Bmv2Client deviceClient;
+ Bmv2Controller controller = handler().get(Bmv2Controller.class);
+ if (controller == null) {
+ log.error("Failed to get BMv2 controller");
+ return;
+ }
+
+ Bmv2DeviceAgent deviceAgent;
try {
- deviceClient = Bmv2ThriftClient.of(deviceId);
+ deviceAgent = controller.getAgent(deviceId);
} catch (Bmv2RuntimeException e) {
- LOG.error("Failed to connect to Bmv2 device", e);
+ log.error("Failed to get Bmv2 device agent for {}: {}", deviceId, e.explain());
return;
}
ImmutableByteSequence bs = ImmutableByteSequence.copyFrom(packet.data());
try {
- deviceClient.transmitPacket(port, bs);
+ deviceAgent.transmitPacket(port, bs);
} catch (Bmv2RuntimeException e) {
- LOG.info("Unable to push packet to device: deviceId={}, packet={}", deviceId, bs);
+ log.warn("Unable to emit packet trough {}: {}", deviceId, e.explain());
}
}
}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Pipeliner.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Pipeliner.java
index b8e4205..1ce30bd 100644
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Pipeliner.java
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Pipeliner.java
@@ -37,8 +37,8 @@
@Override
public void init(DeviceId deviceId, PipelinerContext context) {
- // TODO: get multi-table pipeliner dynamically based on BMv2 device running model
- // Right now we only support single table pipelines
+ // TODO: get multi-table pipeliner dynamically based on BMv2 device running model (hard).
+ // Right now we are able to map flow objectives only in the first table of the pipeline.
pipeliner = new DefaultSingleTablePipeline();
pipeliner.init(deviceId, context);
}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortDiscovery.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortDiscovery.java
deleted file mode 100644
index 0825883..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PortDiscovery.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2014-2016 Open Networking Laboratory
- *
- * 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.bmv2;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import org.onosproject.bmv2.api.runtime.Bmv2Client;
-import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
-import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.SparseAnnotations;
-import org.onosproject.net.behaviour.PortDiscovery;
-import org.onosproject.net.device.DefaultPortDescription;
-import org.onosproject.net.device.PortDescription;
-import org.onosproject.net.driver.AbstractHandlerBehaviour;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collections;
-import java.util.List;
-
-public class Bmv2PortDiscovery extends AbstractHandlerBehaviour
- implements PortDiscovery {
-
- private final Logger log =
- LoggerFactory.getLogger(this.getClass());
-
- @Override
- public List<PortDescription> getPorts() {
- Bmv2Client deviceClient;
- try {
- deviceClient = Bmv2ThriftClient.of(handler().data().deviceId());
- } catch (Bmv2RuntimeException e) {
- log.error("Failed to connect to Bmv2 device", e);
- return Collections.emptyList();
- }
-
- List<PortDescription> portDescriptions = Lists.newArrayList();
-
- try {
-
- deviceClient.getPortsInfo().forEach(
- p -> {
- DefaultAnnotations.Builder builder =
- DefaultAnnotations.builder();
- p.getExtraProperties().forEach(builder::set);
- SparseAnnotations annotations = builder.build();
-
- portDescriptions.add(new DefaultPortDescription(
- PortNumber.portNumber(
- (long) p.portNumber(),
- p.ifaceName()),
- p.isUp(),
- annotations
- ));
- });
- } catch (Bmv2RuntimeException e) {
- log.error("Unable to get port description from Bmv2 device", e);
- }
-
- return ImmutableList.copyOf(portDescriptions);
- }
-}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultFlowRuleTranslator.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultFlowRuleTranslator.java
deleted file mode 100644
index 3fa7673..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultFlowRuleTranslator.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.bmv2.translators;
-
-import com.google.common.annotations.Beta;
-import org.onlab.util.ImmutableByteSequence;
-import org.onosproject.bmv2.api.model.Bmv2ModelField;
-import org.onosproject.bmv2.api.model.Bmv2ModelTable;
-import org.onosproject.bmv2.api.model.Bmv2ModelTableKey;
-import org.onosproject.bmv2.api.runtime.Bmv2Action;
-import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
-import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
-import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
-import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
-import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
-import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.EthCriterion;
-import org.onosproject.net.flow.criteria.EthTypeCriterion;
-import org.onosproject.net.flow.criteria.ExtensionCriterion;
-import org.onosproject.net.flow.criteria.ExtensionSelector;
-import org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes;
-import org.onosproject.net.flow.criteria.PortCriterion;
-import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.Instructions.ExtensionInstructionWrapper;
-
-/**
- * Default Bmv2 flow rule translator implementation.
- * <p>
- * Flow rules are translated into {@link Bmv2TableEntry BMv2 table entries} according to the following logic:
- * <ul>
- * <li> table name: obtained from the Bmv2 model using the flow rule table ID;
- * <li> match key: if the flow rule selector defines only a criterion of type {@link Criterion.Type#EXTENSION EXTENSION}
- * , then the latter is expected to contain a {@link Bmv2ExtensionSelector Bmv2ExtensionSelector}, which should provide
- * a match key already formatted for the given table; otherwise a match key is built using the
- * {@link TranslatorConfig#fieldToCriterionTypeMap() mapping} defined by this translator configuration.
- * <li> action: if the flow rule treatment contains only one instruction of type
- * {@link Instruction.Type#EXTENSION EXTENSION}, then the latter is expected to contain a {@link Bmv2ExtensionTreatment}
- * , which should provide a {@link Bmv2Action} already formatted for the given table; otherwise, an action is
- * {@link TranslatorConfig#buildAction(TrafficTreatment) built} using this translator configuration.
- * <li> priority: the same as the flow rule.
- * <li> timeout: if the table supports timeout, use the same as the flow rule, otherwise none (i.e. permanent entry).
- * </ul>
- */
-@Beta
-public class Bmv2DefaultFlowRuleTranslator implements Bmv2FlowRuleTranslator {
-
- private final TranslatorConfig config;
-
- public Bmv2DefaultFlowRuleTranslator(TranslatorConfig config) {
- this.config = config;
- }
-
- private static Bmv2TernaryMatchParam buildTernaryParam(Bmv2ModelField field, Criterion criterion, int byteWidth)
- throws Bmv2FlowRuleTranslatorException {
-
- // Value and mask will be filled according to criterion type
- ImmutableByteSequence value;
- ImmutableByteSequence mask = null;
-
- switch (criterion.type()) {
- case IN_PORT:
- // FIXME: allow port numbers of variable bit length (based on model), truncating when necessary
- short port = (short) ((PortCriterion) criterion).port().toLong();
- value = ImmutableByteSequence.copyFrom(port);
- break;
- case ETH_DST:
- EthCriterion c = (EthCriterion) criterion;
- value = ImmutableByteSequence.copyFrom(c.mac().toBytes());
- if (c.mask() != null) {
- mask = ImmutableByteSequence.copyFrom(c.mask().toBytes());
- }
- break;
- case ETH_SRC:
- EthCriterion c2 = (EthCriterion) criterion;
- value = ImmutableByteSequence.copyFrom(c2.mac().toBytes());
- if (c2.mask() != null) {
- mask = ImmutableByteSequence.copyFrom(c2.mask().toBytes());
- }
- break;
- case ETH_TYPE:
- short ethType = ((EthTypeCriterion) criterion).ethType().toShort();
- value = ImmutableByteSequence.copyFrom(ethType);
- break;
- // TODO: implement building for other criterion types (easy with DefaultCriterion of ONOS-4034)
- default:
- throw new Bmv2FlowRuleTranslatorException("Feature not implemented, ternary builder for criterion" +
- "type: " + criterion.type().name());
- }
-
- if (mask == null) {
- // no mask, all ones
- mask = ImmutableByteSequence.ofOnes(byteWidth);
- }
-
- return new Bmv2TernaryMatchParam(value, mask);
- }
-
- private static Bmv2MatchKey getMatchKeyFromExtension(ExtensionCriterion criterion)
- throws Bmv2FlowRuleTranslatorException {
-
- ExtensionSelector extSelector = criterion.extensionSelector();
-
- if (extSelector.type() == ExtensionSelectorTypes.P4_BMV2_MATCH_KEY.type()) {
- if (extSelector instanceof Bmv2ExtensionSelector) {
- return ((Bmv2ExtensionSelector) extSelector).matchKey();
- } else {
- throw new Bmv2FlowRuleTranslatorException("Unable to decode extension selector " + extSelector);
- }
- } else {
- throw new Bmv2FlowRuleTranslatorException("Unsupported extension selector type " + extSelector.type());
- }
- }
-
- private static Bmv2Action getActionFromExtension(Instructions.ExtensionInstructionWrapper inst)
- throws Bmv2FlowRuleTranslatorException {
-
- ExtensionTreatment extTreatment = inst.extensionInstruction();
-
- if (extTreatment.type() == ExtensionTreatmentTypes.P4_BMV2_ACTION.type()) {
- if (extTreatment instanceof Bmv2ExtensionTreatment) {
- return ((Bmv2ExtensionTreatment) extTreatment).getAction();
- } else {
- throw new Bmv2FlowRuleTranslatorException("Unable to decode treatment extension: " + extTreatment);
- }
- } else {
- throw new Bmv2FlowRuleTranslatorException("Unsupported treatment extension type: " + extTreatment.type());
- }
- }
-
- private static Bmv2MatchKey buildMatchKey(TranslatorConfig config, TrafficSelector selector, Bmv2ModelTable table)
- throws Bmv2FlowRuleTranslatorException {
-
- Bmv2MatchKey.Builder matchKeyBuilder = Bmv2MatchKey.builder();
-
- for (Bmv2ModelTableKey key : table.keys()) {
-
- String fieldName = key.field().header().name() + "." + key.field().type().name();
- int byteWidth = (int) Math.ceil((double) key.field().type().bitWidth() / 8.0);
- Criterion.Type criterionType = config.fieldToCriterionTypeMap().get(fieldName);
-
- if (criterionType == null || selector.getCriterion(criterionType) == null) {
- // A mapping is not available or the selector doesn't have such a type
- switch (key.matchType()) {
- case TERNARY:
- // Wildcard field
- matchKeyBuilder.withWildcard(byteWidth);
- break;
- case LPM:
- // LPM with prefix 0
- matchKeyBuilder.add(new Bmv2LpmMatchParam(ImmutableByteSequence.ofZeros(byteWidth), 0));
- break;
- default:
- throw new Bmv2FlowRuleTranslatorException("Match field not supported: " + fieldName);
- }
- // Next key
- continue;
- }
-
- Criterion criterion = selector.getCriterion(criterionType);
- Bmv2TernaryMatchParam matchParam = null;
-
- switch (key.matchType()) {
- case TERNARY:
- matchParam = buildTernaryParam(key.field(), criterion, byteWidth);
- break;
- default:
- // TODO: implement other match param builders (exact, LPM, etc.)
- throw new Bmv2FlowRuleTranslatorException("Feature not implemented, match param builder: "
- + key.matchType().name());
- }
-
- matchKeyBuilder.add(matchParam);
- }
-
- return matchKeyBuilder.build();
- }
-
- @Override
- public Bmv2TableEntry translate(FlowRule rule)
- throws Bmv2FlowRuleTranslatorException {
-
- int tableId = rule.tableId();
-
- Bmv2ModelTable table = config.model().table(tableId);
-
- if (table == null) {
- throw new Bmv2FlowRuleTranslatorException("Unknown table ID: " + tableId);
- }
-
- /* Translate selector */
-
- TrafficSelector selector = rule.selector();
- Bmv2MatchKey bmv2MatchKey = null;
-
- // If selector has only 1 criterion of type extension, use that
- Criterion criterion = selector.getCriterion(Criterion.Type.EXTENSION);
- if (criterion != null) {
- if (selector.criteria().size() == 1) {
- bmv2MatchKey = getMatchKeyFromExtension((ExtensionCriterion) criterion);
- } else {
- throw new Bmv2FlowRuleTranslatorException("Unable to translate traffic selector, found multiple " +
- "criteria of which one is an extension: " +
- selector.toString());
- }
- }
-
- if (bmv2MatchKey == null) {
- // not an extension
- bmv2MatchKey = buildMatchKey(config, selector, table);
- }
-
- /* Translate treatment */
-
- TrafficTreatment treatment = rule.treatment();
- Bmv2Action bmv2Action = null;
-
- // If treatment has only 1 instruction of type extension, use that
- for (Instruction inst : treatment.allInstructions()) {
- if (inst.type() == Instruction.Type.EXTENSION) {
- if (treatment.allInstructions().size() == 1) {
- bmv2Action = getActionFromExtension((ExtensionInstructionWrapper) inst);
- } else {
- throw new Bmv2FlowRuleTranslatorException("Unable to translate traffic treatment, found multiple " +
- "instructions of which one is an extension: " +
- selector.toString());
- }
- }
- }
-
- if (bmv2Action == null) {
- // No extension, use config to build action
- bmv2Action = config.buildAction(treatment);
- }
-
- if (bmv2Action == null) {
- // Config returned null
- throw new Bmv2FlowRuleTranslatorException("Unable to translate treatment: " + treatment);
- }
-
- Bmv2TableEntry.Builder tableEntryBuilder = Bmv2TableEntry.builder();
-
- // In BMv2 0 is the highest priority, i.e. the opposite than ONOS.
- int newPriority = Integer.MAX_VALUE - rule.priority();
-
- tableEntryBuilder
- .withTableName(table.name())
- .withPriority(newPriority)
- .withMatchKey(bmv2MatchKey)
- .withAction(bmv2Action);
-
- if (!rule.isPermanent()) {
- if (table.hasTimeouts()) {
- tableEntryBuilder.withTimeout((double) rule.timeout());
- }
- //FIXME: add warn log or exception?
- }
-
- return tableEntryBuilder.build();
- }
-
- @Override
- public TranslatorConfig config() {
- return this.config;
- }
-}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultTranslatorConfig.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultTranslatorConfig.java
deleted file mode 100644
index dc7bdcd..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2DefaultTranslatorConfig.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.bmv2.translators;
-
-import com.google.common.annotations.Beta;
-import org.onosproject.bmv2.api.model.Bmv2Model;
-import org.onosproject.net.flow.criteria.Criterion;
-
-import java.util.Map;
-
-import static org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator.TranslatorConfig;
-
-/**
- * Default implementation of a BMv2 flow rule translator configuration.
- */
-@Beta
-public abstract class Bmv2DefaultTranslatorConfig implements TranslatorConfig {
-
- private final Bmv2Model model;
- private final Map<String, Criterion.Type> fieldMap;
-
- /**
- * Creates a new translator configuration.
- *
- * @param model a BMv2 packet processing model
- * @param fieldMap a field-to-criterion type map
- */
- protected Bmv2DefaultTranslatorConfig(Bmv2Model model, Map<String, Criterion.Type> fieldMap) {
- this.model = model;
- this.fieldMap = fieldMap;
- }
-
- @Override
- public Bmv2Model model() {
- return this.model;
- }
-
- @Override
- public Map<String, Criterion.Type> fieldToCriterionTypeMap() {
- return this.fieldMap;
- }
-}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2FlowRuleTranslator.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2FlowRuleTranslator.java
deleted file mode 100644
index e81da0b..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2FlowRuleTranslator.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.bmv2.translators;
-
-import com.google.common.annotations.Beta;
-import org.onosproject.bmv2.api.model.Bmv2Model;
-import org.onosproject.bmv2.api.runtime.Bmv2Action;
-import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-
-import java.util.Map;
-
-/**
- * Translator of ONOS flow rules to BMv2 table entries. Translation depends on a
- * {@link TranslatorConfig translator configuration}.
- */
-@Beta
-public interface Bmv2FlowRuleTranslator {
-
- /**
- * Returns a new BMv2 table entry equivalent to the given flow rule.
- *
- * @param rule a flow rule
- * @return a BMv2 table entry
- * @throws Bmv2FlowRuleTranslatorException if the flow rule cannot be
- * translated
- */
- Bmv2TableEntry translate(FlowRule rule) throws Bmv2FlowRuleTranslatorException;
-
- /**
- * Returns the configuration of this translator.
- *
- * @return a translator configuration
- */
- TranslatorConfig config();
-
- /**
- * BMv2 flow rules translator configuration. Such a configuration is used to
- * generate table entries that are compatible with a given {@link Bmv2Model}.
- */
- @Beta
- interface TranslatorConfig {
- /**
- * Return the {@link Bmv2Model} associated with this configuration.
- *
- * @return a BMv2 model
- */
- Bmv2Model model();
-
- /**
- * Returns a map describing a one-to-one relationship between BMv2
- * header field names and ONOS criterion types. Header field names are
- * formatted using the notation {@code header_name.field_name}
- * representing a specific header field instance extracted by the BMv2
- * parser (e.g. {@code ethernet.dstAddr}).
- *
- * @return a map where the keys represent BMv2 header field names and
- * values are criterion types
- */
- Map<String, Criterion.Type> fieldToCriterionTypeMap();
-
- /**
- * Return a BMv2 action that is equivalent to the given ONOS traffic
- * treatment.
- *
- * @param treatment a traffic treatment
- * @return a BMv2 action object
- * @throws Bmv2FlowRuleTranslatorException if the treatment cannot be
- * translated to a BMv2 action
- */
- Bmv2Action buildAction(TrafficTreatment treatment)
- throws Bmv2FlowRuleTranslatorException;
- }
-}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2FlowRuleTranslatorException.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2FlowRuleTranslatorException.java
deleted file mode 100644
index acc9377..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2FlowRuleTranslatorException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.bmv2.translators;
-
-/**
- * BMv2 flow rule translator exception.
- */
-public class Bmv2FlowRuleTranslatorException extends Exception {
-
- Bmv2FlowRuleTranslatorException(String msg) {
- super(msg);
- }
-}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimpleTranslatorConfig.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimpleTranslatorConfig.java
deleted file mode 100644
index 4a105a3..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/Bmv2SimpleTranslatorConfig.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.bmv2.translators;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableMap;
-import org.onlab.util.ImmutableByteSequence;
-import org.onosproject.bmv2.api.model.Bmv2Model;
-import org.onosproject.bmv2.api.runtime.Bmv2Action;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-
-import java.util.Map;
-
-/**
- * Implementation of a Bmv2 flow rule translator configuration for the
- * simple.p4 model.
- */
-@Beta
-public class Bmv2SimpleTranslatorConfig extends Bmv2DefaultTranslatorConfig {
-
- // Lazily populate field map.
- private static final Map<String, Criterion.Type> FIELD_MAP = ImmutableMap.of(
- "standard_metadata.ingress_port", Criterion.Type.IN_PORT,
- "ethernet.dstAddr", Criterion.Type.ETH_DST,
- "ethernet.srcAddr", Criterion.Type.ETH_SRC,
- "ethernet.etherType", Criterion.Type.ETH_TYPE);
-
- /**
- * Creates a new simple pipeline translator configuration.
- */
- public Bmv2SimpleTranslatorConfig(Bmv2Model model) {
- // Populate fieldMap.
- super(model, FIELD_MAP);
- }
-
- private static Bmv2Action buildDropAction() {
- return Bmv2Action.builder()
- .withName("_drop")
- .build();
- }
-
- private static Bmv2Action buildPushToCpAction() {
- return Bmv2Action.builder()
- .withName("send_to_cpu")
- .build();
- }
-
- private static Bmv2Action buildFwdAction(Instructions.OutputInstruction inst)
- throws Bmv2FlowRuleTranslatorException {
-
- Bmv2Action.Builder actionBuilder = Bmv2Action.builder();
-
- actionBuilder.withName("fwd");
-
- if (inst.port().isLogical()) {
- if (inst.port() == PortNumber.CONTROLLER) {
- return buildPushToCpAction();
- } else {
- throw new Bmv2FlowRuleTranslatorException(
- "Output logic port number not supported: " + inst);
- }
- }
-
- actionBuilder.addParameter(
- ImmutableByteSequence.copyFrom((short) inst.port().toLong()));
-
- return actionBuilder.build();
- }
-
- @Override
- public Bmv2Action buildAction(TrafficTreatment treatment)
- throws Bmv2FlowRuleTranslatorException {
-
-
- if (treatment.allInstructions().size() == 0) {
- // No instructions means drop.
- return buildDropAction();
- } else if (treatment.allInstructions().size() > 1) {
- // Otherwise, we understand treatments with only 1 instruction.
- throw new Bmv2FlowRuleTranslatorException(
- "Treatment not supported, more than 1 instructions found: "
- + treatment.toString());
- }
-
- Instruction instruction = treatment.allInstructions().get(0);
-
- switch (instruction.type()) {
- case OUTPUT:
- return buildFwdAction((Instructions.OutputInstruction) instruction);
- case NOACTION:
- return buildDropAction();
- default:
- throw new Bmv2FlowRuleTranslatorException(
- "Instruction type not supported: "
- + instruction.type().name());
- }
- }
-}
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/package-info.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/package-info.java
deleted file mode 100644
index 97963f0..0000000
--- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/translators/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.
- */
-
-/**
- * Translators of ONOS abstractions to BMv2 model-dependent abstractions.
- */
-package org.onosproject.drivers.bmv2.translators;
\ No newline at end of file
diff --git a/drivers/bmv2/src/main/resources/bmv2-drivers.xml b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
index bf1baac..e8a7cb8 100644
--- a/drivers/bmv2/src/main/resources/bmv2-drivers.xml
+++ b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
@@ -16,14 +16,18 @@
~ limitations under the License.
-->
<drivers>
- <driver name="bmv2-thrift" manufacturer="p4.org" hwVersion="bmv2" swVersion="unknown">
- <behaviour api="org.onosproject.net.behaviour.PortDiscovery"
- impl="org.onosproject.drivers.bmv2.Bmv2PortDiscovery"/>
+ <driver name="bmv2-thrift" manufacturer="p4.org" hwVersion="bmv2" swVersion="n/a">
+ <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+ impl="org.onosproject.drivers.bmv2.Bmv2DeviceDescriptionDiscovery"/>
<behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
impl="org.onosproject.drivers.bmv2.Bmv2FlowRuleProgrammable"/>
<behaviour api="org.onosproject.net.behaviour.Pipeliner"
impl="org.onosproject.drivers.bmv2.Bmv2Pipeliner"/>
<behaviour api="org.onosproject.net.packet.PacketProgrammable"
impl="org.onosproject.drivers.bmv2.Bmv2PacketProgrammable"/>
+ <behaviour api="org.onosproject.net.behaviour.ExtensionSelectorResolver"
+ impl="org.onosproject.drivers.bmv2.Bmv2ExtensionSelectorResolver"/>
+ <behaviour api="org.onosproject.net.behaviour.ExtensionTreatmentResolver"
+ impl="org.onosproject.drivers.bmv2.Bmv2ExtensionTreatmentResolver"/>
</driver>
</drivers>
diff --git a/drivers/bmv2/src/test/java/org/onosproject/drivers/bmv2/Bmv2DefaultFlowRuleTranslatorTest.java b/drivers/bmv2/src/test/java/org/onosproject/drivers/bmv2/Bmv2DefaultFlowRuleTranslatorTest.java
deleted file mode 100644
index c602eea..0000000
--- a/drivers/bmv2/src/test/java/org/onosproject/drivers/bmv2/Bmv2DefaultFlowRuleTranslatorTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright 2014-2016 Open Networking Laboratory
- *
- * 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.bmv2;
-
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonObject;
-import com.google.common.testing.EqualsTester;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.MacAddress;
-import org.onosproject.bmv2.api.model.Bmv2Model;
-import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
-import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
-import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
-import org.onosproject.drivers.bmv2.translators.Bmv2SimpleTranslatorConfig;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.Random;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-/**
- * Tests for {@link Bmv2DefaultFlowRuleTranslator}.
- */
-public class Bmv2DefaultFlowRuleTranslatorTest {
-
- private static final String JSON_CONFIG_PATH = "/simple.json";
- private Random random = new Random();
- private Bmv2Model model;
- private Bmv2FlowRuleTranslator.TranslatorConfig config;
- private Bmv2FlowRuleTranslator translator;
-
- @Before
- public void setUp() throws Exception {
- InputStream inputStream = Bmv2SimpleTranslatorConfig.class
- .getResourceAsStream(JSON_CONFIG_PATH);
- InputStreamReader reader = new InputStreamReader(inputStream);
- BufferedReader bufReader = new BufferedReader(reader);
- JsonObject json = null;
- try {
- json = Json.parse(bufReader).asObject();
- } catch (IOException e) {
- throw new RuntimeException("Unable to parse JSON file: " + e.getMessage());
- }
-
- this.model = Bmv2Model.parse(json);
- this.config = new Bmv2SimpleTranslatorConfig(model);
- this.translator = new Bmv2DefaultFlowRuleTranslator(config);
-
- }
-
-
- @Test
- public void testCompiler() throws Exception {
-
- DeviceId deviceId = DeviceId.NONE;
- ApplicationId appId = new DefaultApplicationId(1, "test");
- int tableId = 0;
- MacAddress ethDstMac = MacAddress.valueOf(random.nextLong());
- MacAddress ethSrcMac = MacAddress.valueOf(random.nextLong());
- short ethType = (short) (0x0000FFFF & random.nextInt());
- short outPort = (short) random.nextInt(65);
- short inPort = (short) random.nextInt(65);
- int timeout = random.nextInt(100);
- int priority = random.nextInt(100);
-
- TrafficSelector matchInPort1 = DefaultTrafficSelector
- .builder()
- .matchInPort(PortNumber.portNumber(inPort))
- .matchEthDst(ethDstMac)
- .matchEthSrc(ethSrcMac)
- .matchEthType(ethType)
- .build();
-
- TrafficTreatment outPort2 = DefaultTrafficTreatment
- .builder()
- .setOutput(PortNumber.portNumber(outPort))
- .build();
-
- FlowRule rule1 = DefaultFlowRule.builder()
- .forDevice(deviceId)
- .forTable(tableId)
- .fromApp(appId)
- .withSelector(matchInPort1)
- .withTreatment(outPort2)
- .makeTemporary(timeout)
- .withPriority(priority)
- .build();
-
- FlowRule rule2 = DefaultFlowRule.builder()
- .forDevice(deviceId)
- .forTable(tableId)
- .fromApp(appId)
- .withSelector(matchInPort1)
- .withTreatment(outPort2)
- .makeTemporary(timeout)
- .withPriority(priority)
- .build();
-
- Bmv2TableEntry entry1 = translator.translate(rule1);
- Bmv2TableEntry entry2 = translator.translate(rule1);
-
- // check equality, i.e. same rules must produce same entries
- new EqualsTester()
- .addEqualityGroup(rule1, rule2)
- .addEqualityGroup(entry1, entry2)
- .testEquals();
-
- int numMatchParams = model.table(0).keys().size();
- // parse values stored in entry1
- Bmv2TernaryMatchParam inPortParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(0);
- Bmv2TernaryMatchParam ethDstParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(1);
- Bmv2TernaryMatchParam ethSrcParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(2);
- Bmv2TernaryMatchParam ethTypeParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(3);
- double expectedTimeout = (double) (model.table(0).hasTimeouts() ? rule1.timeout() : -1);
-
- // check that the number of parameters in the entry is the same as the number of table keys
- assertThat("Incorrect number of match parameters",
- entry1.matchKey().matchParams().size(), is(equalTo(numMatchParams)));
-
- // check that values stored in entry are the same used for the flow rule
- assertThat("Incorrect inPort match param value",
- inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
- assertThat("Incorrect ethDestMac match param value",
- ethDstParam.value().asArray(), is(equalTo(ethDstMac.toBytes())));
- assertThat("Incorrect ethSrcMac match param value",
- ethSrcParam.value().asArray(), is(equalTo(ethSrcMac.toBytes())));
- assertThat("Incorrect ethType match param value",
- ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
- assertThat("Incorrect priority value",
- entry1.priority(), is(equalTo(Integer.MAX_VALUE - rule1.priority())));
- assertThat("Incorrect timeout value",
- entry1.timeout(), is(equalTo(expectedTimeout)));
-
- }
-}
\ No newline at end of file
diff --git a/drivers/bmv2/src/test/resources/simple.json b/drivers/bmv2/src/test/resources/simple.json
deleted file mode 100644
index 7ff6446..0000000
--- a/drivers/bmv2/src/test/resources/simple.json
+++ /dev/null
@@ -1,397 +0,0 @@
-{
- "header_types": [
- {
- "name": "standard_metadata_t",
- "id": 0,
- "fields": [
- [
- "ingress_port",
- 9
- ],
- [
- "packet_length",
- 32
- ],
- [
- "egress_spec",
- 9
- ],
- [
- "egress_port",
- 9
- ],
- [
- "egress_instance",
- 32
- ],
- [
- "instance_type",
- 32
- ],
- [
- "clone_spec",
- 32
- ],
- [
- "_padding",
- 5
- ]
- ],
- "length_exp": null,
- "max_length": null
- },
- {
- "name": "ethernet_t",
- "id": 1,
- "fields": [
- [
- "dstAddr",
- 48
- ],
- [
- "srcAddr",
- 48
- ],
- [
- "etherType",
- 16
- ]
- ],
- "length_exp": null,
- "max_length": null
- },
- {
- "name": "intrinsic_metadata_t",
- "id": 2,
- "fields": [
- [
- "ingress_global_timestamp",
- 32
- ],
- [
- "lf_field_list",
- 32
- ],
- [
- "mcast_grp",
- 16
- ],
- [
- "egress_rid",
- 16
- ]
- ],
- "length_exp": null,
- "max_length": null
- }
- ],
- "headers": [
- {
- "name": "standard_metadata",
- "id": 0,
- "header_type": "standard_metadata_t",
- "metadata": true
- },
- {
- "name": "ethernet",
- "id": 1,
- "header_type": "ethernet_t",
- "metadata": false
- },
- {
- "name": "intrinsic_metadata",
- "id": 2,
- "header_type": "intrinsic_metadata_t",
- "metadata": true
- }
- ],
- "header_stacks": [],
- "parsers": [
- {
- "name": "parser",
- "id": 0,
- "init_state": "start",
- "parse_states": [
- {
- "name": "start",
- "id": 0,
- "parser_ops": [],
- "transition_key": [],
- "transitions": [
- {
- "value": "default",
- "mask": null,
- "next_state": "parse_ethernet"
- }
- ]
- },
- {
- "name": "parse_ethernet",
- "id": 1,
- "parser_ops": [
- {
- "op": "extract",
- "parameters": [
- {
- "type": "regular",
- "value": "ethernet"
- }
- ]
- }
- ],
- "transition_key": [],
- "transitions": [
- {
- "value": "default",
- "mask": null,
- "next_state": null
- }
- ]
- }
- ]
- }
- ],
- "deparsers": [
- {
- "name": "deparser",
- "id": 0,
- "order": [
- "ethernet"
- ]
- }
- ],
- "meter_arrays": [],
- "actions": [
- {
- "name": "flood",
- "id": 0,
- "runtime_data": [],
- "primitives": [
- {
- "op": "modify_field",
- "parameters": [
- {
- "type": "field",
- "value": [
- "intrinsic_metadata",
- "mcast_grp"
- ]
- },
- {
- "type": "field",
- "value": [
- "standard_metadata",
- "ingress_port"
- ]
- }
- ]
- }
- ]
- },
- {
- "name": "_drop",
- "id": 1,
- "runtime_data": [],
- "primitives": [
- {
- "op": "modify_field",
- "parameters": [
- {
- "type": "field",
- "value": [
- "standard_metadata",
- "egress_spec"
- ]
- },
- {
- "type": "hexstr",
- "value": "0x1ff"
- }
- ]
- }
- ]
- },
- {
- "name": "fwd",
- "id": 2,
- "runtime_data": [
- {
- "name": "port",
- "bitwidth": 9
- }
- ],
- "primitives": [
- {
- "op": "modify_field",
- "parameters": [
- {
- "type": "field",
- "value": [
- "standard_metadata",
- "egress_spec"
- ]
- },
- {
- "type": "runtime_data",
- "value": 0
- }
- ]
- }
- ]
- },
- {
- "name": "push_to_cp",
- "id": 3,
- "runtime_data": [],
- "primitives": [
- {
- "op": "modify_field",
- "parameters": [
- {
- "type": "field",
- "value": [
- "standard_metadata",
- "egress_spec"
- ]
- },
- {
- "type": "hexstr",
- "value": "0x200"
- }
- ]
- }
- ]
- }
- ],
- "pipelines": [
- {
- "name": "ingress",
- "id": 0,
- "init_table": "table0",
- "tables": [
- {
- "name": "table0",
- "id": 0,
- "match_type": "ternary",
- "type": "simple",
- "max_size": 16384,
- "with_counters": false,
- "direct_meters": null,
- "support_timeout": false,
- "key": [
- {
- "match_type": "ternary",
- "target": [
- "standard_metadata",
- "ingress_port"
- ],
- "mask": null
- },
- {
- "match_type": "ternary",
- "target": [
- "ethernet",
- "dstAddr"
- ],
- "mask": null
- },
- {
- "match_type": "ternary",
- "target": [
- "ethernet",
- "srcAddr"
- ],
- "mask": null
- },
- {
- "match_type": "ternary",
- "target": [
- "ethernet",
- "etherType"
- ],
- "mask": null
- }
- ],
- "actions": [
- "fwd",
- "flood",
- "push_to_cp",
- "_drop"
- ],
- "next_tables": {
- "fwd": null,
- "flood": null,
- "push_to_cp": null,
- "_drop": null
- },
- "default_action": null,
- "base_default_next": null
- }
- ],
- "conditionals": []
- },
- {
- "name": "egress",
- "id": 1,
- "init_table": null,
- "tables": [],
- "conditionals": []
- }
- ],
- "calculations": [],
- "checksums": [],
- "learn_lists": [],
- "field_lists": [],
- "counter_arrays": [],
- "register_arrays": [],
- "force_arith": [
- [
- "standard_metadata",
- "ingress_port"
- ],
- [
- "standard_metadata",
- "packet_length"
- ],
- [
- "standard_metadata",
- "egress_spec"
- ],
- [
- "standard_metadata",
- "egress_port"
- ],
- [
- "standard_metadata",
- "egress_instance"
- ],
- [
- "standard_metadata",
- "instance_type"
- ],
- [
- "standard_metadata",
- "clone_spec"
- ],
- [
- "standard_metadata",
- "_padding"
- ],
- [
- "intrinsic_metadata",
- "ingress_global_timestamp"
- ],
- [
- "intrinsic_metadata",
- "lf_field_list"
- ],
- [
- "intrinsic_metadata",
- "mcast_grp"
- ],
- [
- "intrinsic_metadata",
- "egress_rid"
- ]
- ]
-}
\ No newline at end of file
diff --git a/drivers/pom.xml b/drivers/pom.xml
index 26e2ff7..99db6e1 100644
--- a/drivers/pom.xml
+++ b/drivers/pom.xml
@@ -41,7 +41,7 @@
<module>utilities</module>
<module>lumentum</module>
<module>bti</module>
- <!--<module>bmv2</module>-->
+ <module>bmv2</module>
<module>corsa</module>
<module>optical</module>
</modules>
diff --git a/protocols/bmv2/api/pom.xml b/protocols/bmv2/api/pom.xml
index 13ad693..6b029ac 100644
--- a/protocols/bmv2/api/pom.xml
+++ b/protocols/bmv2/api/pom.xml
@@ -21,7 +21,8 @@
<parent>
<artifactId>onos-bmv2-protocol</artifactId>
<groupId>org.onosproject</groupId>
- <version>1.6.0-SNAPSHOT</version>
+ <version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/protocols/bmv2/ctl/pom.xml b/protocols/bmv2/ctl/pom.xml
index 5e8ae37..f92d4b6 100644
--- a/protocols/bmv2/ctl/pom.xml
+++ b/protocols/bmv2/ctl/pom.xml
@@ -21,7 +21,8 @@
<parent>
<artifactId>onos-bmv2-protocol</artifactId>
<groupId>org.onosproject</groupId>
- <version>1.6.0-SNAPSHOT</version>
+ <version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/protocols/bmv2/pom.xml b/protocols/bmv2/pom.xml
index ffa8623..e972c1d 100644
--- a/protocols/bmv2/pom.xml
+++ b/protocols/bmv2/pom.xml
@@ -22,6 +22,7 @@
<artifactId>onos-protocols</artifactId>
<groupId>org.onosproject</groupId>
<version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/protocols/bmv2/thrift-api/pom.xml b/protocols/bmv2/thrift-api/pom.xml
index 655b84b..054aa02 100644
--- a/protocols/bmv2/thrift-api/pom.xml
+++ b/protocols/bmv2/thrift-api/pom.xml
@@ -21,7 +21,8 @@
<parent>
<artifactId>onos-bmv2-protocol</artifactId>
<groupId>org.onosproject</groupId>
- <version>1.6.0-SNAPSHOT</version>
+ <version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/providers/bmv2/app/features.xml b/providers/bmv2/app/features.xml
index f13d07c..2c5b9a1 100644
--- a/providers/bmv2/app/features.xml
+++ b/providers/bmv2/app/features.xml
@@ -22,7 +22,9 @@
<bundle>mvn:${project.groupId}/onos-bmv2-provider-device/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-provider-packet/${project.version}</bundle>
<bundle>mvn:org.apache.thrift/libthrift/0.9.3</bundle>
- <bundle>mvn:${project.groupId}/onos-bmv2-protocol/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-bmv2-protocol-api/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-bmv2-protocol-ctl/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-bmv2-protocol-thrift-api/${project.version}</bundle>
</feature>
</features>
diff --git a/providers/bmv2/app/pom.xml b/providers/bmv2/app/pom.xml
index 382b27e..f372476 100644
--- a/providers/bmv2/app/pom.xml
+++ b/providers/bmv2/app/pom.xml
@@ -22,6 +22,7 @@
<artifactId>onos-bmv2-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -33,7 +34,7 @@
<properties>
<onos.app.name>org.onosproject.bmv2</onos.app.name>
- <onos.app.title>BMv2 Provider</onos.app.title>
+ <onos.app.title>BMv2 Providers</onos.app.title>
<onos.app.category>Provider</onos.app.category>
</properties>
diff --git a/providers/bmv2/device/pom.xml b/providers/bmv2/device/pom.xml
index cc0b8d0..95227dd 100644
--- a/providers/bmv2/device/pom.xml
+++ b/providers/bmv2/device/pom.xml
@@ -22,6 +22,7 @@
<artifactId>onos-bmv2-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -33,12 +34,12 @@
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-drivers-bmv2</artifactId>
+ <artifactId>onos-core-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-core-common</artifactId>
+ <artifactId>onos-bmv2-protocol-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
diff --git a/providers/bmv2/device/src/main/java/org/onosproject/provider/bmv2/device/impl/Bmv2DeviceProvider.java b/providers/bmv2/device/src/main/java/org/onosproject/provider/bmv2/device/impl/Bmv2DeviceProvider.java
index 69d9a43..30fcb99 100644
--- a/providers/bmv2/device/src/main/java/org/onosproject/provider/bmv2/device/impl/Bmv2DeviceProvider.java
+++ b/providers/bmv2/device/src/main/java/org/onosproject/provider/bmv2/device/impl/Bmv2DeviceProvider.java
@@ -23,36 +23,38 @@
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
-import org.onlab.packet.ChassisId;
-import org.onlab.util.HexString;
import org.onlab.util.Timer;
-import org.onosproject.bmv2.api.runtime.Bmv2ControlPlaneServer;
import org.onosproject.bmv2.api.runtime.Bmv2Device;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
-import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
+import org.onosproject.bmv2.api.service.Bmv2Controller;
+import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
+import org.onosproject.bmv2.api.service.Bmv2DeviceListener;
import org.onosproject.common.net.AbstractDeviceProvider;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.config.basics.ConfigException;
-import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.PortDiscovery;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.net.driver.Driver;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
+import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -60,9 +62,9 @@
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.bmv2.api.runtime.Bmv2Device.*;
-import static org.onosproject.bmv2.ctl.Bmv2ThriftClient.forceDisconnectOf;
-import static org.onosproject.bmv2.ctl.Bmv2ThriftClient.ping;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
+import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.getPortStatistics;
+import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.initCounters;
import static org.slf4j.LoggerFactory.getLogger;
/**
@@ -71,11 +73,24 @@
@Component(immediate = true)
public class Bmv2DeviceProvider extends AbstractDeviceProvider {
- private static final Logger LOG = getLogger(Bmv2DeviceProvider.class);
-
private static final String APP_NAME = "org.onosproject.bmv2";
- private static final String UNKNOWN = "unknown";
- private static final int POLL_INTERVAL = 5; // seconds
+
+ private static final int POLL_INTERVAL = 5_000; // milliseconds
+
+ private final Logger log = getLogger(this.getClass());
+
+ private final ExecutorService executorService = Executors
+ .newFixedThreadPool(16, groupedThreads("onos/bmv2", "device-discovery", log));
+
+ private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
+
+ private final ConfigFactory cfgFactory = new InternalConfigFactory();
+
+ private final ConcurrentMap<DeviceId, DeviceDescription> activeDevices = Maps.newConcurrentMap();
+
+ private final DevicePoller devicePoller = new DevicePoller();
+
+ private final InternalDeviceListener deviceListener = new InternalDeviceListener();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry netCfgService;
@@ -87,16 +102,11 @@
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected Bmv2ControlPlaneServer controlPlaneServer;
+ protected Bmv2Controller controller;
- private final ExecutorService deviceDiscoveryExecutor = Executors
- .newFixedThreadPool(5, groupedThreads("onos/bmv2", "device-discovery", LOG));
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected Bmv2DeviceContextService contextService;
- private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
- private final ConfigFactory cfgFactory = new InternalConfigFactory();
- private final ConcurrentMap<DeviceId, Boolean> activeDevices = Maps.newConcurrentMap();
- private final DevicePoller devicePoller = new DevicePoller();
- private final InternalHelloListener helloListener = new InternalHelloListener();
private ApplicationId appId;
/**
@@ -111,7 +121,7 @@
appId = coreService.registerApplication(APP_NAME);
netCfgService.registerConfigFactory(cfgFactory);
netCfgService.addListener(cfgListener);
- controlPlaneServer.addHelloListener(helloListener);
+ controller.addDeviceListener(deviceListener);
devicePoller.start();
super.activate();
}
@@ -119,16 +129,16 @@
@Override
protected void deactivate() {
devicePoller.stop();
- controlPlaneServer.removeHelloListener(helloListener);
+ controller.removeDeviceListener(deviceListener);
try {
activeDevices.forEach((did, value) -> {
- deviceDiscoveryExecutor.execute(() -> disconnectDevice(did));
+ executorService.execute(() -> disconnectDevice(did));
});
- deviceDiscoveryExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
+ executorService.awaitTermination(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
- LOG.error("Device discovery threads did not terminate");
+ log.error("Device discovery threads did not terminate");
}
- deviceDiscoveryExecutor.shutdownNow();
+ executorService.shutdownNow();
netCfgService.unregisterConfigFactory(cfgFactory);
netCfgService.removeListener(cfgListener);
super.deactivate();
@@ -137,14 +147,12 @@
@Override
public void triggerProbe(DeviceId deviceId) {
// Asynchronously trigger probe task.
- deviceDiscoveryExecutor.execute(() -> executeProbe(deviceId));
+ executorService.execute(() -> executeProbe(deviceId));
}
private void executeProbe(DeviceId did) {
boolean reachable = isReachable(did);
- LOG.debug("Probed device: id={}, reachable={}",
- did.toString(),
- reachable);
+ log.debug("Probed device: id={}, reachable={}", did.toString(), reachable);
if (reachable) {
discoverDevice(did);
} else {
@@ -154,85 +162,108 @@
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
- LOG.debug("roleChanged() is not yet implemented");
+ log.debug("roleChanged() is not yet implemented");
// TODO: implement mastership handling
}
@Override
public boolean isReachable(DeviceId deviceId) {
- return ping(deviceId);
+ return controller.isReacheable(deviceId);
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
- LOG.debug("changePortState() is not yet implemented");
- // TODO: implement port handling
+ log.warn("changePortState() not supported");
}
private void discoverDevice(DeviceId did) {
- LOG.debug("Starting device discovery... deviceId={}", did);
-
- // Atomically notify device to core and update port information.
- activeDevices.compute(did, (k, v) -> {
- if (!deviceService.isAvailable(did)) {
- // Device not available in the core, connect it now.
- DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder()
- .set(AnnotationKeys.PROTOCOL, SCHEME);
- dumpJsonConfigToAnnotations(did, annotationsBuilder);
- DeviceDescription descr = new DefaultDeviceDescription(
- did.uri(), Device.Type.SWITCH, MANUFACTURER, HW_VERSION,
- UNKNOWN, UNKNOWN, new ChassisId(), annotationsBuilder.build());
- // Reset device state (cleanup entries, etc.)
- resetDeviceState(did);
- providerService.deviceConnected(did, descr);
+ log.debug("Starting device discovery... deviceId={}", did);
+ activeDevices.compute(did, (k, lastDescription) -> {
+ DeviceDescription thisDescription = getDeviceDescription(did);
+ if (thisDescription != null) {
+ boolean descriptionChanged = lastDescription != null &&
+ (!Objects.equals(thisDescription, lastDescription) ||
+ !Objects.equals(thisDescription.annotations(), lastDescription.annotations()));
+ if (descriptionChanged || !deviceService.isAvailable(did)) {
+ // Device description changed or device not available in the core.
+ if (contextService.notifyDeviceChange(did)) {
+ // Configuration is the expected one, we can proceed notifying the core.
+ resetDeviceState(did);
+ initPortCounters(did);
+ providerService.deviceConnected(did, thisDescription);
+ updatePortsAndStats(did);
+ }
+ }
+ return thisDescription;
+ } else {
+ log.warn("Unable to get device description for {}", did);
+ return lastDescription;
}
- updatePorts(did);
- return true;
});
}
- private void dumpJsonConfigToAnnotations(DeviceId did, DefaultAnnotations.Builder builder) {
- // TODO: store json config string somewhere else, possibly in a Bmv2Controller (see ONOS-4419)
- try {
- String md5 = Bmv2ThriftClient.of(did).getJsonConfigMd5();
- // Convert to hex string for readability.
- md5 = HexString.toHexString(md5.getBytes());
- String jsonString = Bmv2ThriftClient.of(did).dumpJsonConfig();
- builder.set("bmv2JsonConfigMd5", md5);
- builder.set("bmv2JsonConfigValue", jsonString);
- } catch (Bmv2RuntimeException e) {
- LOG.warn("Unable to dump device JSON config from device {}: {}", did, e.toString());
+ private DeviceDescription getDeviceDescription(DeviceId did) {
+ Device device = deviceService.getDevice(did);
+ DeviceDescriptionDiscovery discovery = null;
+ if (device == null) {
+ // Device not yet in the core. Manually get a driver.
+ Driver driver = driverService.getDriver(MANUFACTURER, HW_VERSION, SW_VERSION);
+ if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
+ discovery = driver.createBehaviour(new DefaultDriverHandler(new DefaultDriverData(driver, did)),
+ DeviceDescriptionDiscovery.class);
+ }
+ } else if (device.is(DeviceDescriptionDiscovery.class)) {
+ discovery = device.as(DeviceDescriptionDiscovery.class);
+ }
+ if (discovery == null) {
+ log.warn("No DeviceDescriptionDiscovery behavior for device {}", did);
+ return null;
+ } else {
+ return discovery.discoverDeviceDetails();
}
}
private void resetDeviceState(DeviceId did) {
try {
- Bmv2ThriftClient.of(did).resetState();
+ controller.getAgent(did).resetState();
} catch (Bmv2RuntimeException e) {
- LOG.warn("Unable to reset {}: {}", did, e.toString());
+ log.warn("Unable to reset {}: {}", did, e.toString());
}
}
- private void updatePorts(DeviceId did) {
+ private void initPortCounters(DeviceId did) {
+ try {
+ initCounters(controller.getAgent(did));
+ } catch (Bmv2RuntimeException e) {
+ log.warn("Unable to init counter on {}: {}", did, e.explain());
+ }
+ }
+
+ private void updatePortsAndStats(DeviceId did) {
Device device = deviceService.getDevice(did);
- if (device.is(PortDiscovery.class)) {
- PortDiscovery portConfig = device.as(PortDiscovery.class);
- List<PortDescription> portDescriptions = portConfig.getPorts();
- providerService.updatePorts(did, portDescriptions);
+ if (device.is(DeviceDescriptionDiscovery.class)) {
+ DeviceDescriptionDiscovery discovery = device.as(DeviceDescriptionDiscovery.class);
+ List<PortDescription> portDescriptions = discovery.discoverPortDetails();
+ if (portDescriptions != null) {
+ providerService.updatePorts(did, portDescriptions);
+ }
} else {
- LOG.warn("No PortDiscovery behavior for device {}", did);
+ log.warn("No DeviceDescriptionDiscovery behavior for device {}", did);
+ }
+ try {
+ Collection<PortStatistics> portStats = getPortStatistics(controller.getAgent(did),
+ deviceService.getPorts(did));
+ providerService.updatePortStatistics(did, portStats);
+ } catch (Bmv2RuntimeException e) {
+ log.warn("Unable to get port statistics for {}: {}", did, e.explain());
}
}
private void disconnectDevice(DeviceId did) {
- LOG.debug("Trying to disconnect device from core... deviceId={}", did);
-
- // Atomically disconnect device.
+ log.debug("Trying to disconnect device from core... deviceId={}", did);
activeDevices.compute(did, (k, v) -> {
if (deviceService.isAvailable(did)) {
providerService.deviceDisconnected(did);
- // Make sure to close the transport session with device. Do we really need this?
- forceDisconnectOf(did);
}
return null;
});
@@ -269,10 +300,10 @@
triggerProbe(bmv2Device.asDeviceId());
});
} catch (ConfigException e) {
- LOG.error("Unable to read config: " + e);
+ log.error("Unable to read config: " + e);
}
} else {
- LOG.error("Unable to read config (was null)");
+ log.error("Unable to read config (was null)");
}
}
@@ -285,18 +316,18 @@
}
/**
- * Listener triggered by Bmv2ControlPlaneServer each time a hello message is received.
+ * Listener triggered by the BMv2 controller each time a hello message is received.
*/
- private class InternalHelloListener implements Bmv2ControlPlaneServer.HelloListener {
+ private class InternalDeviceListener implements Bmv2DeviceListener {
@Override
- public void handleHello(Bmv2Device device) {
+ public void handleHello(Bmv2Device device, int instanceId, String jsonConfigMd5) {
log.debug("Received hello from {}", device);
triggerProbe(device.asDeviceId());
}
}
/**
- * Task that periodically trigger device probes.
+ * Task that periodically trigger device probes to check for device status and update port informations.
*/
private class DevicePoller implements TimerTask {
@@ -304,20 +335,31 @@
private Timeout timeout;
@Override
- public void run(Timeout timeout) throws Exception {
- if (timeout.isCancelled()) {
+ public void run(Timeout tout) throws Exception {
+ if (tout.isCancelled()) {
return;
}
- log.debug("Executing polling on {} devices...", activeDevices.size());
- activeDevices.forEach((did, value) -> triggerProbe(did));
- timeout.getTimer().newTimeout(this, POLL_INTERVAL, TimeUnit.SECONDS);
+ activeDevices.keySet()
+ .stream()
+ // Filter out devices not yet created in the core.
+ .filter(did -> deviceService.getDevice(did) != null)
+ .forEach(did -> executorService.execute(() -> pollingTask(did)));
+ tout.getTimer().newTimeout(this, POLL_INTERVAL, TimeUnit.MILLISECONDS);
+ }
+
+ private void pollingTask(DeviceId deviceId) {
+ if (isReachable(deviceId)) {
+ updatePortsAndStats(deviceId);
+ } else {
+ disconnectDevice(deviceId);
+ }
}
/**
* Starts the collector.
*/
- synchronized void start() {
- LOG.info("Starting device poller...");
+ synchronized void start() {
+ log.info("Starting device poller...");
timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
}
@@ -325,7 +367,7 @@
* Stops the collector.
*/
synchronized void stop() {
- LOG.info("Stopping device poller...");
+ log.info("Stopping device poller...");
timeout.cancel();
}
}
diff --git a/providers/bmv2/device/src/main/java/org/onosproject/provider/bmv2/device/impl/Bmv2PortStatisticsGetter.java b/providers/bmv2/device/src/main/java/org/onosproject/provider/bmv2/device/impl/Bmv2PortStatisticsGetter.java
new file mode 100644
index 0000000..ff94ab0
--- /dev/null
+++ b/providers/bmv2/device/src/main/java/org/onosproject/provider/bmv2/device/impl/Bmv2PortStatisticsGetter.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.provider.bmv2.device.impl;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onosproject.bmv2.api.runtime.Bmv2Action;
+import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
+import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DefaultPortStatistics;
+import org.onosproject.net.device.PortStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Utility class to read port statistics from a BMv2 device.
+ */
+final class Bmv2PortStatisticsGetter {
+
+ // TODO: make counters configuration dependent
+
+ private static final String TABLE_NAME = "port_count_table";
+ private static final String ACTION_NAME = "count_packet";
+ private static final String EGRESS_COUNTER = "egress_port_counter";
+ private static final String INGRESS_COUNTER = "ingress_port_counter";
+
+ private static final Logger log = LoggerFactory.getLogger(Bmv2PortStatisticsGetter.class);
+
+ private Bmv2PortStatisticsGetter() {
+ // ban constructor.
+ }
+
+ /**
+ * Returns a collection of port statistics for given ports using the given BMv2 device agent.
+ *
+ * @param deviceAgent a device agent
+ * @param ports a collection of ports
+ * @return a collection of port statistics
+ */
+ static Collection<PortStatistics> getPortStatistics(Bmv2DeviceAgent deviceAgent, Collection<Port> ports) {
+
+ List<PortStatistics> ps = Lists.newArrayList();
+
+ for (Port port : ports) {
+ int portNumber = (int) port.number().toLong();
+ try {
+ Pair<Long, Long> egressCounter = deviceAgent.readCounter(EGRESS_COUNTER, portNumber);
+ Pair<Long, Long> ingressCounter = deviceAgent.readCounter(INGRESS_COUNTER, portNumber);
+ ps.add(DefaultPortStatistics.builder()
+ .setPort(portNumber)
+ .setBytesSent(egressCounter.getLeft())
+ .setPacketsSent(egressCounter.getRight())
+ .setBytesReceived(ingressCounter.getLeft())
+ .setPacketsReceived(ingressCounter.getRight())
+ .build());
+ } catch (Bmv2RuntimeException e) {
+ log.info("Unable to read port statistics from {}: {}", port, e.explain());
+ }
+ }
+
+ return ps;
+ }
+
+ /**
+ * Initialize port counters on the given device agent.
+ *
+ * @param deviceAgent a device agent.
+ */
+ static void initCounters(Bmv2DeviceAgent deviceAgent) {
+ try {
+ deviceAgent.setTableDefaultAction(TABLE_NAME, Bmv2Action.builder().withName(ACTION_NAME).build());
+ } catch (Bmv2RuntimeException e) {
+ log.debug("Failed to provision counters on {}: {}", deviceAgent.deviceId(), e.explain());
+ }
+ }
+}
+
diff --git a/providers/bmv2/packet/pom.xml b/providers/bmv2/packet/pom.xml
index bb2644f..74fd3ed 100644
--- a/providers/bmv2/packet/pom.xml
+++ b/providers/bmv2/packet/pom.xml
@@ -22,6 +22,7 @@
<artifactId>onos-bmv2-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -34,12 +35,12 @@
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-drivers-bmv2</artifactId>
+ <artifactId>onos-core-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
- <artifactId>onos-core-common</artifactId>
+ <artifactId>onos-bmv2-protocol-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
diff --git a/providers/bmv2/packet/src/main/java/org/onosproject/provider/bmv2/packet/impl/Bmv2PacketProvider.java b/providers/bmv2/packet/src/main/java/org/onosproject/provider/bmv2/packet/impl/Bmv2PacketProvider.java
index edb6159..ff2e71a 100644
--- a/providers/bmv2/packet/src/main/java/org/onosproject/provider/bmv2/packet/impl/Bmv2PacketProvider.java
+++ b/providers/bmv2/packet/src/main/java/org/onosproject/provider/bmv2/packet/impl/Bmv2PacketProvider.java
@@ -23,12 +23,14 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
import org.onlab.util.ImmutableByteSequence;
-import org.onosproject.bmv2.api.runtime.Bmv2ControlPlaneServer;
import org.onosproject.bmv2.api.runtime.Bmv2Device;
+import org.onosproject.bmv2.api.service.Bmv2Controller;
+import org.onosproject.bmv2.api.service.Bmv2PacketListener;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -49,6 +51,12 @@
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
+import java.util.Optional;
+
+import static org.onosproject.net.PortNumber.FLOOD;
+import static org.onosproject.net.flow.DefaultTrafficTreatment.emptyTreatment;
+import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
+import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
/**
* Implementation of a packet provider for BMv2.
@@ -56,11 +64,11 @@
@Component(immediate = true)
public class Bmv2PacketProvider extends AbstractProvider implements PacketProvider {
- private static final Logger LOG = LoggerFactory.getLogger(Bmv2PacketProvider.class);
+ private final Logger log = LoggerFactory.getLogger(Bmv2PacketProvider.class);
private static final String APP_NAME = "org.onosproject.bmv2";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected Bmv2ControlPlaneServer controlPlaneServer;
+ protected Bmv2Controller controller;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -86,28 +94,28 @@
protected void activate() {
providerService = providerRegistry.register(this);
coreService.registerApplication(APP_NAME);
- controlPlaneServer.addPacketListener(packetListener);
- LOG.info("Started");
+ controller.addPacketListener(packetListener);
+ log.info("Started");
}
@Deactivate
public void deactivate() {
- controlPlaneServer.removePacketListener(packetListener);
+ controller.removePacketListener(packetListener);
providerRegistry.unregister(this);
providerService = null;
- LOG.info("Stopped");
+ log.info("Stopped");
}
@Override
public void emit(OutboundPacket packet) {
if (packet != null) {
- DeviceId did = packet.sendThrough();
- Device device = deviceService.getDevice(did);
+ DeviceId deviceId = packet.sendThrough();
+ Device device = deviceService.getDevice(deviceId);
if (device.is(PacketProgrammable.class)) {
PacketProgrammable packetProgrammable = device.as(PacketProgrammable.class);
packetProgrammable.emit(packet);
} else {
- LOG.info("Unable to send packet, no PacketProgrammable behavior for device {}", did);
+ log.info("No PacketProgrammable behavior for device {}", deviceId);
}
}
}
@@ -117,47 +125,75 @@
*/
private class Bmv2PacketContext extends DefaultPacketContext {
- public Bmv2PacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) {
+ Bmv2PacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) {
super(time, inPkt, outPkt, block);
}
@Override
public void send() {
- if (!this.block()) {
- if (this.outPacket().treatment() == null) {
- TrafficTreatment treatment = (this.treatmentBuilder() == null)
- ? DefaultTrafficTreatment.emptyTreatment()
- : this.treatmentBuilder().build();
- OutboundPacket newPkt = new DefaultOutboundPacket(this.outPacket().sendThrough(),
- treatment,
- this.outPacket().data());
- emit(newPkt);
- } else {
- emit(outPacket());
- }
+
+ if (this.block()) {
+ log.info("Unable to send, packet context not blocked");
+ return;
+ }
+
+ DeviceId deviceId = outPacket().sendThrough();
+ ByteBuffer rawData = outPacket().data();
+
+ TrafficTreatment treatment;
+ if (outPacket().treatment() == null) {
+ treatment = (treatmentBuilder() == null) ? emptyTreatment() : treatmentBuilder().build();
} else {
- LOG.info("Unable to send, packet context not blocked");
+ treatment = outPacket().treatment();
+ }
+
+ // BMv2 doesn't support FLOOD for packet-outs.
+ // Workaround here is to perform multiple emits, one for each device port != packet inPort.
+ Optional<OutputInstruction> floodInst = treatment.allInstructions()
+ .stream()
+ .filter(i -> i.type().equals(OUTPUT))
+ .map(i -> (OutputInstruction) i)
+ .filter(i -> i.port().equals(FLOOD))
+ .findAny();
+
+ if (floodInst.isPresent() && treatment.allInstructions().size() == 1) {
+ // Only one instruction and is FLOOD. Do the trick.
+ PortNumber inPort = inPacket().receivedFrom().port();
+ deviceService.getPorts(outPacket().sendThrough())
+ .stream()
+ .map(Port::number)
+ .filter(port -> !port.equals(inPort))
+ .map(outPort -> DefaultTrafficTreatment.builder().setOutput(outPort).build())
+ .map(outTreatment -> new DefaultOutboundPacket(deviceId, outTreatment, rawData))
+ .forEach(Bmv2PacketProvider.this::emit);
+ } else {
+ // Not FLOOD treatment, what to do is up to driver.
+ emit(new DefaultOutboundPacket(deviceId, treatment, rawData));
}
}
}
/**
- * Internal packet listener to get packet events from the Bmv2ControlPlaneServer.
+ * Internal packet listener to handle packet-in events received from the BMv2 controller.
*/
- private class InternalPacketListener implements Bmv2ControlPlaneServer.PacketListener {
+ private class InternalPacketListener implements Bmv2PacketListener {
+
@Override
public void handlePacketIn(Bmv2Device device, int inputPort, long reason, int tableId, int contextId,
ImmutableByteSequence packet) {
+ Ethernet ethPkt = new Ethernet();
+ ethPkt.deserialize(packet.asArray(), 0, packet.size());
- Ethernet eth = new Ethernet();
- eth.deserialize(packet.asArray(), 0, packet.size());
+ DeviceId deviceId = device.asDeviceId();
+ ConnectPoint receivedFrom = new ConnectPoint(deviceId, PortNumber.portNumber(inputPort));
- InboundPacket inPkt = new DefaultInboundPacket(new ConnectPoint(device.asDeviceId(),
- PortNumber.portNumber(inputPort)),
- eth, ByteBuffer.wrap(packet.asArray()));
- OutboundPacket outPkt = new DefaultOutboundPacket(device.asDeviceId(), null,
- ByteBuffer.wrap(packet.asArray()));
+ ByteBuffer rawData = ByteBuffer.wrap(packet.asArray());
+
+ InboundPacket inPkt = new DefaultInboundPacket(receivedFrom, ethPkt, rawData);
+ OutboundPacket outPkt = new DefaultOutboundPacket(deviceId, null, rawData);
+
PacketContext pktCtx = new Bmv2PacketContext(System.currentTimeMillis(), inPkt, outPkt, false);
+
providerService.processPacket(pktCtx);
}
}
diff --git a/providers/bmv2/pom.xml b/providers/bmv2/pom.xml
index cdd43a8..2afe826 100644
--- a/providers/bmv2/pom.xml
+++ b/providers/bmv2/pom.xml
@@ -22,6 +22,7 @@
<artifactId>onos-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.7.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/providers/pom.xml b/providers/pom.xml
index 726fd95..e2f915d 100644
--- a/providers/pom.xml
+++ b/providers/pom.xml
@@ -46,7 +46,7 @@
<module>lldpcommon</module>
<module>lldp</module>
<module>netcfglinks</module>
- <!--<module>bmv2</module>-->
+ <module>bmv2</module>
<module>isis</module>
</modules>