ONOS-4410 Implemented PacketProgrammable and Pipeliner behaviors in the
BMv2 driver
Also other minor fixes / refactorings
Change-Id: I2205890b76471e8e8490beccd6b36e5358f8d407
diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
new file mode 100644
index 0000000..082d1d6
--- /dev/null
+++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2FlowRuleProgrammable.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.bmv2;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.onosproject.bmv2.api.runtime.Bmv2Client;
+import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
+import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
+import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
+import org.onosproject.bmv2.ctl.Bmv2ThriftClient;
+import org.onosproject.drivers.bmv2.translators.Bmv2DefaultFlowRuleTranslator;
+import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslator;
+import org.onosproject.drivers.bmv2.translators.Bmv2FlowRuleTranslatorException;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Flow rule programmable device behaviour implementation for BMv2.
+ */
+public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour
+ implements FlowRuleProgrammable {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(Bmv2FlowRuleProgrammable.class);
+ // There's no Bmv2 client method to poll flow entries from the device device. gitNeed a local store.
+ private static final ConcurrentMap<Triple<DeviceId, String, Bmv2MatchKey>, Pair<Long, FlowEntry>>
+ ENTRIES_MAP = Maps.newConcurrentMap();
+ private static final Bmv2FlowRuleTranslator TRANSLATOR = new Bmv2DefaultFlowRuleTranslator();
+
+ @Override
+ public Collection<FlowEntry> getFlowEntries() {
+
+ DeviceId deviceId = handler().data().deviceId();
+
+ List<FlowEntry> entryList = Lists.newArrayList();
+
+ // FIXME: improve this, e.g. might store a separate Map<DeviceId, Collection<FlowEntry>>
+ ENTRIES_MAP.forEach((key, value) -> {
+ if (key.getLeft() == deviceId && value != null) {
+ entryList.add(value.getRight());
+ }
+ });
+
+ return Collections.unmodifiableCollection(entryList);
+ }
+
+ @Override
+ public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+
+ return processFlowRules(rules, Operation.APPLY);
+ }
+
+ @Override
+ public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+
+ return processFlowRules(rules, Operation.REMOVE);
+ }
+
+ private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
+
+ DeviceId deviceId = handler().data().deviceId();
+
+ Bmv2Client deviceClient;
+ try {
+ deviceClient = Bmv2ThriftClient.of(deviceId);
+ } catch (Bmv2RuntimeException e) {
+ LOG.error("Failed to connect to Bmv2 device", e);
+ return Collections.emptyList();
+ }
+
+ List<FlowRule> processedFlowRules = Lists.newArrayList();
+
+ for (FlowRule rule : rules) {
+
+ Bmv2TableEntry bmv2Entry;
+
+ try {
+ bmv2Entry = TRANSLATOR.translate(rule);
+ } catch (Bmv2FlowRuleTranslatorException e) {
+ LOG.error("Unable to translate flow rule: {}", e.getMessage());
+ continue;
+ }
+
+ String tableName = bmv2Entry.tableName();
+ Triple<DeviceId, String, Bmv2MatchKey> entryKey = Triple.of(deviceId, tableName, bmv2Entry.matchKey());
+
+ /*
+ From here on threads are synchronized over entryKey, i.e. serialize operations
+ over the same matchKey of a specific table and device.
+ */
+ ENTRIES_MAP.compute(entryKey, (key, value) -> {
+ try {
+ if (operation == Operation.APPLY) {
+ // Apply entry
+ long entryId;
+ if (value == null) {
+ // New entry
+ entryId = deviceClient.addTableEntry(bmv2Entry);
+ } else {
+ // Existing entry
+ entryId = value.getKey();
+ // FIXME: check if priority or timeout changed
+ // In this case we should to re-add the entry (not modify)
+ deviceClient.modifyTableEntry(tableName, entryId, bmv2Entry.action());
+ }
+ // TODO: evaluate flow entry life, bytes and packets
+ FlowEntry flowEntry = new DefaultFlowEntry(
+ rule, FlowEntry.FlowEntryState.ADDED, 0, 0, 0);
+ value = Pair.of(entryId, flowEntry);
+ } else {
+ // Remove entry
+ if (value == null) {
+ // Entry not found in map, how come?
+ LOG.debug("Trying to remove entry, but entry ID not found: " + entryKey);
+ } else {
+ deviceClient.deleteTableEntry(tableName, value.getKey());
+ value = null;
+ }
+ }
+ // If here, no exceptions... things went well :)
+ processedFlowRules.add(rule);
+ } catch (Bmv2RuntimeException e) {
+ LOG.error("Unable to " + operation.name().toLowerCase() + " flow rule", e);
+ } catch (Exception e) {
+ LOG.error("Uncaught exception while processing flow rule", e);
+ }
+ return value;
+ });
+ }
+
+ return processedFlowRules;
+ }
+
+ private enum Operation {
+ APPLY, REMOVE
+ }
+}
\ No newline at end of file