Carmelo Cascone
Committed by Brian O'Connor

Various changes in BMv2 driver and provider modules

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
Showing 31 changed files with 741 additions and 1525 deletions
...@@ -20,6 +20,6 @@ ...@@ -20,6 +20,6 @@
20 <feature name="${project.artifactId}" version="${project.version}" 20 <feature name="${project.artifactId}" version="${project.version}"
21 description="${project.description}"> 21 description="${project.description}">
22 <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle> 22 <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
23 - <bundle>mvn:${project.groupId}/onos-bmv2-protocol/${project.version}</bundle> 23 + <bundle>mvn:${project.groupId}/onos-bmv2-protocol-api/${project.version}</bundle>
24 </feature> 24 </feature>
25 </features> 25 </features>
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-drivers-general</artifactId> 22 <artifactId>onos-drivers-general</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 <modelVersion>4.0.0</modelVersion> 27 <modelVersion>4.0.0</modelVersion>
27 28
...@@ -36,7 +37,7 @@ ...@@ -36,7 +37,7 @@
36 <onos.app.name>org.onosproject.drivers.bmv2</onos.app.name> 37 <onos.app.name>org.onosproject.drivers.bmv2</onos.app.name>
37 <onos.app.origin>ON.Lab</onos.app.origin> 38 <onos.app.origin>ON.Lab</onos.app.origin>
38 <onos.app.category>Drivers</onos.app.category> 39 <onos.app.category>Drivers</onos.app.category>
39 - <onos.app.title>BMv2 Device Drivers</onos.app.title> 40 + <onos.app.title>BMv2 Drivers</onos.app.title>
40 <onos.app.url>http://onosproject.org</onos.app.url> 41 <onos.app.url>http://onosproject.org</onos.app.url>
41 <onos.app.requires> 42 <onos.app.requires>
42 org.onosproject.bmv2 43 org.onosproject.bmv2
...@@ -46,7 +47,7 @@ ...@@ -46,7 +47,7 @@
46 <dependencies> 47 <dependencies>
47 <dependency> 48 <dependency>
48 <groupId>org.onosproject</groupId> 49 <groupId>org.onosproject</groupId>
49 - <artifactId>onos-bmv2-protocol</artifactId> 50 + <artifactId>onos-bmv2-protocol-api</artifactId>
50 <version>${project.version}</version> 51 <version>${project.version}</version>
51 </dependency> 52 </dependency>
52 <dependency> 53 <dependency>
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.drivers.bmv2;
18 +
19 +import com.google.common.collect.ImmutableList;
20 +import com.google.common.collect.Lists;
21 +import org.onlab.packet.ChassisId;
22 +import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
23 +import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
24 +import org.onosproject.bmv2.api.service.Bmv2Controller;
25 +import org.onosproject.net.AnnotationKeys;
26 +import org.onosproject.net.DefaultAnnotations;
27 +import org.onosproject.net.DeviceId;
28 +import org.onosproject.net.PortNumber;
29 +import org.onosproject.net.device.DefaultDeviceDescription;
30 +import org.onosproject.net.device.DefaultPortDescription;
31 +import org.onosproject.net.device.DeviceDescription;
32 +import org.onosproject.net.device.DeviceDescriptionDiscovery;
33 +import org.onosproject.net.device.PortDescription;
34 +import org.onosproject.net.driver.AbstractHandlerBehaviour;
35 +import org.slf4j.Logger;
36 +import org.slf4j.LoggerFactory;
37 +
38 +import java.math.BigInteger;
39 +import java.util.List;
40 +
41 +import static org.onosproject.bmv2.api.runtime.Bmv2Device.*;
42 +import static org.onosproject.net.Device.Type.SWITCH;
43 +
44 +/**
45 + * Implementation of the device description discovery behaviour for BMv2.
46 + */
47 +public class Bmv2DeviceDescriptionDiscovery extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
48 +
49 + private static final String JSON_CONFIG_MD5 = "bmv2JsonConfigMd5";
50 + private static final String PROCESS_INSTANCE_ID = "bmv2ProcessInstanceId";
51 +
52 + private final Logger log = LoggerFactory.getLogger(this.getClass());
53 +
54 + private Bmv2Controller controller;
55 +
56 + private boolean init() {
57 + controller = handler().get(Bmv2Controller.class);
58 + if (controller == null) {
59 + log.warn("Failed to get a BMv2 controller");
60 + return false;
61 + }
62 + return true;
63 + }
64 +
65 + @Override
66 + public DeviceDescription discoverDeviceDetails() {
67 +
68 + if (!init()) {
69 + return null;
70 + }
71 +
72 + DeviceId deviceId = handler().data().deviceId();
73 +
74 + Bmv2DeviceAgent deviceAgent;
75 + try {
76 + deviceAgent = controller.getAgent(deviceId);
77 + } catch (Bmv2RuntimeException e) {
78 + log.error("Failed to connect to Bmv2 device", e);
79 + return null;
80 + }
81 +
82 + DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder();
83 +
84 + try {
85 + String md5 = deviceAgent.getJsonConfigMd5();
86 + BigInteger i = new BigInteger(1, md5.getBytes());
87 + annotationsBuilder.set(JSON_CONFIG_MD5, String.format("%1$032X", i).toLowerCase());
88 + } catch (Bmv2RuntimeException e) {
89 + log.warn("Unable to dump JSON configuration from {}: {}", deviceId, e.explain());
90 + }
91 + try {
92 + int instanceId = deviceAgent.getProcessInstanceId();
93 + annotationsBuilder.set(PROCESS_INSTANCE_ID, String.valueOf(instanceId));
94 + } catch (Bmv2RuntimeException e) {
95 + log.warn("Unable to get process instance ID from {}: {}", deviceId, e.explain());
96 + }
97 +
98 + annotationsBuilder.set(AnnotationKeys.PROTOCOL, PROTOCOL);
99 +
100 + return new DefaultDeviceDescription(deviceId.uri(),
101 + SWITCH,
102 + MANUFACTURER,
103 + HW_VERSION,
104 + SW_VERSION,
105 + SERIAL_NUMBER,
106 + new ChassisId(),
107 + annotationsBuilder.build());
108 + }
109 +
110 + @Override
111 + public List<PortDescription> discoverPortDetails() {
112 +
113 + if (!init()) {
114 + return null;
115 + }
116 +
117 + DeviceId deviceId = handler().data().deviceId();
118 +
119 + Bmv2DeviceAgent deviceAgent;
120 + try {
121 + deviceAgent = controller.getAgent(deviceId);
122 + } catch (Bmv2RuntimeException e) {
123 + log.error("Failed to connect to Bmv2 device", e);
124 + return null;
125 + }
126 +
127 + List<PortDescription> portDescriptions = Lists.newArrayList();
128 +
129 + try {
130 + deviceAgent.getPortsInfo().forEach(p -> {
131 + PortNumber portNumber = PortNumber.portNumber((long) p.number(), p.ifaceName());
132 + portDescriptions.add(new DefaultPortDescription(portNumber, p.isUp(), DefaultAnnotations.EMPTY));
133 + });
134 + } catch (Bmv2RuntimeException e) {
135 + log.error("Unable to get port descriptions of {}: {}", deviceId, e);
136 + }
137 +
138 + return ImmutableList.copyOf(portDescriptions);
139 + }
140 +}
...@@ -20,7 +20,7 @@ import org.apache.felix.scr.annotations.Component; ...@@ -20,7 +20,7 @@ import org.apache.felix.scr.annotations.Component;
20 import org.onosproject.net.driver.AbstractDriverLoader; 20 import org.onosproject.net.driver.AbstractDriverLoader;
21 21
22 /** 22 /**
23 - * Loader for barefoot drivers from specific xml. 23 + * Loader for BMv2 drivers from xml file.
24 */ 24 */
25 @Component(immediate = true) 25 @Component(immediate = true)
26 public class Bmv2DriversLoader extends AbstractDriverLoader { 26 public class Bmv2DriversLoader extends AbstractDriverLoader {
......
...@@ -14,7 +14,29 @@ ...@@ -14,7 +14,29 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 +package org.onosproject.drivers.bmv2;
18 +
19 +import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
20 +import org.onosproject.net.behaviour.ExtensionSelectorResolver;
21 +import org.onosproject.net.driver.AbstractHandlerBehaviour;
22 +import org.onosproject.net.flow.criteria.ExtensionSelector;
23 +import org.onosproject.net.flow.criteria.ExtensionSelectorType;
24 +
25 +import java.util.Collections;
26 +
27 +import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS;
28 +
17 /** 29 /**
18 - * Translators of ONOS abstractions to BMv2 model-dependent abstractions. 30 + * Implementation of the extension selector resolver behaviour for BMv2.
19 */ 31 */
20 -package org.onosproject.drivers.bmv2.translators;
...\ No newline at end of file ...\ No newline at end of file
32 +public class Bmv2ExtensionSelectorResolver extends AbstractHandlerBehaviour implements ExtensionSelectorResolver {
33 +
34 + @Override
35 + public ExtensionSelector getExtensionSelector(ExtensionSelectorType type) {
36 + if (type.equals(BMV2_MATCH_PARAMS.type())) {
37 + return new Bmv2ExtensionSelector(Collections.emptyMap());
38 + }
39 +
40 + return null;
41 + }
42 +}
......
...@@ -14,14 +14,26 @@ ...@@ -14,14 +14,26 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17 -package org.onosproject.drivers.bmv2.translators; 17 +package org.onosproject.drivers.bmv2;
18 +
19 +import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
20 +import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
21 +import org.onosproject.net.driver.AbstractHandlerBehaviour;
22 +import org.onosproject.net.flow.instructions.ExtensionTreatment;
23 +import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
24 +
25 +import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
18 26
19 /** 27 /**
20 - * BMv2 flow rule translator exception. 28 + * Implementation of the extension treatment resolver behavior for BMv2.
21 */ 29 */
22 -public class Bmv2FlowRuleTranslatorException extends Exception { 30 +public class Bmv2ExtensionTreatmentResolver extends AbstractHandlerBehaviour implements ExtensionTreatmentResolver {
23 31
24 - Bmv2FlowRuleTranslatorException(String msg) { 32 + @Override
25 - super(msg); 33 + public ExtensionTreatment getExtensionInstruction(ExtensionTreatmentType type) {
34 + if (type.equals(BMV2_ACTION.type())) {
35 + return new Bmv2ExtensionTreatment(null);
36 + }
37 + return null;
26 } 38 }
27 } 39 }
......
...@@ -16,28 +16,25 @@ ...@@ -16,28 +16,25 @@
16 16
17 package org.onosproject.drivers.bmv2; 17 package org.onosproject.drivers.bmv2;
18 18
19 -import com.eclipsesource.json.Json;
20 -import com.google.common.cache.CacheBuilder;
21 -import com.google.common.cache.CacheLoader;
22 -import com.google.common.cache.LoadingCache;
23 import com.google.common.collect.Lists; 19 import com.google.common.collect.Lists;
24 import com.google.common.collect.Maps; 20 import com.google.common.collect.Maps;
25 -import com.google.common.collect.Sets;
26 import org.apache.commons.lang3.tuple.Pair; 21 import org.apache.commons.lang3.tuple.Pair;
27 -import org.apache.commons.lang3.tuple.Triple; 22 +import org.onosproject.bmv2.api.context.Bmv2Configuration;
28 -import org.onosproject.bmv2.api.model.Bmv2Model; 23 +import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
29 -import org.onosproject.bmv2.api.runtime.Bmv2Client; 24 +import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
25 +import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslatorException;
26 +import org.onosproject.bmv2.api.context.Bmv2Interpreter;
27 +import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
28 +import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
30 import org.onosproject.bmv2.api.runtime.Bmv2MatchKey; 29 import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
30 +import org.onosproject.bmv2.api.runtime.Bmv2ParsedTableEntry;
31 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; 31 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
32 import org.onosproject.bmv2.api.runtime.Bmv2TableEntry; 32 import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
33 -import org.onosproject.bmv2.ctl.Bmv2ThriftClient; 33 +import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
34 -import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator; 34 +import org.onosproject.bmv2.api.service.Bmv2Controller;
35 -import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator; 35 +import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
36 -import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslatorException; 36 +import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
37 -import org.onosproject.drivers.bmv2.translators.Bmv2SimpleTranslatorConfig;
38 -import org.onosproject.net.Device;
39 import org.onosproject.net.DeviceId; 37 import org.onosproject.net.DeviceId;
40 -import org.onosproject.net.device.DeviceService;
41 import org.onosproject.net.driver.AbstractHandlerBehaviour; 38 import org.onosproject.net.driver.AbstractHandlerBehaviour;
42 import org.onosproject.net.flow.DefaultFlowEntry; 39 import org.onosproject.net.flow.DefaultFlowEntry;
43 import org.onosproject.net.flow.FlowEntry; 40 import org.onosproject.net.flow.FlowEntry;
...@@ -50,97 +47,130 @@ import java.util.Collection; ...@@ -50,97 +47,130 @@ import java.util.Collection;
50 import java.util.Collections; 47 import java.util.Collections;
51 import java.util.Date; 48 import java.util.Date;
52 import java.util.List; 49 import java.util.List;
53 -import java.util.Set;
54 import java.util.concurrent.ConcurrentMap; 50 import java.util.concurrent.ConcurrentMap;
55 -import java.util.concurrent.ExecutionException;
56 -import java.util.concurrent.TimeUnit;
57 51
52 +import static org.onosproject.bmv2.api.runtime.Bmv2RuntimeException.Code.*;
58 import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED; 53 import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
59 54
60 /** 55 /**
61 - * Flow rule programmable device behaviour implementation for BMv2. 56 + * Implementation of the flow rule programmable behaviour for BMv2.
62 */ 57 */
63 -public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour 58 +public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
64 - implements FlowRuleProgrammable { 59 +
65 - 60 + private final Logger log = LoggerFactory.getLogger(this.getClass());
66 - private static final Logger LOG = 61 +
67 - LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class); 62 + // Needed to synchronize operations over the same table entry.
68 - 63 + private static final ConcurrentMap<Bmv2TableEntryReference, Boolean> ENTRY_LOCKS = Maps.newConcurrentMap();
69 - // There's no Bmv2 client method to poll flow entries from the device. Use a local store. 64 +
70 - // FIXME: this information should be distributed across instances of the cluster. 65 + private Bmv2Controller controller;
71 - private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, TimestampedFlowRule>> 66 + private Bmv2TableEntryService tableEntryService;
72 - ENTRIES_MAP = Maps.newConcurrentMap(); 67 + private Bmv2DeviceContextService contextService;
73 - 68 +
74 - // Cache model objects instead of parsing the JSON each time. 69 + private boolean init() {
75 - private static final LoadingCache<String, Bmv2Model> MODEL_CACHE = CacheBuilder.newBuilder() 70 + controller = handler().get(Bmv2Controller.class);
76 - .expireAfterAccess(60, TimeUnit.SECONDS) 71 + tableEntryService = handler().get(Bmv2TableEntryService.class);
77 - .build(new CacheLoader<String, Bmv2Model>() { 72 + contextService = handler().get(Bmv2DeviceContextService.class);
78 - @Override 73 + if (controller == null) {
79 - public Bmv2Model load(String jsonString) throws Exception { 74 + log.warn("Failed to get a BMv2 controller");
80 - // Expensive call. 75 + return false;
81 - return Bmv2Model.parse(Json.parse(jsonString).asObject()); 76 + }
82 - } 77 + if (tableEntryService == null) {
83 - }); 78 + log.warn("Failed to get a BMv2 table entry service");
79 + return false;
80 + }
81 + if (contextService == null) {
82 + log.warn("Failed to get a BMv2 device context service");
83 + return false;
84 + }
85 + return true;
86 + }
84 87
85 @Override 88 @Override
86 public Collection<FlowEntry> getFlowEntries() { 89 public Collection<FlowEntry> getFlowEntries() {
87 90
91 + if (!init()) {
92 + return Collections.emptyList();
93 + }
94 +
88 DeviceId deviceId = handler().data().deviceId(); 95 DeviceId deviceId = handler().data().deviceId();
89 96
90 - Bmv2Client deviceClient; 97 + Bmv2DeviceAgent deviceAgent;
91 try { 98 try {
92 - deviceClient = Bmv2ThriftClient.of(deviceId); 99 + deviceAgent = controller.getAgent(deviceId);
93 } catch (Bmv2RuntimeException e) { 100 } catch (Bmv2RuntimeException e) {
94 - LOG.error("Failed to connect to Bmv2 device", e); 101 + log.error("Failed to get BMv2 device agent: {}", e.explain());
95 return Collections.emptyList(); 102 return Collections.emptyList();
96 } 103 }
97 104
98 - Bmv2Model model = getTranslator(deviceId).config().model(); 105 + Bmv2DeviceContext context = contextService.getContext(deviceId);
106 + if (context == null) {
107 + log.warn("Unable to get device context for {}", deviceId);
108 + }
109 +
110 + Bmv2Interpreter interpreter = context.interpreter();
111 + Bmv2Configuration configuration = context.configuration();
99 112
100 List<FlowEntry> entryList = Lists.newArrayList(); 113 List<FlowEntry> entryList = Lists.newArrayList();
101 114
102 - model.tables().forEach(table -> { 115 + configuration.tables().forEach(table -> {
103 - // For each table declared in the model for this device, do: 116 + // For each table in the configuration AND exposed by the interpreter.
104 - try { 117 + if (!interpreter.tableIdMap().inverse().containsKey(table.name())) {
105 - // Bmv2 doesn't support proper polling for table entries, but only a string based table dump. 118 + return;
106 - // The trick here is to first dump the entry ids currently installed in the device for a given table, 119 + }
107 - // and then filter ENTRIES_MAP based on the retrieved values. 120 +
108 - Set<Long> installedEntryIds = Sets.newHashSet(deviceClient.getInstalledEntryIds(table.name())); 121 + // Bmv2 doesn't support proper polling for table entries, but only a string based table dump.
109 - ENTRIES_MAP.forEach((key, value) -> { 122 + // The trick here is to first dump the entries currently installed in the device for a given table,
110 - if (key.getLeft() == deviceId && key.getMiddle() == table.name() 123 + // and then query a service for the corresponding, previously applied, flow rule.
111 - && value != null) { 124 + List<Bmv2ParsedTableEntry> installedEntries = tableEntryService.getTableEntries(deviceId, table.name());
112 - long entryId = value.getKey(); 125 + installedEntries.forEach(parsedEntry -> {
113 - // Filter entries_map for this device and table. 126 + Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId,
114 - if (installedEntryIds.contains(entryId)) { 127 + table.name(),
115 - // Entry is installed. 128 + parsedEntry.matchKey());
116 - long bytes = 0L; 129 + ENTRY_LOCKS.compute(entryRef, (key, value) -> {
117 - long packets = 0L; 130 +
118 - if (table.hasCounters()) { 131 + Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookupEntryReference(entryRef);
119 - // Read counter values from device. 132 +
120 - try { 133 + if (frWrapper == null) {
121 - Pair<Long, Long> counterValue = deviceClient.readTableEntryCounter(table.name(), 134 + log.warn("missing reference from table entry service, BUG? " +
122 - entryId); 135 + "deviceId={}, tableName={}, matchKey={}",
123 - bytes = counterValue.getLeft(); 136 + deviceId, table.name(), entryRef.matchKey());
124 - packets = counterValue.getRight(); 137 + return null;
125 - } catch (Bmv2RuntimeException e) { 138 + }
126 - LOG.warn("Unable to get counter values for entry {} of table {} of device {}: {}", 139 +
127 - entryId, table.name(), deviceId, e.toString()); 140 + long remoteEntryId = parsedEntry.entryId();
128 - } 141 + long localEntryId = frWrapper.entryId();
129 - } 142 +
130 - TimestampedFlowRule tsRule = value.getRight(); 143 + if (remoteEntryId != localEntryId) {
131 - FlowEntry entry = new DefaultFlowEntry(tsRule.rule(), ADDED, 144 + log.warn("getFlowEntries(): inconsistent entry id! BUG? Updating it... remote={}, local={}",
132 - tsRule.lifeInSeconds(), packets, bytes); 145 + remoteEntryId, localEntryId);
133 - entryList.add(entry); 146 + frWrapper = new Bmv2FlowRuleWrapper(frWrapper.rule(), remoteEntryId,
134 - } else { 147 + frWrapper.creationDate());
135 - // No such entry on device, can remove from local store. 148 + tableEntryService.bindEntryReference(entryRef, frWrapper);
136 - ENTRIES_MAP.remove(key); 149 + }
150 +
151 + long bytes = 0L;
152 + long packets = 0L;
153 +
154 + if (table.hasCounters()) {
155 + // Read counter values from device.
156 + try {
157 + Pair<Long, Long> counterValue = deviceAgent.readTableEntryCounter(table.name(),
158 + remoteEntryId);
159 + bytes = counterValue.getLeft();
160 + packets = counterValue.getRight();
161 + } catch (Bmv2RuntimeException e) {
162 + log.warn("Unable to get counters for entry {}/{} of device {}: {}",
163 + table.name(), remoteEntryId, deviceId, e.explain());
137 } 164 }
138 } 165 }
166 +
167 + FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
168 + packets, bytes);
169 + entryList.add(entry);
170 + return true;
139 }); 171 });
140 - } catch (Bmv2RuntimeException e) { 172 +
141 - LOG.error("Unable to get flow entries for table {} of device {}: {}", 173 + });
142 - table.name(), deviceId, e.toString());
143 - }
144 }); 174 });
145 175
146 return Collections.unmodifiableCollection(entryList); 176 return Collections.unmodifiableCollection(entryList);
...@@ -160,17 +190,27 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -160,17 +190,27 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
160 190
161 private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) { 191 private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
162 192
193 + if (!init()) {
194 + return Collections.emptyList();
195 + }
196 +
163 DeviceId deviceId = handler().data().deviceId(); 197 DeviceId deviceId = handler().data().deviceId();
164 198
165 - Bmv2Client deviceClient; 199 + Bmv2DeviceAgent deviceAgent;
166 try { 200 try {
167 - deviceClient = Bmv2ThriftClient.of(deviceId); 201 + deviceAgent = controller.getAgent(deviceId);
168 } catch (Bmv2RuntimeException e) { 202 } catch (Bmv2RuntimeException e) {
169 - LOG.error("Failed to connect to Bmv2 device", e); 203 + log.error("Failed to get BMv2 device agent: {}", e.explain());
170 return Collections.emptyList(); 204 return Collections.emptyList();
171 } 205 }
172 206
173 - Bmv2FlowRuleTranslator translator = getTranslator(deviceId); 207 + Bmv2DeviceContext context = contextService.getContext(deviceId);
208 + if (context == null) {
209 + log.error("Unable to get device context for {}", deviceId);
210 + return Collections.emptyList();
211 + }
212 +
213 + Bmv2FlowRuleTranslator translator = tableEntryService.getFlowRuleTranslator();
174 214
175 List<FlowRule> processedFlowRules = Lists.newArrayList(); 215 List<FlowRule> processedFlowRules = Lists.newArrayList();
176 216
...@@ -179,120 +219,114 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour ...@@ -179,120 +219,114 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
179 Bmv2TableEntry bmv2Entry; 219 Bmv2TableEntry bmv2Entry;
180 220
181 try { 221 try {
182 - bmv2Entry = translator.translate(rule); 222 + bmv2Entry = translator.translate(rule, context);
183 } catch (Bmv2FlowRuleTranslatorException e) { 223 } catch (Bmv2FlowRuleTranslatorException e) {
184 - LOG.error("Unable to translate flow rule: {}", e.getMessage()); 224 + log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
185 continue; 225 continue;
186 } 226 }
187 227
188 String tableName = bmv2Entry.tableName(); 228 String tableName = bmv2Entry.tableName();
189 - Triple<DeviceId, String, Bmv2MatchKey> entryKey = Triple.of(deviceId, tableName, bmv2Entry.matchKey()); 229 + Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey());
190 230
191 /* 231 /*
192 From here on threads are synchronized over entryKey, i.e. serialize operations 232 From here on threads are synchronized over entryKey, i.e. serialize operations
193 over the same matchKey of a specific table and device. 233 over the same matchKey of a specific table and device.
194 */ 234 */
195 - ENTRIES_MAP.compute(entryKey, (key, value) -> { 235 + ENTRY_LOCKS.compute(entryRef, (key, value) -> {
236 + // Get from store
237 + Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookupEntryReference(entryRef);
196 try { 238 try {
197 if (operation == Operation.APPLY) { 239 if (operation == Operation.APPLY) {
198 // Apply entry 240 // Apply entry
199 long entryId; 241 long entryId;
200 - if (value != null) { 242 + if (frWrapper != null) {
201 // Existing entry. 243 // Existing entry.
202 - entryId = value.getKey(); 244 + entryId = frWrapper.entryId();
203 - try { 245 + // Tentatively delete entry before re-adding.
204 - // Tentatively delete entry before re-adding. 246 + // It might not exist on device due to inconsistencies.
205 - // It might not exist on device due to inconsistencies. 247 + silentlyRemove(deviceAgent, entryRef.tableName(), entryId);
206 - deviceClient.deleteTableEntry(bmv2Entry.tableName(), entryId);
207 - value = null;
208 - } catch (Bmv2RuntimeException e) {
209 - // Silently drop exception as we can probably fix this by re-adding the entry.
210 - }
211 } 248 }
212 // Add entry. 249 // Add entry.
213 - entryId = deviceClient.addTableEntry(bmv2Entry); 250 + entryId = doAddEntry(deviceAgent, bmv2Entry);
214 - value = Pair.of(entryId, new TimestampedFlowRule(rule)); 251 + frWrapper = new Bmv2FlowRuleWrapper(rule, entryId, new Date());
215 } else { 252 } else {
216 // Remove entry 253 // Remove entry
217 - if (value == null) { 254 + if (frWrapper == null) {
218 // Entry not found in map, how come? 255 // Entry not found in map, how come?
219 - LOG.debug("Trying to remove entry, but entry ID not found: " + entryKey); 256 + forceRemove(deviceAgent, entryRef.tableName(), entryRef.matchKey());
220 } else { 257 } else {
221 - deviceClient.deleteTableEntry(tableName, value.getKey()); 258 + long entryId = frWrapper.entryId();
222 - value = null; 259 + doRemove(deviceAgent, entryRef.tableName(), entryId, entryRef.matchKey());
223 } 260 }
261 + frWrapper = null;
224 } 262 }
225 // If here, no exceptions... things went well :) 263 // If here, no exceptions... things went well :)
226 processedFlowRules.add(rule); 264 processedFlowRules.add(rule);
227 } catch (Bmv2RuntimeException e) { 265 } catch (Bmv2RuntimeException e) {
228 - LOG.warn("Unable to {} flow rule: {}", operation.name().toLowerCase(), e.toString()); 266 + log.warn("Unable to {} flow rule: {}", operation.name(), e.explain());
267 + }
268 + // Update binding in table entry service.
269 + if (frWrapper != null) {
270 + tableEntryService.bindEntryReference(entryRef, frWrapper);
271 + return true;
272 + } else {
273 + tableEntryService.unbindEntryReference(entryRef);
274 + return null;
229 } 275 }
230 - return value;
231 }); 276 });
232 } 277 }
233 278
234 return processedFlowRules; 279 return processedFlowRules;
235 } 280 }
236 281
237 - /** 282 + private long doAddEntry(Bmv2DeviceAgent agent, Bmv2TableEntry entry) throws Bmv2RuntimeException {
238 - * Gets the appropriate flow rule translator based on the device running configuration. 283 + try {
239 - * 284 + return agent.addTableEntry(entry);
240 - * @param deviceId a device id 285 + } catch (Bmv2RuntimeException e) {
241 - * @return a flow rule translator 286 + if (e.getCode() != TABLE_DUPLICATE_ENTRY) {
242 - */ 287 + forceRemove(agent, entry.tableName(), entry.matchKey());
243 - private Bmv2FlowRuleTranslator getTranslator(DeviceId deviceId) { 288 + return agent.addTableEntry(entry);
244 - 289 + } else {
245 - DeviceService deviceService = handler().get(DeviceService.class); 290 + throw e;
246 - if (deviceService == null) { 291 + }
247 - LOG.error("Unable to get device service");
248 - return null;
249 } 292 }
293 + }
250 294
251 - Device device = deviceService.getDevice(deviceId); 295 + private void doRemove(Bmv2DeviceAgent agent, String tableName, long entryId, Bmv2MatchKey matchKey)
252 - if (device == null) { 296 + throws Bmv2RuntimeException {
253 - LOG.error("Unable to get device {}", deviceId); 297 + try {
254 - return null; 298 + agent.deleteTableEntry(tableName, entryId);
299 + } catch (Bmv2RuntimeException e) {
300 + if (e.getCode() == TABLE_INVALID_HANDLE || e.getCode() == TABLE_EXPIRED_HANDLE) {
301 + // entry is not there with the declared ID, try with a forced remove.
302 + forceRemove(agent, tableName, matchKey);
303 + } else {
304 + throw e;
305 + }
255 } 306 }
307 + }
256 308
257 - String jsonString = device.annotations().value("bmv2JsonConfigValue"); 309 + private void forceRemove(Bmv2DeviceAgent agent, String tableName, Bmv2MatchKey matchKey)
258 - if (jsonString == null) { 310 + throws Bmv2RuntimeException {
259 - LOG.error("Unable to read bmv2 JSON config from device {}", deviceId); 311 + // Find the entryID (expensive call!)
260 - return null; 312 + for (Bmv2ParsedTableEntry pEntry : tableEntryService.getTableEntries(agent.deviceId(), tableName)) {
313 + if (pEntry.matchKey().equals(matchKey)) {
314 + // Remove entry and drop exceptions.
315 + silentlyRemove(agent, tableName, pEntry.entryId());
316 + break;
317 + }
261 } 318 }
319 + }
262 320
263 - Bmv2Model model; 321 + private void silentlyRemove(Bmv2DeviceAgent agent, String tableName, long entryId) {
264 try { 322 try {
265 - model = MODEL_CACHE.get(jsonString); 323 + agent.deleteTableEntry(tableName, entryId);
266 - } catch (ExecutionException e) { 324 + } catch (Bmv2RuntimeException e) {
267 - LOG.error("Unable to parse bmv2 JSON config for device {}:", deviceId, e.getCause()); 325 + // do nothing
268 - return null;
269 } 326 }
270 -
271 - // TODO: get translator config dynamically.
272 - // Now it's hardcoded, selection should be based on the device bmv2 model.
273 - Bmv2FlowRuleTranslator.TranslatorConfig translatorConfig = new Bmv2SimpleTranslatorConfig(model);
274 - return new Bmv2DefaultFlowRuleTranslator(translatorConfig);
275 } 327 }
276 328
277 private enum Operation { 329 private enum Operation {
278 APPLY, REMOVE 330 APPLY, REMOVE
279 } 331 }
280 -
281 - private class TimestampedFlowRule {
282 - private final FlowRule rule;
283 - private final Date addedDate;
284 -
285 - public TimestampedFlowRule(FlowRule rule) {
286 - this.rule = rule;
287 - this.addedDate = new Date();
288 - }
289 -
290 - public FlowRule rule() {
291 - return rule;
292 - }
293 -
294 - public long lifeInSeconds() {
295 - return (new Date().getTime() - addedDate.getTime()) / 1000;
296 - }
297 - }
298 } 332 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -17,56 +17,59 @@ ...@@ -17,56 +17,59 @@
17 package org.onosproject.drivers.bmv2; 17 package org.onosproject.drivers.bmv2;
18 18
19 import org.onlab.util.ImmutableByteSequence; 19 import org.onlab.util.ImmutableByteSequence;
20 -import org.onosproject.bmv2.api.runtime.Bmv2Client; 20 +import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
21 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; 21 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
22 -import org.onosproject.bmv2.ctl.Bmv2ThriftClient; 22 +import org.onosproject.bmv2.api.service.Bmv2Controller;
23 import org.onosproject.net.DeviceId; 23 import org.onosproject.net.DeviceId;
24 import org.onosproject.net.driver.AbstractHandlerBehaviour; 24 import org.onosproject.net.driver.AbstractHandlerBehaviour;
25 import org.onosproject.net.flow.TrafficTreatment; 25 import org.onosproject.net.flow.TrafficTreatment;
26 -import org.onosproject.net.flow.instructions.Instructions;
27 import org.onosproject.net.packet.OutboundPacket; 26 import org.onosproject.net.packet.OutboundPacket;
28 import org.onosproject.net.packet.PacketProgrammable; 27 import org.onosproject.net.packet.PacketProgrammable;
29 import org.slf4j.Logger; 28 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory; 29 import org.slf4j.LoggerFactory;
31 30
31 +import java.util.List;
32 +
32 import static java.lang.Math.toIntExact; 33 import static java.lang.Math.toIntExact;
33 -import static org.onosproject.net.PortNumber.FLOOD; 34 +import static java.util.stream.Collectors.toList;
34 import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT; 35 import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
36 +import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
35 37
36 /** 38 /**
37 - * Packet programmable device behaviour implementation for BMv2. 39 + * Implementation of the packet programmable behaviour for BMv2.
38 */ 40 */
39 public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable { 41 public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable {
40 42
41 - private static final Logger LOG = 43 + private final Logger log = LoggerFactory.getLogger(this.getClass());
42 - LoggerFactory.getLogger(Bmv2PacketProgrammable.class);
43 44
44 @Override 45 @Override
45 public void emit(OutboundPacket packet) { 46 public void emit(OutboundPacket packet) {
46 47
47 TrafficTreatment treatment = packet.treatment(); 48 TrafficTreatment treatment = packet.treatment();
48 49
49 - treatment.allInstructions().forEach(inst -> { 50 + // BMv2 supports only OUTPUT instructions.
50 - if (inst.type().equals(OUTPUT)) { 51 + List<OutputInstruction> outInstructions = treatment.allInstructions()
51 - Instructions.OutputInstruction outInst = (Instructions.OutputInstruction) inst; 52 + .stream()
52 - if (outInst.port().isLogical()) { 53 + .filter(i -> i.type().equals(OUTPUT))
53 - if (outInst.port() == FLOOD) { 54 + .map(i -> (OutputInstruction) i)
54 - // TODO: implement flood 55 + .collect(toList());
55 - LOG.info("Flood not implemented", outInst); 56 +
56 - } 57 + if (treatment.allInstructions().size() != outInstructions.size()) {
57 - LOG.info("Output on logical port not supported: {}", outInst); 58 + // There are other instructions that are not of type OUTPUT
58 - } else { 59 + log.warn("Dropping emit request, treatment nor supported: {}", treatment);
59 - try { 60 + return;
60 - long longPort = outInst.port().toLong(); 61 + }
61 - int portNumber = toIntExact(longPort); 62 +
62 - send(portNumber, packet); 63 + outInstructions.forEach(outInst -> {
63 - } catch (ArithmeticException e) { 64 + if (outInst.port().isLogical()) {
64 - LOG.error("Port number overflow! Cannot send packet on port {} (long), as the bmv2" + 65 + log.warn("Dropping emit request, logical port not supported: {}", outInst.port());
65 - " device only accepts int port values.");
66 - }
67 - }
68 } else { 66 } else {
69 - LOG.info("Instruction type not supported: {}", inst.type().name()); 67 + try {
68 + int portNumber = toIntExact(outInst.port().toLong());
69 + send(portNumber, packet);
70 + } catch (ArithmeticException e) {
71 + log.error("Dropping emit request, port number too big: {}", outInst.port().toLong());
72 + }
70 } 73 }
71 }); 74 });
72 } 75 }
...@@ -75,19 +78,25 @@ public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements ...@@ -75,19 +78,25 @@ public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements
75 78
76 DeviceId deviceId = handler().data().deviceId(); 79 DeviceId deviceId = handler().data().deviceId();
77 80
78 - Bmv2Client deviceClient; 81 + Bmv2Controller controller = handler().get(Bmv2Controller.class);
82 + if (controller == null) {
83 + log.error("Failed to get BMv2 controller");
84 + return;
85 + }
86 +
87 + Bmv2DeviceAgent deviceAgent;
79 try { 88 try {
80 - deviceClient = Bmv2ThriftClient.of(deviceId); 89 + deviceAgent = controller.getAgent(deviceId);
81 } catch (Bmv2RuntimeException e) { 90 } catch (Bmv2RuntimeException e) {
82 - LOG.error("Failed to connect to Bmv2 device", e); 91 + log.error("Failed to get Bmv2 device agent for {}: {}", deviceId, e.explain());
83 return; 92 return;
84 } 93 }
85 94
86 ImmutableByteSequence bs = ImmutableByteSequence.copyFrom(packet.data()); 95 ImmutableByteSequence bs = ImmutableByteSequence.copyFrom(packet.data());
87 try { 96 try {
88 - deviceClient.transmitPacket(port, bs); 97 + deviceAgent.transmitPacket(port, bs);
89 } catch (Bmv2RuntimeException e) { 98 } catch (Bmv2RuntimeException e) {
90 - LOG.info("Unable to push packet to device: deviceId={}, packet={}", deviceId, bs); 99 + log.warn("Unable to emit packet trough {}: {}", deviceId, e.explain());
91 } 100 }
92 } 101 }
93 } 102 }
......
...@@ -37,8 +37,8 @@ public class Bmv2Pipeliner extends AbstractHandlerBehaviour implements Pipeliner ...@@ -37,8 +37,8 @@ public class Bmv2Pipeliner extends AbstractHandlerBehaviour implements Pipeliner
37 37
38 @Override 38 @Override
39 public void init(DeviceId deviceId, PipelinerContext context) { 39 public void init(DeviceId deviceId, PipelinerContext context) {
40 - // TODO: get multi-table pipeliner dynamically based on BMv2 device running model 40 + // TODO: get multi-table pipeliner dynamically based on BMv2 device running model (hard).
41 - // Right now we only support single table pipelines 41 + // Right now we are able to map flow objectives only in the first table of the pipeline.
42 pipeliner = new DefaultSingleTablePipeline(); 42 pipeliner = new DefaultSingleTablePipeline();
43 pipeliner.init(deviceId, context); 43 pipeliner.init(deviceId, context);
44 } 44 }
......
1 -/*
2 - * Copyright 2014-2016 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.drivers.bmv2;
18 -
19 -import com.google.common.collect.ImmutableList;
20 -import com.google.common.collect.Lists;
21 -import org.onosproject.bmv2.api.runtime.Bmv2Client;
22 -import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
23 -import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
24 -import org.onosproject.net.DefaultAnnotations;
25 -import org.onosproject.net.PortNumber;
26 -import org.onosproject.net.SparseAnnotations;
27 -import org.onosproject.net.behaviour.PortDiscovery;
28 -import org.onosproject.net.device.DefaultPortDescription;
29 -import org.onosproject.net.device.PortDescription;
30 -import org.onosproject.net.driver.AbstractHandlerBehaviour;
31 -import org.slf4j.Logger;
32 -import org.slf4j.LoggerFactory;
33 -
34 -import java.util.Collections;
35 -import java.util.List;
36 -
37 -public class Bmv2PortDiscovery extends AbstractHandlerBehaviour
38 - implements PortDiscovery {
39 -
40 - private final Logger log =
41 - LoggerFactory.getLogger(this.getClass());
42 -
43 - @Override
44 - public List<PortDescription> getPorts() {
45 - Bmv2Client deviceClient;
46 - try {
47 - deviceClient = Bmv2ThriftClient.of(handler().data().deviceId());
48 - } catch (Bmv2RuntimeException e) {
49 - log.error("Failed to connect to Bmv2 device", e);
50 - return Collections.emptyList();
51 - }
52 -
53 - List<PortDescription> portDescriptions = Lists.newArrayList();
54 -
55 - try {
56 -
57 - deviceClient.getPortsInfo().forEach(
58 - p -> {
59 - DefaultAnnotations.Builder builder =
60 - DefaultAnnotations.builder();
61 - p.getExtraProperties().forEach(builder::set);
62 - SparseAnnotations annotations = builder.build();
63 -
64 - portDescriptions.add(new DefaultPortDescription(
65 - PortNumber.portNumber(
66 - (long) p.portNumber(),
67 - p.ifaceName()),
68 - p.isUp(),
69 - annotations
70 - ));
71 - });
72 - } catch (Bmv2RuntimeException e) {
73 - log.error("Unable to get port description from Bmv2 device", e);
74 - }
75 -
76 - return ImmutableList.copyOf(portDescriptions);
77 - }
78 -}
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.drivers.bmv2.translators;
18 -
19 -import com.google.common.annotations.Beta;
20 -import org.onlab.util.ImmutableByteSequence;
21 -import org.onosproject.bmv2.api.model.Bmv2ModelField;
22 -import org.onosproject.bmv2.api.model.Bmv2ModelTable;
23 -import org.onosproject.bmv2.api.model.Bmv2ModelTableKey;
24 -import org.onosproject.bmv2.api.runtime.Bmv2Action;
25 -import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
26 -import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
27 -import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
28 -import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
29 -import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
30 -import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
31 -import org.onosproject.net.flow.FlowRule;
32 -import org.onosproject.net.flow.TrafficSelector;
33 -import org.onosproject.net.flow.TrafficTreatment;
34 -import org.onosproject.net.flow.criteria.Criterion;
35 -import org.onosproject.net.flow.criteria.EthCriterion;
36 -import org.onosproject.net.flow.criteria.EthTypeCriterion;
37 -import org.onosproject.net.flow.criteria.ExtensionCriterion;
38 -import org.onosproject.net.flow.criteria.ExtensionSelector;
39 -import org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes;
40 -import org.onosproject.net.flow.criteria.PortCriterion;
41 -import org.onosproject.net.flow.instructions.ExtensionTreatment;
42 -import org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes;
43 -import org.onosproject.net.flow.instructions.Instruction;
44 -import org.onosproject.net.flow.instructions.Instructions;
45 -import org.onosproject.net.flow.instructions.Instructions.ExtensionInstructionWrapper;
46 -
47 -/**
48 - * Default Bmv2 flow rule translator implementation.
49 - * <p>
50 - * Flow rules are translated into {@link Bmv2TableEntry BMv2 table entries} according to the following logic:
51 - * <ul>
52 - * <li> table name: obtained from the Bmv2 model using the flow rule table ID;
53 - * <li> match key: if the flow rule selector defines only a criterion of type {@link Criterion.Type#EXTENSION EXTENSION}
54 - * , then the latter is expected to contain a {@link Bmv2ExtensionSelector Bmv2ExtensionSelector}, which should provide
55 - * a match key already formatted for the given table; otherwise a match key is built using the
56 - * {@link TranslatorConfig#fieldToCriterionTypeMap() mapping} defined by this translator configuration.
57 - * <li> action: if the flow rule treatment contains only one instruction of type
58 - * {@link Instruction.Type#EXTENSION EXTENSION}, then the latter is expected to contain a {@link Bmv2ExtensionTreatment}
59 - * , which should provide a {@link Bmv2Action} already formatted for the given table; otherwise, an action is
60 - * {@link TranslatorConfig#buildAction(TrafficTreatment) built} using this translator configuration.
61 - * <li> priority: the same as the flow rule.
62 - * <li> timeout: if the table supports timeout, use the same as the flow rule, otherwise none (i.e. permanent entry).
63 - * </ul>
64 - */
65 -@Beta
66 -public class Bmv2DefaultFlowRuleTranslator implements Bmv2FlowRuleTranslator {
67 -
68 - private final TranslatorConfig config;
69 -
70 - public Bmv2DefaultFlowRuleTranslator(TranslatorConfig config) {
71 - this.config = config;
72 - }
73 -
74 - private static Bmv2TernaryMatchParam buildTernaryParam(Bmv2ModelField field, Criterion criterion, int byteWidth)
75 - throws Bmv2FlowRuleTranslatorException {
76 -
77 - // Value and mask will be filled according to criterion type
78 - ImmutableByteSequence value;
79 - ImmutableByteSequence mask = null;
80 -
81 - switch (criterion.type()) {
82 - case IN_PORT:
83 - // FIXME: allow port numbers of variable bit length (based on model), truncating when necessary
84 - short port = (short) ((PortCriterion) criterion).port().toLong();
85 - value = ImmutableByteSequence.copyFrom(port);
86 - break;
87 - case ETH_DST:
88 - EthCriterion c = (EthCriterion) criterion;
89 - value = ImmutableByteSequence.copyFrom(c.mac().toBytes());
90 - if (c.mask() != null) {
91 - mask = ImmutableByteSequence.copyFrom(c.mask().toBytes());
92 - }
93 - break;
94 - case ETH_SRC:
95 - EthCriterion c2 = (EthCriterion) criterion;
96 - value = ImmutableByteSequence.copyFrom(c2.mac().toBytes());
97 - if (c2.mask() != null) {
98 - mask = ImmutableByteSequence.copyFrom(c2.mask().toBytes());
99 - }
100 - break;
101 - case ETH_TYPE:
102 - short ethType = ((EthTypeCriterion) criterion).ethType().toShort();
103 - value = ImmutableByteSequence.copyFrom(ethType);
104 - break;
105 - // TODO: implement building for other criterion types (easy with DefaultCriterion of ONOS-4034)
106 - default:
107 - throw new Bmv2FlowRuleTranslatorException("Feature not implemented, ternary builder for criterion" +
108 - "type: " + criterion.type().name());
109 - }
110 -
111 - if (mask == null) {
112 - // no mask, all ones
113 - mask = ImmutableByteSequence.ofOnes(byteWidth);
114 - }
115 -
116 - return new Bmv2TernaryMatchParam(value, mask);
117 - }
118 -
119 - private static Bmv2MatchKey getMatchKeyFromExtension(ExtensionCriterion criterion)
120 - throws Bmv2FlowRuleTranslatorException {
121 -
122 - ExtensionSelector extSelector = criterion.extensionSelector();
123 -
124 - if (extSelector.type() == ExtensionSelectorTypes.P4_BMV2_MATCH_KEY.type()) {
125 - if (extSelector instanceof Bmv2ExtensionSelector) {
126 - return ((Bmv2ExtensionSelector) extSelector).matchKey();
127 - } else {
128 - throw new Bmv2FlowRuleTranslatorException("Unable to decode extension selector " + extSelector);
129 - }
130 - } else {
131 - throw new Bmv2FlowRuleTranslatorException("Unsupported extension selector type " + extSelector.type());
132 - }
133 - }
134 -
135 - private static Bmv2Action getActionFromExtension(Instructions.ExtensionInstructionWrapper inst)
136 - throws Bmv2FlowRuleTranslatorException {
137 -
138 - ExtensionTreatment extTreatment = inst.extensionInstruction();
139 -
140 - if (extTreatment.type() == ExtensionTreatmentTypes.P4_BMV2_ACTION.type()) {
141 - if (extTreatment instanceof Bmv2ExtensionTreatment) {
142 - return ((Bmv2ExtensionTreatment) extTreatment).getAction();
143 - } else {
144 - throw new Bmv2FlowRuleTranslatorException("Unable to decode treatment extension: " + extTreatment);
145 - }
146 - } else {
147 - throw new Bmv2FlowRuleTranslatorException("Unsupported treatment extension type: " + extTreatment.type());
148 - }
149 - }
150 -
151 - private static Bmv2MatchKey buildMatchKey(TranslatorConfig config, TrafficSelector selector, Bmv2ModelTable table)
152 - throws Bmv2FlowRuleTranslatorException {
153 -
154 - Bmv2MatchKey.Builder matchKeyBuilder = Bmv2MatchKey.builder();
155 -
156 - for (Bmv2ModelTableKey key : table.keys()) {
157 -
158 - String fieldName = key.field().header().name() + "." + key.field().type().name();
159 - int byteWidth = (int) Math.ceil((double) key.field().type().bitWidth() / 8.0);
160 - Criterion.Type criterionType = config.fieldToCriterionTypeMap().get(fieldName);
161 -
162 - if (criterionType == null || selector.getCriterion(criterionType) == null) {
163 - // A mapping is not available or the selector doesn't have such a type
164 - switch (key.matchType()) {
165 - case TERNARY:
166 - // Wildcard field
167 - matchKeyBuilder.withWildcard(byteWidth);
168 - break;
169 - case LPM:
170 - // LPM with prefix 0
171 - matchKeyBuilder.add(new Bmv2LpmMatchParam(ImmutableByteSequence.ofZeros(byteWidth), 0));
172 - break;
173 - default:
174 - throw new Bmv2FlowRuleTranslatorException("Match field not supported: " + fieldName);
175 - }
176 - // Next key
177 - continue;
178 - }
179 -
180 - Criterion criterion = selector.getCriterion(criterionType);
181 - Bmv2TernaryMatchParam matchParam = null;
182 -
183 - switch (key.matchType()) {
184 - case TERNARY:
185 - matchParam = buildTernaryParam(key.field(), criterion, byteWidth);
186 - break;
187 - default:
188 - // TODO: implement other match param builders (exact, LPM, etc.)
189 - throw new Bmv2FlowRuleTranslatorException("Feature not implemented, match param builder: "
190 - + key.matchType().name());
191 - }
192 -
193 - matchKeyBuilder.add(matchParam);
194 - }
195 -
196 - return matchKeyBuilder.build();
197 - }
198 -
199 - @Override
200 - public Bmv2TableEntry translate(FlowRule rule)
201 - throws Bmv2FlowRuleTranslatorException {
202 -
203 - int tableId = rule.tableId();
204 -
205 - Bmv2ModelTable table = config.model().table(tableId);
206 -
207 - if (table == null) {
208 - throw new Bmv2FlowRuleTranslatorException("Unknown table ID: " + tableId);
209 - }
210 -
211 - /* Translate selector */
212 -
213 - TrafficSelector selector = rule.selector();
214 - Bmv2MatchKey bmv2MatchKey = null;
215 -
216 - // If selector has only 1 criterion of type extension, use that
217 - Criterion criterion = selector.getCriterion(Criterion.Type.EXTENSION);
218 - if (criterion != null) {
219 - if (selector.criteria().size() == 1) {
220 - bmv2MatchKey = getMatchKeyFromExtension((ExtensionCriterion) criterion);
221 - } else {
222 - throw new Bmv2FlowRuleTranslatorException("Unable to translate traffic selector, found multiple " +
223 - "criteria of which one is an extension: " +
224 - selector.toString());
225 - }
226 - }
227 -
228 - if (bmv2MatchKey == null) {
229 - // not an extension
230 - bmv2MatchKey = buildMatchKey(config, selector, table);
231 - }
232 -
233 - /* Translate treatment */
234 -
235 - TrafficTreatment treatment = rule.treatment();
236 - Bmv2Action bmv2Action = null;
237 -
238 - // If treatment has only 1 instruction of type extension, use that
239 - for (Instruction inst : treatment.allInstructions()) {
240 - if (inst.type() == Instruction.Type.EXTENSION) {
241 - if (treatment.allInstructions().size() == 1) {
242 - bmv2Action = getActionFromExtension((ExtensionInstructionWrapper) inst);
243 - } else {
244 - throw new Bmv2FlowRuleTranslatorException("Unable to translate traffic treatment, found multiple " +
245 - "instructions of which one is an extension: " +
246 - selector.toString());
247 - }
248 - }
249 - }
250 -
251 - if (bmv2Action == null) {
252 - // No extension, use config to build action
253 - bmv2Action = config.buildAction(treatment);
254 - }
255 -
256 - if (bmv2Action == null) {
257 - // Config returned null
258 - throw new Bmv2FlowRuleTranslatorException("Unable to translate treatment: " + treatment);
259 - }
260 -
261 - Bmv2TableEntry.Builder tableEntryBuilder = Bmv2TableEntry.builder();
262 -
263 - // In BMv2 0 is the highest priority, i.e. the opposite than ONOS.
264 - int newPriority = Integer.MAX_VALUE - rule.priority();
265 -
266 - tableEntryBuilder
267 - .withTableName(table.name())
268 - .withPriority(newPriority)
269 - .withMatchKey(bmv2MatchKey)
270 - .withAction(bmv2Action);
271 -
272 - if (!rule.isPermanent()) {
273 - if (table.hasTimeouts()) {
274 - tableEntryBuilder.withTimeout((double) rule.timeout());
275 - }
276 - //FIXME: add warn log or exception?
277 - }
278 -
279 - return tableEntryBuilder.build();
280 - }
281 -
282 - @Override
283 - public TranslatorConfig config() {
284 - return this.config;
285 - }
286 -}
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.drivers.bmv2.translators;
18 -
19 -import com.google.common.annotations.Beta;
20 -import org.onosproject.bmv2.api.model.Bmv2Model;
21 -import org.onosproject.net.flow.criteria.Criterion;
22 -
23 -import java.util.Map;
24 -
25 -import static org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator.TranslatorConfig;
26 -
27 -/**
28 - * Default implementation of a BMv2 flow rule translator configuration.
29 - */
30 -@Beta
31 -public abstract class Bmv2DefaultTranslatorConfig implements TranslatorConfig {
32 -
33 - private final Bmv2Model model;
34 - private final Map<String, Criterion.Type> fieldMap;
35 -
36 - /**
37 - * Creates a new translator configuration.
38 - *
39 - * @param model a BMv2 packet processing model
40 - * @param fieldMap a field-to-criterion type map
41 - */
42 - protected Bmv2DefaultTranslatorConfig(Bmv2Model model, Map<String, Criterion.Type> fieldMap) {
43 - this.model = model;
44 - this.fieldMap = fieldMap;
45 - }
46 -
47 - @Override
48 - public Bmv2Model model() {
49 - return this.model;
50 - }
51 -
52 - @Override
53 - public Map<String, Criterion.Type> fieldToCriterionTypeMap() {
54 - return this.fieldMap;
55 - }
56 -}
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.drivers.bmv2.translators;
18 -
19 -import com.google.common.annotations.Beta;
20 -import org.onosproject.bmv2.api.model.Bmv2Model;
21 -import org.onosproject.bmv2.api.runtime.Bmv2Action;
22 -import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
23 -import org.onosproject.net.flow.FlowRule;
24 -import org.onosproject.net.flow.TrafficTreatment;
25 -import org.onosproject.net.flow.criteria.Criterion;
26 -
27 -import java.util.Map;
28 -
29 -/**
30 - * Translator of ONOS flow rules to BMv2 table entries. Translation depends on a
31 - * {@link TranslatorConfig translator configuration}.
32 - */
33 -@Beta
34 -public interface Bmv2FlowRuleTranslator {
35 -
36 - /**
37 - * Returns a new BMv2 table entry equivalent to the given flow rule.
38 - *
39 - * @param rule a flow rule
40 - * @return a BMv2 table entry
41 - * @throws Bmv2FlowRuleTranslatorException if the flow rule cannot be
42 - * translated
43 - */
44 - Bmv2TableEntry translate(FlowRule rule) throws Bmv2FlowRuleTranslatorException;
45 -
46 - /**
47 - * Returns the configuration of this translator.
48 - *
49 - * @return a translator configuration
50 - */
51 - TranslatorConfig config();
52 -
53 - /**
54 - * BMv2 flow rules translator configuration. Such a configuration is used to
55 - * generate table entries that are compatible with a given {@link Bmv2Model}.
56 - */
57 - @Beta
58 - interface TranslatorConfig {
59 - /**
60 - * Return the {@link Bmv2Model} associated with this configuration.
61 - *
62 - * @return a BMv2 model
63 - */
64 - Bmv2Model model();
65 -
66 - /**
67 - * Returns a map describing a one-to-one relationship between BMv2
68 - * header field names and ONOS criterion types. Header field names are
69 - * formatted using the notation {@code header_name.field_name}
70 - * representing a specific header field instance extracted by the BMv2
71 - * parser (e.g. {@code ethernet.dstAddr}).
72 - *
73 - * @return a map where the keys represent BMv2 header field names and
74 - * values are criterion types
75 - */
76 - Map<String, Criterion.Type> fieldToCriterionTypeMap();
77 -
78 - /**
79 - * Return a BMv2 action that is equivalent to the given ONOS traffic
80 - * treatment.
81 - *
82 - * @param treatment a traffic treatment
83 - * @return a BMv2 action object
84 - * @throws Bmv2FlowRuleTranslatorException if the treatment cannot be
85 - * translated to a BMv2 action
86 - */
87 - Bmv2Action buildAction(TrafficTreatment treatment)
88 - throws Bmv2FlowRuleTranslatorException;
89 - }
90 -}
1 -/*
2 - * Copyright 2016-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.drivers.bmv2.translators;
18 -
19 -import com.google.common.annotations.Beta;
20 -import com.google.common.collect.ImmutableMap;
21 -import org.onlab.util.ImmutableByteSequence;
22 -import org.onosproject.bmv2.api.model.Bmv2Model;
23 -import org.onosproject.bmv2.api.runtime.Bmv2Action;
24 -import org.onosproject.net.PortNumber;
25 -import org.onosproject.net.flow.TrafficTreatment;
26 -import org.onosproject.net.flow.criteria.Criterion;
27 -import org.onosproject.net.flow.instructions.Instruction;
28 -import org.onosproject.net.flow.instructions.Instructions;
29 -
30 -import java.util.Map;
31 -
32 -/**
33 - * Implementation of a Bmv2 flow rule translator configuration for the
34 - * simple.p4 model.
35 - */
36 -@Beta
37 -public class Bmv2SimpleTranslatorConfig extends Bmv2DefaultTranslatorConfig {
38 -
39 - // Lazily populate field map.
40 - private static final Map<String, Criterion.Type> FIELD_MAP = ImmutableMap.of(
41 - "standard_metadata.ingress_port", Criterion.Type.IN_PORT,
42 - "ethernet.dstAddr", Criterion.Type.ETH_DST,
43 - "ethernet.srcAddr", Criterion.Type.ETH_SRC,
44 - "ethernet.etherType", Criterion.Type.ETH_TYPE);
45 -
46 - /**
47 - * Creates a new simple pipeline translator configuration.
48 - */
49 - public Bmv2SimpleTranslatorConfig(Bmv2Model model) {
50 - // Populate fieldMap.
51 - super(model, FIELD_MAP);
52 - }
53 -
54 - private static Bmv2Action buildDropAction() {
55 - return Bmv2Action.builder()
56 - .withName("_drop")
57 - .build();
58 - }
59 -
60 - private static Bmv2Action buildPushToCpAction() {
61 - return Bmv2Action.builder()
62 - .withName("send_to_cpu")
63 - .build();
64 - }
65 -
66 - private static Bmv2Action buildFwdAction(Instructions.OutputInstruction inst)
67 - throws Bmv2FlowRuleTranslatorException {
68 -
69 - Bmv2Action.Builder actionBuilder = Bmv2Action.builder();
70 -
71 - actionBuilder.withName("fwd");
72 -
73 - if (inst.port().isLogical()) {
74 - if (inst.port() == PortNumber.CONTROLLER) {
75 - return buildPushToCpAction();
76 - } else {
77 - throw new Bmv2FlowRuleTranslatorException(
78 - "Output logic port number not supported: " + inst);
79 - }
80 - }
81 -
82 - actionBuilder.addParameter(
83 - ImmutableByteSequence.copyFrom((short) inst.port().toLong()));
84 -
85 - return actionBuilder.build();
86 - }
87 -
88 - @Override
89 - public Bmv2Action buildAction(TrafficTreatment treatment)
90 - throws Bmv2FlowRuleTranslatorException {
91 -
92 -
93 - if (treatment.allInstructions().size() == 0) {
94 - // No instructions means drop.
95 - return buildDropAction();
96 - } else if (treatment.allInstructions().size() > 1) {
97 - // Otherwise, we understand treatments with only 1 instruction.
98 - throw new Bmv2FlowRuleTranslatorException(
99 - "Treatment not supported, more than 1 instructions found: "
100 - + treatment.toString());
101 - }
102 -
103 - Instruction instruction = treatment.allInstructions().get(0);
104 -
105 - switch (instruction.type()) {
106 - case OUTPUT:
107 - return buildFwdAction((Instructions.OutputInstruction) instruction);
108 - case NOACTION:
109 - return buildDropAction();
110 - default:
111 - throw new Bmv2FlowRuleTranslatorException(
112 - "Instruction type not supported: "
113 - + instruction.type().name());
114 - }
115 - }
116 -}
...@@ -16,14 +16,18 @@ ...@@ -16,14 +16,18 @@
16 ~ limitations under the License. 16 ~ limitations under the License.
17 --> 17 -->
18 <drivers> 18 <drivers>
19 - <driver name="bmv2-thrift" manufacturer="p4.org" hwVersion="bmv2" swVersion="unknown"> 19 + <driver name="bmv2-thrift" manufacturer="p4.org" hwVersion="bmv2" swVersion="n/a">
20 - <behaviour api="org.onosproject.net.behaviour.PortDiscovery" 20 + <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
21 - impl="org.onosproject.drivers.bmv2.Bmv2PortDiscovery"/> 21 + impl="org.onosproject.drivers.bmv2.Bmv2DeviceDescriptionDiscovery"/>
22 <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable" 22 <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
23 impl="org.onosproject.drivers.bmv2.Bmv2FlowRuleProgrammable"/> 23 impl="org.onosproject.drivers.bmv2.Bmv2FlowRuleProgrammable"/>
24 <behaviour api="org.onosproject.net.behaviour.Pipeliner" 24 <behaviour api="org.onosproject.net.behaviour.Pipeliner"
25 impl="org.onosproject.drivers.bmv2.Bmv2Pipeliner"/> 25 impl="org.onosproject.drivers.bmv2.Bmv2Pipeliner"/>
26 <behaviour api="org.onosproject.net.packet.PacketProgrammable" 26 <behaviour api="org.onosproject.net.packet.PacketProgrammable"
27 impl="org.onosproject.drivers.bmv2.Bmv2PacketProgrammable"/> 27 impl="org.onosproject.drivers.bmv2.Bmv2PacketProgrammable"/>
28 + <behaviour api="org.onosproject.net.behaviour.ExtensionSelectorResolver"
29 + impl="org.onosproject.drivers.bmv2.Bmv2ExtensionSelectorResolver"/>
30 + <behaviour api="org.onosproject.net.behaviour.ExtensionTreatmentResolver"
31 + impl="org.onosproject.drivers.bmv2.Bmv2ExtensionTreatmentResolver"/>
28 </driver> 32 </driver>
29 </drivers> 33 </drivers>
......
1 -/*
2 - * Copyright 2014-2016 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -
17 -package org.onosproject.drivers.bmv2;
18 -
19 -import com.eclipsesource.json.Json;
20 -import com.eclipsesource.json.JsonObject;
21 -import com.google.common.testing.EqualsTester;
22 -import org.junit.Before;
23 -import org.junit.Test;
24 -import org.onlab.packet.MacAddress;
25 -import org.onosproject.bmv2.api.model.Bmv2Model;
26 -import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
27 -import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
28 -import org.onosproject.core.ApplicationId;
29 -import org.onosproject.core.DefaultApplicationId;
30 -import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
31 -import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
32 -import org.onosproject.drivers.bmv2.translators.Bmv2SimpleTranslatorConfig;
33 -import org.onosproject.net.DeviceId;
34 -import org.onosproject.net.PortNumber;
35 -import org.onosproject.net.flow.DefaultFlowRule;
36 -import org.onosproject.net.flow.DefaultTrafficSelector;
37 -import org.onosproject.net.flow.DefaultTrafficTreatment;
38 -import org.onosproject.net.flow.FlowRule;
39 -import org.onosproject.net.flow.TrafficSelector;
40 -import org.onosproject.net.flow.TrafficTreatment;
41 -
42 -import java.io.BufferedReader;
43 -import java.io.IOException;
44 -import java.io.InputStream;
45 -import java.io.InputStreamReader;
46 -import java.util.Random;
47 -
48 -import static org.hamcrest.CoreMatchers.equalTo;
49 -import static org.hamcrest.CoreMatchers.is;
50 -import static org.hamcrest.MatcherAssert.assertThat;
51 -
52 -/**
53 - * Tests for {@link Bmv2DefaultFlowRuleTranslator}.
54 - */
55 -public class Bmv2DefaultFlowRuleTranslatorTest {
56 -
57 - private static final String JSON_CONFIG_PATH = "/simple.json";
58 - private Random random = new Random();
59 - private Bmv2Model model;
60 - private Bmv2FlowRuleTranslator.TranslatorConfig config;
61 - private Bmv2FlowRuleTranslator translator;
62 -
63 - @Before
64 - public void setUp() throws Exception {
65 - InputStream inputStream = Bmv2SimpleTranslatorConfig.class
66 - .getResourceAsStream(JSON_CONFIG_PATH);
67 - InputStreamReader reader = new InputStreamReader(inputStream);
68 - BufferedReader bufReader = new BufferedReader(reader);
69 - JsonObject json = null;
70 - try {
71 - json = Json.parse(bufReader).asObject();
72 - } catch (IOException e) {
73 - throw new RuntimeException("Unable to parse JSON file: " + e.getMessage());
74 - }
75 -
76 - this.model = Bmv2Model.parse(json);
77 - this.config = new Bmv2SimpleTranslatorConfig(model);
78 - this.translator = new Bmv2DefaultFlowRuleTranslator(config);
79 -
80 - }
81 -
82 -
83 - @Test
84 - public void testCompiler() throws Exception {
85 -
86 - DeviceId deviceId = DeviceId.NONE;
87 - ApplicationId appId = new DefaultApplicationId(1, "test");
88 - int tableId = 0;
89 - MacAddress ethDstMac = MacAddress.valueOf(random.nextLong());
90 - MacAddress ethSrcMac = MacAddress.valueOf(random.nextLong());
91 - short ethType = (short) (0x0000FFFF & random.nextInt());
92 - short outPort = (short) random.nextInt(65);
93 - short inPort = (short) random.nextInt(65);
94 - int timeout = random.nextInt(100);
95 - int priority = random.nextInt(100);
96 -
97 - TrafficSelector matchInPort1 = DefaultTrafficSelector
98 - .builder()
99 - .matchInPort(PortNumber.portNumber(inPort))
100 - .matchEthDst(ethDstMac)
101 - .matchEthSrc(ethSrcMac)
102 - .matchEthType(ethType)
103 - .build();
104 -
105 - TrafficTreatment outPort2 = DefaultTrafficTreatment
106 - .builder()
107 - .setOutput(PortNumber.portNumber(outPort))
108 - .build();
109 -
110 - FlowRule rule1 = DefaultFlowRule.builder()
111 - .forDevice(deviceId)
112 - .forTable(tableId)
113 - .fromApp(appId)
114 - .withSelector(matchInPort1)
115 - .withTreatment(outPort2)
116 - .makeTemporary(timeout)
117 - .withPriority(priority)
118 - .build();
119 -
120 - FlowRule rule2 = DefaultFlowRule.builder()
121 - .forDevice(deviceId)
122 - .forTable(tableId)
123 - .fromApp(appId)
124 - .withSelector(matchInPort1)
125 - .withTreatment(outPort2)
126 - .makeTemporary(timeout)
127 - .withPriority(priority)
128 - .build();
129 -
130 - Bmv2TableEntry entry1 = translator.translate(rule1);
131 - Bmv2TableEntry entry2 = translator.translate(rule1);
132 -
133 - // check equality, i.e. same rules must produce same entries
134 - new EqualsTester()
135 - .addEqualityGroup(rule1, rule2)
136 - .addEqualityGroup(entry1, entry2)
137 - .testEquals();
138 -
139 - int numMatchParams = model.table(0).keys().size();
140 - // parse values stored in entry1
141 - Bmv2TernaryMatchParam inPortParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(0);
142 - Bmv2TernaryMatchParam ethDstParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(1);
143 - Bmv2TernaryMatchParam ethSrcParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(2);
144 - Bmv2TernaryMatchParam ethTypeParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(3);
145 - double expectedTimeout = (double) (model.table(0).hasTimeouts() ? rule1.timeout() : -1);
146 -
147 - // check that the number of parameters in the entry is the same as the number of table keys
148 - assertThat("Incorrect number of match parameters",
149 - entry1.matchKey().matchParams().size(), is(equalTo(numMatchParams)));
150 -
151 - // check that values stored in entry are the same used for the flow rule
152 - assertThat("Incorrect inPort match param value",
153 - inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
154 - assertThat("Incorrect ethDestMac match param value",
155 - ethDstParam.value().asArray(), is(equalTo(ethDstMac.toBytes())));
156 - assertThat("Incorrect ethSrcMac match param value",
157 - ethSrcParam.value().asArray(), is(equalTo(ethSrcMac.toBytes())));
158 - assertThat("Incorrect ethType match param value",
159 - ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
160 - assertThat("Incorrect priority value",
161 - entry1.priority(), is(equalTo(Integer.MAX_VALUE - rule1.priority())));
162 - assertThat("Incorrect timeout value",
163 - entry1.timeout(), is(equalTo(expectedTimeout)));
164 -
165 - }
166 -}
...\ No newline at end of file ...\ No newline at end of file
1 -{
2 - "header_types": [
3 - {
4 - "name": "standard_metadata_t",
5 - "id": 0,
6 - "fields": [
7 - [
8 - "ingress_port",
9 - 9
10 - ],
11 - [
12 - "packet_length",
13 - 32
14 - ],
15 - [
16 - "egress_spec",
17 - 9
18 - ],
19 - [
20 - "egress_port",
21 - 9
22 - ],
23 - [
24 - "egress_instance",
25 - 32
26 - ],
27 - [
28 - "instance_type",
29 - 32
30 - ],
31 - [
32 - "clone_spec",
33 - 32
34 - ],
35 - [
36 - "_padding",
37 - 5
38 - ]
39 - ],
40 - "length_exp": null,
41 - "max_length": null
42 - },
43 - {
44 - "name": "ethernet_t",
45 - "id": 1,
46 - "fields": [
47 - [
48 - "dstAddr",
49 - 48
50 - ],
51 - [
52 - "srcAddr",
53 - 48
54 - ],
55 - [
56 - "etherType",
57 - 16
58 - ]
59 - ],
60 - "length_exp": null,
61 - "max_length": null
62 - },
63 - {
64 - "name": "intrinsic_metadata_t",
65 - "id": 2,
66 - "fields": [
67 - [
68 - "ingress_global_timestamp",
69 - 32
70 - ],
71 - [
72 - "lf_field_list",
73 - 32
74 - ],
75 - [
76 - "mcast_grp",
77 - 16
78 - ],
79 - [
80 - "egress_rid",
81 - 16
82 - ]
83 - ],
84 - "length_exp": null,
85 - "max_length": null
86 - }
87 - ],
88 - "headers": [
89 - {
90 - "name": "standard_metadata",
91 - "id": 0,
92 - "header_type": "standard_metadata_t",
93 - "metadata": true
94 - },
95 - {
96 - "name": "ethernet",
97 - "id": 1,
98 - "header_type": "ethernet_t",
99 - "metadata": false
100 - },
101 - {
102 - "name": "intrinsic_metadata",
103 - "id": 2,
104 - "header_type": "intrinsic_metadata_t",
105 - "metadata": true
106 - }
107 - ],
108 - "header_stacks": [],
109 - "parsers": [
110 - {
111 - "name": "parser",
112 - "id": 0,
113 - "init_state": "start",
114 - "parse_states": [
115 - {
116 - "name": "start",
117 - "id": 0,
118 - "parser_ops": [],
119 - "transition_key": [],
120 - "transitions": [
121 - {
122 - "value": "default",
123 - "mask": null,
124 - "next_state": "parse_ethernet"
125 - }
126 - ]
127 - },
128 - {
129 - "name": "parse_ethernet",
130 - "id": 1,
131 - "parser_ops": [
132 - {
133 - "op": "extract",
134 - "parameters": [
135 - {
136 - "type": "regular",
137 - "value": "ethernet"
138 - }
139 - ]
140 - }
141 - ],
142 - "transition_key": [],
143 - "transitions": [
144 - {
145 - "value": "default",
146 - "mask": null,
147 - "next_state": null
148 - }
149 - ]
150 - }
151 - ]
152 - }
153 - ],
154 - "deparsers": [
155 - {
156 - "name": "deparser",
157 - "id": 0,
158 - "order": [
159 - "ethernet"
160 - ]
161 - }
162 - ],
163 - "meter_arrays": [],
164 - "actions": [
165 - {
166 - "name": "flood",
167 - "id": 0,
168 - "runtime_data": [],
169 - "primitives": [
170 - {
171 - "op": "modify_field",
172 - "parameters": [
173 - {
174 - "type": "field",
175 - "value": [
176 - "intrinsic_metadata",
177 - "mcast_grp"
178 - ]
179 - },
180 - {
181 - "type": "field",
182 - "value": [
183 - "standard_metadata",
184 - "ingress_port"
185 - ]
186 - }
187 - ]
188 - }
189 - ]
190 - },
191 - {
192 - "name": "_drop",
193 - "id": 1,
194 - "runtime_data": [],
195 - "primitives": [
196 - {
197 - "op": "modify_field",
198 - "parameters": [
199 - {
200 - "type": "field",
201 - "value": [
202 - "standard_metadata",
203 - "egress_spec"
204 - ]
205 - },
206 - {
207 - "type": "hexstr",
208 - "value": "0x1ff"
209 - }
210 - ]
211 - }
212 - ]
213 - },
214 - {
215 - "name": "fwd",
216 - "id": 2,
217 - "runtime_data": [
218 - {
219 - "name": "port",
220 - "bitwidth": 9
221 - }
222 - ],
223 - "primitives": [
224 - {
225 - "op": "modify_field",
226 - "parameters": [
227 - {
228 - "type": "field",
229 - "value": [
230 - "standard_metadata",
231 - "egress_spec"
232 - ]
233 - },
234 - {
235 - "type": "runtime_data",
236 - "value": 0
237 - }
238 - ]
239 - }
240 - ]
241 - },
242 - {
243 - "name": "push_to_cp",
244 - "id": 3,
245 - "runtime_data": [],
246 - "primitives": [
247 - {
248 - "op": "modify_field",
249 - "parameters": [
250 - {
251 - "type": "field",
252 - "value": [
253 - "standard_metadata",
254 - "egress_spec"
255 - ]
256 - },
257 - {
258 - "type": "hexstr",
259 - "value": "0x200"
260 - }
261 - ]
262 - }
263 - ]
264 - }
265 - ],
266 - "pipelines": [
267 - {
268 - "name": "ingress",
269 - "id": 0,
270 - "init_table": "table0",
271 - "tables": [
272 - {
273 - "name": "table0",
274 - "id": 0,
275 - "match_type": "ternary",
276 - "type": "simple",
277 - "max_size": 16384,
278 - "with_counters": false,
279 - "direct_meters": null,
280 - "support_timeout": false,
281 - "key": [
282 - {
283 - "match_type": "ternary",
284 - "target": [
285 - "standard_metadata",
286 - "ingress_port"
287 - ],
288 - "mask": null
289 - },
290 - {
291 - "match_type": "ternary",
292 - "target": [
293 - "ethernet",
294 - "dstAddr"
295 - ],
296 - "mask": null
297 - },
298 - {
299 - "match_type": "ternary",
300 - "target": [
301 - "ethernet",
302 - "srcAddr"
303 - ],
304 - "mask": null
305 - },
306 - {
307 - "match_type": "ternary",
308 - "target": [
309 - "ethernet",
310 - "etherType"
311 - ],
312 - "mask": null
313 - }
314 - ],
315 - "actions": [
316 - "fwd",
317 - "flood",
318 - "push_to_cp",
319 - "_drop"
320 - ],
321 - "next_tables": {
322 - "fwd": null,
323 - "flood": null,
324 - "push_to_cp": null,
325 - "_drop": null
326 - },
327 - "default_action": null,
328 - "base_default_next": null
329 - }
330 - ],
331 - "conditionals": []
332 - },
333 - {
334 - "name": "egress",
335 - "id": 1,
336 - "init_table": null,
337 - "tables": [],
338 - "conditionals": []
339 - }
340 - ],
341 - "calculations": [],
342 - "checksums": [],
343 - "learn_lists": [],
344 - "field_lists": [],
345 - "counter_arrays": [],
346 - "register_arrays": [],
347 - "force_arith": [
348 - [
349 - "standard_metadata",
350 - "ingress_port"
351 - ],
352 - [
353 - "standard_metadata",
354 - "packet_length"
355 - ],
356 - [
357 - "standard_metadata",
358 - "egress_spec"
359 - ],
360 - [
361 - "standard_metadata",
362 - "egress_port"
363 - ],
364 - [
365 - "standard_metadata",
366 - "egress_instance"
367 - ],
368 - [
369 - "standard_metadata",
370 - "instance_type"
371 - ],
372 - [
373 - "standard_metadata",
374 - "clone_spec"
375 - ],
376 - [
377 - "standard_metadata",
378 - "_padding"
379 - ],
380 - [
381 - "intrinsic_metadata",
382 - "ingress_global_timestamp"
383 - ],
384 - [
385 - "intrinsic_metadata",
386 - "lf_field_list"
387 - ],
388 - [
389 - "intrinsic_metadata",
390 - "mcast_grp"
391 - ],
392 - [
393 - "intrinsic_metadata",
394 - "egress_rid"
395 - ]
396 - ]
397 -}
...\ No newline at end of file ...\ No newline at end of file
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
41 <module>utilities</module> 41 <module>utilities</module>
42 <module>lumentum</module> 42 <module>lumentum</module>
43 <module>bti</module> 43 <module>bti</module>
44 - <!--<module>bmv2</module>--> 44 + <module>bmv2</module>
45 <module>corsa</module> 45 <module>corsa</module>
46 <module>optical</module> 46 <module>optical</module>
47 </modules> 47 </modules>
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-bmv2-protocol</artifactId> 22 <artifactId>onos-bmv2-protocol</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 <modelVersion>4.0.0</modelVersion> 27 <modelVersion>4.0.0</modelVersion>
27 28
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-bmv2-protocol</artifactId> 22 <artifactId>onos-bmv2-protocol</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 <modelVersion>4.0.0</modelVersion> 27 <modelVersion>4.0.0</modelVersion>
27 28
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-protocols</artifactId> 22 <artifactId>onos-protocols</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 <modelVersion>4.0.0</modelVersion> 27 <modelVersion>4.0.0</modelVersion>
27 28
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-bmv2-protocol</artifactId> 22 <artifactId>onos-bmv2-protocol</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 27
27 <modelVersion>4.0.0</modelVersion> 28 <modelVersion>4.0.0</modelVersion>
......
...@@ -22,7 +22,9 @@ ...@@ -22,7 +22,9 @@
22 <bundle>mvn:${project.groupId}/onos-bmv2-provider-device/${project.version}</bundle> 22 <bundle>mvn:${project.groupId}/onos-bmv2-provider-device/${project.version}</bundle>
23 <bundle>mvn:${project.groupId}/onos-bmv2-provider-packet/${project.version}</bundle> 23 <bundle>mvn:${project.groupId}/onos-bmv2-provider-packet/${project.version}</bundle>
24 <bundle>mvn:org.apache.thrift/libthrift/0.9.3</bundle> 24 <bundle>mvn:org.apache.thrift/libthrift/0.9.3</bundle>
25 - <bundle>mvn:${project.groupId}/onos-bmv2-protocol/${project.version}</bundle> 25 + <bundle>mvn:${project.groupId}/onos-bmv2-protocol-api/${project.version}</bundle>
26 + <bundle>mvn:${project.groupId}/onos-bmv2-protocol-ctl/${project.version}</bundle>
27 + <bundle>mvn:${project.groupId}/onos-bmv2-protocol-thrift-api/${project.version}</bundle>
26 </feature> 28 </feature>
27 </features> 29 </features>
28 30
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-bmv2-providers</artifactId> 22 <artifactId>onos-bmv2-providers</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 27
27 <modelVersion>4.0.0</modelVersion> 28 <modelVersion>4.0.0</modelVersion>
...@@ -33,7 +34,7 @@ ...@@ -33,7 +34,7 @@
33 34
34 <properties> 35 <properties>
35 <onos.app.name>org.onosproject.bmv2</onos.app.name> 36 <onos.app.name>org.onosproject.bmv2</onos.app.name>
36 - <onos.app.title>BMv2 Provider</onos.app.title> 37 + <onos.app.title>BMv2 Providers</onos.app.title>
37 <onos.app.category>Provider</onos.app.category> 38 <onos.app.category>Provider</onos.app.category>
38 </properties> 39 </properties>
39 40
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-bmv2-providers</artifactId> 22 <artifactId>onos-bmv2-providers</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 <modelVersion>4.0.0</modelVersion> 27 <modelVersion>4.0.0</modelVersion>
27 28
...@@ -33,12 +34,12 @@ ...@@ -33,12 +34,12 @@
33 <dependencies> 34 <dependencies>
34 <dependency> 35 <dependency>
35 <groupId>org.onosproject</groupId> 36 <groupId>org.onosproject</groupId>
36 - <artifactId>onos-drivers-bmv2</artifactId> 37 + <artifactId>onos-core-common</artifactId>
37 <version>${project.version}</version> 38 <version>${project.version}</version>
38 </dependency> 39 </dependency>
39 <dependency> 40 <dependency>
40 <groupId>org.onosproject</groupId> 41 <groupId>org.onosproject</groupId>
41 - <artifactId>onos-core-common</artifactId> 42 + <artifactId>onos-bmv2-protocol-api</artifactId>
42 <version>${project.version}</version> 43 <version>${project.version}</version>
43 </dependency> 44 </dependency>
44 </dependencies> 45 </dependencies>
......
...@@ -23,36 +23,38 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; ...@@ -23,36 +23,38 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
23 import org.jboss.netty.util.HashedWheelTimer; 23 import org.jboss.netty.util.HashedWheelTimer;
24 import org.jboss.netty.util.Timeout; 24 import org.jboss.netty.util.Timeout;
25 import org.jboss.netty.util.TimerTask; 25 import org.jboss.netty.util.TimerTask;
26 -import org.onlab.packet.ChassisId;
27 -import org.onlab.util.HexString;
28 import org.onlab.util.Timer; 26 import org.onlab.util.Timer;
29 -import org.onosproject.bmv2.api.runtime.Bmv2ControlPlaneServer;
30 import org.onosproject.bmv2.api.runtime.Bmv2Device; 27 import org.onosproject.bmv2.api.runtime.Bmv2Device;
31 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; 28 import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
32 -import org.onosproject.bmv2.ctl.Bmv2ThriftClient; 29 +import org.onosproject.bmv2.api.service.Bmv2Controller;
30 +import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
31 +import org.onosproject.bmv2.api.service.Bmv2DeviceListener;
33 import org.onosproject.common.net.AbstractDeviceProvider; 32 import org.onosproject.common.net.AbstractDeviceProvider;
34 import org.onosproject.core.ApplicationId; 33 import org.onosproject.core.ApplicationId;
35 import org.onosproject.core.CoreService; 34 import org.onosproject.core.CoreService;
36 import org.onosproject.incubator.net.config.basics.ConfigException; 35 import org.onosproject.incubator.net.config.basics.ConfigException;
37 -import org.onosproject.net.AnnotationKeys;
38 -import org.onosproject.net.DefaultAnnotations;
39 import org.onosproject.net.Device; 36 import org.onosproject.net.Device;
40 import org.onosproject.net.DeviceId; 37 import org.onosproject.net.DeviceId;
41 import org.onosproject.net.MastershipRole; 38 import org.onosproject.net.MastershipRole;
42 import org.onosproject.net.PortNumber; 39 import org.onosproject.net.PortNumber;
43 -import org.onosproject.net.behaviour.PortDiscovery;
44 import org.onosproject.net.config.ConfigFactory; 40 import org.onosproject.net.config.ConfigFactory;
45 import org.onosproject.net.config.NetworkConfigEvent; 41 import org.onosproject.net.config.NetworkConfigEvent;
46 import org.onosproject.net.config.NetworkConfigListener; 42 import org.onosproject.net.config.NetworkConfigListener;
47 import org.onosproject.net.config.NetworkConfigRegistry; 43 import org.onosproject.net.config.NetworkConfigRegistry;
48 -import org.onosproject.net.device.DefaultDeviceDescription;
49 import org.onosproject.net.device.DeviceDescription; 44 import org.onosproject.net.device.DeviceDescription;
45 +import org.onosproject.net.device.DeviceDescriptionDiscovery;
50 import org.onosproject.net.device.DeviceService; 46 import org.onosproject.net.device.DeviceService;
51 import org.onosproject.net.device.PortDescription; 47 import org.onosproject.net.device.PortDescription;
48 +import org.onosproject.net.device.PortStatistics;
49 +import org.onosproject.net.driver.DefaultDriverData;
50 +import org.onosproject.net.driver.DefaultDriverHandler;
51 +import org.onosproject.net.driver.Driver;
52 import org.onosproject.net.provider.ProviderId; 52 import org.onosproject.net.provider.ProviderId;
53 import org.slf4j.Logger; 53 import org.slf4j.Logger;
54 54
55 +import java.util.Collection;
55 import java.util.List; 56 import java.util.List;
57 +import java.util.Objects;
56 import java.util.concurrent.ConcurrentMap; 58 import java.util.concurrent.ConcurrentMap;
57 import java.util.concurrent.ExecutorService; 59 import java.util.concurrent.ExecutorService;
58 import java.util.concurrent.Executors; 60 import java.util.concurrent.Executors;
...@@ -60,9 +62,9 @@ import java.util.concurrent.TimeUnit; ...@@ -60,9 +62,9 @@ import java.util.concurrent.TimeUnit;
60 62
61 import static org.onlab.util.Tools.groupedThreads; 63 import static org.onlab.util.Tools.groupedThreads;
62 import static org.onosproject.bmv2.api.runtime.Bmv2Device.*; 64 import static org.onosproject.bmv2.api.runtime.Bmv2Device.*;
63 -import static org.onosproject.bmv2.ctl.Bmv2ThriftClient.forceDisconnectOf;
64 -import static org.onosproject.bmv2.ctl.Bmv2ThriftClient.ping;
65 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; 65 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
66 +import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.getPortStatistics;
67 +import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.initCounters;
66 import static org.slf4j.LoggerFactory.getLogger; 68 import static org.slf4j.LoggerFactory.getLogger;
67 69
68 /** 70 /**
...@@ -71,11 +73,24 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -71,11 +73,24 @@ import static org.slf4j.LoggerFactory.getLogger;
71 @Component(immediate = true) 73 @Component(immediate = true)
72 public class Bmv2DeviceProvider extends AbstractDeviceProvider { 74 public class Bmv2DeviceProvider extends AbstractDeviceProvider {
73 75
74 - private static final Logger LOG = getLogger(Bmv2DeviceProvider.class);
75 -
76 private static final String APP_NAME = "org.onosproject.bmv2"; 76 private static final String APP_NAME = "org.onosproject.bmv2";
77 - private static final String UNKNOWN = "unknown"; 77 +
78 - private static final int POLL_INTERVAL = 5; // seconds 78 + private static final int POLL_INTERVAL = 5_000; // milliseconds
79 +
80 + private final Logger log = getLogger(this.getClass());
81 +
82 + private final ExecutorService executorService = Executors
83 + .newFixedThreadPool(16, groupedThreads("onos/bmv2", "device-discovery", log));
84 +
85 + private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
86 +
87 + private final ConfigFactory cfgFactory = new InternalConfigFactory();
88 +
89 + private final ConcurrentMap<DeviceId, DeviceDescription> activeDevices = Maps.newConcurrentMap();
90 +
91 + private final DevicePoller devicePoller = new DevicePoller();
92 +
93 + private final InternalDeviceListener deviceListener = new InternalDeviceListener();
79 94
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected NetworkConfigRegistry netCfgService; 96 protected NetworkConfigRegistry netCfgService;
...@@ -87,16 +102,11 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -87,16 +102,11 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
87 protected DeviceService deviceService; 102 protected DeviceService deviceService;
88 103
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 - protected Bmv2ControlPlaneServer controlPlaneServer; 105 + protected Bmv2Controller controller;
91 106
92 - private final ExecutorService deviceDiscoveryExecutor = Executors 107 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 - .newFixedThreadPool(5, groupedThreads("onos/bmv2", "device-discovery", LOG)); 108 + protected Bmv2DeviceContextService contextService;
94 109
95 - private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
96 - private final ConfigFactory cfgFactory = new InternalConfigFactory();
97 - private final ConcurrentMap<DeviceId, Boolean> activeDevices = Maps.newConcurrentMap();
98 - private final DevicePoller devicePoller = new DevicePoller();
99 - private final InternalHelloListener helloListener = new InternalHelloListener();
100 private ApplicationId appId; 110 private ApplicationId appId;
101 111
102 /** 112 /**
...@@ -111,7 +121,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -111,7 +121,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
111 appId = coreService.registerApplication(APP_NAME); 121 appId = coreService.registerApplication(APP_NAME);
112 netCfgService.registerConfigFactory(cfgFactory); 122 netCfgService.registerConfigFactory(cfgFactory);
113 netCfgService.addListener(cfgListener); 123 netCfgService.addListener(cfgListener);
114 - controlPlaneServer.addHelloListener(helloListener); 124 + controller.addDeviceListener(deviceListener);
115 devicePoller.start(); 125 devicePoller.start();
116 super.activate(); 126 super.activate();
117 } 127 }
...@@ -119,16 +129,16 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -119,16 +129,16 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
119 @Override 129 @Override
120 protected void deactivate() { 130 protected void deactivate() {
121 devicePoller.stop(); 131 devicePoller.stop();
122 - controlPlaneServer.removeHelloListener(helloListener); 132 + controller.removeDeviceListener(deviceListener);
123 try { 133 try {
124 activeDevices.forEach((did, value) -> { 134 activeDevices.forEach((did, value) -> {
125 - deviceDiscoveryExecutor.execute(() -> disconnectDevice(did)); 135 + executorService.execute(() -> disconnectDevice(did));
126 }); 136 });
127 - deviceDiscoveryExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS); 137 + executorService.awaitTermination(1000, TimeUnit.MILLISECONDS);
128 } catch (InterruptedException e) { 138 } catch (InterruptedException e) {
129 - LOG.error("Device discovery threads did not terminate"); 139 + log.error("Device discovery threads did not terminate");
130 } 140 }
131 - deviceDiscoveryExecutor.shutdownNow(); 141 + executorService.shutdownNow();
132 netCfgService.unregisterConfigFactory(cfgFactory); 142 netCfgService.unregisterConfigFactory(cfgFactory);
133 netCfgService.removeListener(cfgListener); 143 netCfgService.removeListener(cfgListener);
134 super.deactivate(); 144 super.deactivate();
...@@ -137,14 +147,12 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -137,14 +147,12 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
137 @Override 147 @Override
138 public void triggerProbe(DeviceId deviceId) { 148 public void triggerProbe(DeviceId deviceId) {
139 // Asynchronously trigger probe task. 149 // Asynchronously trigger probe task.
140 - deviceDiscoveryExecutor.execute(() -> executeProbe(deviceId)); 150 + executorService.execute(() -> executeProbe(deviceId));
141 } 151 }
142 152
143 private void executeProbe(DeviceId did) { 153 private void executeProbe(DeviceId did) {
144 boolean reachable = isReachable(did); 154 boolean reachable = isReachable(did);
145 - LOG.debug("Probed device: id={}, reachable={}", 155 + log.debug("Probed device: id={}, reachable={}", did.toString(), reachable);
146 - did.toString(),
147 - reachable);
148 if (reachable) { 156 if (reachable) {
149 discoverDevice(did); 157 discoverDevice(did);
150 } else { 158 } else {
...@@ -154,85 +162,108 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -154,85 +162,108 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
154 162
155 @Override 163 @Override
156 public void roleChanged(DeviceId deviceId, MastershipRole newRole) { 164 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
157 - LOG.debug("roleChanged() is not yet implemented"); 165 + log.debug("roleChanged() is not yet implemented");
158 // TODO: implement mastership handling 166 // TODO: implement mastership handling
159 } 167 }
160 168
161 @Override 169 @Override
162 public boolean isReachable(DeviceId deviceId) { 170 public boolean isReachable(DeviceId deviceId) {
163 - return ping(deviceId); 171 + return controller.isReacheable(deviceId);
164 } 172 }
165 173
166 @Override 174 @Override
167 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) { 175 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
168 - LOG.debug("changePortState() is not yet implemented"); 176 + log.warn("changePortState() not supported");
169 - // TODO: implement port handling
170 } 177 }
171 178
172 private void discoverDevice(DeviceId did) { 179 private void discoverDevice(DeviceId did) {
173 - LOG.debug("Starting device discovery... deviceId={}", did); 180 + log.debug("Starting device discovery... deviceId={}", did);
174 - 181 + activeDevices.compute(did, (k, lastDescription) -> {
175 - // Atomically notify device to core and update port information. 182 + DeviceDescription thisDescription = getDeviceDescription(did);
176 - activeDevices.compute(did, (k, v) -> { 183 + if (thisDescription != null) {
177 - if (!deviceService.isAvailable(did)) { 184 + boolean descriptionChanged = lastDescription != null &&
178 - // Device not available in the core, connect it now. 185 + (!Objects.equals(thisDescription, lastDescription) ||
179 - DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder() 186 + !Objects.equals(thisDescription.annotations(), lastDescription.annotations()));
180 - .set(AnnotationKeys.PROTOCOL, SCHEME); 187 + if (descriptionChanged || !deviceService.isAvailable(did)) {
181 - dumpJsonConfigToAnnotations(did, annotationsBuilder); 188 + // Device description changed or device not available in the core.
182 - DeviceDescription descr = new DefaultDeviceDescription( 189 + if (contextService.notifyDeviceChange(did)) {
183 - did.uri(), Device.Type.SWITCH, MANUFACTURER, HW_VERSION, 190 + // Configuration is the expected one, we can proceed notifying the core.
184 - UNKNOWN, UNKNOWN, new ChassisId(), annotationsBuilder.build()); 191 + resetDeviceState(did);
185 - // Reset device state (cleanup entries, etc.) 192 + initPortCounters(did);
186 - resetDeviceState(did); 193 + providerService.deviceConnected(did, thisDescription);
187 - providerService.deviceConnected(did, descr); 194 + updatePortsAndStats(did);
195 + }
196 + }
197 + return thisDescription;
198 + } else {
199 + log.warn("Unable to get device description for {}", did);
200 + return lastDescription;
188 } 201 }
189 - updatePorts(did);
190 - return true;
191 }); 202 });
192 } 203 }
193 204
194 - private void dumpJsonConfigToAnnotations(DeviceId did, DefaultAnnotations.Builder builder) { 205 + private DeviceDescription getDeviceDescription(DeviceId did) {
195 - // TODO: store json config string somewhere else, possibly in a Bmv2Controller (see ONOS-4419) 206 + Device device = deviceService.getDevice(did);
207 + DeviceDescriptionDiscovery discovery = null;
208 + if (device == null) {
209 + // Device not yet in the core. Manually get a driver.
210 + Driver driver = driverService.getDriver(MANUFACTURER, HW_VERSION, SW_VERSION);
211 + if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
212 + discovery = driver.createBehaviour(new DefaultDriverHandler(new DefaultDriverData(driver, did)),
213 + DeviceDescriptionDiscovery.class);
214 + }
215 + } else if (device.is(DeviceDescriptionDiscovery.class)) {
216 + discovery = device.as(DeviceDescriptionDiscovery.class);
217 + }
218 + if (discovery == null) {
219 + log.warn("No DeviceDescriptionDiscovery behavior for device {}", did);
220 + return null;
221 + } else {
222 + return discovery.discoverDeviceDetails();
223 + }
224 + }
225 +
226 + private void resetDeviceState(DeviceId did) {
196 try { 227 try {
197 - String md5 = Bmv2ThriftClient.of(did).getJsonConfigMd5(); 228 + controller.getAgent(did).resetState();
198 - // Convert to hex string for readability.
199 - md5 = HexString.toHexString(md5.getBytes());
200 - String jsonString = Bmv2ThriftClient.of(did).dumpJsonConfig();
201 - builder.set("bmv2JsonConfigMd5", md5);
202 - builder.set("bmv2JsonConfigValue", jsonString);
203 } catch (Bmv2RuntimeException e) { 229 } catch (Bmv2RuntimeException e) {
204 - LOG.warn("Unable to dump device JSON config from device {}: {}", did, e.toString()); 230 + log.warn("Unable to reset {}: {}", did, e.toString());
205 } 231 }
206 } 232 }
207 233
208 - private void resetDeviceState(DeviceId did) { 234 + private void initPortCounters(DeviceId did) {
209 try { 235 try {
210 - Bmv2ThriftClient.of(did).resetState(); 236 + initCounters(controller.getAgent(did));
211 } catch (Bmv2RuntimeException e) { 237 } catch (Bmv2RuntimeException e) {
212 - LOG.warn("Unable to reset {}: {}", did, e.toString()); 238 + log.warn("Unable to init counter on {}: {}", did, e.explain());
213 } 239 }
214 } 240 }
215 241
216 - private void updatePorts(DeviceId did) { 242 + private void updatePortsAndStats(DeviceId did) {
217 Device device = deviceService.getDevice(did); 243 Device device = deviceService.getDevice(did);
218 - if (device.is(PortDiscovery.class)) { 244 + if (device.is(DeviceDescriptionDiscovery.class)) {
219 - PortDiscovery portConfig = device.as(PortDiscovery.class); 245 + DeviceDescriptionDiscovery discovery = device.as(DeviceDescriptionDiscovery.class);
220 - List<PortDescription> portDescriptions = portConfig.getPorts(); 246 + List<PortDescription> portDescriptions = discovery.discoverPortDetails();
221 - providerService.updatePorts(did, portDescriptions); 247 + if (portDescriptions != null) {
248 + providerService.updatePorts(did, portDescriptions);
249 + }
222 } else { 250 } else {
223 - LOG.warn("No PortDiscovery behavior for device {}", did); 251 + log.warn("No DeviceDescriptionDiscovery behavior for device {}", did);
252 + }
253 + try {
254 + Collection<PortStatistics> portStats = getPortStatistics(controller.getAgent(did),
255 + deviceService.getPorts(did));
256 + providerService.updatePortStatistics(did, portStats);
257 + } catch (Bmv2RuntimeException e) {
258 + log.warn("Unable to get port statistics for {}: {}", did, e.explain());
224 } 259 }
225 } 260 }
226 261
227 private void disconnectDevice(DeviceId did) { 262 private void disconnectDevice(DeviceId did) {
228 - LOG.debug("Trying to disconnect device from core... deviceId={}", did); 263 + log.debug("Trying to disconnect device from core... deviceId={}", did);
229 -
230 - // Atomically disconnect device.
231 activeDevices.compute(did, (k, v) -> { 264 activeDevices.compute(did, (k, v) -> {
232 if (deviceService.isAvailable(did)) { 265 if (deviceService.isAvailable(did)) {
233 providerService.deviceDisconnected(did); 266 providerService.deviceDisconnected(did);
234 - // Make sure to close the transport session with device. Do we really need this?
235 - forceDisconnectOf(did);
236 } 267 }
237 return null; 268 return null;
238 }); 269 });
...@@ -269,10 +300,10 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -269,10 +300,10 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
269 triggerProbe(bmv2Device.asDeviceId()); 300 triggerProbe(bmv2Device.asDeviceId());
270 }); 301 });
271 } catch (ConfigException e) { 302 } catch (ConfigException e) {
272 - LOG.error("Unable to read config: " + e); 303 + log.error("Unable to read config: " + e);
273 } 304 }
274 } else { 305 } else {
275 - LOG.error("Unable to read config (was null)"); 306 + log.error("Unable to read config (was null)");
276 } 307 }
277 } 308 }
278 309
...@@ -285,18 +316,18 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -285,18 +316,18 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
285 } 316 }
286 317
287 /** 318 /**
288 - * Listener triggered by Bmv2ControlPlaneServer each time a hello message is received. 319 + * Listener triggered by the BMv2 controller each time a hello message is received.
289 */ 320 */
290 - private class InternalHelloListener implements Bmv2ControlPlaneServer.HelloListener { 321 + private class InternalDeviceListener implements Bmv2DeviceListener {
291 @Override 322 @Override
292 - public void handleHello(Bmv2Device device) { 323 + public void handleHello(Bmv2Device device, int instanceId, String jsonConfigMd5) {
293 log.debug("Received hello from {}", device); 324 log.debug("Received hello from {}", device);
294 triggerProbe(device.asDeviceId()); 325 triggerProbe(device.asDeviceId());
295 } 326 }
296 } 327 }
297 328
298 /** 329 /**
299 - * Task that periodically trigger device probes. 330 + * Task that periodically trigger device probes to check for device status and update port informations.
300 */ 331 */
301 private class DevicePoller implements TimerTask { 332 private class DevicePoller implements TimerTask {
302 333
...@@ -304,20 +335,31 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -304,20 +335,31 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
304 private Timeout timeout; 335 private Timeout timeout;
305 336
306 @Override 337 @Override
307 - public void run(Timeout timeout) throws Exception { 338 + public void run(Timeout tout) throws Exception {
308 - if (timeout.isCancelled()) { 339 + if (tout.isCancelled()) {
309 return; 340 return;
310 } 341 }
311 - log.debug("Executing polling on {} devices...", activeDevices.size()); 342 + activeDevices.keySet()
312 - activeDevices.forEach((did, value) -> triggerProbe(did)); 343 + .stream()
313 - timeout.getTimer().newTimeout(this, POLL_INTERVAL, TimeUnit.SECONDS); 344 + // Filter out devices not yet created in the core.
345 + .filter(did -> deviceService.getDevice(did) != null)
346 + .forEach(did -> executorService.execute(() -> pollingTask(did)));
347 + tout.getTimer().newTimeout(this, POLL_INTERVAL, TimeUnit.MILLISECONDS);
348 + }
349 +
350 + private void pollingTask(DeviceId deviceId) {
351 + if (isReachable(deviceId)) {
352 + updatePortsAndStats(deviceId);
353 + } else {
354 + disconnectDevice(deviceId);
355 + }
314 } 356 }
315 357
316 /** 358 /**
317 * Starts the collector. 359 * Starts the collector.
318 */ 360 */
319 - synchronized void start() { 361 + synchronized void start() {
320 - LOG.info("Starting device poller..."); 362 + log.info("Starting device poller...");
321 timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS); 363 timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
322 } 364 }
323 365
...@@ -325,7 +367,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -325,7 +367,7 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
325 * Stops the collector. 367 * Stops the collector.
326 */ 368 */
327 synchronized void stop() { 369 synchronized void stop() {
328 - LOG.info("Stopping device poller..."); 370 + log.info("Stopping device poller...");
329 timeout.cancel(); 371 timeout.cancel();
330 } 372 }
331 } 373 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.provider.bmv2.device.impl;
18 +
19 +import com.google.common.collect.Lists;
20 +import org.apache.commons.lang3.tuple.Pair;
21 +import org.onosproject.bmv2.api.runtime.Bmv2Action;
22 +import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
23 +import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
24 +import org.onosproject.net.Port;
25 +import org.onosproject.net.device.DefaultPortStatistics;
26 +import org.onosproject.net.device.PortStatistics;
27 +import org.slf4j.Logger;
28 +import org.slf4j.LoggerFactory;
29 +
30 +import java.util.Collection;
31 +import java.util.List;
32 +
33 +/**
34 + * Utility class to read port statistics from a BMv2 device.
35 + */
36 +final class Bmv2PortStatisticsGetter {
37 +
38 + // TODO: make counters configuration dependent
39 +
40 + private static final String TABLE_NAME = "port_count_table";
41 + private static final String ACTION_NAME = "count_packet";
42 + private static final String EGRESS_COUNTER = "egress_port_counter";
43 + private static final String INGRESS_COUNTER = "ingress_port_counter";
44 +
45 + private static final Logger log = LoggerFactory.getLogger(Bmv2PortStatisticsGetter.class);
46 +
47 + private Bmv2PortStatisticsGetter() {
48 + // ban constructor.
49 + }
50 +
51 + /**
52 + * Returns a collection of port statistics for given ports using the given BMv2 device agent.
53 + *
54 + * @param deviceAgent a device agent
55 + * @param ports a collection of ports
56 + * @return a collection of port statistics
57 + */
58 + static Collection<PortStatistics> getPortStatistics(Bmv2DeviceAgent deviceAgent, Collection<Port> ports) {
59 +
60 + List<PortStatistics> ps = Lists.newArrayList();
61 +
62 + for (Port port : ports) {
63 + int portNumber = (int) port.number().toLong();
64 + try {
65 + Pair<Long, Long> egressCounter = deviceAgent.readCounter(EGRESS_COUNTER, portNumber);
66 + Pair<Long, Long> ingressCounter = deviceAgent.readCounter(INGRESS_COUNTER, portNumber);
67 + ps.add(DefaultPortStatistics.builder()
68 + .setPort(portNumber)
69 + .setBytesSent(egressCounter.getLeft())
70 + .setPacketsSent(egressCounter.getRight())
71 + .setBytesReceived(ingressCounter.getLeft())
72 + .setPacketsReceived(ingressCounter.getRight())
73 + .build());
74 + } catch (Bmv2RuntimeException e) {
75 + log.info("Unable to read port statistics from {}: {}", port, e.explain());
76 + }
77 + }
78 +
79 + return ps;
80 + }
81 +
82 + /**
83 + * Initialize port counters on the given device agent.
84 + *
85 + * @param deviceAgent a device agent.
86 + */
87 + static void initCounters(Bmv2DeviceAgent deviceAgent) {
88 + try {
89 + deviceAgent.setTableDefaultAction(TABLE_NAME, Bmv2Action.builder().withName(ACTION_NAME).build());
90 + } catch (Bmv2RuntimeException e) {
91 + log.debug("Failed to provision counters on {}: {}", deviceAgent.deviceId(), e.explain());
92 + }
93 + }
94 +}
95 +
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-bmv2-providers</artifactId> 22 <artifactId>onos-bmv2-providers</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 <modelVersion>4.0.0</modelVersion> 27 <modelVersion>4.0.0</modelVersion>
27 28
...@@ -34,12 +35,12 @@ ...@@ -34,12 +35,12 @@
34 <dependencies> 35 <dependencies>
35 <dependency> 36 <dependency>
36 <groupId>org.onosproject</groupId> 37 <groupId>org.onosproject</groupId>
37 - <artifactId>onos-drivers-bmv2</artifactId> 38 + <artifactId>onos-core-common</artifactId>
38 <version>${project.version}</version> 39 <version>${project.version}</version>
39 </dependency> 40 </dependency>
40 <dependency> 41 <dependency>
41 <groupId>org.onosproject</groupId> 42 <groupId>org.onosproject</groupId>
42 - <artifactId>onos-core-common</artifactId> 43 + <artifactId>onos-bmv2-protocol-api</artifactId>
43 <version>${project.version}</version> 44 <version>${project.version}</version>
44 </dependency> 45 </dependency>
45 </dependencies> 46 </dependencies>
......
...@@ -23,12 +23,14 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -23,12 +23,14 @@ import org.apache.felix.scr.annotations.Reference;
23 import org.apache.felix.scr.annotations.ReferenceCardinality; 23 import org.apache.felix.scr.annotations.ReferenceCardinality;
24 import org.onlab.packet.Ethernet; 24 import org.onlab.packet.Ethernet;
25 import org.onlab.util.ImmutableByteSequence; 25 import org.onlab.util.ImmutableByteSequence;
26 -import org.onosproject.bmv2.api.runtime.Bmv2ControlPlaneServer;
27 import org.onosproject.bmv2.api.runtime.Bmv2Device; 26 import org.onosproject.bmv2.api.runtime.Bmv2Device;
27 +import org.onosproject.bmv2.api.service.Bmv2Controller;
28 +import org.onosproject.bmv2.api.service.Bmv2PacketListener;
28 import org.onosproject.core.CoreService; 29 import org.onosproject.core.CoreService;
29 import org.onosproject.net.ConnectPoint; 30 import org.onosproject.net.ConnectPoint;
30 import org.onosproject.net.Device; 31 import org.onosproject.net.Device;
31 import org.onosproject.net.DeviceId; 32 import org.onosproject.net.DeviceId;
33 +import org.onosproject.net.Port;
32 import org.onosproject.net.PortNumber; 34 import org.onosproject.net.PortNumber;
33 import org.onosproject.net.device.DeviceService; 35 import org.onosproject.net.device.DeviceService;
34 import org.onosproject.net.flow.DefaultTrafficTreatment; 36 import org.onosproject.net.flow.DefaultTrafficTreatment;
...@@ -49,6 +51,12 @@ import org.slf4j.Logger; ...@@ -49,6 +51,12 @@ import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory; 51 import org.slf4j.LoggerFactory;
50 52
51 import java.nio.ByteBuffer; 53 import java.nio.ByteBuffer;
54 +import java.util.Optional;
55 +
56 +import static org.onosproject.net.PortNumber.FLOOD;
57 +import static org.onosproject.net.flow.DefaultTrafficTreatment.emptyTreatment;
58 +import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
59 +import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
52 60
53 /** 61 /**
54 * Implementation of a packet provider for BMv2. 62 * Implementation of a packet provider for BMv2.
...@@ -56,11 +64,11 @@ import java.nio.ByteBuffer; ...@@ -56,11 +64,11 @@ import java.nio.ByteBuffer;
56 @Component(immediate = true) 64 @Component(immediate = true)
57 public class Bmv2PacketProvider extends AbstractProvider implements PacketProvider { 65 public class Bmv2PacketProvider extends AbstractProvider implements PacketProvider {
58 66
59 - private static final Logger LOG = LoggerFactory.getLogger(Bmv2PacketProvider.class); 67 + private final Logger log = LoggerFactory.getLogger(Bmv2PacketProvider.class);
60 private static final String APP_NAME = "org.onosproject.bmv2"; 68 private static final String APP_NAME = "org.onosproject.bmv2";
61 69
62 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 - protected Bmv2ControlPlaneServer controlPlaneServer; 71 + protected Bmv2Controller controller;
64 72
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected CoreService coreService; 74 protected CoreService coreService;
...@@ -86,28 +94,28 @@ public class Bmv2PacketProvider extends AbstractProvider implements PacketProvid ...@@ -86,28 +94,28 @@ public class Bmv2PacketProvider extends AbstractProvider implements PacketProvid
86 protected void activate() { 94 protected void activate() {
87 providerService = providerRegistry.register(this); 95 providerService = providerRegistry.register(this);
88 coreService.registerApplication(APP_NAME); 96 coreService.registerApplication(APP_NAME);
89 - controlPlaneServer.addPacketListener(packetListener); 97 + controller.addPacketListener(packetListener);
90 - LOG.info("Started"); 98 + log.info("Started");
91 } 99 }
92 100
93 @Deactivate 101 @Deactivate
94 public void deactivate() { 102 public void deactivate() {
95 - controlPlaneServer.removePacketListener(packetListener); 103 + controller.removePacketListener(packetListener);
96 providerRegistry.unregister(this); 104 providerRegistry.unregister(this);
97 providerService = null; 105 providerService = null;
98 - LOG.info("Stopped"); 106 + log.info("Stopped");
99 } 107 }
100 108
101 @Override 109 @Override
102 public void emit(OutboundPacket packet) { 110 public void emit(OutboundPacket packet) {
103 if (packet != null) { 111 if (packet != null) {
104 - DeviceId did = packet.sendThrough(); 112 + DeviceId deviceId = packet.sendThrough();
105 - Device device = deviceService.getDevice(did); 113 + Device device = deviceService.getDevice(deviceId);
106 if (device.is(PacketProgrammable.class)) { 114 if (device.is(PacketProgrammable.class)) {
107 PacketProgrammable packetProgrammable = device.as(PacketProgrammable.class); 115 PacketProgrammable packetProgrammable = device.as(PacketProgrammable.class);
108 packetProgrammable.emit(packet); 116 packetProgrammable.emit(packet);
109 } else { 117 } else {
110 - LOG.info("Unable to send packet, no PacketProgrammable behavior for device {}", did); 118 + log.info("No PacketProgrammable behavior for device {}", deviceId);
111 } 119 }
112 } 120 }
113 } 121 }
...@@ -117,47 +125,75 @@ public class Bmv2PacketProvider extends AbstractProvider implements PacketProvid ...@@ -117,47 +125,75 @@ public class Bmv2PacketProvider extends AbstractProvider implements PacketProvid
117 */ 125 */
118 private class Bmv2PacketContext extends DefaultPacketContext { 126 private class Bmv2PacketContext extends DefaultPacketContext {
119 127
120 - public Bmv2PacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) { 128 + Bmv2PacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) {
121 super(time, inPkt, outPkt, block); 129 super(time, inPkt, outPkt, block);
122 } 130 }
123 131
124 @Override 132 @Override
125 public void send() { 133 public void send() {
126 - if (!this.block()) { 134 +
127 - if (this.outPacket().treatment() == null) { 135 + if (this.block()) {
128 - TrafficTreatment treatment = (this.treatmentBuilder() == null) 136 + log.info("Unable to send, packet context not blocked");
129 - ? DefaultTrafficTreatment.emptyTreatment() 137 + return;
130 - : this.treatmentBuilder().build(); 138 + }
131 - OutboundPacket newPkt = new DefaultOutboundPacket(this.outPacket().sendThrough(), 139 +
132 - treatment, 140 + DeviceId deviceId = outPacket().sendThrough();
133 - this.outPacket().data()); 141 + ByteBuffer rawData = outPacket().data();
134 - emit(newPkt); 142 +
135 - } else { 143 + TrafficTreatment treatment;
136 - emit(outPacket()); 144 + if (outPacket().treatment() == null) {
137 - } 145 + treatment = (treatmentBuilder() == null) ? emptyTreatment() : treatmentBuilder().build();
146 + } else {
147 + treatment = outPacket().treatment();
148 + }
149 +
150 + // BMv2 doesn't support FLOOD for packet-outs.
151 + // Workaround here is to perform multiple emits, one for each device port != packet inPort.
152 + Optional<OutputInstruction> floodInst = treatment.allInstructions()
153 + .stream()
154 + .filter(i -> i.type().equals(OUTPUT))
155 + .map(i -> (OutputInstruction) i)
156 + .filter(i -> i.port().equals(FLOOD))
157 + .findAny();
158 +
159 + if (floodInst.isPresent() && treatment.allInstructions().size() == 1) {
160 + // Only one instruction and is FLOOD. Do the trick.
161 + PortNumber inPort = inPacket().receivedFrom().port();
162 + deviceService.getPorts(outPacket().sendThrough())
163 + .stream()
164 + .map(Port::number)
165 + .filter(port -> !port.equals(inPort))
166 + .map(outPort -> DefaultTrafficTreatment.builder().setOutput(outPort).build())
167 + .map(outTreatment -> new DefaultOutboundPacket(deviceId, outTreatment, rawData))
168 + .forEach(Bmv2PacketProvider.this::emit);
138 } else { 169 } else {
139 - LOG.info("Unable to send, packet context not blocked"); 170 + // Not FLOOD treatment, what to do is up to driver.
171 + emit(new DefaultOutboundPacket(deviceId, treatment, rawData));
140 } 172 }
141 } 173 }
142 } 174 }
143 175
144 /** 176 /**
145 - * Internal packet listener to get packet events from the Bmv2ControlPlaneServer. 177 + * Internal packet listener to handle packet-in events received from the BMv2 controller.
146 */ 178 */
147 - private class InternalPacketListener implements Bmv2ControlPlaneServer.PacketListener { 179 + private class InternalPacketListener implements Bmv2PacketListener {
180 +
148 @Override 181 @Override
149 public void handlePacketIn(Bmv2Device device, int inputPort, long reason, int tableId, int contextId, 182 public void handlePacketIn(Bmv2Device device, int inputPort, long reason, int tableId, int contextId,
150 ImmutableByteSequence packet) { 183 ImmutableByteSequence packet) {
184 + Ethernet ethPkt = new Ethernet();
185 + ethPkt.deserialize(packet.asArray(), 0, packet.size());
151 186
152 - Ethernet eth = new Ethernet(); 187 + DeviceId deviceId = device.asDeviceId();
153 - eth.deserialize(packet.asArray(), 0, packet.size()); 188 + ConnectPoint receivedFrom = new ConnectPoint(deviceId, PortNumber.portNumber(inputPort));
189 +
190 + ByteBuffer rawData = ByteBuffer.wrap(packet.asArray());
191 +
192 + InboundPacket inPkt = new DefaultInboundPacket(receivedFrom, ethPkt, rawData);
193 + OutboundPacket outPkt = new DefaultOutboundPacket(deviceId, null, rawData);
154 194
155 - InboundPacket inPkt = new DefaultInboundPacket(new ConnectPoint(device.asDeviceId(),
156 - PortNumber.portNumber(inputPort)),
157 - eth, ByteBuffer.wrap(packet.asArray()));
158 - OutboundPacket outPkt = new DefaultOutboundPacket(device.asDeviceId(), null,
159 - ByteBuffer.wrap(packet.asArray()));
160 PacketContext pktCtx = new Bmv2PacketContext(System.currentTimeMillis(), inPkt, outPkt, false); 195 PacketContext pktCtx = new Bmv2PacketContext(System.currentTimeMillis(), inPkt, outPkt, false);
196 +
161 providerService.processPacket(pktCtx); 197 providerService.processPacket(pktCtx);
162 } 198 }
163 } 199 }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 <artifactId>onos-providers</artifactId> 22 <artifactId>onos-providers</artifactId>
23 <groupId>org.onosproject</groupId> 23 <groupId>org.onosproject</groupId>
24 <version>1.6.0-SNAPSHOT</version> 24 <version>1.6.0-SNAPSHOT</version>
25 + <relativePath>../pom.xml</relativePath>
25 </parent> 26 </parent>
26 27
27 <modelVersion>4.0.0</modelVersion> 28 <modelVersion>4.0.0</modelVersion>
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
46 <module>lldpcommon</module> 46 <module>lldpcommon</module>
47 <module>lldp</module> 47 <module>lldp</module>
48 <module>netcfglinks</module> 48 <module>netcfglinks</module>
49 - <!--<module>bmv2</module>--> 49 + <module>bmv2</module>
50 <module>isis</module> 50 <module>isis</module>
51 </modules> 51 </modules>
52 52
......