P4-related cherry-picks for 1.12
Squashed. Includes the following commits from master:
8b19a07297 Fixed onos.py
74174bf177 Fix 'unable to translate flow rule' in p4-tutorial icmpdropper
4555c5f308 Minor refactoring of BMv2 mininet scripts
07b2b70f53 Refactored PI-ECMP app to use action profiles of basic.p4
6ffd3772b7 ONOS-7050 First stab at PI translation store
2d4271fc20 ONOS-7050 Refactored PI translation service and store
3874b44821 ONOS-7050 Refactored P4Runtime FRP to use distributed stores
41efe435be ONOS-7050 Refactored P4Runtime GP to use distributed stores
806f7b7418 ONOS-6810 Implement Mastership handling in general DeviceProvider
c7922a4b40 ONOS-7267 Fix pipeconf UI
Change-Id: I279b6477f48ebec768b494799feb12faadbd559c
diff --git a/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/IcmpDropper.java b/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/IcmpDropper.java
index e1f54f6..ce1c682 100644
--- a/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/IcmpDropper.java
+++ b/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/IcmpDropper.java
@@ -116,7 +116,7 @@
}
private void installDropRule(DeviceId deviceId) {
- PiMatchFieldId ipv4ProtoFieldId = PiMatchFieldId.of("ipv4.protocol");
+ PiMatchFieldId ipv4ProtoFieldId = PiMatchFieldId.of("hdr.ipv4.protocol");
PiActionId dropActionId = PiActionId.of("_drop");
PiCriterion piCriterion = PiCriterion.builder()
diff --git a/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java b/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java
index 60c0e38..7c91bf3 100644
--- a/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java
+++ b/apps/pi-demo/common/src/main/java/org/onosproject/pi/demo/app/common/AbstractUpgradableFabricApp.java
@@ -16,7 +16,6 @@
package org.onosproject.pi.demo.app.common;
-import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -43,6 +42,7 @@
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.group.GroupService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipeconfId;
@@ -55,6 +55,7 @@
import org.onosproject.net.topology.TopologyVertex;
import org.slf4j.Logger;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -81,17 +82,19 @@
import static org.slf4j.LoggerFactory.getLogger;
/**
- * Abstract implementation of an app providing fabric connectivity for a 2-stage Clos topology of P4Runtime devices.
+ * Abstract implementation of an app providing fabric connectivity for a 2-stage
+ * Clos topology of P4Runtime devices.
*/
@Component(immediate = true)
public abstract class AbstractUpgradableFabricApp {
- private static final Map<String, AbstractUpgradableFabricApp> APP_HANDLES = Maps.newConcurrentMap();
+ private static final Map<String, AbstractUpgradableFabricApp>
+ APP_HANDLES = Maps.newConcurrentMap();
// TOPO_SIZE should be the same of the --size argument when running bmv2-demo.py
private static final int TOPO_SIZE = 2;
private static final boolean WITH_IMBALANCED_STRIPING = false;
- protected static final int HASHED_LINKS = TOPO_SIZE + (WITH_IMBALANCED_STRIPING ? 1 : 0);
+ private static final int HASHED_LINKS = TOPO_SIZE + (WITH_IMBALANCED_STRIPING ? 1 : 0);
private static final int FLOW_PRIORITY = 100;
private static final int CHECK_TOPOLOGY_INTERVAL_SECONDS = 5;
@@ -122,6 +125,9 @@
private FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupService groupService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ApplicationAdminService appService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -137,7 +143,7 @@
private AbstractUpgradableFabricApp otherApp;
private boolean flowRuleGenerated = false;
- private ApplicationId appId;
+ protected ApplicationId appId;
private Collection<PiPipeconf> appPipeconfs;
@@ -156,7 +162,8 @@
* @param appName app name
* @param appPipeconfs collection of compatible pipeconfs
*/
- protected AbstractUpgradableFabricApp(String appName, Collection<PiPipeconf> appPipeconfs) {
+ protected AbstractUpgradableFabricApp(String appName,
+ Collection<PiPipeconf> appPipeconfs) {
this.appName = checkNotNull(appName);
this.appPipeconfs = checkNotNull(appPipeconfs);
checkArgument(appPipeconfs.size() > 0, "appPipeconfs cannot have size 0");
@@ -171,11 +178,13 @@
if (APP_HANDLES.size() > 0) {
if (APP_HANDLES.size() > 1) {
- throw new IllegalStateException("Found more than 1 active app handles");
+ throw new IllegalStateException(
+ "Found more than 1 active app handles");
}
otherAppFound = true;
otherApp = APP_HANDLES.values().iterator().next();
- log.info("Found other fabric app active, signaling to freeze to {}...", otherApp.appName);
+ log.info("Found other fabric app active, signaling to freeze to {}...",
+ otherApp.appName);
otherApp.setAppFreezed(true);
}
@@ -221,12 +230,12 @@
pipeconfFlags = Maps.newConcurrentMap();
}
- /*
- Schedules a thread that periodically checks the topology, as soon as it corresponds to the expected
- one, it generates the necessary flow rules and starts the deploy process on each device.
- */
- scheduledExecutorService.scheduleAtFixedRate(this::checkTopologyAndGenerateFlowRules,
- 0, CHECK_TOPOLOGY_INTERVAL_SECONDS, TimeUnit.SECONDS);
+ // Schedules a thread that periodically checks the topology, as soon as
+ // it corresponds to the expected one, it generates the necessary flow
+ // rules and starts the deploy process on each device.
+ scheduledExecutorService.scheduleAtFixedRate(
+ this::checkTopologyAndGenerateFlowRules,
+ 0, CHECK_TOPOLOGY_INTERVAL_SECONDS, TimeUnit.SECONDS);
}
private void setAppFreezed(boolean appFreezed) {
@@ -239,7 +248,8 @@
}
/**
- * Perform device initialization. Returns true if the operation was successful, false otherwise.
+ * Perform device initialization. Returns true if the operation was
+ * successful, false otherwise.
*
* @param deviceId a device id
* @return a boolean value
@@ -247,8 +257,8 @@
public abstract boolean initDevice(DeviceId deviceId);
/**
- * Generates a list of flow rules for the given leaf switch, source host, destination hosts, spine switches and
- * topology.
+ * Generates a list of flow rules for the given leaf switch, source host,
+ * destination hosts, spine switches and topology.
*
* @param leaf a leaf device id
* @param srcHost a source host
@@ -258,12 +268,15 @@
* @return a list of flow rules
* @throws FlowRuleGeneratorException if flow rules cannot be generated
*/
- public abstract List<FlowRule> generateLeafRules(DeviceId leaf, Host srcHost, Collection<Host> dstHosts,
- Collection<DeviceId> spines, Topology topology)
+ public abstract List<FlowRule> generateLeafRules(DeviceId leaf, Host srcHost,
+ Set<Host> dstHosts,
+ Set<DeviceId> spines,
+ Topology topology)
throws FlowRuleGeneratorException;
/**
- * Generates a list of flow rules for the given spine switch, destination hosts and topology.
+ * Generates a list of flow rules for the given spine switch, destination
+ * hosts and topology.
*
* @param deviceId a spine device id
* @param dstHosts a collection of destination hosts
@@ -271,7 +284,9 @@
* @return a list of flow rules
* @throws FlowRuleGeneratorException if flow rules cannot be generated
*/
- public abstract List<FlowRule> generateSpineRules(DeviceId deviceId, Collection<Host> dstHosts, Topology topology)
+ public abstract List<FlowRule> generateSpineRules(DeviceId deviceId,
+ Set<Host> dstHosts,
+ Topology topology)
throws FlowRuleGeneratorException;
private void deployAllDevices() {
@@ -301,7 +316,7 @@
*
* @param device a device
*/
- public void deployDevice(Device device) {
+ private void deployDevice(Device device) {
DeviceId deviceId = device.id();
@@ -317,7 +332,7 @@
pipeconfFlags.put(device.id(), true);
} else {
log.warn("Wrong pipeconf for {}, expecting {}, but found {}, aborting deploy",
- deviceId, MoreObjects.toStringHelper(appPipeconfs),
+ deviceId, Arrays.toString(appPipeconfs.toArray()),
piPipeconfService.ofDevice(deviceId).get());
return;
}
@@ -331,7 +346,8 @@
// Install rules.
if (!ruleFlags.getOrDefault(deviceId, false) &&
deviceFlowRules.containsKey(deviceId)) {
- log.info("Installing {} rules for {}...", deviceFlowRules.get(deviceId).size(), deviceId);
+ log.info("Installing {} rules for {}...",
+ deviceFlowRules.get(deviceId).size(), deviceId);
installFlowRules(deviceFlowRules.get(deviceId));
ruleFlags.put(deviceId, true);
}
@@ -352,7 +368,8 @@
}
/**
- * Generates flow rules to provide host-to-host connectivity for the given topology and hosts.
+ * Generates flow rules to provide host-to-host connectivity for the given
+ * topology and hosts.
*/
private synchronized void checkTopologyAndGenerateFlowRules() {
@@ -374,7 +391,7 @@
.forEach(did -> (isSpine(did, topo) ? spines : leafs).add(did));
if (spines.size() != TOPO_SIZE || leafs.size() != TOPO_SIZE) {
- log.info("Invalid leaf/spine switches count, aborting... > leafCount={}, spineCount={}",
+ log.info("Invalid leaf/spine count, aborting... > leafCount={}, spineCount={}",
spines.size(), leafs.size());
return;
}
@@ -383,7 +400,8 @@
int portCount = deviceService.getPorts(did).size();
// Expected port count: num leafs + 1 redundant leaf link (if imbalanced)
if (portCount != HASHED_LINKS) {
- log.info("Invalid port count for spine, aborting... > deviceId={}, portCount={}", did, portCount);
+ log.info("Invalid port count for spine, aborting... > deviceId={}, portCount={}",
+ did, portCount);
return;
}
}
@@ -391,7 +409,8 @@
int portCount = deviceService.getPorts(did).size();
// Expected port count: num spines + host port + 1 redundant spine link
if (portCount != HASHED_LINKS + 1) {
- log.info("Invalid port count for leaf, aborting... > deviceId={}, portCount={}", did, portCount);
+ log.info("Invalid port count for leaf, aborting... > deviceId={}, portCount={}",
+ did, portCount);
return;
}
}
@@ -400,7 +419,8 @@
Map<DeviceId, Host> hostMap = Maps.newHashMap();
hosts.forEach(h -> hostMap.put(h.location().deviceId(), h));
if (hosts.size() != TOPO_SIZE || !leafs.equals(hostMap.keySet())) {
- log.info("Wrong host configuration, aborting... > hostCount={}, hostMapz={}", hosts.size(), hostMap);
+ log.info("Wrong host configuration, aborting... > hostCount={}, hostMapz={}",
+ hosts.size(), hostMap);
return;
}
@@ -409,14 +429,18 @@
try {
for (DeviceId deviceId : leafs) {
Host srcHost = hostMap.get(deviceId);
- Set<Host> dstHosts = hosts.stream().filter(h -> h != srcHost).collect(toSet());
- newFlowRules.addAll(generateLeafRules(deviceId, srcHost, dstHosts, spines, topo));
+ Set<Host> dstHosts = hosts.stream()
+ .filter(h -> h != srcHost)
+ .collect(toSet());
+ newFlowRules.addAll(generateLeafRules(deviceId, srcHost,
+ dstHosts, spines, topo));
}
for (DeviceId deviceId : spines) {
newFlowRules.addAll(generateSpineRules(deviceId, hosts, topo));
}
} catch (FlowRuleGeneratorException e) {
- log.warn("Exception while executing flow rule generator: {}", e.getMessage());
+ log.warn("Exception while executing flow rule generator: {}",
+ e.getMessage());
return;
}
@@ -428,12 +452,13 @@
// All good!
// Divide flow rules per device id...
- ImmutableMap.Builder<DeviceId, List<FlowRule>> mapBuilder = ImmutableMap.builder();
+ ImmutableMap.Builder<DeviceId, List<FlowRule>> mapBuilder =
+ ImmutableMap.builder();
concat(spines.stream(), leafs.stream())
- .map(deviceId -> ImmutableList.copyOf(newFlowRules
- .stream()
- .filter(fr -> fr.deviceId().equals(deviceId))
- .iterator()))
+ .map(deviceId -> ImmutableList.copyOf(
+ newFlowRules.stream()
+ .filter(fr -> fr.deviceId().equals(deviceId))
+ .iterator()))
.forEach(frs -> mapBuilder.put(frs.get(0).deviceId(), frs));
this.deviceFlowRules = mapBuilder.build();
@@ -443,7 +468,8 @@
// Avoid other executions to modify the generated flow rules.
flowRuleGenerated = true;
- log.info("Generated {} flow rules for {} devices", newFlowRules.size(), spines.size() + leafs.size());
+ log.info("Generated {} flow rules for {} devices",
+ newFlowRules.size(), spines.size() + leafs.size());
spawnTask(this::deployAllDevices);
}
@@ -455,11 +481,13 @@
* @param tableId a table id
* @return a new flow rule builder
*/
- protected FlowRule.Builder flowRuleBuilder(DeviceId did, PiTableId tableId) throws FlowRuleGeneratorException {
+ protected FlowRule.Builder flowRuleBuilder(DeviceId did, PiTableId tableId)
+ throws FlowRuleGeneratorException {
final Device device = deviceService.getDevice(did);
if (!device.is(PiPipelineInterpreter.class)) {
- throw new FlowRuleGeneratorException(format("Device %s has no PiPipelineInterpreter", did));
+ throw new FlowRuleGeneratorException(format(
+ "Device %s has no PiPipelineInterpreter", did));
}
return DefaultFlowRule.builder()
@@ -486,12 +514,13 @@
protected boolean isFabricPort(Port port, Topology topology) {
// True if the port connects this device to another infrastructure device.
- return topologyService.isInfrastructure(topology, new ConnectPoint(port.element().id(), port.number()));
+ return topologyService.isInfrastructure(
+ topology, new ConnectPoint(port.element().id(), port.number()));
}
/**
- * A listener of device events that executes a device deploy task each time a device is added, updated or
- * re-connects.
+ * A listener of device events that executes a device deploy task each time
+ * a device is added, updated or re-connects.
*/
private class InternalDeviceListener implements DeviceListener {
@Override
@@ -512,12 +541,12 @@
/**
* An exception occurred while generating flow rules for this fabric.
*/
- public class FlowRuleGeneratorException extends Exception {
+ public static class FlowRuleGeneratorException extends Exception {
public FlowRuleGeneratorException() {
}
- public FlowRuleGeneratorException(String msg) {
+ FlowRuleGeneratorException(String msg) {
super(msg);
}
}
diff --git a/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java b/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java
index 5a40df4..bb15afc 100644
--- a/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java
+++ b/apps/pi-demo/ecmp/src/main/java/org/onosproject/pi/demo/app/ecmp/EcmpFabricApp.java
@@ -18,9 +18,12 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.felix.scr.annotations.Component;
-import org.onlab.util.ImmutableByteSequence;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Path;
@@ -29,12 +32,17 @@
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroupId;
import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiGroupKey;
import org.onosproject.net.pi.runtime.PiTableAction;
import org.onosproject.net.topology.DefaultTopologyVertex;
import org.onosproject.net.topology.Topology;
@@ -42,37 +50,44 @@
import org.onosproject.pi.demo.app.common.AbstractUpgradableFabricApp;
import org.onosproject.pipelines.basic.PipeconfLoader;
-import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
-import static java.lang.String.format;
import static java.util.Collections.singleton;
import static java.util.stream.Collectors.toSet;
-import static org.onlab.packet.EthType.EtherType.IPV4;
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRF_WCMP_SELECTOR_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_NEXT_HOP_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_SET_NEXT_HOP_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_NEXT_HOP_ID;
-import static org.onosproject.pipelines.basic.BasicConstants.HDR_SELECTOR_ID;
import static org.onosproject.pipelines.basic.BasicConstants.TBL_TABLE0_ID;
-import static org.onosproject.pipelines.basic.EcmpConstants.TBL_ECMP_TABLE_ID;
-
+import static org.onosproject.pipelines.basic.BasicConstants.TBL_WCMP_TABLE_ID;
/**
- * Implementation of an upgradable fabric app for the ECMP pipeconf.
+ * Implementation of an upgradable fabric app for the Basic pipeconf (basic.p4)
+ * with ECMP support.
*/
@Component(immediate = true)
public class EcmpFabricApp extends AbstractUpgradableFabricApp {
private static final String APP_NAME = "org.onosproject.pi-ecmp";
- private static final Map<DeviceId, Map<Set<PortNumber>, Short>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
+ private static final Map<DeviceId, Map<Set<PortNumber>, Short>>
+ DEVICE_GROUP_ID_MAP = Maps.newHashMap();
+
+ private final Set<Pair<DeviceId, GroupKey>> groupKeys = Sets.newHashSet();
public EcmpFabricApp() {
- super(APP_NAME, singleton(PipeconfLoader.ECMP_PIPECONF));
+ super(APP_NAME, singleton(PipeconfLoader.BASIC_PIPECONF));
+ }
+
+ @Deactivate
+ public void deactivate() {
+ groupKeys.forEach(pair -> groupService.removeGroup(
+ pair.getLeft(), pair.getRight(), appId));
+ super.deactivate();
}
@Override
@@ -82,8 +97,10 @@
}
@Override
- public List<FlowRule> generateLeafRules(DeviceId leaf, Host srcHost, Collection<Host> dstHosts,
- Collection<DeviceId> availableSpines, Topology topo)
+ public List<FlowRule> generateLeafRules(DeviceId leaf, Host localHost,
+ Set<Host> remoteHosts,
+ Set<DeviceId> availableSpines,
+ Topology topo)
throws FlowRuleGeneratorException {
// Get ports which connect this leaf switch to hosts.
@@ -91,18 +108,19 @@
.stream()
.filter(port -> !isFabricPort(port, topo))
.map(Port::number)
- .collect(Collectors.toSet());
+ .collect(toSet());
// Get ports which connect this leaf to the given available spines.
TopologyGraph graph = topologyService.getGraph(topo);
- Set<PortNumber> fabricPorts = graph.getEdgesFrom(new DefaultTopologyVertex(leaf))
+ Set<PortNumber> fabricPorts = graph
+ .getEdgesFrom(new DefaultTopologyVertex(leaf))
.stream()
.filter(e -> availableSpines.contains(e.dst().deviceId()))
.map(e -> e.link().src().port())
- .collect(Collectors.toSet());
+ .collect(toSet());
if (hostPorts.size() != 1 || fabricPorts.size() == 0) {
- log.error("Leaf switch has invalid port configuration: hostPorts={}, fabricPorts={}",
+ log.error("Leaf has invalid port configuration: hostPorts={}, fabricPorts={}",
hostPorts.size(), fabricPorts.size());
throw new FlowRuleGeneratorException();
}
@@ -110,41 +128,40 @@
List<FlowRule> rules = Lists.newArrayList();
- TrafficTreatment treatment;
- if (fabricPorts.size() > 1) {
- // Do ECMP.
- Pair<PiTableAction, List<FlowRule>> result = provisionEcmpPiTableAction(leaf, fabricPorts);
- rules.addAll(result.getRight());
- treatment = DefaultTrafficTreatment.builder().piTableAction(result.getLeft()).build();
- } else {
- // Output on port.
- PortNumber outPort = fabricPorts.iterator().next();
- treatment = DefaultTrafficTreatment.builder().setOutput(outPort).build();
- }
+ // From local host to remote ones.
+ for (Host remoteHost : remoteHosts) {
+ int groupId = provisionGroup(leaf, fabricPorts);
- // From srHost to dstHosts.
- for (Host dstHost : dstHosts) {
- FlowRule rule = flowRuleBuilder(leaf, TBL_TABLE0_ID)
- .withSelector(
- DefaultTrafficSelector.builder()
- .matchInPort(hostPort)
- .matchEthType(IPV4.ethType().toShort())
- .matchEthSrc(srcHost.mac())
- .matchEthDst(dstHost.mac())
- .build())
- .withTreatment(treatment)
+ rules.add(groupFlowRule(leaf, groupId));
+
+ PiTableAction piTableAction = PiAction.builder()
+ .withId(ACT_SET_NEXT_HOP_ID)
+ .withParameter(new PiActionParam(
+ ACT_PRM_NEXT_HOP_ID,
+ copyFrom(groupId)))
.build();
- rules.add(rule);
+
+ for (IpAddress ipAddr : remoteHost.ipAddresses()) {
+ FlowRule rule = flowRuleBuilder(leaf, TBL_TABLE0_ID)
+ .withSelector(
+ DefaultTrafficSelector.builder()
+ .matchIPDst(ipAddr.toIpPrefix())
+ .build())
+ .withTreatment(
+ DefaultTrafficTreatment.builder()
+ .piTableAction(piTableAction)
+ .build())
+ .build();
+ rules.add(rule);
+ }
}
- // From fabric ports to this leaf host.
- for (PortNumber port : fabricPorts) {
+ // From remote hosts to the local one
+ for (IpAddress dstIpAddr : localHost.ipAddresses()) {
FlowRule rule = flowRuleBuilder(leaf, TBL_TABLE0_ID)
.withSelector(
DefaultTrafficSelector.builder()
- .matchInPort(port)
- .matchEthType(IPV4.ethType().toShort())
- .matchEthDst(srcHost.mac())
+ .matchIPDst(dstIpAddr.toIpPrefix())
.build())
.withTreatment(
DefaultTrafficTreatment.builder()
@@ -158,97 +175,105 @@
}
@Override
- public List<FlowRule> generateSpineRules(DeviceId deviceId, Collection<Host> dstHosts, Topology topo)
+ public List<FlowRule> generateSpineRules(DeviceId spine, Set<Host> hosts,
+ Topology topo)
throws FlowRuleGeneratorException {
List<FlowRule> rules = Lists.newArrayList();
- // for each host
- for (Host dstHost : dstHosts) {
+ // For each host pair (src -> dst)
+ for (Host dstHost : hosts) {
- Set<Path> paths = topologyService.getPaths(topo, deviceId, dstHost.location().deviceId());
+ Set<Path> paths = topologyService.getPaths(
+ topo, spine, dstHost.location().deviceId());
if (paths.size() == 0) {
- log.warn("Can't find any path between spine {} and host {}", deviceId, dstHost);
+ log.warn("No path between spine {} and host {}",
+ spine, dstHost);
throw new FlowRuleGeneratorException();
}
- TrafficTreatment treatment;
+ Set<PortNumber> ports = paths.stream()
+ .map(p -> p.src().port())
+ .collect(toSet());
- if (paths.size() == 1) {
- // Only one path, do output on port.
- PortNumber port = paths.iterator().next().src().port();
- treatment = DefaultTrafficTreatment.builder().setOutput(port).build();
- } else {
- // Multiple paths, do ECMP.
- Set<PortNumber> portNumbers = paths.stream().map(p -> p.src().port()).collect(toSet());
- Pair<PiTableAction, List<FlowRule>> result = provisionEcmpPiTableAction(deviceId, portNumbers);
- rules.addAll(result.getRight());
- treatment = DefaultTrafficTreatment.builder().piTableAction(result.getLeft()).build();
- }
+ int groupId = provisionGroup(spine, ports);
- FlowRule rule = flowRuleBuilder(deviceId, TBL_TABLE0_ID)
- .withSelector(
- DefaultTrafficSelector.builder()
- .matchEthType(IPV4.ethType().toShort())
- .matchEthDst(dstHost.mac())
- .build())
- .withTreatment(treatment)
+ rules.add(groupFlowRule(spine, groupId));
+
+ PiTableAction piTableAction = PiAction.builder()
+ .withId(ACT_SET_NEXT_HOP_ID)
+ .withParameter(new PiActionParam(ACT_PRM_NEXT_HOP_ID,
+ copyFrom(groupId)))
.build();
- rules.add(rule);
+ for (IpAddress dstIpAddr : dstHost.ipAddresses()) {
+ FlowRule rule = flowRuleBuilder(spine, TBL_TABLE0_ID)
+ .withSelector(DefaultTrafficSelector.builder()
+ .matchIPDst(dstIpAddr.toIpPrefix())
+ .build())
+ .withTreatment(DefaultTrafficTreatment.builder()
+ .piTableAction(piTableAction)
+ .build())
+ .build();
+ rules.add(rule);
+ }
}
return rules;
}
- private Pair<PiTableAction, List<FlowRule>> provisionEcmpPiTableAction(DeviceId deviceId,
- Set<PortNumber> fabricPorts)
+ /**
+ * Provisions an ECMP group for the given device and set of ports, returns
+ * the group ID.
+ */
+ private int provisionGroup(DeviceId deviceId, Set<PortNumber> ports)
throws FlowRuleGeneratorException {
- // Install ECMP group table entries that map from hash values to actual fabric ports...
- int groupId = groupIdOf(deviceId, fabricPorts);
- if (fabricPorts.size() != HASHED_LINKS) {
- throw new FlowRuleGeneratorException(format(
- "Invalid number of fabric ports for %s, expected %d but found %d",
- deviceId, HASHED_LINKS, fabricPorts.size()));
- }
- Iterator<PortNumber> portIterator = fabricPorts.iterator();
- List<FlowRule> rules = Lists.newArrayList();
- for (short i = 0; i < HASHED_LINKS; i++) {
- FlowRule rule = flowRuleBuilder(deviceId, TBL_ECMP_TABLE_ID)
- .withSelector(
- buildEcmpTrafficSelector(groupId, i))
- .withTreatment(
- DefaultTrafficTreatment.builder()
- .setOutput(portIterator.next())
- .build())
- .build();
- rules.add(rule);
- }
+ int groupId = groupIdOf(deviceId, ports);
- PiTableAction piTableAction = buildEcmpPiTableAction(groupId);
+ // Group buckets
+ List<GroupBucket> bucketList = ports.stream()
+ .map(port -> DefaultTrafficTreatment.builder()
+ .setOutput(port)
+ .build())
+ .map(DefaultGroupBucket::createSelectGroupBucket)
+ .collect(Collectors.toList());
- return Pair.of(piTableAction, rules);
+ // Group cookie (with action profile ID)
+ PiGroupKey groupKey = new PiGroupKey(TBL_WCMP_TABLE_ID,
+ ACT_PRF_WCMP_SELECTOR_ID,
+ groupId);
+
+ log.info("Adding group {} to {}...", groupId, deviceId);
+ groupService.addGroup(
+ new DefaultGroupDescription(deviceId,
+ GroupDescription.Type.SELECT,
+ new GroupBuckets(bucketList),
+ groupKey,
+ groupId,
+ appId));
+
+ groupKeys.add(ImmutablePair.of(deviceId, groupKey));
+
+ return groupId;
}
- private PiTableAction buildEcmpPiTableAction(int groupId) {
-
- return PiAction.builder()
- .withId(ACT_SET_NEXT_HOP_ID)
- .withParameter(new PiActionParam(ACT_PRM_NEXT_HOP_ID,
- ImmutableByteSequence.copyFrom(groupId)))
- .build();
- }
-
- private TrafficSelector buildEcmpTrafficSelector(int groupId, int selector) {
- Criterion ecmpCriterion = PiCriterion.builder()
- .matchExact(HDR_NEXT_HOP_ID, groupId)
- .matchExact(HDR_SELECTOR_ID, selector)
- .build();
-
- return DefaultTrafficSelector.builder()
- .matchPi((PiCriterion) ecmpCriterion)
+ private FlowRule groupFlowRule(DeviceId deviceId, int groupId)
+ throws FlowRuleGeneratorException {
+ return flowRuleBuilder(deviceId, TBL_WCMP_TABLE_ID)
+ .withSelector(
+ DefaultTrafficSelector.builder()
+ .matchPi(
+ PiCriterion.builder()
+ .matchExact(HDR_NEXT_HOP_ID,
+ groupId)
+ .build())
+ .build())
+ .withTreatment(
+ DefaultTrafficTreatment.builder()
+ .piTableAction(PiActionGroupId.of(groupId))
+ .build())
.build();
}
@@ -259,4 +284,4 @@
return DEVICE_GROUP_ID_MAP.get(deviceId).computeIfAbsent(ports, (pp) ->
(short) (DEVICE_GROUP_ID_MAP.get(deviceId).size() + 1));
}
-}
\ No newline at end of file
+}
diff --git a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
index 27a604c..b62007f 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java
@@ -18,12 +18,13 @@
import org.onosproject.core.ApplicationId;
import org.onosproject.core.GroupId;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.service.PiTranslatable;
/**
* Represents a generalized match & action pair to be applied to an
* infrastructure device.
*/
-public interface FlowRule {
+public interface FlowRule extends PiTranslatable {
IndexTableId DEFAULT_TABLE = IndexTableId.of(0);
int MAX_TIMEOUT = 60;
diff --git a/core/api/src/main/java/org/onosproject/net/group/Group.java b/core/api/src/main/java/org/onosproject/net/group/Group.java
index 3e2494c..39c6a3d 100644
--- a/core/api/src/main/java/org/onosproject/net/group/Group.java
+++ b/core/api/src/main/java/org/onosproject/net/group/Group.java
@@ -16,11 +16,12 @@
package org.onosproject.net.group;
import org.onosproject.core.GroupId;
+import org.onosproject.net.pi.service.PiTranslatable;
/**
* ONOS representation of group that is stored in the system.
*/
-public interface Group extends GroupDescription {
+public interface Group extends GroupDescription, PiTranslatable {
/**
* State of the group object in ONOS.
*/
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
index 1d9a94b..9084bea 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
@@ -34,7 +34,7 @@
* Instance of an action group of a protocol-independent pipeline.
*/
@Beta
-public final class PiActionGroup {
+public final class PiActionGroup implements PiEntity {
private final PiActionGroupId id;
private final PiActionGroupType type;
@@ -125,6 +125,11 @@
return new Builder();
}
+ @Override
+ public PiEntityType piEntityType() {
+ return PiEntityType.GROUP;
+ }
+
/**
* Builder of action groups.
*/
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupHandle.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupHandle.java
new file mode 100644
index 0000000..25a035b
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupHandle.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Global identifier of a PI action group applied to a device, uniquely defined
+ * by a device ID, action profile ID and group ID.
+ */
+@Beta
+public final class PiActionGroupHandle extends PiHandle<PiActionGroup> {
+
+ private PiActionGroupHandle(DeviceId deviceId, PiActionGroup group) {
+ super(deviceId, group);
+ }
+
+ /**
+ * Creates a new handle for the given device ID and PI action group.
+ *
+ * @param deviceId device ID
+ * @param group PI action group
+ * @return PI action group handle
+ */
+ public static PiActionGroupHandle of(DeviceId deviceId,
+ PiActionGroup group) {
+ return new PiActionGroupHandle(deviceId, group);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(deviceId(),
+ piEntity().actionProfileId(),
+ piEntity().id());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PiActionGroupHandle that = (PiActionGroupHandle) o;
+ return Objects.equal(deviceId(), that.deviceId()) &&
+ Objects.equal(piEntity().actionProfileId(),
+ that.piEntity().actionProfileId()) &&
+ Objects.equal(piEntity().id(), piEntity().id());
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("deviceId", deviceId())
+ .add("actionProfileId", piEntity().actionProfileId())
+ .add("groupId", piEntity().id())
+ .toString();
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntity.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntity.java
new file mode 100644
index 0000000..c3d5a01
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstraction of an entity of a protocol-independent that can be read or write
+ * at runtime.
+ */
+@Beta
+public interface PiEntity {
+
+ /**
+ * Returns the type of this entity.
+ *
+ * @return entity type
+ */
+ PiEntityType piEntityType();
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
new file mode 100644
index 0000000..e01e520
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiEntityType.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Type of runtime entity of a protocol-independent pipeline.
+ */
+@Beta
+public enum PiEntityType {
+ /**
+ * Table entry.
+ */
+ TABLE_ENTRY,
+
+ /**
+ * Action profile group.
+ */
+ GROUP
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHandle.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHandle.java
new file mode 100644
index 0000000..e8e70d1
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHandle.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.DeviceId;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Global identifier of a PI entity applied to a device, unique in the scope of
+ * the whole network.
+ */
+@Beta
+public abstract class PiHandle<E extends PiEntity> {
+
+ private final DeviceId deviceId;
+ private final E piEntity;
+
+ protected PiHandle(DeviceId deviceId, E piEntity) {
+ this.deviceId = checkNotNull(deviceId);
+ this.piEntity = checkNotNull(piEntity);
+ }
+
+ /**
+ * Returns the device ID of this handle.
+ *
+ * @return device ID
+ */
+ public final DeviceId deviceId() {
+ return deviceId;
+ }
+
+ /**
+ * Returns the type of entity identified by this handle.
+ *
+ * @return PI entity type
+ */
+ public final PiEntityType entityType() {
+ return piEntity.piEntityType();
+ }
+
+ /**
+ * The entity to which this handle is associated.
+ *
+ * @return PI entity
+ */
+ public final E piEntity() {
+ return piEntity;
+ }
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public abstract String toString();
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
index cd7e493..5770c6b 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
@@ -30,7 +30,7 @@
* Instance of a table entry in a protocol-independent pipeline.
*/
@Beta
-public final class PiTableEntry {
+public final class PiTableEntry implements PiEntity {
public static final PiTableEntry EMTPY = new PiTableEntry();
@@ -160,6 +160,11 @@
return new Builder();
}
+ @Override
+ public PiEntityType piEntityType() {
+ return PiEntityType.TABLE_ENTRY;
+ }
+
public static final class Builder {
private PiTableId tableId;
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntryHandle.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntryHandle.java
new file mode 100644
index 0000000..7eeb7f6
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntryHandle.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onosproject.net.DeviceId;
+
+/**
+ * Global identifier of a PI table entry applied on a device, uniquely defined
+ * by a device ID, table ID and match key.
+ */
+@Beta
+public final class PiTableEntryHandle extends PiHandle<PiTableEntry> {
+
+ private PiTableEntryHandle(DeviceId deviceId, PiTableEntry entry) {
+ super(deviceId, entry);
+ }
+
+ /**
+ * Creates a new handle for the given PI table entry and device ID.
+ *
+ * @param deviceId device ID
+ * @param entry PI table entry
+ * @return PI table entry handle
+ */
+ public static PiTableEntryHandle of(DeviceId deviceId, PiTableEntry entry) {
+ return new PiTableEntryHandle(deviceId, entry);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(deviceId(),
+ piEntity().table(),
+ piEntity().matchKey());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final PiTableEntryHandle other = (PiTableEntryHandle) obj;
+ return Objects.equal(this.deviceId(), other.deviceId())
+ && Objects.equal(this.piEntity().table(),
+ other.piEntity().table())
+ && Objects.equal(this.piEntity().matchKey(),
+ other.piEntity().matchKey());
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("deviceId", deviceId())
+ .add("tableId", piEntity().table())
+ .add("matchKey", piEntity().matchKey())
+ .toString();
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiFlowRuleTranslationStore.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiFlowRuleTranslationStore.java
new file mode 100644
index 0000000..a80de10
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiFlowRuleTranslationStore.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+
+/**
+ * A PI translation store that keeps track of which flow rules have been
+ * translated to which PI table entries.
+ */
+@Beta
+public interface PiFlowRuleTranslationStore
+ extends PiTranslationStore<FlowRule, PiTableEntry> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiFlowRuleTranslator.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiFlowRuleTranslator.java
new file mode 100644
index 0000000..cc82f73
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiFlowRuleTranslator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+
+/**
+ * A translator of flow rules to PI table entries.
+ */
+@Beta
+public interface PiFlowRuleTranslator
+ extends PiTranslator<FlowRule, PiTableEntry> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiGroupTranslationStore.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiGroupTranslationStore.java
new file mode 100644
index 0000000..4fe526a
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiGroupTranslationStore.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+
+/**
+ * A PI translation store that keeps track of which groups have been
+ * translated to which PI action groups.
+ */
+@Beta
+public interface PiGroupTranslationStore
+ extends PiTranslationStore<Group, PiActionGroup> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiGroupTranslator.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiGroupTranslator.java
new file mode 100644
index 0000000..d5eb5af
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiGroupTranslator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+
+/**
+ * A translator of groups to PI action groups.
+ */
+@Beta
+public interface PiGroupTranslator
+ extends PiTranslator<Group, PiActionGroup> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatable.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatable.java
new file mode 100644
index 0000000..316c3ed
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatable.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstraction of protocol-dependent (PD) entity that can be translated to an
+ * equivalent protocol-independent (PI) one.
+ */
+@Beta
+public interface PiTranslatable {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatedEntity.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatedEntity.java
new file mode 100644
index 0000000..4ca094b
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslatedEntity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiEntityType;
+import org.onosproject.net.pi.runtime.PiHandle;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of the result of a PD-to-PI translation associated to a PI
+ * entity handle.
+ */
+@Beta
+public final class PiTranslatedEntity<T extends PiTranslatable, E extends PiEntity> {
+
+ private final T original;
+ private final E translated;
+ private final PiHandle<E> handle;
+
+ /**
+ * Creates a new translated entity.
+ *
+ * @param original PD entity
+ * @param translated PI entity
+ * @param handle PI entity handle
+ */
+ public PiTranslatedEntity(T original, E translated, PiHandle<E> handle) {
+ this.original = checkNotNull(original);
+ this.translated = checkNotNull(translated);
+ this.handle = checkNotNull(handle);
+ }
+
+ /**
+ * Returns the type of the translated entity.
+ *
+ * @return type of the translated entity
+ */
+ public final PiEntityType entityType() {
+ return translated.piEntityType();
+ }
+
+ /**
+ * Returns the original PD entity.
+ *
+ * @return instance of PI translatable entity
+ */
+ public final T original() {
+ return original;
+ }
+
+ /**
+ * Returns the translated PI entity.
+ *
+ * @return PI entity
+ */
+ public final E translated() {
+ return translated;
+ }
+
+ /**
+ * Returns the PI entity handle.
+ *
+ * @return PI entity handle
+ */
+ public final PiHandle<E> handle() {
+ return handle;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationEvent.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationEvent.java
new file mode 100644
index 0000000..9e2411d
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationEvent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.event.AbstractEvent;
+import org.onosproject.net.pi.runtime.PiEntity;
+
+/**
+ * Signals an event related to the translation of a protocol-dependent (PD)
+ * entity to a protocol-independent (PI) one.
+ */
+@Beta
+public final class PiTranslationEvent<T extends PiTranslatable, E extends PiEntity>
+ extends AbstractEvent<PiTranslationEvent.Type, PiTranslatedEntity<T, E>> {
+
+ /**
+ * Type of event.
+ */
+ public enum Type {
+ /**
+ * Signals that A PD entity has been translated to a PI one, and the
+ * mapping between the two entities has been learned by the system.
+ */
+ LEARNED,
+
+ /**
+ * Signals that a previously learned mapping between a PD entity and its
+ * PI counterpart has been removed.
+ */
+ FORGOT,
+ }
+
+ /**
+ * Creates a new translation event.
+ *
+ * @param type type of event
+ * @param subject subject of event
+ */
+ public PiTranslationEvent(Type type, PiTranslatedEntity<T, E> subject) {
+ super(type, subject);
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationException.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationException.java
new file mode 100644
index 0000000..d8a6c63
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Signals that an error was encountered while translating an entity.
+ */
+@Beta
+public final class PiTranslationException extends Exception {
+
+ /**
+ * Creates a new exception with the given message.
+ *
+ * @param message a message
+ */
+ public PiTranslationException(String message) {
+ super(message);
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
index 210b7e0..c7a95c3 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationService.java
@@ -17,54 +17,25 @@
package org.onosproject.net.pi.service;
import com.google.common.annotations.Beta;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.pi.model.PiPipeconf;
-import org.onosproject.net.pi.runtime.PiActionGroup;
-import org.onosproject.net.pi.runtime.PiTableEntry;
/**
- * A service to translate protocol-dependent entities to protocol-independent ones.
+ * A service to translate protocol-dependent (PD) entities to
+ * protocol-independent (PI) ones.
*/
@Beta
public interface PiTranslationService {
/**
- * Returns a PI table entry equivalent to the given flow rule for the given protocol-independent pipeline
- * configuration.
+ * Returns a flow rule translator.
*
- * @param rule a flow rule
- * @param pipeconf a pipeline configuration
- * @return a table entry
- * @throws PiTranslationException if the flow rule cannot be translated
+ * @return flow rule translator
*/
- PiTableEntry translateFlowRule(FlowRule rule, PiPipeconf pipeconf)
- throws PiTranslationException;
+ PiFlowRuleTranslator flowRuleTranslator();
/**
- * Returns a PI action group equivalent to the given group for the given protocol-independent pipeline
- * configuration.
+ * Returns a group translator.
*
- * @param group a group
- * @param pipeconf a pipeline configuration
- * @return a PI action group
- * @throws PiTranslationException if the group cannot be translated
+ * @return group translator
*/
- PiActionGroup translateGroup(Group group, PiPipeconf pipeconf)
- throws PiTranslationException;
-
- /**
- * Signals that an error was encountered while translating an entity.
- */
- class PiTranslationException extends Exception {
-
- /**
- * Creates a new exception with the given message.
- *
- * @param message a message
- */
- public PiTranslationException(String message) {
- super(message);
- }
- }
+ PiGroupTranslator groupTranslator();
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStore.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStore.java
new file mode 100644
index 0000000..6274deb
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStore.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiHandle;
+import org.onosproject.store.Store;
+
+/**
+ * PI translation store abstraction that maintains a mapping between a PI entity
+ * handle and a translated entity.
+ *
+ * @param <T> PD entity class (translatable to PI)
+ * @param <E> PI entity class
+ */
+@Beta
+public interface PiTranslationStore<T extends PiTranslatable, E extends PiEntity>
+ extends Store<PiTranslationEvent<T, E>, PiTranslationStoreDelegate<T, E>> {
+
+ /**
+ * Adds or update a mapping between the given PI entity handle and
+ * translated entity.
+ *
+ * @param handle PI entity handle
+ * @param entity PI translated entity
+ */
+ void addOrUpdate(PiHandle<E> handle, PiTranslatedEntity<T, E> entity);
+
+ /**
+ * Returns a PI translated entity for the given handle. Returns null if this
+ * store does not contain a mapping between the two for the given pipeconf
+ * ID.
+ *
+ * @param handle PI entity handle
+ * @return PI translated entity
+ */
+ PiTranslatedEntity<T, E> get(PiHandle<E> handle);
+
+ /**
+ * Removes a previously added mapping for the given PI entity handle.
+ *
+ * @param handle PI entity handle
+ */
+ void remove(PiHandle<E> handle);
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStoreDelegate.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStoreDelegate.java
new file mode 100644
index 0000000..2a8d16f
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslationStoreDelegate.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * PI translation store delegate abstraction.
+ *
+ * @param <T> PD entity class (translatable to PI)
+ * @param <E> PI entity class
+ */
+@Beta
+public interface PiTranslationStoreDelegate
+ <T extends PiTranslatable, E extends PiEntity>
+ extends StoreDelegate<PiTranslationEvent<T, E>> {
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslator.java b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslator.java
new file mode 100644
index 0000000..202636a
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/service/PiTranslator.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.service;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiHandle;
+
+import java.util.Optional;
+
+/**
+ * A translator of PI entities to equivalent PD ones which offer means to learn
+ * translated entities for later use.
+ *
+ * @param <T> PD entity class (translatable to PI)
+ * @param <E> PI entity class
+ */
+@Beta
+public interface PiTranslator<T extends PiTranslatable, E extends PiEntity> {
+
+ /**
+ * Translate the given PD entity (original) and returns a PI entity that is
+ * equivalent to he PD one for the given pipeconf.
+ *
+ * @param original PD entity
+ * @param pipeconf pipeconf
+ * @return PI entity
+ * @throws PiTranslationException if a translation is not possible (see
+ * message for an explanation)
+ */
+ E translate(T original, PiPipeconf pipeconf)
+ throws PiTranslationException;
+
+ /**
+ * Stores a mapping between the given translated entity and handle.
+ *
+ * @param handle PI entity handle
+ * @param entity PI translated entity
+ */
+ void learn(PiHandle<E> handle, PiTranslatedEntity<T, E> entity);
+
+ /**
+ * Returns a PI translated entity that was previously associated with the
+ * given handle, if present. If not present, it means a mapping between the
+ * two has not been learned by the system (via {@link #learn(PiHandle,
+ * PiTranslatedEntity)}) or that it has been removed (via {@link
+ * #forget(PiHandle)}). the
+ *
+ * @param handle PI entity handle
+ * @return optional PI translated entity
+ */
+ Optional<PiTranslatedEntity<T, E>> lookup(PiHandle<E> handle);
+
+ /**
+ * Removes any mapping for the given PI entity handle.
+ *
+ * @param handle PI entity handle.
+ */
+ void forget(PiHandle<E> handle);
+}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/AbstractPiTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/AbstractPiTranslatorImpl.java
new file mode 100644
index 0000000..5d7178c
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/AbstractPiTranslatorImpl.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.pi.impl;
+
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiHandle;
+import org.onosproject.net.pi.service.PiTranslatable;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
+import org.onosproject.net.pi.service.PiTranslationStore;
+import org.onosproject.net.pi.service.PiTranslator;
+
+import java.util.Optional;
+
+/**
+ * Abstract implementation of a PI translator backed by a PI translation store.
+ *
+ * @param <T> PD entity class
+ * @param <E> PI entity class
+ */
+public abstract class AbstractPiTranslatorImpl
+ <T extends PiTranslatable, E extends PiEntity>
+ implements PiTranslator<T, E> {
+
+ private final PiTranslationStore<T, E> store;
+
+ AbstractPiTranslatorImpl(PiTranslationStore<T, E> store) {
+ this.store = store;
+ }
+
+ @Override
+ public void learn(PiHandle<E> handle, PiTranslatedEntity<T, E> entity) {
+ store.addOrUpdate(handle, entity);
+ }
+
+ @Override
+ public Optional<PiTranslatedEntity<T, E>> lookup(PiHandle<E> handle) {
+ return Optional.ofNullable(store.get(handle));
+ }
+
+ @Override
+ public void forget(PiHandle<E> handle) {
+ store.remove(handle);
+ }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java b/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
index 22d53fd..7f43e22 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
@@ -86,13 +86,13 @@
import org.onosproject.net.pi.runtime.PiFieldMatch;
import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
+import org.onosproject.net.pi.service.PiTranslationException;
import java.util.Map;
import static java.lang.String.format;
import static org.onlab.util.ImmutableByteSequence.ByteSequenceTrimException;
import static org.onosproject.net.pi.impl.CriterionTranslator.CriterionTranslatorException;
-import static org.onosproject.net.pi.service.PiTranslationService.PiTranslationException;
/**
* Helper class to translate criterion instances to PI field matches.
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslator.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
similarity index 98%
rename from core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslator.java
rename to core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
index d856fd3..f04e7b0 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslator.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
@@ -46,6 +46,7 @@
import org.onosproject.net.pi.runtime.PiTableAction;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
+import org.onosproject.net.pi.service.PiTranslationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,17 +63,16 @@
import static org.onosproject.net.pi.impl.CriterionTranslatorHelper.translateCriterion;
import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
import static org.onosproject.net.pi.impl.PiUtils.translateTableId;
-import static org.onosproject.net.pi.service.PiTranslationService.PiTranslationException;
/**
* Implementation of flow rule translation logic.
*/
-final class PiFlowRuleTranslator {
+final class PiFlowRuleTranslatorImpl {
public static final int MAX_PI_PRIORITY = (int) Math.pow(2, 24);
- private static final Logger log = LoggerFactory.getLogger(PiFlowRuleTranslator.class);
+ private static final Logger log = LoggerFactory.getLogger(PiFlowRuleTranslatorImpl.class);
- private PiFlowRuleTranslator() {
+ private PiFlowRuleTranslatorImpl() {
// Hide constructor.
}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslator.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslatorImpl.java
similarity index 95%
rename from core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslator.java
rename to core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslatorImpl.java
index 255ccb9..eeabebc 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslator.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiGroupTranslatorImpl.java
@@ -29,21 +29,21 @@
import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
import org.onosproject.net.pi.runtime.PiGroupKey;
import org.onosproject.net.pi.runtime.PiTableAction;
-import org.onosproject.net.pi.service.PiTranslationService.PiTranslationException;
+import org.onosproject.net.pi.service.PiTranslationException;
import java.nio.ByteBuffer;
import static java.lang.String.format;
-import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.translateTreatment;
+import static org.onosproject.net.pi.impl.PiFlowRuleTranslatorImpl.translateTreatment;
import static org.onosproject.net.pi.impl.PiUtils.getInterpreterOrNull;
import static org.onosproject.net.pi.runtime.PiTableAction.Type.ACTION;
/**
* Implementation of group translation logic.
*/
-final class PiGroupTranslator {
+final class PiGroupTranslatorImpl {
- private PiGroupTranslator() {
+ private PiGroupTranslatorImpl() {
// Hides constructor.
}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java
index d60e9b3..212cf1c 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfManager.java
@@ -28,6 +28,9 @@
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onlab.util.ItemNotFoundException;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
@@ -79,6 +82,9 @@
protected NetworkConfigRegistry cfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -87,6 +93,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PiPipeconfMappingStore pipeconfMappingStore;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
// Registered pipeconf are replicated through the app subsystem and registered on app activated events.
protected ConcurrentHashMap<PiPipeconfId, PiPipeconf> piPipeconfs = new ConcurrentHashMap<>();
@@ -200,21 +209,28 @@
// due to 1:1:1 pipeconf:driver:provider maybe find better way
DriverProvider provider = new PiPipeconfDriverProviderInternal(completeDriver);
- //we register to the dirver susbystem the driver provider containing the merged driver
+ //we register to the driver susbystem the driver provider containing the merged driver
driverAdminService.registerProvider(provider);
}
// Changing the configuration for the device to enforce the full driver with pipipeconf
- // and base behaviours
- ObjectNode newCfg = (ObjectNode) basicDeviceConfig.node();
- newCfg = newCfg.put(DRIVER, completeDriverName);
- ObjectMapper mapper = new ObjectMapper();
- JsonNode newCfgNode = mapper.convertValue(newCfg, JsonNode.class);
- cfgService.applyConfig(deviceId, BasicDeviceConfig.class, newCfgNode);
- // Completable future is needed for when this method will also apply the pipeline to the device.
- // FIXME (maybe): the pipeline is currently applied by the general device provider. But we store here
- // the association between device and pipeconf.
- pipeconfMappingStore.createOrUpdateBinding(deviceId, pipeconfId);
+ // and base behaviours, updating binding only first time something changes
+ NodeId leaderNodeId = leadershipService.getLeader("deploy-" +
+ deviceId.toString() + "-pipeconf");
+ NodeId localNodeId = clusterService.getLocalNode().id();
+
+ if (!basicDeviceConfig.driver().equals(completeDriverName) && localNodeId.equals(leaderNodeId)) {
+ ObjectNode newCfg = (ObjectNode) basicDeviceConfig.node();
+ newCfg = newCfg.put(DRIVER, completeDriverName);
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode newCfgNode = mapper.convertValue(newCfg, JsonNode.class);
+ log.debug("New driver {} for device {}", completeDriverName, deviceId);
+ cfgService.applyConfig(deviceId, BasicDeviceConfig.class, newCfgNode);
+ // Completable future is needed for when this method will also apply the pipeline to the device.
+ // FIXME (maybe): the pipeline is currently applied by the general device provider.
+ // But we store here the association between device and pipeconf.
+ pipeconfMappingStore.createOrUpdateBinding(deviceId, pipeconfId);
+ }
operationResult.complete(true);
}
});
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
index 3cb0a09..9780fac 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
@@ -30,12 +30,17 @@
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.runtime.PiActionGroup;
import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.service.PiFlowRuleTranslationStore;
+import org.onosproject.net.pi.service.PiFlowRuleTranslator;
+import org.onosproject.net.pi.service.PiGroupTranslationStore;
+import org.onosproject.net.pi.service.PiGroupTranslator;
+import org.onosproject.net.pi.service.PiTranslationException;
import org.onosproject.net.pi.service.PiTranslationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Implementation of the protocol-independent translation service.
+ * Implementation of the PI translation service.
*/
@Component(immediate = true)
@Service
@@ -48,24 +53,37 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private PiFlowRuleTranslationStore flowRuleTranslationStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private PiGroupTranslationStore groupTranslationStore;
+
+ private PiFlowRuleTranslator flowRuleTranslator;
+ private PiGroupTranslator groupTranslator;
+
@Activate
public void activate() {
+ flowRuleTranslator = new InternalFlowRuleTranslator(flowRuleTranslationStore);
+ groupTranslator = new InternalGroupTranslator(groupTranslationStore);
log.info("Started");
}
@Deactivate
public void deactivate() {
+ flowRuleTranslator = null;
+ groupTranslator = null;
log.info("Stopped");
}
@Override
- public PiTableEntry translateFlowRule(FlowRule rule, PiPipeconf pipeconf) throws PiTranslationException {
- return PiFlowRuleTranslator.translate(rule, pipeconf, getDevice(rule.deviceId()));
+ public PiFlowRuleTranslator flowRuleTranslator() {
+ return flowRuleTranslator;
}
@Override
- public PiActionGroup translateGroup(Group group, PiPipeconf pipeconf) throws PiTranslationException {
- return PiGroupTranslator.translate(group, pipeconf, getDevice(group.deviceId()));
+ public PiGroupTranslator groupTranslator() {
+ return groupTranslator;
}
private Device getDevice(DeviceId deviceId) throws PiTranslationException {
@@ -75,5 +93,37 @@
}
return device;
}
+
+ private final class InternalFlowRuleTranslator
+ extends AbstractPiTranslatorImpl<FlowRule, PiTableEntry>
+ implements PiFlowRuleTranslator {
+
+ private InternalFlowRuleTranslator(PiFlowRuleTranslationStore store) {
+ super(store);
+ }
+
+ @Override
+ public PiTableEntry translate(FlowRule original, PiPipeconf pipeconf)
+ throws PiTranslationException {
+ return PiFlowRuleTranslatorImpl
+ .translate(original, pipeconf, getDevice(original.deviceId()));
+ }
+ }
+
+ private final class InternalGroupTranslator
+ extends AbstractPiTranslatorImpl<Group, PiActionGroup>
+ implements PiGroupTranslator {
+
+ private InternalGroupTranslator(PiGroupTranslationStore store) {
+ super(store);
+ }
+
+ @Override
+ public PiActionGroup translate(Group original, PiPipeconf pipeconf)
+ throws PiTranslationException {
+ return PiGroupTranslatorImpl
+ .translate(original, pipeconf, getDevice(original.deviceId()));
+ }
+ }
}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiUtils.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiUtils.java
index 6e93d67..78bca5f 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiUtils.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiUtils.java
@@ -22,7 +22,7 @@
import org.onosproject.net.pi.model.PiPipeconf;
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.model.PiTableId;
-import org.onosproject.net.pi.service.PiTranslationService;
+import org.onosproject.net.pi.service.PiTranslationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,23 +60,23 @@
}
static PiTableId translateTableId(TableId tableId, PiPipelineInterpreter interpreter)
- throws PiTranslationService.PiTranslationException {
+ throws PiTranslationException {
switch (tableId.type()) {
case PIPELINE_INDEPENDENT:
return (PiTableId) tableId;
case INDEX:
IndexTableId indexId = (IndexTableId) tableId;
if (interpreter == null) {
- throw new PiTranslationService.PiTranslationException(format(
+ throw new PiTranslationException(format(
"Unable to map table ID '%d' from index to PI: missing interpreter", indexId.id()));
} else if (!interpreter.mapFlowRuleTableId(indexId.id()).isPresent()) {
- throw new PiTranslationService.PiTranslationException(format(
+ throw new PiTranslationException(format(
"Unable to map table ID '%d' from index to PI: missing ID in interpreter", indexId.id()));
} else {
return interpreter.mapFlowRuleTableId(indexId.id()).get();
}
default:
- throw new PiTranslationService.PiTranslationException(format(
+ throw new PiTranslationException(format(
"Unrecognized table ID type %s", tableId.type().name()));
}
}
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
index d13def7..9a80e9f 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
@@ -67,7 +67,7 @@
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onlab.util.ImmutableByteSequence.fit;
import static org.onosproject.net.group.GroupDescription.Type.SELECT;
-import static org.onosproject.net.pi.impl.PiFlowRuleTranslator.MAX_PI_PRIORITY;
+import static org.onosproject.net.pi.impl.PiFlowRuleTranslatorImpl.MAX_PI_PRIORITY;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRF_WCMP_SELECTOR_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.ACT_SET_EGRESS_PORT_ID;
@@ -80,7 +80,7 @@
import static org.onosproject.pipelines.basic.BasicConstants.TBL_WCMP_TABLE_ID;
/**
- * Tests for {@link PiFlowRuleTranslator}.
+ * Tests for {@link PiFlowRuleTranslatorImpl}.
*/
@SuppressWarnings("ConstantConditions")
public class PiTranslatorServiceTest {
@@ -161,8 +161,8 @@
.withPriority(priority)
.build();
- PiTableEntry entry1 = PiFlowRuleTranslator.translate(rule1, pipeconf, null);
- PiTableEntry entry2 = PiFlowRuleTranslator.translate(rule1, pipeconf, null);
+ PiTableEntry entry1 = PiFlowRuleTranslatorImpl.translate(rule1, pipeconf, null);
+ PiTableEntry entry2 = PiFlowRuleTranslatorImpl.translate(rule1, pipeconf, null);
// check equality, i.e. same rules must produce same entries
new EqualsTester()
@@ -236,8 +236,8 @@
@Test
public void testTranslateGroups() throws Exception {
- PiActionGroup piGroup1 = PiGroupTranslator.translate(GROUP, pipeconf, null);
- PiActionGroup piGroup2 = PiGroupTranslator.translate(GROUP, pipeconf, null);
+ PiActionGroup piGroup1 = PiGroupTranslatorImpl.translate(GROUP, pipeconf, null);
+ PiActionGroup piGroup2 = PiGroupTranslatorImpl.translate(GROUP, pipeconf, null);
new EqualsTester()
.addEqualityGroup(piGroup1, piGroup2)
diff --git a/core/store/dist/src/main/java/org/onosproject/store/pi/impl/AbstractDistributedPiTranslationStore.java b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/AbstractDistributedPiTranslationStore.java
new file mode 100644
index 0000000..9df6a86
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/AbstractDistributedPiTranslationStore.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.pi.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiHandle;
+import org.onosproject.net.pi.service.PiTranslatable;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
+import org.onosproject.net.pi.service.PiTranslationEvent;
+import org.onosproject.net.pi.service.PiTranslationStore;
+import org.onosproject.net.pi.service.PiTranslationStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Distributed implementation of PiTranslationStore.
+ */
+@Component(immediate = true)
+public abstract class AbstractDistributedPiTranslationStore
+ <T extends PiTranslatable, E extends PiEntity>
+ extends AbstractStore<PiTranslationEvent<T, E>, PiTranslationStoreDelegate<T, E>>
+ implements PiTranslationStore<T, E> {
+
+ private static final String MAP_NAME_TEMPLATE = "onos-pi-translated-%s-map";
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private EventuallyConsistentMap<PiHandle<E>, PiTranslatedEntity<T, E>>
+ translatedEntities;
+
+ private final EventuallyConsistentMapListener
+ <PiHandle<E>, PiTranslatedEntity<T, E>> entityMapListener =
+ new InternalEntityMapListener();
+
+ /**
+ * Returns a string that identifies the map maintained by this store among
+ * others that uses this abstract class.
+ *
+ * @return string
+ */
+ protected abstract String mapSimpleName();
+
+ @Activate
+ public void activate() {
+ final String fullMapName = format(MAP_NAME_TEMPLATE, mapSimpleName());
+ translatedEntities = storageService
+ .<PiHandle<E>, PiTranslatedEntity<T, E>>eventuallyConsistentMapBuilder()
+ .withName(fullMapName)
+ .withSerializer(KryoNamespaces.API)
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+ translatedEntities.addListener(entityMapListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ translatedEntities.removeListener(entityMapListener);
+ translatedEntities = null;
+ log.info("Stopped");
+ }
+
+ @Override
+ public void addOrUpdate(PiHandle<E> handle, PiTranslatedEntity<T, E> entity) {
+ checkNotNull(handle);
+ checkNotNull(entity);
+ checkArgument(handle.entityType().equals(entity.entityType()),
+ "Entity type must be the same for handle and translated entity");
+ translatedEntities.put(handle, entity);
+ }
+
+ @Override
+ public void remove(PiHandle<E> handle) {
+ checkNotNull(handle);
+ translatedEntities.remove(handle);
+ }
+
+ @Override
+ public PiTranslatedEntity<T, E> get(PiHandle<E> handle) {
+ checkNotNull(handle);
+ return translatedEntities.get(handle);
+ }
+
+ public Iterable<PiTranslatedEntity<T, E>> getAll() {
+ return translatedEntities.values();
+ }
+
+ private class InternalEntityMapListener
+ implements EventuallyConsistentMapListener
+ <PiHandle<E>, PiTranslatedEntity<T, E>> {
+
+ @Override
+ public void event(EventuallyConsistentMapEvent<PiHandle<E>,
+ PiTranslatedEntity<T, E>> event) {
+ final PiTranslationEvent.Type type;
+ switch (event.type()) {
+ case PUT:
+ type = PiTranslationEvent.Type.LEARNED;
+ break;
+ case REMOVE:
+ type = PiTranslationEvent.Type.FORGOT;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown event type " + event.type().name());
+ }
+ notifyDelegate(new PiTranslationEvent<>(type, event.value()));
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiFlowRuleTranslationStore.java b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiFlowRuleTranslationStore.java
new file mode 100644
index 0000000..c123ec5
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiFlowRuleTranslationStore.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.pi.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.service.PiFlowRuleTranslationStore;
+
+/**
+ * Distributed implementation of a PI translation store for flow rules.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedPiFlowRuleTranslationStore
+ extends AbstractDistributedPiTranslationStore<FlowRule, PiTableEntry>
+ implements PiFlowRuleTranslationStore {
+
+ private static final String MAP_SIMPLE_NAME = "flowrule";
+
+ @Override
+ protected String mapSimpleName() {
+ return MAP_SIMPLE_NAME;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiGroupTranslationStore.java b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiGroupTranslationStore.java
new file mode 100644
index 0000000..fa62ef1
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onosproject/store/pi/impl/DistributedPiGroupTranslationStore.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.pi.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.service.PiGroupTranslationStore;
+
+/**
+ * Distributed implementation of a PI translation store for groups.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedPiGroupTranslationStore
+ extends AbstractDistributedPiTranslationStore<Group, PiActionGroup>
+ implements PiGroupTranslationStore {
+
+ private static final String MAP_SIMPLE_NAME = "group";
+
+ @Override
+ protected String mapSimpleName() {
+ return MAP_SIMPLE_NAME;
+ }
+}
diff --git a/core/store/dist/src/test/java/org/onosproject/store/pi/impl/DistributedDevicePipeconfMappingStoreTest.java b/core/store/dist/src/test/java/org/onosproject/store/pi/impl/DistributedDevicePipeconfMappingStoreTest.java
index 83b997d..9a31b0c 100644
--- a/core/store/dist/src/test/java/org/onosproject/store/pi/impl/DistributedDevicePipeconfMappingStoreTest.java
+++ b/core/store/dist/src/test/java/org/onosproject/store/pi/impl/DistributedDevicePipeconfMappingStoreTest.java
@@ -119,4 +119,4 @@
store.deviceToPipeconf.clear();
}
-}
\ No newline at end of file
+}
diff --git a/core/store/dist/src/test/java/org/onosproject/store/pi/impl/DistributedPiTranslationStoreTest.java b/core/store/dist/src/test/java/org/onosproject/store/pi/impl/DistributedPiTranslationStoreTest.java
new file mode 100644
index 0000000..2ba52a2
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onosproject/store/pi/impl/DistributedPiTranslationStoreTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.pi.impl;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.RandomUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiEntityType;
+import org.onosproject.net.pi.runtime.PiHandle;
+import org.onosproject.net.pi.service.PiTranslatable;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
+import org.onosproject.store.service.TestStorageService;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for {@link AbstractDistributedPiTranslationStore}.
+ */
+public class DistributedPiTranslationStoreTest {
+
+ private AbstractDistributedPiTranslationStore<PiTranslatable, PiEntity> store;
+
+ private static final int HANDLE_HASH = RandomUtils.nextInt();
+ private static final PiTranslatable PI_TRANSLATABLE =
+ new PiTranslatable() {
+ };
+ private static final PiEntity PI_ENTITY = () -> PiEntityType.TABLE_ENTRY;
+ private static final PiHandle<PiEntity> PI_HANDLE =
+ new PiHandle<PiEntity>(DeviceId.NONE, PI_ENTITY) {
+ @Override
+ public int hashCode() {
+ return HANDLE_HASH;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof PiHandle && other.hashCode() == hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(HANDLE_HASH);
+ }
+ };
+ private static final PiTranslatedEntity<PiTranslatable, PiEntity> TRANSLATED_ENTITY =
+ new PiTranslatedEntity<>(PI_TRANSLATABLE, PI_ENTITY, PI_HANDLE);
+
+ /**
+ * Sets up the store and the storage service test harness.
+ */
+ @Before
+ public void setUp() {
+ store = new AbstractDistributedPiTranslationStore<PiTranslatable, PiEntity>() {
+ @Override
+ protected String mapSimpleName() {
+ return "test";
+ }
+ };
+ store.storageService = new TestStorageService();
+ store.setDelegate(event -> {
+ });
+ store.activate();
+ }
+
+ /**
+ * Tests equality of key and value used in other tests.
+ */
+ @Test
+ public void testEquality() {
+ assertEquals(PI_HANDLE, PI_HANDLE);
+ assertEquals(TRANSLATED_ENTITY, TRANSLATED_ENTITY);
+ }
+
+ /**
+ * Test for activate.
+ */
+ @Test
+ public void activate() {
+ assertNotNull(store.storageService);
+ assertTrue("Store must have delegate",
+ store.hasDelegate());
+ assertTrue("No value should be in the map",
+ Lists.newArrayList(store.getAll()).isEmpty());
+ }
+
+ /**
+ * Test for deactivate.
+ */
+ @Test(expected = NullPointerException.class)
+ public void deactivate() {
+ store.deactivate();
+ store.getAll();
+ }
+
+ /**
+ * Test of value add or update.
+ */
+ @Test
+ public void addOrUpdate() {
+ store.addOrUpdate(PI_HANDLE, TRANSLATED_ENTITY);
+ assertTrue("Value should be in the map",
+ store.get(PI_HANDLE) != null);
+ assertTrue("Exactly 1 value should be in the map",
+ Lists.newArrayList(store.getAll()).size() == 1);
+
+ // Add again, expect 1 value.
+ store.addOrUpdate(PI_HANDLE, TRANSLATED_ENTITY);
+ assertTrue("Exactly 1 value should be in the map",
+ Lists.newArrayList(store.getAll()).size() == 1);
+ }
+
+ /**
+ * Test of value lookup.
+ */
+ @Test
+ public void lookup() throws Exception {
+ clear();
+ addOrUpdate();
+ assertEquals("Wrong value in the map",
+ store.get(PI_HANDLE), TRANSLATED_ENTITY);
+ }
+
+ /**
+ * Test of value removal.
+ */
+ @Test
+ public void clear() {
+ store.remove(PI_HANDLE);
+ assertTrue("Value should NOT be in the map",
+ store.get(PI_HANDLE) == null);
+ assertTrue("No value should be in the map",
+ Lists.newArrayList(store.getAll()).isEmpty());
+ }
+}
diff --git a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
index cc3c05d..6a03032 100644
--- a/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java
@@ -220,6 +220,7 @@
import org.onosproject.net.pi.model.PiTableType;
import org.onosproject.net.pi.runtime.PiAction;
import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiActionGroupHandle;
import org.onosproject.net.pi.runtime.PiActionGroupId;
import org.onosproject.net.pi.runtime.PiActionGroupMember;
import org.onosproject.net.pi.runtime.PiActionGroupMemberId;
@@ -227,9 +228,12 @@
import org.onosproject.net.pi.runtime.PiControlMetadata;
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiEntityType;
import org.onosproject.net.pi.runtime.PiExactFieldMatch;
import org.onosproject.net.pi.runtime.PiFieldMatch;
import org.onosproject.net.pi.runtime.PiGroupKey;
+import org.onosproject.net.pi.runtime.PiHandle;
import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
import org.onosproject.net.pi.runtime.PiMatchKey;
import org.onosproject.net.pi.runtime.PiPacketOperation;
@@ -239,6 +243,9 @@
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
import org.onosproject.net.pi.runtime.PiValidFieldMatch;
+import org.onosproject.net.pi.runtime.PiTableEntryHandle;
+import org.onosproject.net.pi.service.PiTranslatable;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.region.DefaultRegion;
import org.onosproject.net.region.Region;
@@ -630,6 +637,7 @@
// PI Runtime
PiAction.class,
PiActionGroup.class,
+ PiActionGroupHandle.class,
PiActionGroupId.class,
PiActionGroupMember.class,
PiActionGroupMemberId.class,
@@ -637,9 +645,12 @@
PiControlMetadata.class,
PiCounterCellData.class,
PiCounterCellId.class,
+ PiEntity.class,
+ PiEntityType.class,
PiExactFieldMatch.class,
PiFieldMatch.class,
PiGroupKey.class,
+ PiHandle.class,
PiLpmFieldMatch.class,
PiMatchKey.class,
PiPacketOperation.class,
@@ -649,6 +660,10 @@
PiTableEntry.class,
PiTernaryFieldMatch.class,
PiValidFieldMatch.class,
+ // PI service
+ PiTableEntryHandle.class,
+ PiTranslatedEntity.class,
+ PiTranslatable.class,
// Other
PiCriterion.class,
PiInstruction.class
diff --git a/drivers/p4runtime/BUCK b/drivers/p4runtime/BUCK
index b8167e8..09c9bf7 100644
--- a/drivers/p4runtime/BUCK
+++ b/drivers/p4runtime/BUCK
@@ -2,9 +2,11 @@
COMPILE_DEPS = [
'//lib:CORE_DEPS',
+ '//lib:KRYO',
'//protocols/p4runtime/api:onos-protocols-p4runtime-api',
'//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
'//lib:grpc-netty-' + GRPC_VER,
+ '//core/store/serializers:onos-core-serializers',
]
BUNDLES = [
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index 5f95eda..ca9392f 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -20,6 +20,9 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.grpc.StatusRuntimeException;
+import org.onlab.util.SharedExecutors;
+import org.onosproject.drivers.p4runtime.mirror.P4RuntimeTableMirror;
+import org.onosproject.drivers.p4runtime.mirror.TimedEntry;
import org.onosproject.net.flow.DefaultFlowEntry;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
@@ -32,15 +35,17 @@
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiTableEntry;
-import org.onosproject.net.pi.service.PiTranslationService;
+import org.onosproject.net.pi.runtime.PiTableEntryHandle;
+import org.onosproject.net.pi.service.PiFlowRuleTranslator;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
+import org.onosproject.net.pi.service.PiTranslationException;
import org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType;
-import org.onosproject.p4runtime.api.P4RuntimeFlowRuleWrapper;
-import org.onosproject.p4runtime.api.P4RuntimeTableEntryReference;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
@@ -49,6 +54,7 @@
import java.util.stream.Collectors;
import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Collections.singleton;
import static org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable.Operation.APPLY;
import static org.onosproject.drivers.p4runtime.P4RuntimeFlowRuleProgrammable.Operation.REMOVE;
import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
@@ -59,44 +65,40 @@
/**
* Implementation of the flow rule programmable behaviour for P4Runtime.
*/
-public class P4RuntimeFlowRuleProgrammable extends AbstractP4RuntimeHandlerBehaviour implements FlowRuleProgrammable {
+public class P4RuntimeFlowRuleProgrammable
+ extends AbstractP4RuntimeHandlerBehaviour
+ implements FlowRuleProgrammable {
- /*
- When updating an existing rule, if true, we issue a DELETE operation before inserting the new one, otherwise we
- issue a MODIFY operation. This is useful fore devices that do not support MODIFY operations for table entries.
- */
+ // When updating an existing rule, if true, we issue a DELETE operation
+ // before inserting the new one, otherwise we issue a MODIFY operation. This
+ // is useful fore devices that do not support MODIFY operations for table
+ // entries.
// TODO: make this attribute configurable by child drivers (e.g. BMv2 or Tofino)
private boolean deleteEntryBeforeUpdate = true;
- /*
- If true, we ignore re-installing rules that are already known in the ENTRY_STORE, i.e. same match key and action.
- */
- // TODO: can remove this check as soon as the multi-apply-per-same-flow rule bug is fixed.
- private boolean checkEntryStoreBeforeUpdate = true;
+ // If true, we ignore re-installing rules that are already exists the
+ // device, i.e. same match key and action.
+ // FIXME: can remove this check as soon as the multi-apply-per-same-flow rule bug is fixed.
+ private boolean checkStoreBeforeUpdate = true;
- /*
- If true, we avoid querying the device and return the content of the ENTRY_STORE.
- */
- // TODO: set to false after bmv2/PI bug fixed
+ // If true, we avoid querying the device and return what's already known by
+ // the ONOS store.
private boolean ignoreDeviceWhenGet = true;
- /*
- If true, we read all direct counters of a table with one request. Otherwise, send as many request as the number of
- table entries.
- */
- // TODO: set to true as soon as the feature is implemented in P4Runtime.
+ /* If true, we read all direct counters of a table with one request.
+ Otherwise, we send as many requests as the number of table entries. */
+ // FIXME: set to true as soon as the feature is implemented in P4Runtime.
private boolean readAllDirectCounters = false;
// Needed to synchronize operations over the same table entry.
- private static final ConcurrentMap<P4RuntimeTableEntryReference, Lock> ENTRY_LOCKS = Maps.newConcurrentMap();
-
- // TODO: replace with distributed store.
- // Can reuse old BMv2TableEntryService from ONOS 1.6
- private static final ConcurrentMap<P4RuntimeTableEntryReference, P4RuntimeFlowRuleWrapper> ENTRY_STORE =
- Maps.newConcurrentMap();
+ // FIXME: locks should be removed when unused (hint use cache with timeout)
+ private static final ConcurrentMap<PiTableEntryHandle, Lock>
+ ENTRY_LOCKS = Maps.newConcurrentMap();
private PiPipelineModel pipelineModel;
private PiPipelineInterpreter interpreter;
+ private P4RuntimeTableMirror tableMirror;
+ private PiFlowRuleTranslator translator;
@Override
protected boolean setupBehaviour() {
@@ -111,6 +113,8 @@
}
interpreter = device.as(PiPipelineInterpreter.class);
pipelineModel = pipeconf.pipelineModel();
+ tableMirror = handler().get(P4RuntimeTableMirror.class);
+ translator = piTranslationService.flowRuleTranslator();
return true;
}
@@ -122,98 +126,70 @@
}
if (ignoreDeviceWhenGet) {
- return ENTRY_STORE.values().stream()
- .filter(frWrapper -> frWrapper.rule().deviceId().equals(this.deviceId))
- .map(frWrapper -> new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
- 0, 0))
- .collect(Collectors.toList());
+ return getFlowEntriesFromMirror();
}
- ImmutableList.Builder<FlowEntry> resultBuilder = ImmutableList.builder();
- List<PiTableEntry> inconsistentEntries = Lists.newArrayList();
+ final ImmutableList.Builder<FlowEntry> result = ImmutableList.builder();
+ final List<PiTableEntry> inconsistentEntries = Lists.newArrayList();
for (PiTableModel tableModel : pipelineModel.tables()) {
- PiTableId piTableId = tableModel.id();
+ final PiTableId piTableId = tableModel.id();
- Collection<PiTableEntry> installedEntries;
+ // Read table entries.
+ final Collection<PiTableEntry> installedEntries;
try {
- // TODO: optimize by dumping entries and counters in parallel, from ALL tables with the same request.
+ // TODO: optimize by dumping entries and counters in parallel
+ // From ALL tables with the same request.
installedEntries = client.dumpTable(piTableId, pipeconf).get();
} catch (InterruptedException | ExecutionException e) {
if (!(e.getCause() instanceof StatusRuntimeException)) {
// gRPC errors are logged in the client.
- log.error("Exception while dumping table {} of {}", piTableId, deviceId, e);
+ log.error("Exception while dumping table {} of {}",
+ piTableId, deviceId, e);
}
- return Collections.emptyList();
+ continue; // next table
}
- Map<PiTableEntry, PiCounterCellData> counterCellMap;
- try {
- if (interpreter.mapTableCounter(piTableId).isPresent()) {
- PiCounterId piCounterId = interpreter.mapTableCounter(piTableId).get();
- Collection<PiCounterCellData> cellDatas;
- if (readAllDirectCounters) {
- cellDatas = client.readAllCounterCells(Collections.singleton(piCounterId), pipeconf).get();
- } else {
- Set<PiCounterCellId> cellIds = installedEntries.stream()
- .map(entry -> PiCounterCellId.ofDirect(piCounterId, entry))
- .collect(Collectors.toSet());
- cellDatas = client.readCounterCells(cellIds, pipeconf).get();
- }
- counterCellMap = cellDatas.stream()
- .collect(Collectors.toMap(c -> (c.cellId()).tableEntry(), c -> c));
- } else {
- counterCellMap = Collections.emptyMap();
- }
- installedEntries = client.dumpTable(piTableId, pipeconf).get();
- } catch (InterruptedException | ExecutionException e) {
- if (!(e.getCause() instanceof StatusRuntimeException)) {
- // gRPC errors are logged in the client.
- log.error("Exception while reading counters of table {} of {}", piTableId, deviceId, e);
- }
+ if (installedEntries.size() == 0) {
+ continue; // next table
+ }
+
+ // Read table direct counters (if any).
+ final Map<PiTableEntry, PiCounterCellData> counterCellMap;
+ if (interpreter.mapTableCounter(piTableId).isPresent()) {
+ PiCounterId piCounterId = interpreter.mapTableCounter(piTableId).get();
+ counterCellMap = readEntryCounters(piCounterId, installedEntries);
+ } else {
counterCellMap = Collections.emptyMap();
}
+ // Forge flow entries with counter values.
for (PiTableEntry installedEntry : installedEntries) {
- P4RuntimeTableEntryReference entryRef = new P4RuntimeTableEntryReference(deviceId,
- piTableId,
- installedEntry.matchKey());
+ final FlowEntry flowEntry = forgeFlowEntry(
+ installedEntry, counterCellMap.get(installedEntry));
- if (!ENTRY_STORE.containsKey(entryRef)) {
- // Inconsistent entry
+ if (flowEntry == null) {
+ // Entry is on device but unknown to translation service or
+ // device mirror. Inconsistent. Mark for removal.
+ // TODO: make this behaviour configurable
+ // In some cases it's fine for the device to have rules
+ // that were not installed by us.
inconsistentEntries.add(installedEntry);
- continue; // next one.
+ } else {
+ result.add(flowEntry);
}
-
- P4RuntimeFlowRuleWrapper frWrapper = ENTRY_STORE.get(entryRef);
-
- long bytes = 0L;
- long packets = 0L;
- if (counterCellMap.containsKey(installedEntry)) {
- PiCounterCellData counterCellData = counterCellMap.get(installedEntry);
- bytes = counterCellData.bytes();
- packets = counterCellData.packets();
- }
-
- resultBuilder.add(new DefaultFlowEntry(frWrapper.rule(),
- ADDED,
- frWrapper.lifeInSeconds(),
- packets,
- bytes));
}
}
if (inconsistentEntries.size() > 0) {
- log.warn("Found {} entries in {} that are not known by table entry service," +
- " removing them", inconsistentEntries.size(), deviceId);
- inconsistentEntries.forEach(entry -> log.debug(entry.toString()));
- // Async remove them.
- client.writeTableEntries(inconsistentEntries, DELETE, pipeconf);
+ // Async clean up inconsistent entries.
+ SharedExecutors.getSingleThreadExecutor().execute(
+ () -> cleanUpInconsistentEntries(inconsistentEntries));
}
- return resultBuilder.build();
+ return result.build();
}
@Override
@@ -226,109 +202,206 @@
return processFlowRules(rules, REMOVE);
}
- private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
+ private FlowEntry forgeFlowEntry(PiTableEntry entry,
+ PiCounterCellData cellData) {
+ final PiTableEntryHandle handle = PiTableEntryHandle
+ .of(deviceId, entry);
+ final Optional<PiTranslatedEntity<FlowRule, PiTableEntry>>
+ translatedEntity = translator.lookup(handle);
+ final TimedEntry<PiTableEntry> timedEntry = tableMirror.get(handle);
+
+ if (!translatedEntity.isPresent()) {
+ log.debug("Handle not found in store: {}", handle);
+ return null;
+ }
+
+ if (timedEntry == null) {
+ log.debug("Handle not found in device mirror: {}", handle);
+ return null;
+ }
+
+ if (cellData != null) {
+ return new DefaultFlowEntry(translatedEntity.get().original(),
+ ADDED, timedEntry.lifeSec(), cellData.bytes(),
+ cellData.bytes());
+ } else {
+ return new DefaultFlowEntry(translatedEntity.get().original(),
+ ADDED, timedEntry.lifeSec(), 0, 0);
+ }
+ }
+
+ private Collection<FlowEntry> getFlowEntriesFromMirror() {
+ return tableMirror.getAll(deviceId).stream()
+ .map(timedEntry -> forgeFlowEntry(
+ timedEntry.entry(), null))
+ .collect(Collectors.toList());
+ }
+
+ private void cleanUpInconsistentEntries(Collection<PiTableEntry> piEntries) {
+ log.warn("Found {} entries from {} not on translation store, removing them...",
+ piEntries.size(), deviceId);
+ piEntries.forEach(entry -> {
+ log.debug(entry.toString());
+ applyEntry(PiTableEntryHandle.of(deviceId, entry),
+ entry, null, REMOVE);
+ });
+ }
+
+ private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules,
+ Operation driverOperation) {
if (!setupBehaviour()) {
return Collections.emptyList();
}
- ImmutableList.Builder<FlowRule> processedFlowRuleListBuilder = ImmutableList.builder();
+ final ImmutableList.Builder<FlowRule> result = ImmutableList.builder();
- // TODO: send write operations in bulk (e.g. all entries to insert, modify or delete).
+ // TODO: send writes in bulk (e.g. all entries to insert, modify or delete).
// Instead of calling the client for each one of them.
- for (FlowRule rule : rules) {
+ for (FlowRule ruleToApply : rules) {
- PiTableEntry piTableEntry;
-
+ final PiTableEntry piEntryToApply;
try {
- piTableEntry = piTranslationService.translateFlowRule(rule, pipeconf);
- } catch (PiTranslationService.PiTranslationException e) {
- log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
- continue; // next rule
+ piEntryToApply = translator.translate(ruleToApply, pipeconf);
+ } catch (PiTranslationException e) {
+ log.warn("Unable to translate flow rule for pipeconf '{}': {} - {}",
+ pipeconf.id(), e.getMessage(), ruleToApply);
+ // Next rule.
+ continue;
}
- PiTableId tableId = piTableEntry.table();
- P4RuntimeTableEntryReference entryRef = new P4RuntimeTableEntryReference(deviceId,
- tableId, piTableEntry.matchKey());
+ final PiTableEntryHandle handle = PiTableEntryHandle
+ .of(deviceId, piEntryToApply);
- Lock lock = ENTRY_LOCKS.computeIfAbsent(entryRef, k -> new ReentrantLock());
+ // Serialize operations over the same match key/table/device ID.
+ final Lock lock = ENTRY_LOCKS.computeIfAbsent(handle, k -> new ReentrantLock());
lock.lock();
-
try {
-
- P4RuntimeFlowRuleWrapper frWrapper = ENTRY_STORE.get(entryRef);
- WriteOperationType opType = null;
- boolean doApply = true;
-
- if (operation == APPLY) {
- if (frWrapper == null) {
- // Entry is first-timer.
- opType = INSERT;
- } else {
- // This match key already exists in the device.
- if (checkEntryStoreBeforeUpdate &&
- piTableEntry.action().equals(frWrapper.piTableEntry().action())) {
- doApply = false;
- log.debug("Ignoring re-apply of existing entry: {}", piTableEntry);
- }
- if (doApply) {
- if (deleteEntryBeforeUpdate) {
- // We've seen some strange error when trying to modify existing flow rules.
- // Remove before re-adding the modified one.
- try {
- if (client.writeTableEntries(newArrayList(piTableEntry), DELETE, pipeconf).get()) {
- frWrapper = null;
- } else {
- log.warn("Unable to DELETE table entry (before re-adding) in {}: {}",
- deviceId, piTableEntry);
- }
- } catch (InterruptedException | ExecutionException e) {
- log.warn("Exception while deleting table entry:", operation.name(), e);
- }
- opType = INSERT;
- } else {
- opType = MODIFY;
- }
- }
- }
- } else {
- opType = DELETE;
+ if (applyEntry(handle, piEntryToApply,
+ ruleToApply, driverOperation)) {
+ result.add(ruleToApply);
}
-
- if (doApply) {
- try {
- if (client.writeTableEntries(newArrayList(piTableEntry), opType, pipeconf).get()) {
- processedFlowRuleListBuilder.add(rule);
- if (operation == APPLY) {
- frWrapper = new P4RuntimeFlowRuleWrapper(rule, piTableEntry,
- System.currentTimeMillis());
- } else {
- frWrapper = null;
- }
- } else {
- log.warn("Unable to {} table entry in {}: {}", opType.name(), deviceId, piTableEntry);
- }
- } catch (InterruptedException | ExecutionException e) {
- log.warn("Exception while performing {} table entry operation:", operation.name(), e);
- }
- } else {
- processedFlowRuleListBuilder.add(rule);
- }
-
- // Update entryRef binding in table entry service.
- if (frWrapper != null) {
- ENTRY_STORE.put(entryRef, frWrapper);
- } else {
- ENTRY_STORE.remove(entryRef);
- }
-
} finally {
lock.unlock();
}
}
- return processedFlowRuleListBuilder.build();
+ return result.build();
+ }
+
+ /**
+ * Applies the given entry to the device, and returns true if the operation
+ * was successful, false otherwise.
+ */
+ private boolean applyEntry(PiTableEntryHandle handle,
+ PiTableEntry piEntryToApply,
+ FlowRule ruleToApply,
+ Operation driverOperation) {
+ // Depending on the driver operation, and if a matching rule exists on
+ // the device, decide which P4 Runtime write operation to perform for
+ // this entry.
+ final TimedEntry<PiTableEntry> piEntryOnDevice = tableMirror.get(handle);
+ final WriteOperationType p4Operation;
+ if (driverOperation == APPLY) {
+ if (piEntryOnDevice == null) {
+ // Entry is first-timer.
+ p4Operation = INSERT;
+ } else {
+ if (checkStoreBeforeUpdate
+ && piEntryToApply.action().equals(piEntryOnDevice.entry().action())) {
+ log.debug("Ignoring re-apply of existing entry: {}", piEntryToApply);
+ p4Operation = null;
+ } else if (deleteEntryBeforeUpdate) {
+ // Some devices return error when updating existing
+ // entries. If requested, remove entry before
+ // re-inserting the modified one.
+ applyEntry(handle, piEntryOnDevice.entry(), null, REMOVE);
+ p4Operation = INSERT;
+ } else {
+ p4Operation = MODIFY;
+ }
+ }
+ } else {
+ p4Operation = DELETE;
+ }
+
+ if (p4Operation != null) {
+ if (writeEntry(piEntryToApply, p4Operation)) {
+ updateStores(handle, piEntryToApply, ruleToApply, p4Operation);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ // If no operation, let's pretend we applied the rule to the device.
+ return true;
+ }
+ }
+
+ /**
+ * Performs a write operation on the device.
+ */
+ private boolean writeEntry(PiTableEntry entry,
+ WriteOperationType p4Operation) {
+ try {
+ if (client.writeTableEntries(
+ newArrayList(entry), p4Operation, pipeconf).get()) {
+ return true;
+ } else {
+ log.warn("Unable to {} table entry in {}: {}",
+ p4Operation.name(), deviceId, entry);
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Exception while performing {} table entry operation:",
+ p4Operation, e);
+ }
+ return false;
+ }
+
+ private void updateStores(PiTableEntryHandle handle,
+ PiTableEntry entry,
+ FlowRule rule,
+ WriteOperationType p4Operation) {
+ switch (p4Operation) {
+ case INSERT:
+ case MODIFY:
+ tableMirror.put(handle, entry);
+ translator.learn(handle, new PiTranslatedEntity<>(rule, entry, handle));
+ break;
+ case DELETE:
+ tableMirror.remove(handle);
+ translator.forget(handle);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown operation " + p4Operation.name());
+ }
+ }
+
+ private Map<PiTableEntry, PiCounterCellData> readEntryCounters(
+ PiCounterId counterId, Collection<PiTableEntry> tableEntries) {
+ Collection<PiCounterCellData> cellDatas;
+ try {
+ if (readAllDirectCounters) {
+ cellDatas = client.readAllCounterCells(
+ singleton(counterId), pipeconf).get();
+ } else {
+ Set<PiCounterCellId> cellIds = tableEntries.stream()
+ .map(entry -> PiCounterCellId.ofDirect(counterId, entry))
+ .collect(Collectors.toSet());
+ cellDatas = client.readCounterCells(cellIds, pipeconf).get();
+ }
+ return cellDatas.stream()
+ .collect(Collectors.toMap(c -> c.cellId().tableEntry(), c -> c));
+ } catch (InterruptedException | ExecutionException e) {
+ if (!(e.getCause() instanceof StatusRuntimeException)) {
+ // gRPC errors are logged in the client.
+ log.error("Exception while reading counter '{}' from {}: {}",
+ counterId, deviceId, e);
+ }
+ return Collections.emptyMap();
+ }
}
enum Operation {
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
index 464c4b6..fb5e892 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeGroupProgrammable.java
@@ -17,180 +17,86 @@
package org.onosproject.drivers.p4runtime;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.onosproject.core.GroupId;
-import org.onosproject.net.Device;
+import org.onosproject.drivers.p4runtime.mirror.P4RuntimeGroupMirror;
import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.group.DefaultGroup;
import org.onosproject.net.group.Group;
import org.onosproject.net.group.GroupOperation;
import org.onosproject.net.group.GroupOperations;
import org.onosproject.net.group.GroupProgrammable;
import org.onosproject.net.group.GroupStore;
import org.onosproject.net.pi.model.PiActionProfileId;
+import org.onosproject.net.pi.model.PiActionProfileModel;
import org.onosproject.net.pi.runtime.PiActionGroup;
-import org.onosproject.net.pi.runtime.PiActionGroupId;
-import org.onosproject.net.pi.service.PiTranslationService;
-import org.onosproject.p4runtime.api.P4RuntimeClient;
-import org.onosproject.p4runtime.api.P4RuntimeGroupReference;
-import org.onosproject.p4runtime.api.P4RuntimeGroupWrapper;
+import org.onosproject.net.pi.runtime.PiActionGroupHandle;
+import org.onosproject.net.pi.service.PiGroupTranslator;
+import org.onosproject.net.pi.service.PiTranslatedEntity;
+import org.onosproject.net.pi.service.PiTranslationException;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.DELETE;
+import static org.onosproject.p4runtime.api.P4RuntimeClient.WriteOperationType.INSERT;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Implementation of the group programmable behaviour for P4Runtime.
*/
-public class P4RuntimeGroupProgrammable extends AbstractP4RuntimeHandlerBehaviour implements GroupProgrammable {
- private static final String ACT_GRP_MEMS = "action group members";
- private static final String DELETE = "delete";
- private static final String ACT_GRP = "action group";
- private static final String INSERT = "insert";
+public class P4RuntimeGroupProgrammable
+ extends AbstractP4RuntimeHandlerBehaviour
+ implements GroupProgrammable {
+
+ private enum Operation {
+ APPLY, REMOVE
+ }
+
+ private static final String ACT_GRP_MEMS_STR = "action group members";
+ private static final String DELETE_STR = "delete";
+ private static final String ACT_GRP_STR = "action group";
+ private static final String INSERT_STR = "insert";
+
private static final Logger log = getLogger(P4RuntimeGroupProgrammable.class);
- /*
- * About action groups in P4runtime:
- * The type field is a place holder in p4runtime.proto right now, and we haven't defined it yet. You can assume all
- * the groups are "select" as per the OF spec. As a remainder, in the P4 terminology a member corresponds to an OF
- * bucket. Each member can also be used directly in the match table (kind of like an OF indirect group).
- */
+ // If true, we ignore re-installing groups that are already known in the
+ // device mirror.
+ private boolean checkMirrorBeforeUpdate = true;
- // TODO: make this attribute configurable by child drivers (e.g. BMv2 or Tofino)
- /*
- When updating an existing rule, if true, we issue a DELETE operation before inserting the new one, otherwise we
- issue a MODIFY operation. This is useful fore devices that do not support MODIFY operations for table entries.
- */
- private boolean deleteBeforeUpdate = true;
-
- // TODO: can remove this check as soon as the multi-apply-per-same-flow rule bug is fixed.
- /*
- If true, we ignore re-installing rules that are already known in the ENTRY_STORE, i.e. same match key and action.
- */
- private boolean checkStoreBeforeUpdate = true;
+ private GroupStore groupStore;
+ private P4RuntimeGroupMirror groupMirror;
+ private PiGroupTranslator translator;
// Needed to synchronize operations over the same group.
- private static final Map<P4RuntimeGroupReference, Lock> GROUP_LOCKS = Maps.newConcurrentMap();
-
- // TODO: replace with distribute store
- private static final Map<P4RuntimeGroupReference, P4RuntimeGroupWrapper> GROUP_STORE = Maps.newConcurrentMap();
+ private static final Map<PiActionGroupHandle, Lock> GROUP_LOCKS =
+ Maps.newConcurrentMap();
@Override
- public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
+ protected boolean setupBehaviour() {
+ if (!super.setupBehaviour()) {
+ return false;
+ }
+ groupMirror = this.handler().get(P4RuntimeGroupMirror.class);
+ groupStore = handler().get(GroupStore.class);
+ translator = piTranslationService.groupTranslator();
+ return true;
+ }
+
+ @Override
+ public void performGroupOperation(DeviceId deviceId,
+ GroupOperations groupOps) {
if (!setupBehaviour()) {
return;
}
-
- Device device = handler().get(DeviceService.class).getDevice(deviceId);
-
- for (GroupOperation groupOp : groupOps.operations()) {
- processGroupOp(device, groupOp);
- }
- }
-
- private void processGroupOp(Device device, GroupOperation groupOp) {
- GroupId groupId = groupOp.groupId();
- GroupStore groupStore = handler().get(GroupStore.class);
- Group group = groupStore.getGroup(device.id(), groupId);
-
- PiActionGroup piActionGroup;
- try {
- piActionGroup = piTranslationService.translateGroup(group, pipeconf);
- } catch (PiTranslationService.PiTranslationException e) {
- log.warn("Unable translate group, aborting group operation {}: {}", groupOp.opType(), e.getMessage());
- return;
- }
-
- P4RuntimeGroupReference groupRef = new P4RuntimeGroupReference(deviceId, piActionGroup.actionProfileId(),
- piActionGroup.id());
-
- Lock lock = GROUP_LOCKS.computeIfAbsent(groupRef, k -> new ReentrantLock());
- lock.lock();
-
- try {
- P4RuntimeGroupWrapper oldGroupWrapper = GROUP_STORE.get(groupRef);
- P4RuntimeGroupWrapper newGroupWrapper = new P4RuntimeGroupWrapper(piActionGroup, group,
- System.currentTimeMillis());
- switch (groupOp.opType()) {
- case ADD:
- case MODIFY:
- if (writeGroupToDevice(oldGroupWrapper, piActionGroup)) {
- GROUP_STORE.put(groupRef, newGroupWrapper);
- }
- break;
- case DELETE:
- if (deleteGroupFromDevice(piActionGroup)) {
- GROUP_STORE.remove(groupRef);
- }
- break;
- default:
- log.warn("Group operation {} not supported", groupOp.opType());
- }
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Installs action group and members to device via client interface.
- *
- * @param oldGroupWrapper old group wrapper for the group; null if not exists
- * @param piActionGroup the action group to be installed
- * @return true if install success; false otherwise
- */
- private boolean writeGroupToDevice(P4RuntimeGroupWrapper oldGroupWrapper, PiActionGroup piActionGroup) {
- boolean success = true;
- CompletableFuture<Boolean> writeSuccess;
- if (checkStoreBeforeUpdate && oldGroupWrapper != null &&
- oldGroupWrapper.piActionGroup().equals(piActionGroup)) {
- // Action group already exists, ignore it
- return true;
- }
- if (deleteBeforeUpdate && oldGroupWrapper != null) {
- success = deleteGroupFromDevice(oldGroupWrapper.piActionGroup());
- }
- writeSuccess = client.writeActionGroupMembers(piActionGroup,
- P4RuntimeClient.WriteOperationType.INSERT,
- pipeconf);
- success = success && completeSuccess(writeSuccess, ACT_GRP_MEMS, INSERT);
-
- writeSuccess = client.writeActionGroup(piActionGroup,
- P4RuntimeClient.WriteOperationType.INSERT,
- pipeconf);
- success = success && completeSuccess(writeSuccess, ACT_GRP, INSERT);
- return success;
- }
-
- private boolean deleteGroupFromDevice(PiActionGroup piActionGroup) {
- boolean success;
- CompletableFuture<Boolean> writeSuccess;
- writeSuccess = client.writeActionGroup(piActionGroup,
- P4RuntimeClient.WriteOperationType.DELETE,
- pipeconf);
- success = completeSuccess(writeSuccess, ACT_GRP, DELETE);
- writeSuccess = client.writeActionGroupMembers(piActionGroup,
- P4RuntimeClient.WriteOperationType.DELETE,
- pipeconf);
- success = success && completeSuccess(writeSuccess, ACT_GRP_MEMS, DELETE);
- return success;
- }
-
- private boolean completeSuccess(CompletableFuture<Boolean> completableFuture,
- String topic, String action) {
- try {
- return completableFuture.get();
- } catch (InterruptedException | ExecutionException e) {
- log.warn("Can't {} {} due to {}", action, topic, e.getMessage());
- return false;
- }
+ groupOps.operations().forEach(op -> processGroupOp(deviceId, op));
}
@Override
@@ -198,58 +104,147 @@
if (!setupBehaviour()) {
return Collections.emptyList();
}
+ return pipeconf.pipelineModel().actionProfiles().stream()
+ .map(PiActionProfileModel::id)
+ .flatMap(this::streamGroupsFromDevice)
+ .collect(Collectors.toList());
+ }
- Collection<Group> result = Sets.newHashSet();
- Collection<PiActionProfileId> piActionProfileIds = Sets.newHashSet();
+ private void processGroupOp(DeviceId deviceId, GroupOperation groupOp) {
+ final Group pdGroup = groupStore.getGroup(deviceId, groupOp.groupId());
- // TODO: find better way to get all action profile ids. e.g. by providing them in the interpreter
- GROUP_STORE.forEach((groupRef, wrapper) -> piActionProfileIds.add(groupRef.actionProfileId()));
+ final PiActionGroup piGroup;
+ try {
+ piGroup = translator.translate(pdGroup, pipeconf);
+ } catch (PiTranslationException e) {
+ log.warn("Unable translate group, aborting {} operation: {}",
+ groupOp.opType(), e.getMessage());
+ return;
+ }
- AtomicBoolean success = new AtomicBoolean(true);
- piActionProfileIds.forEach(actionProfileId -> {
- Collection<PiActionGroup> piActionGroups = Sets.newHashSet();
- try {
- Collection<PiActionGroup> groupsFromDevice =
- client.dumpGroups(actionProfileId, pipeconf).get();
- if (groupsFromDevice == null) {
- // Got error
- success.set(false);
- } else {
- piActionGroups.addAll(groupsFromDevice);
- }
- } catch (ExecutionException | InterruptedException e) {
- log.error("Exception while dumping groups for action profile {}: {}",
- actionProfileId.id(), deviceId, e);
- success.set(false);
+ final PiActionGroupHandle handle = PiActionGroupHandle.of(deviceId, piGroup);
+
+ final PiActionGroup groupOnDevice = groupMirror.get(handle) == null
+ ? null
+ : groupMirror.get(handle).entry();
+
+ final Lock lock = GROUP_LOCKS.computeIfAbsent(handle, k -> new ReentrantLock());
+ lock.lock();
+ try {
+ final Operation operation;
+ switch (groupOp.opType()) {
+ case ADD:
+ case MODIFY:
+ operation = Operation.APPLY;
+ break;
+ case DELETE:
+ operation = Operation.REMOVE;
+ break;
+ default:
+ log.warn("Group operation {} not supported", groupOp.opType());
+ return;
}
-
- piActionGroups.forEach(piActionGroup -> {
- PiActionGroupId actionGroupId = piActionGroup.id();
- P4RuntimeGroupReference groupRef =
- new P4RuntimeGroupReference(deviceId, actionProfileId, actionGroupId);
- P4RuntimeGroupWrapper wrapper = GROUP_STORE.get(groupRef);
-
- if (wrapper == null) {
- // group exists in client, but can't find in ONOS
- log.warn("Can't find action profile group {} from local store.",
- groupRef);
- return;
- }
- if (!wrapper.piActionGroup().equals(piActionGroup)) {
- log.warn("Group from device is different to group from local store.");
- return;
- }
- result.add(wrapper.group());
-
- });
- });
-
- if (!success.get()) {
- // Got error while dump groups from device.
- return Collections.emptySet();
- } else {
- return result;
+ processPiGroup(handle, piGroup,
+ groupOnDevice, pdGroup, operation);
+ } finally {
+ lock.unlock();
}
}
+ private void processPiGroup(PiActionGroupHandle handle,
+ PiActionGroup groupToApply,
+ PiActionGroup groupOnDevice,
+ Group pdGroup, Operation operation) {
+ if (operation == Operation.APPLY) {
+ if (groupOnDevice != null) {
+ if (checkMirrorBeforeUpdate
+ && groupOnDevice.equals(groupToApply)) {
+ // Group on device has the same members, ignore operation.
+ return;
+ }
+ // Remove before adding it.
+ processPiGroup(handle, groupToApply, groupOnDevice,
+ pdGroup, Operation.REMOVE);
+ }
+ if (writeGroupToDevice(groupToApply)) {
+ groupMirror.put(handle, groupToApply);
+ translator.learn(handle, new PiTranslatedEntity<>(
+ pdGroup, groupToApply, handle));
+ }
+ } else {
+ if (deleteGroupFromDevice(groupToApply)) {
+ groupMirror.remove(handle);
+ translator.forget(handle);
+ }
+ }
+ }
+
+ private boolean writeGroupToDevice(PiActionGroup groupToApply) {
+ // First insert members, then group.
+ // The operation is deemed successful if both operations are successful.
+ // FIXME: add transactional semantics, i.e. remove members if group fails.
+ final boolean membersSuccess = completeFuture(
+ client.writeActionGroupMembers(groupToApply, INSERT, pipeconf),
+ ACT_GRP_MEMS_STR, INSERT_STR);
+ return membersSuccess && completeFuture(
+ client.writeActionGroup(groupToApply, INSERT, pipeconf),
+ ACT_GRP_STR, INSERT_STR);
+ }
+
+ private boolean deleteGroupFromDevice(PiActionGroup piActionGroup) {
+ // First delete group, then members.
+ // The operation is deemed successful if both operations are successful.
+ final boolean groupSuccess = completeFuture(
+ client.writeActionGroup(piActionGroup, DELETE, pipeconf),
+ ACT_GRP_STR, DELETE_STR);
+ return groupSuccess && completeFuture(
+ client.writeActionGroupMembers(piActionGroup, DELETE, pipeconf),
+ ACT_GRP_MEMS_STR, DELETE_STR);
+ }
+
+ private boolean completeFuture(CompletableFuture<Boolean> completableFuture,
+ String topic, String action) {
+ try {
+ if (completableFuture.get()) {
+ return true;
+ } else {
+ log.warn("Unable to {} {}", action, topic);
+ return false;
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Exception while performing {} {}: {}", action, topic, e.getMessage());
+ log.debug("Exception", e);
+ return false;
+ }
+ }
+
+ private Stream<Group> streamGroupsFromDevice(PiActionProfileId actProfId) {
+ try {
+ // Read PI groups and return original PD one.
+ return client.dumpGroups(actProfId, pipeconf).get().stream()
+ .map(this::forgeGroupEntry)
+ .filter(Objects::nonNull);
+ } catch (ExecutionException | InterruptedException e) {
+ log.error("Exception while dumping groups from action profile '{}' on {}: {}",
+ actProfId.id(), deviceId, e);
+ return Stream.empty();
+ }
+ }
+
+ private Group forgeGroupEntry(PiActionGroup piGroup) {
+ final PiActionGroupHandle handle = PiActionGroupHandle.of(deviceId, piGroup);
+ if (!translator.lookup(handle).isPresent()) {
+ log.warn("Missing PI group from translation store: {} - {}:{}",
+ pipeconf.id(), piGroup.actionProfileId(),
+ piGroup.id());
+ return null;
+ }
+ final long life = groupMirror.get(handle) != null
+ ? groupMirror.get(handle).lifeSec() : 0;
+ final Group original = translator.lookup(handle).get().original();
+ final DefaultGroup forgedGroup = new DefaultGroup(original.id(), original);
+ forgedGroup.setState(Group.GroupState.ADDED);
+ forgedGroup.setLife(life);
+ return forgedGroup;
+ }
}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java
index 7e41a99..f49ad18 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeHandshaker.java
@@ -66,7 +66,7 @@
client = controller.getClient(deviceId);
if (client == null || !controller.isReacheable(deviceId)) {
- result.complete(MastershipRole.STANDBY);
+ result.complete(MastershipRole.NONE);
return result;
}
if (newRole.equals(MastershipRole.MASTER)) {
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/AbstractDistributedP4RuntimeMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/AbstractDistributedP4RuntimeMirror.java
new file mode 100644
index 0000000..be0b0b3
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/AbstractDistributedP4RuntimeMirror.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import com.google.common.annotations.Beta;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiHandle;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Abstract implementation of a distributed P4Runtime mirror, backed by an
+ * {@link EventuallyConsistentMap}.
+ *
+ * @param <H> handle class
+ * @param <E> entry class
+ */
+@Beta
+@Component(immediate = true)
+public abstract class AbstractDistributedP4RuntimeMirror
+ <H extends PiHandle, E extends PiEntity>
+ implements P4RuntimeMirror<H, E> {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private StorageService storageService;
+
+ private EventuallyConsistentMap<H, TimedEntry<E>> mirrorMap;
+
+ @Activate
+ public void activate() {
+ mirrorMap = storageService
+ .<H, TimedEntry<E>>eventuallyConsistentMapBuilder()
+ .withName(mapName())
+ .withSerializer(storeSerializer())
+ .withTimestampProvider((k, v) -> new WallClockTimestamp())
+ .build();
+ log.info("Started");
+ }
+
+ abstract String mapName();
+
+ abstract KryoNamespace storeSerializer();
+
+ @Deactivate
+ public void deactivate() {
+ mirrorMap = null;
+ log.info("Stopped");
+ }
+
+ @Override
+ public Collection<TimedEntry<E>> getAll(DeviceId deviceId) {
+ checkNotNull(deviceId);
+ return mirrorMap.entrySet().stream()
+ .filter(entry -> entry.getKey().deviceId().equals(deviceId))
+ .map(Map.Entry::getValue)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public TimedEntry<E> get(H handle) {
+ checkNotNull(handle);
+ return mirrorMap.get(handle);
+ }
+
+ @Override
+ public void put(H handle, E entry) {
+ checkNotNull(handle);
+ checkNotNull(entry);
+ final long now = new WallClockTimestamp().unixTimestamp();
+ final TimedEntry<E> timedEntry = new TimedEntry<>(now, entry);
+ mirrorMap.put(handle, timedEntry);
+ }
+
+ @Override
+ public void remove(H handle) {
+ checkNotNull(handle);
+ mirrorMap.remove(handle);
+ }
+
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeGroupMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeGroupMirror.java
new file mode 100644
index 0000000..4c963a6
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeGroupMirror.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiActionGroupHandle;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Distributed implementation of a P4Runtime group mirror.
+ */
+@Component(immediate = true)
+@Service
+public final class DistributedP4RuntimeGroupMirror
+ extends AbstractDistributedP4RuntimeMirror
+ <PiActionGroupHandle, PiActionGroup>
+ implements P4RuntimeGroupMirror {
+
+ private static final String DIST_MAP_NAME = "onos-p4runtime-group-mirror";
+
+ @Override
+ String mapName() {
+ return DIST_MAP_NAME;
+ }
+
+ @Override
+ KryoNamespace storeSerializer() {
+ return KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(TimedEntry.class)
+ .build();
+ }
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeTableMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeTableMirror.java
new file mode 100644
index 0000000..f37cf44
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/DistributedP4RuntimeTableMirror.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.runtime.PiTableEntryHandle;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+/**
+ * Distributed implementation of a P4Runtime table mirror.
+ */
+@Component(immediate = true)
+@Service
+public final class DistributedP4RuntimeTableMirror
+ extends AbstractDistributedP4RuntimeMirror
+ <PiTableEntryHandle, PiTableEntry>
+ implements P4RuntimeTableMirror {
+
+ private static final String DIST_MAP_NAME = "onos-p4runtime-table-mirror";
+
+ @Override
+ String mapName() {
+ return DIST_MAP_NAME;
+ }
+
+ @Override
+ KryoNamespace storeSerializer() {
+ return KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(TimedEntry.class)
+ .build();
+ }
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeGroupMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeGroupMirror.java
new file mode 100644
index 0000000..f363e71
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeGroupMirror.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiActionGroupHandle;
+
+/**
+ * Mirror of action groups installed on a P4Runtime device.
+ */
+public interface P4RuntimeGroupMirror
+ extends P4RuntimeMirror<PiActionGroupHandle, PiActionGroup> {
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMirror.java
new file mode 100644
index 0000000..ab18c9d
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeMirror.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.net.pi.runtime.PiHandle;
+
+import java.util.Collection;
+
+/**
+ * Service to keep track of the device state for a given class of PI entities.
+ * The need of this service comes from the fact that P4 Runtime makes a
+ * distinction between INSERT and MODIFY operations, while ONOS drivers use a
+ * more generic "APPLY" behaviour (i.e. ADD or UPDATE). When applying an entry,
+ * we need to know if another one with the same handle (e.g. table entry with
+ * same match key) is already on the device to decide between INSERT or MODIFY.
+ * Moreover, this service maintains a "timed" version of PI entities such that
+ * we can compute the life of the entity on the device.
+ *
+ * @param <H> Handle class
+ * @param <E> Entity class
+ */
+@Beta
+public interface P4RuntimeMirror
+ <H extends PiHandle, E extends PiEntity> {
+
+ /**
+ * Returns all entries for the given device ID.
+ *
+ * @param deviceId device ID
+ * @return collection of table entries
+ */
+ Collection<TimedEntry<E>> getAll(DeviceId deviceId);
+
+ /**
+ * Returns entry associated to the given handle, if present, otherwise
+ * null.
+ *
+ * @param handle handle
+ * @return PI table entry
+ */
+ TimedEntry<E> get(H handle);
+
+ /**
+ * Stores the given entry associating it to the given handle.
+ *
+ * @param handle handle
+ * @param entry entry
+ */
+ void put(H handle, E entry);
+
+ /**
+ * Removes the entry associated to the given handle.
+ *
+ * @param handle handle
+ */
+ void remove(H handle);
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeTableMirror.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeTableMirror.java
new file mode 100644
index 0000000..318e2b0
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/P4RuntimeTableMirror.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.onosproject.net.pi.runtime.PiTableEntryHandle;
+
+/**
+ * Mirror of table entries installed on a P4Runtime device.
+ */
+public interface P4RuntimeTableMirror
+ extends P4RuntimeMirror<PiTableEntryHandle, PiTableEntry> {
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/TimedEntry.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/TimedEntry.java
new file mode 100644
index 0000000..76b44a0
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/TimedEntry.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.p4runtime.mirror;
+
+import org.onosproject.net.pi.runtime.PiEntity;
+import org.onosproject.store.service.WallClockTimestamp;
+
+public class TimedEntry<E extends PiEntity> {
+
+ private final long timestamp;
+ private final E entity;
+
+ TimedEntry(long timestamp, E entity) {
+ this.timestamp = timestamp;
+ this.entity = entity;
+ }
+
+ public long timestamp() {
+ return timestamp;
+ }
+
+ public E entry() {
+ return entity;
+ }
+
+ public long lifeSec() {
+ final long now = new WallClockTimestamp().unixTimestamp();
+ return (now - timestamp) / 1000;
+ }
+}
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/package-info.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/package-info.java
new file mode 100644
index 0000000..d9b21d6
--- /dev/null
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/mirror/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * P4 Runtime device mirror.
+ */
+package org.onosproject.drivers.p4runtime.mirror;
diff --git a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java
index b71acbf..b6ee3c2 100644
--- a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java
+++ b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicConstants.java
@@ -38,12 +38,15 @@
public static final String DOT = ".";
public static final String HDR = "hdr";
public static final String ETHERNET = "ethernet";
+ public static final String IPV4 = "ipv4";
public static final String LOCAL_METADATA = "local_metadata";
public static final String STANDARD_METADATA = "standard_metadata";
public static final PiMatchFieldId HDR_IN_PORT_ID = PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
public static final PiMatchFieldId HDR_ETH_DST_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
public static final PiMatchFieldId HDR_ETH_SRC_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
public static final PiMatchFieldId HDR_ETH_TYPE_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
+ public static final PiMatchFieldId HDR_IPV4_DST_ID = PiMatchFieldId.of(HDR + DOT + IPV4 + DOT + "dst_addr");
+ public static final PiMatchFieldId HDR_IPV4_SRC_ID = PiMatchFieldId.of(HDR + DOT + IPV4 + DOT + "src_addr");
public static final PiMatchFieldId HDR_NEXT_HOP_ID = PiMatchFieldId.of(LOCAL_METADATA + DOT + "next_hop_id");
public static final PiMatchFieldId HDR_SELECTOR_ID = PiMatchFieldId.of(LOCAL_METADATA + DOT + "selector");
// Table IDs
@@ -66,7 +69,7 @@
public static final PiActionParamId ACT_PRM_PORT_ID = PiActionParamId.of("port");
public static final PiActionParamId ACT_PRM_NEXT_HOP_ID = PiActionParamId.of("next_hop_id");
// Action Profile IDs
- public static final PiActionProfileId ACT_PRF_WCMP_SELECTOR_ID = PiActionProfileId.of("wcmp_selector");
+ public static final PiActionProfileId ACT_PRF_WCMP_SELECTOR_ID = PiActionProfileId.of("wcmp_control.wcmp_selector");
// Packet Metadata IDs
public static final PiControlMetadataId PKT_META_EGRESS_PORT_ID = PiControlMetadataId.of("egress_port");
public static final PiControlMetadataId PKT_META_INGRESS_PORT_ID = PiControlMetadataId.of("ingress_port");
diff --git a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java
index cc0ef46..8e7b399 100644
--- a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java
+++ b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/BasicInterpreterImpl.java
@@ -66,6 +66,8 @@
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_SRC_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_ETH_TYPE_ID;
import static org.onosproject.pipelines.basic.BasicConstants.HDR_IN_PORT_ID;
+import static org.onosproject.pipelines.basic.BasicConstants.HDR_IPV4_DST_ID;
+import static org.onosproject.pipelines.basic.BasicConstants.HDR_IPV4_SRC_ID;
import static org.onosproject.pipelines.basic.BasicConstants.PKT_META_EGRESS_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.PKT_META_INGRESS_PORT_ID;
import static org.onosproject.pipelines.basic.BasicConstants.PORT_BITWIDTH;
@@ -93,6 +95,8 @@
.put(Criterion.Type.ETH_DST, HDR_ETH_DST_ID)
.put(Criterion.Type.ETH_SRC, HDR_ETH_SRC_ID)
.put(Criterion.Type.ETH_TYPE, HDR_ETH_TYPE_ID)
+ .put(Criterion.Type.IPV4_SRC, HDR_IPV4_SRC_ID)
+ .put(Criterion.Type.IPV4_DST, HDR_IPV4_DST_ID)
.build();
@Override
diff --git a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/PipeconfLoader.java b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/PipeconfLoader.java
index 399d4db..9b33883 100644
--- a/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/PipeconfLoader.java
+++ b/pipelines/basic/src/main/java/org/onosproject/pipelines/basic/PipeconfLoader.java
@@ -50,14 +50,9 @@
private static final String BASIC_JSON_PATH = "/p4c-out/bmv2/basic.json";
private static final String BASIC_P4INFO = "/p4c-out/bmv2/basic.p4info";
- private static final PiPipeconfId ECMP_PIPECONF_ID = new PiPipeconfId("org.onosproject.pipelines.ecmp");
- private static final String ECMP_JSON_PATH = "/p4c-out/bmv2/ecmp.json";
- private static final String ECMP_P4INFO = "/p4c-out/bmv2/ecmp.p4info";
-
public static final PiPipeconf BASIC_PIPECONF = buildBasicPipeconf();
- public static final PiPipeconf ECMP_PIPECONF = buildEcmpPipeconf();
- private static final Collection<PiPipeconf> ALL_PIPECONFS = ImmutableList.of(BASIC_PIPECONF, ECMP_PIPECONF);
+ private static final Collection<PiPipeconf> ALL_PIPECONFS = ImmutableList.of(BASIC_PIPECONF);
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private PiPipeconfService piPipeconfService;
@@ -90,21 +85,6 @@
.build();
}
- private static PiPipeconf buildEcmpPipeconf() {
- final URL jsonUrl = PipeconfLoader.class.getResource(ECMP_JSON_PATH);
- final URL p4InfoUrl = PipeconfLoader.class.getResource(ECMP_P4INFO);
-
- return DefaultPiPipeconf.builder()
- .withId(ECMP_PIPECONF_ID)
- .withPipelineModel(parseP4Info(p4InfoUrl))
- .addBehaviour(PiPipelineInterpreter.class, EcmpInterpreterImpl.class)
- .addBehaviour(Pipeliner.class, DefaultSingleTablePipeline.class)
- .addBehaviour(PortStatisticsDiscovery.class, PortStatisticsDiscoveryImpl.class)
- .addExtension(P4_INFO_TEXT, p4InfoUrl)
- .addExtension(BMV2_JSON, jsonUrl)
- .build();
- }
-
private static PiPipelineModel parseP4Info(URL p4InfoUrl) {
try {
return P4InfoParser.parse(p4InfoUrl);
diff --git a/pipelines/basic/src/main/resources/Makefile b/pipelines/basic/src/main/resources/Makefile
index f695e02..2348ba6 100644
--- a/pipelines/basic/src/main/resources/Makefile
+++ b/pipelines/basic/src/main/resources/Makefile
@@ -1,14 +1,10 @@
-all: basic ecmp
+all: basic
basic: basic.p4
p4c-bm2-ss -o p4c-out/bmv2/basic.json \
--p4runtime-file p4c-out/bmv2/basic.p4info \
--p4runtime-format text basic.p4
-ecmp: ecmp.p4
- p4c-bm2-ss -o p4c-out/bmv2/ecmp.json \
- --p4runtime-file p4c-out/bmv2/ecmp.p4info \
- --p4runtime-format text ecmp.p4
clean:
rm -rf p4c-out/bmv2/*.json
- rm -rf p4c-out/bmv2/*.p4info
\ No newline at end of file
+ rm -rf p4c-out/bmv2/*.p4info
diff --git a/pipelines/basic/src/main/resources/ecmp.p4 b/pipelines/basic/src/main/resources/ecmp.p4
deleted file mode 100644
index ab86c39..0000000
--- a/pipelines/basic/src/main/resources/ecmp.p4
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <core.p4>
-#include <v1model.p4>
-
-#include "include/headers.p4"
-#include "include/defines.p4"
-#include "include/parsers.p4"
-#include "include/actions.p4"
-#include "include/port_counters.p4"
-#include "include/checksums.p4"
-#include "include/packet_io.p4"
-#include "include/table0.p4"
-
-// FIXME: this program is obsolete and should be removed.
-// The PI ECMP demo app should be refactored to use the WCMP capability of default.p4
-
-// Expected number of ports of an ECMP group.
-// This value is fixed, .i.e. we do not support ECMP over port groups of different
-// size. Due to hardware limitations, this value must be constant and a power of 2.
-
-#define ECMP_GROUP_SIZE 128w2
-
-//------------------------------------------------------------------------------
-// INGRESS PIPELINE
-//------------------------------------------------------------------------------
-
-control ingress(inout headers_t hdr,
- inout local_metadata_t local_metadata,
- inout standard_metadata_t standard_metadata) {
-
- direct_counter(CounterType.packets_and_bytes) ecmp_table_counter;
-
- table ecmp_table {
- key = {
- local_metadata.next_hop_id : exact;
- local_metadata.selector : exact;
- }
- actions = {
- set_egress_port(standard_metadata);
- }
- counters = ecmp_table_counter;
- }
-
- action set_ecmp_selector() {
- hash(local_metadata.selector, HashAlgorithm.crc16, (bit<64>) 0,
- {
- hdr.ipv4.src_addr,
- hdr.ipv4.dst_addr,
- hdr.ipv4.protocol,
- local_metadata.l4_src_port,
- local_metadata.l4_dst_port
- },
- ECMP_GROUP_SIZE);
- }
-
- apply {
- port_counters_ingress.apply(hdr, standard_metadata);
- packetio_ingress.apply(hdr, standard_metadata);
- table0_control.apply(hdr, local_metadata, standard_metadata);
- if (local_metadata.next_hop_id > 0) {
- set_ecmp_selector();
- ecmp_table.apply();
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// EGRESS PIPELINE
-//------------------------------------------------------------------------------
-
-control egress(inout headers_t hdr,
- inout local_metadata_t local_metadata,
- inout standard_metadata_t standard_metadata) {
-
- apply {
- port_counters_egress.apply(hdr, standard_metadata);
- packetio_egress.apply(hdr, standard_metadata);
- }
-}
-
-//------------------------------------------------------------------------------
-// SWITCH INSTANTIATION
-//------------------------------------------------------------------------------
-
-V1Switch(parser_impl(),
- verify_checksum_control(),
- ingress(),
- egress(),
- compute_checksum_control(),
- deparser()) main;
diff --git a/pipelines/basic/src/main/resources/include/headers.p4 b/pipelines/basic/src/main/resources/include/headers.p4
index b943c82..038e952 100644
--- a/pipelines/basic/src/main/resources/include/headers.p4
+++ b/pipelines/basic/src/main/resources/include/headers.p4
@@ -84,7 +84,6 @@
bit<16> l4_src_port;
bit<16> l4_dst_port;
next_hop_id_t next_hop_id;
- bit<16> selector;
}
#endif
diff --git a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
index a38b290..f1a65d1 100644
--- a/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
+++ b/pipelines/basic/src/main/resources/p4c-out/bmv2/basic.json
@@ -13,8 +13,7 @@
["tmp_0", 32, false],
["local_metadata_t.l4_src_port", 16, false],
["local_metadata_t.l4_dst_port", 16, false],
- ["local_metadata_t.next_hop_id", 16, false],
- ["local_metadata_t.selector", 16, false]
+ ["local_metadata_t.next_hop_id", 16, false]
]
},
{
diff --git a/pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.json b/pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.json
deleted file mode 100644
index 9c687f9..0000000
--- a/pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.json
+++ /dev/null
@@ -1,1244 +0,0 @@
-{
- "program" : "ecmp.p4",
- "__meta__" : {
- "version" : [2, 7],
- "compiler" : "https://github.com/p4lang/p4c"
- },
- "header_types" : [
- {
- "name" : "scalars_0",
- "id" : 0,
- "fields" : [
- ["tmp", 32, false],
- ["tmp_0", 32, false],
- ["local_metadata_t.l4_src_port", 16, false],
- ["local_metadata_t.l4_dst_port", 16, false],
- ["local_metadata_t.next_hop_id", 16, false],
- ["local_metadata_t.selector", 16, false]
- ]
- },
- {
- "name" : "ethernet_t",
- "id" : 1,
- "fields" : [
- ["dst_addr", 48, false],
- ["src_addr", 48, false],
- ["ether_type", 16, false]
- ]
- },
- {
- "name" : "ipv4_t",
- "id" : 2,
- "fields" : [
- ["version", 4, false],
- ["ihl", 4, false],
- ["diffserv", 8, false],
- ["len", 16, false],
- ["identification", 16, false],
- ["flags", 3, false],
- ["frag_offset", 13, false],
- ["ttl", 8, false],
- ["protocol", 8, false],
- ["hdr_checksum", 16, false],
- ["src_addr", 32, false],
- ["dst_addr", 32, false]
- ]
- },
- {
- "name" : "tcp_t",
- "id" : 3,
- "fields" : [
- ["src_port", 16, false],
- ["dst_port", 16, false],
- ["seq_no", 32, false],
- ["ack_no", 32, false],
- ["data_offset", 4, false],
- ["res", 3, false],
- ["ecn", 3, false],
- ["ctrl", 6, false],
- ["window", 16, false],
- ["checksum", 16, false],
- ["urgent_ptr", 16, false]
- ]
- },
- {
- "name" : "udp_t",
- "id" : 4,
- "fields" : [
- ["src_port", 16, false],
- ["dst_port", 16, false],
- ["length_", 16, false],
- ["checksum", 16, false]
- ]
- },
- {
- "name" : "packet_out_header_t",
- "id" : 5,
- "fields" : [
- ["egress_port", 9, false],
- ["_padding", 7, false]
- ]
- },
- {
- "name" : "packet_in_header_t",
- "id" : 6,
- "fields" : [
- ["ingress_port", 9, false],
- ["_padding_0", 7, false]
- ]
- },
- {
- "name" : "standard_metadata",
- "id" : 7,
- "fields" : [
- ["ingress_port", 9, false],
- ["egress_spec", 9, false],
- ["egress_port", 9, false],
- ["clone_spec", 32, false],
- ["instance_type", 32, false],
- ["drop", 1, false],
- ["recirculate_port", 16, false],
- ["packet_length", 32, false],
- ["enq_timestamp", 32, false],
- ["enq_qdepth", 19, false],
- ["deq_timedelta", 32, false],
- ["deq_qdepth", 19, false],
- ["ingress_global_timestamp", 48, false],
- ["lf_field_list", 32, false],
- ["mcast_grp", 16, false],
- ["resubmit_flag", 1, false],
- ["egress_rid", 16, false],
- ["checksum_error", 1, false],
- ["_padding_1", 4, false]
- ]
- }
- ],
- "headers" : [
- {
- "name" : "scalars",
- "id" : 0,
- "header_type" : "scalars_0",
- "metadata" : true,
- "pi_omit" : true
- },
- {
- "name" : "standard_metadata",
- "id" : 1,
- "header_type" : "standard_metadata",
- "metadata" : true,
- "pi_omit" : true
- },
- {
- "name" : "ethernet",
- "id" : 2,
- "header_type" : "ethernet_t",
- "metadata" : false,
- "pi_omit" : true
- },
- {
- "name" : "ipv4",
- "id" : 3,
- "header_type" : "ipv4_t",
- "metadata" : false,
- "pi_omit" : true
- },
- {
- "name" : "tcp",
- "id" : 4,
- "header_type" : "tcp_t",
- "metadata" : false,
- "pi_omit" : true
- },
- {
- "name" : "udp",
- "id" : 5,
- "header_type" : "udp_t",
- "metadata" : false,
- "pi_omit" : true
- },
- {
- "name" : "packet_out",
- "id" : 6,
- "header_type" : "packet_out_header_t",
- "metadata" : false,
- "pi_omit" : true
- },
- {
- "name" : "packet_in",
- "id" : 7,
- "header_type" : "packet_in_header_t",
- "metadata" : false,
- "pi_omit" : true
- }
- ],
- "header_stacks" : [],
- "header_union_types" : [],
- "header_unions" : [],
- "header_union_stacks" : [],
- "field_lists" : [],
- "errors" : [
- ["NoError", 1],
- ["PacketTooShort", 2],
- ["NoMatch", 3],
- ["StackOutOfBounds", 4],
- ["HeaderTooShort", 5],
- ["ParserTimeout", 6]
- ],
- "enums" : [],
- "parsers" : [
- {
- "name" : "parser",
- "id" : 0,
- "init_state" : "start",
- "parse_states" : [
- {
- "name" : "start",
- "id" : 0,
- "parser_ops" : [],
- "transitions" : [
- {
- "value" : "0x00ff",
- "mask" : null,
- "next_state" : "parse_packet_out"
- },
- {
- "value" : "default",
- "mask" : null,
- "next_state" : "parse_ethernet"
- }
- ],
- "transition_key" : [
- {
- "type" : "field",
- "value" : ["standard_metadata", "ingress_port"]
- }
- ]
- },
- {
- "name" : "parse_packet_out",
- "id" : 1,
- "parser_ops" : [
- {
- "parameters" : [
- {
- "type" : "regular",
- "value" : "packet_out"
- }
- ],
- "op" : "extract"
- }
- ],
- "transitions" : [
- {
- "value" : "default",
- "mask" : null,
- "next_state" : "parse_ethernet"
- }
- ],
- "transition_key" : []
- },
- {
- "name" : "parse_ethernet",
- "id" : 2,
- "parser_ops" : [
- {
- "parameters" : [
- {
- "type" : "regular",
- "value" : "ethernet"
- }
- ],
- "op" : "extract"
- }
- ],
- "transitions" : [
- {
- "value" : "0x0800",
- "mask" : null,
- "next_state" : "parse_ipv4"
- },
- {
- "value" : "default",
- "mask" : null,
- "next_state" : null
- }
- ],
- "transition_key" : [
- {
- "type" : "field",
- "value" : ["ethernet", "ether_type"]
- }
- ]
- },
- {
- "name" : "parse_ipv4",
- "id" : 3,
- "parser_ops" : [
- {
- "parameters" : [
- {
- "type" : "regular",
- "value" : "ipv4"
- }
- ],
- "op" : "extract"
- }
- ],
- "transitions" : [
- {
- "value" : "0x06",
- "mask" : null,
- "next_state" : "parse_tcp"
- },
- {
- "value" : "0x11",
- "mask" : null,
- "next_state" : "parse_udp"
- },
- {
- "value" : "default",
- "mask" : null,
- "next_state" : null
- }
- ],
- "transition_key" : [
- {
- "type" : "field",
- "value" : ["ipv4", "protocol"]
- }
- ]
- },
- {
- "name" : "parse_tcp",
- "id" : 4,
- "parser_ops" : [
- {
- "parameters" : [
- {
- "type" : "regular",
- "value" : "tcp"
- }
- ],
- "op" : "extract"
- },
- {
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.l4_src_port"]
- },
- {
- "type" : "field",
- "value" : ["tcp", "src_port"]
- }
- ],
- "op" : "set"
- },
- {
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.l4_dst_port"]
- },
- {
- "type" : "field",
- "value" : ["tcp", "dst_port"]
- }
- ],
- "op" : "set"
- }
- ],
- "transitions" : [
- {
- "value" : "default",
- "mask" : null,
- "next_state" : null
- }
- ],
- "transition_key" : []
- },
- {
- "name" : "parse_udp",
- "id" : 5,
- "parser_ops" : [
- {
- "parameters" : [
- {
- "type" : "regular",
- "value" : "udp"
- }
- ],
- "op" : "extract"
- },
- {
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.l4_src_port"]
- },
- {
- "type" : "field",
- "value" : ["udp", "src_port"]
- }
- ],
- "op" : "set"
- },
- {
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.l4_dst_port"]
- },
- {
- "type" : "field",
- "value" : ["udp", "dst_port"]
- }
- ],
- "op" : "set"
- }
- ],
- "transitions" : [
- {
- "value" : "default",
- "mask" : null,
- "next_state" : null
- }
- ],
- "transition_key" : []
- }
- ]
- }
- ],
- "deparsers" : [
- {
- "name" : "deparser",
- "id" : 0,
- "source_info" : {
- "filename" : "include/parsers.p4",
- "line" : 72,
- "column" : 8,
- "source_fragment" : "deparser"
- },
- "order" : ["packet_in", "ethernet", "ipv4", "tcp", "udp"]
- }
- ],
- "meter_arrays" : [],
- "counter_arrays" : [
- {
- "name" : "port_counters_ingress.ingress_port_counter",
- "id" : 0,
- "source_info" : {
- "filename" : "include/port_counters.p4",
- "line" : 26,
- "column" : 38,
- "source_fragment" : "ingress_port_counter"
- },
- "size" : 511,
- "is_direct" : false
- },
- {
- "name" : "table0_control.table0_counter",
- "id" : 1,
- "is_direct" : true,
- "binding" : "table0_control.table0"
- },
- {
- "name" : "ecmp_table_counter",
- "id" : 2,
- "is_direct" : true,
- "binding" : "ecmp_table"
- },
- {
- "name" : "port_counters_egress.egress_port_counter",
- "id" : 3,
- "source_info" : {
- "filename" : "include/port_counters.p4",
- "line" : 36,
- "column" : 38,
- "source_fragment" : "egress_port_counter"
- },
- "size" : 511,
- "is_direct" : false
- }
- ],
- "register_arrays" : [],
- "calculations" : [
- {
- "name" : "calc",
- "id" : 0,
- "algo" : "crc16",
- "input" : [
- {
- "type" : "field",
- "value" : ["ipv4", "src_addr"]
- },
- {
- "type" : "field",
- "value" : ["ipv4", "dst_addr"]
- },
- {
- "type" : "field",
- "value" : ["ipv4", "protocol"]
- },
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.l4_src_port"]
- },
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.l4_dst_port"]
- }
- ]
- }
- ],
- "learn_lists" : [],
- "actions" : [
- {
- "name" : "set_egress_port",
- "id" : 0,
- "runtime_data" : [
- {
- "name" : "port",
- "bitwidth" : 9
- }
- ],
- "primitives" : [
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["standard_metadata", "egress_spec"]
- },
- {
- "type" : "runtime_data",
- "value" : 0
- }
- ],
- "source_info" : {
- "filename" : "include/actions.p4",
- "line" : 28,
- "column" : 36,
- "source_fragment" : "port; ..."
- }
- }
- ]
- },
- {
- "name" : "set_egress_port",
- "id" : 1,
- "runtime_data" : [
- {
- "name" : "port",
- "bitwidth" : 9
- }
- ],
- "primitives" : [
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["standard_metadata", "egress_spec"]
- },
- {
- "type" : "runtime_data",
- "value" : 0
- }
- ],
- "source_info" : {
- "filename" : "include/actions.p4",
- "line" : 28,
- "column" : 36,
- "source_fragment" : "port; ..."
- }
- }
- ]
- },
- {
- "name" : "send_to_cpu",
- "id" : 2,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["standard_metadata", "egress_spec"]
- },
- {
- "type" : "hexstr",
- "value" : "0x00ff"
- }
- ],
- "source_info" : {
- "filename" : "include/headers.p4",
- "line" : 19,
- "column" : 24,
- "source_fragment" : "255; ..."
- }
- }
- ]
- },
- {
- "name" : "_drop",
- "id" : 3,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "drop",
- "parameters" : [],
- "source_info" : {
- "filename" : "include/actions.p4",
- "line" : 32,
- "column" : 4,
- "source_fragment" : "mark_to_drop()"
- }
- }
- ]
- },
- {
- "name" : "NoAction",
- "id" : 4,
- "runtime_data" : [],
- "primitives" : []
- },
- {
- "name" : "table0_control.set_next_hop_id",
- "id" : 5,
- "runtime_data" : [
- {
- "name" : "next_hop_id",
- "bitwidth" : 16
- }
- ],
- "primitives" : [
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.next_hop_id"]
- },
- {
- "type" : "runtime_data",
- "value" : 0
- }
- ],
- "source_info" : {
- "filename" : "include/table0.p4",
- "line" : 30,
- "column" : 8,
- "source_fragment" : "local_metadata.next_hop_id = next_hop_id"
- }
- }
- ]
- },
- {
- "name" : "set_ecmp_selector",
- "id" : 6,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "modify_field_with_hash_based_offset",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.selector"]
- },
- {
- "type" : "hexstr",
- "value" : "0x0000000000000000"
- },
- {
- "type" : "calculation",
- "value" : "calc"
- },
- {
- "type" : "hexstr",
- "value" : "0x00000000000000000000000000000002"
- }
- ],
- "source_info" : {
- "filename" : "ecmp.p4",
- "line" : 60,
- "column" : 8,
- "source_fragment" : "hash(local_metadata.selector, HashAlgorithm.crc16, (bit<64>) 0, ..."
- }
- }
- ]
- },
- {
- "name" : "act",
- "id" : 7,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["standard_metadata", "egress_spec"]
- },
- {
- "type" : "field",
- "value" : ["packet_out", "egress_port"]
- }
- ],
- "source_info" : {
- "filename" : "include/packet_io.p4",
- "line" : 27,
- "column" : 12,
- "source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
- }
- },
- {
- "op" : "remove_header",
- "parameters" : [
- {
- "type" : "header",
- "value" : "packet_out"
- }
- ],
- "source_info" : {
- "filename" : "include/packet_io.p4",
- "line" : 28,
- "column" : 12,
- "source_fragment" : "hdr.packet_out.setInvalid()"
- }
- }
- ]
- },
- {
- "name" : "act_0",
- "id" : 8,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "tmp"]
- },
- {
- "type" : "expression",
- "value" : {
- "type" : "expression",
- "value" : {
- "op" : "&",
- "left" : {
- "type" : "field",
- "value" : ["standard_metadata", "ingress_port"]
- },
- "right" : {
- "type" : "hexstr",
- "value" : "0xffffffff"
- }
- }
- }
- }
- ]
- },
- {
- "op" : "count",
- "parameters" : [
- {
- "type" : "counter_array",
- "value" : "port_counters_ingress.ingress_port_counter"
- },
- {
- "type" : "field",
- "value" : ["scalars", "tmp"]
- }
- ],
- "source_info" : {
- "filename" : "include/port_counters.p4",
- "line" : 29,
- "column" : 8,
- "source_fragment" : "ingress_port_counter.count((bit<32>) standard_metadata.ingress_port)"
- }
- }
- ]
- },
- {
- "name" : "act_1",
- "id" : 9,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "add_header",
- "parameters" : [
- {
- "type" : "header",
- "value" : "packet_in"
- }
- ],
- "source_info" : {
- "filename" : "include/packet_io.p4",
- "line" : 38,
- "column" : 12,
- "source_fragment" : "hdr.packet_in.setValid()"
- }
- },
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["packet_in", "ingress_port"]
- },
- {
- "type" : "field",
- "value" : ["standard_metadata", "ingress_port"]
- }
- ],
- "source_info" : {
- "filename" : "include/packet_io.p4",
- "line" : 39,
- "column" : 12,
- "source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
- }
- }
- ]
- },
- {
- "name" : "act_2",
- "id" : 10,
- "runtime_data" : [],
- "primitives" : [
- {
- "op" : "assign",
- "parameters" : [
- {
- "type" : "field",
- "value" : ["scalars", "tmp_0"]
- },
- {
- "type" : "expression",
- "value" : {
- "type" : "expression",
- "value" : {
- "op" : "&",
- "left" : {
- "type" : "field",
- "value" : ["standard_metadata", "egress_port"]
- },
- "right" : {
- "type" : "hexstr",
- "value" : "0xffffffff"
- }
- }
- }
- }
- ]
- },
- {
- "op" : "count",
- "parameters" : [
- {
- "type" : "counter_array",
- "value" : "port_counters_egress.egress_port_counter"
- },
- {
- "type" : "field",
- "value" : ["scalars", "tmp_0"]
- }
- ],
- "source_info" : {
- "filename" : "include/port_counters.p4",
- "line" : 39,
- "column" : 8,
- "source_fragment" : "egress_port_counter.count((bit<32>) standard_metadata.egress_port)"
- }
- }
- ]
- }
- ],
- "pipelines" : [
- {
- "name" : "ingress",
- "id" : 0,
- "source_info" : {
- "filename" : "ecmp.p4",
- "line" : 42,
- "column" : 8,
- "source_fragment" : "ingress"
- },
- "init_table" : "tbl_act",
- "tables" : [
- {
- "name" : "tbl_act",
- "id" : 0,
- "key" : [],
- "match_type" : "exact",
- "type" : "simple",
- "max_size" : 1024,
- "with_counters" : false,
- "support_timeout" : false,
- "direct_meters" : null,
- "action_ids" : [8],
- "actions" : ["act_0"],
- "base_default_next" : "node_3",
- "next_tables" : {
- "act_0" : "node_3"
- },
- "default_entry" : {
- "action_id" : 8,
- "action_const" : true,
- "action_data" : [],
- "action_entry_const" : true
- }
- },
- {
- "name" : "tbl_act_0",
- "id" : 1,
- "key" : [],
- "match_type" : "exact",
- "type" : "simple",
- "max_size" : 1024,
- "with_counters" : false,
- "support_timeout" : false,
- "direct_meters" : null,
- "action_ids" : [7],
- "actions" : ["act"],
- "base_default_next" : null,
- "next_tables" : {
- "act" : null
- },
- "default_entry" : {
- "action_id" : 7,
- "action_const" : true,
- "action_data" : [],
- "action_entry_const" : true
- }
- },
- {
- "name" : "table0_control.table0",
- "id" : 2,
- "source_info" : {
- "filename" : "include/table0.p4",
- "line" : 33,
- "column" : 10,
- "source_fragment" : "table0"
- },
- "key" : [
- {
- "match_type" : "ternary",
- "target" : ["standard_metadata", "ingress_port"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["ethernet", "src_addr"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["ethernet", "dst_addr"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["ethernet", "ether_type"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["ipv4", "src_addr"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["ipv4", "dst_addr"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["ipv4", "protocol"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["scalars", "local_metadata_t.l4_src_port"],
- "mask" : null
- },
- {
- "match_type" : "ternary",
- "target" : ["scalars", "local_metadata_t.l4_dst_port"],
- "mask" : null
- }
- ],
- "match_type" : "ternary",
- "type" : "simple",
- "max_size" : 1024,
- "support_timeout" : false,
- "direct_meters" : null,
- "action_ids" : [0, 2, 5, 3],
- "actions" : ["set_egress_port", "send_to_cpu", "table0_control.set_next_hop_id", "_drop"],
- "base_default_next" : "node_6",
- "next_tables" : {
- "set_egress_port" : "node_6",
- "send_to_cpu" : "node_6",
- "table0_control.set_next_hop_id" : "node_6",
- "_drop" : "node_6"
- },
- "default_entry" : {
- "action_id" : 3,
- "action_const" : true,
- "action_data" : [],
- "action_entry_const" : true
- }
- },
- {
- "name" : "tbl_set_ecmp_selector",
- "id" : 3,
- "key" : [],
- "match_type" : "exact",
- "type" : "simple",
- "max_size" : 1024,
- "with_counters" : false,
- "support_timeout" : false,
- "direct_meters" : null,
- "action_ids" : [6],
- "actions" : ["set_ecmp_selector"],
- "base_default_next" : "ecmp_table",
- "next_tables" : {
- "set_ecmp_selector" : "ecmp_table"
- },
- "default_entry" : {
- "action_id" : 6,
- "action_const" : true,
- "action_data" : [],
- "action_entry_const" : true
- }
- },
- {
- "name" : "ecmp_table",
- "id" : 4,
- "source_info" : {
- "filename" : "ecmp.p4",
- "line" : 48,
- "column" : 10,
- "source_fragment" : "ecmp_table"
- },
- "key" : [
- {
- "match_type" : "exact",
- "target" : ["scalars", "local_metadata_t.next_hop_id"],
- "mask" : null
- },
- {
- "match_type" : "exact",
- "target" : ["scalars", "local_metadata_t.selector"],
- "mask" : null
- }
- ],
- "match_type" : "exact",
- "type" : "simple",
- "max_size" : 1024,
- "support_timeout" : false,
- "direct_meters" : null,
- "action_ids" : [1, 4],
- "actions" : ["set_egress_port", "NoAction"],
- "base_default_next" : null,
- "next_tables" : {
- "set_egress_port" : null,
- "NoAction" : null
- },
- "default_entry" : {
- "action_id" : 4,
- "action_const" : false,
- "action_data" : [],
- "action_entry_const" : false
- }
- }
- ],
- "action_profiles" : [],
- "conditionals" : [
- {
- "name" : "node_3",
- "id" : 0,
- "source_info" : {
- "filename" : "include/packet_io.p4",
- "line" : 26,
- "column" : 12,
- "source_fragment" : "standard_metadata.ingress_port == CPU_PORT"
- },
- "expression" : {
- "type" : "expression",
- "value" : {
- "op" : "==",
- "left" : {
- "type" : "field",
- "value" : ["standard_metadata", "ingress_port"]
- },
- "right" : {
- "type" : "hexstr",
- "value" : "0x00ff"
- }
- }
- },
- "true_next" : "tbl_act_0",
- "false_next" : "table0_control.table0"
- },
- {
- "name" : "node_6",
- "id" : 1,
- "source_info" : {
- "filename" : "ecmp.p4",
- "line" : 75,
- "column" : 12,
- "source_fragment" : "local_metadata.next_hop_id > 0"
- },
- "expression" : {
- "type" : "expression",
- "value" : {
- "op" : ">",
- "left" : {
- "type" : "field",
- "value" : ["scalars", "local_metadata_t.next_hop_id"]
- },
- "right" : {
- "type" : "hexstr",
- "value" : "0x0000"
- }
- }
- },
- "false_next" : null,
- "true_next" : "tbl_set_ecmp_selector"
- }
- ]
- },
- {
- "name" : "egress",
- "id" : 1,
- "source_info" : {
- "filename" : "ecmp.p4",
- "line" : 86,
- "column" : 8,
- "source_fragment" : "egress"
- },
- "init_table" : "tbl_act_1",
- "tables" : [
- {
- "name" : "tbl_act_1",
- "id" : 5,
- "key" : [],
- "match_type" : "exact",
- "type" : "simple",
- "max_size" : 1024,
- "with_counters" : false,
- "support_timeout" : false,
- "direct_meters" : null,
- "action_ids" : [10],
- "actions" : ["act_2"],
- "base_default_next" : "node_12",
- "next_tables" : {
- "act_2" : "node_12"
- },
- "default_entry" : {
- "action_id" : 10,
- "action_const" : true,
- "action_data" : [],
- "action_entry_const" : true
- }
- },
- {
- "name" : "tbl_act_2",
- "id" : 6,
- "key" : [],
- "match_type" : "exact",
- "type" : "simple",
- "max_size" : 1024,
- "with_counters" : false,
- "support_timeout" : false,
- "direct_meters" : null,
- "action_ids" : [9],
- "actions" : ["act_1"],
- "base_default_next" : null,
- "next_tables" : {
- "act_1" : null
- },
- "default_entry" : {
- "action_id" : 9,
- "action_const" : true,
- "action_data" : [],
- "action_entry_const" : true
- }
- }
- ],
- "action_profiles" : [],
- "conditionals" : [
- {
- "name" : "node_12",
- "id" : 2,
- "source_info" : {
- "filename" : "include/packet_io.p4",
- "line" : 37,
- "column" : 12,
- "source_fragment" : "standard_metadata.egress_port == CPU_PORT"
- },
- "expression" : {
- "type" : "expression",
- "value" : {
- "op" : "==",
- "left" : {
- "type" : "field",
- "value" : ["standard_metadata", "egress_port"]
- },
- "right" : {
- "type" : "hexstr",
- "value" : "0x00ff"
- }
- }
- },
- "false_next" : null,
- "true_next" : "tbl_act_2"
- }
- ]
- }
- ],
- "checksums" : [],
- "force_arith" : [],
- "extern_instances" : [],
- "field_aliases" : [
- [
- "queueing_metadata.enq_timestamp",
- ["standard_metadata", "enq_timestamp"]
- ],
- [
- "queueing_metadata.enq_qdepth",
- ["standard_metadata", "enq_qdepth"]
- ],
- [
- "queueing_metadata.deq_timedelta",
- ["standard_metadata", "deq_timedelta"]
- ],
- [
- "queueing_metadata.deq_qdepth",
- ["standard_metadata", "deq_qdepth"]
- ],
- [
- "intrinsic_metadata.ingress_global_timestamp",
- ["standard_metadata", "ingress_global_timestamp"]
- ],
- [
- "intrinsic_metadata.lf_field_list",
- ["standard_metadata", "lf_field_list"]
- ],
- [
- "intrinsic_metadata.mcast_grp",
- ["standard_metadata", "mcast_grp"]
- ],
- [
- "intrinsic_metadata.resubmit_flag",
- ["standard_metadata", "resubmit_flag"]
- ],
- [
- "intrinsic_metadata.egress_rid",
- ["standard_metadata", "egress_rid"]
- ]
- ]
-}
\ No newline at end of file
diff --git a/pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.p4info b/pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.p4info
deleted file mode 100644
index 08a3dd9..0000000
--- a/pipelines/basic/src/main/resources/p4c-out/bmv2/ecmp.p4info
+++ /dev/null
@@ -1,224 +0,0 @@
-tables {
- preamble {
- id: 33571508
- name: "table0_control.table0"
- alias: "table0"
- }
- match_fields {
- id: 1
- name: "standard_metadata.ingress_port"
- bitwidth: 9
- match_type: TERNARY
- }
- match_fields {
- id: 2
- name: "hdr.ethernet.src_addr"
- bitwidth: 48
- match_type: TERNARY
- }
- match_fields {
- id: 3
- name: "hdr.ethernet.dst_addr"
- bitwidth: 48
- match_type: TERNARY
- }
- match_fields {
- id: 4
- name: "hdr.ethernet.ether_type"
- bitwidth: 16
- match_type: TERNARY
- }
- match_fields {
- id: 5
- name: "hdr.ipv4.src_addr"
- bitwidth: 32
- match_type: TERNARY
- }
- match_fields {
- id: 6
- name: "hdr.ipv4.dst_addr"
- bitwidth: 32
- match_type: TERNARY
- }
- match_fields {
- id: 7
- name: "hdr.ipv4.protocol"
- bitwidth: 8
- match_type: TERNARY
- }
- match_fields {
- id: 8
- name: "local_metadata.l4_src_port"
- bitwidth: 16
- match_type: TERNARY
- }
- match_fields {
- id: 9
- name: "local_metadata.l4_dst_port"
- bitwidth: 16
- match_type: TERNARY
- }
- action_refs {
- id: 16794308
- }
- action_refs {
- id: 16829080
- }
- action_refs {
- id: 16802895
- }
- action_refs {
- id: 16784184
- }
- const_default_action_id: 16784184
- direct_resource_ids: 302046050
- size: 1024
-}
-tables {
- preamble {
- id: 33601431
- name: "ecmp_table"
- alias: "ecmp_table"
- }
- match_fields {
- id: 1
- name: "local_metadata.next_hop_id"
- bitwidth: 16
- match_type: EXACT
- }
- match_fields {
- id: 2
- name: "local_metadata.selector"
- bitwidth: 16
- match_type: EXACT
- }
- action_refs {
- id: 16794308
- }
- action_refs {
- id: 16800567
- annotations: "@defaultonly()"
- }
- direct_resource_ids: 302010883
- size: 1024
-}
-actions {
- preamble {
- id: 16794308
- name: "set_egress_port"
- alias: "set_egress_port"
- }
- params {
- id: 1
- name: "port"
- bitwidth: 9
- }
-}
-actions {
- preamble {
- id: 16829080
- name: "send_to_cpu"
- alias: "send_to_cpu"
- }
-}
-actions {
- preamble {
- id: 16784184
- name: "_drop"
- alias: "_drop"
- }
-}
-actions {
- preamble {
- id: 16800567
- name: "NoAction"
- alias: "NoAction"
- }
-}
-actions {
- preamble {
- id: 16802895
- name: "table0_control.set_next_hop_id"
- alias: "set_next_hop_id"
- }
- params {
- id: 1
- name: "next_hop_id"
- bitwidth: 16
- }
-}
-actions {
- preamble {
- id: 16789898
- name: "set_ecmp_selector"
- alias: "set_ecmp_selector"
- }
-}
-counters {
- preamble {
- id: 302012579
- name: "port_counters_ingress.ingress_port_counter"
- alias: "ingress_port_counter"
- }
- spec {
- unit: PACKETS
- }
- size: 511
-}
-counters {
- preamble {
- id: 302012501
- name: "port_counters_egress.egress_port_counter"
- alias: "egress_port_counter"
- }
- spec {
- unit: PACKETS
- }
- size: 511
-}
-direct_counters {
- preamble {
- id: 302046050
- name: "table0_control.table0_counter"
- alias: "table0_counter"
- }
- spec {
- unit: BOTH
- }
- direct_table_id: 33571508
-}
-direct_counters {
- preamble {
- id: 302010883
- name: "ecmp_table_counter"
- alias: "ecmp_table_counter"
- }
- spec {
- unit: BOTH
- }
- direct_table_id: 33601431
-}
-controller_packet_metadata {
- preamble {
- id: 2868941301
- name: "packet_in"
- annotations: "@controller_header(\"packet_in\")"
- }
- metadata {
- id: 1
- name: "ingress_port"
- bitwidth: 9
- }
-}
-controller_packet_metadata {
- preamble {
- id: 2868916615
- name: "packet_out"
- annotations: "@controller_header(\"packet_out\")"
- }
- metadata {
- id: 1
- name: "egress_port"
- bitwidth: 9
- }
-}
diff --git a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java
index 91b9db0..269936a 100644
--- a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java
+++ b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java
@@ -168,6 +168,8 @@
doDummyMessage(channels.get(channelId));
return true;
} catch (IOException e) {
+ log.warn("Error in sending dummy message to device {}", channelId);
+ log.debug("Exception ", e);
return false;
}
} finally {
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeFlowRuleWrapper.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeFlowRuleWrapper.java
deleted file mode 100644
index 634116f..0000000
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeFlowRuleWrapper.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.p4runtime.api;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.pi.runtime.PiTableEntry;
-
-/**
- * A wrapper for a ONOS flow rule installed on a P4Runtime device.
- */
-@Beta
-public final class P4RuntimeFlowRuleWrapper {
-
- private final FlowRule rule;
- private final PiTableEntry piTableEntry;
- private final long installedOnMillis;
-
- /**
- * Creates a new flow rule wrapper.
- *
- * @param rule a flow rule
- * @param piTableEntry PI table entry
- * @param installedOnMillis the time (in milliseconds, since January 1, 1970 UTC) when the flow rule was installed
- * on the device
- */
- public P4RuntimeFlowRuleWrapper(FlowRule rule, PiTableEntry piTableEntry, long installedOnMillis) {
- this.rule = rule;
- this.piTableEntry = piTableEntry;
- this.installedOnMillis = installedOnMillis;
- }
-
- /**
- * Returns the flow rule contained by this wrapper.
- *
- * @return a flow rule
- */
- public FlowRule rule() {
- return rule;
- }
-
- /**
- * Returns the PI table entry defined by this wrapper.
- *
- * @return table entry
- */
- public PiTableEntry piTableEntry() {
- return piTableEntry;
- }
-
- /**
- * Return the number of seconds since when this flow rule was installed on the device.
- *
- * @return an integer value
- */
- public long lifeInSeconds() {
- return (System.currentTimeMillis() - installedOnMillis) / 1000;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(rule, installedOnMillis);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- final P4RuntimeFlowRuleWrapper other = (P4RuntimeFlowRuleWrapper) obj;
- return Objects.equal(this.rule, other.rule)
- && Objects.equal(this.installedOnMillis, other.installedOnMillis);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("rule", rule)
- .add("installedOnMillis", installedOnMillis)
- .toString();
- }
-}
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeGroupReference.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeGroupReference.java
deleted file mode 100644
index 55c9e61..0000000
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeGroupReference.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.p4runtime.api;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.pi.model.PiActionProfileId;
-import org.onosproject.net.pi.runtime.PiActionGroupId;
-
-/**
- * Class containing the reference for a group in P4Runtime.
- */
-@Beta
-public final class P4RuntimeGroupReference {
- private final DeviceId deviceId;
- private final PiActionProfileId piActionProfileId;
- private final PiActionGroupId groupId;
-
- /**
- * Creates P4 runtime group reference.
- *
- * @param deviceId the device id of group
- * @param piActionProfileId the action profile id
- * @param groupId the group Id of group
- */
- public P4RuntimeGroupReference(DeviceId deviceId, PiActionProfileId piActionProfileId,
- PiActionGroupId groupId) {
- this.deviceId = deviceId;
- this.piActionProfileId = piActionProfileId;
- this.groupId = groupId;
- }
-
- /**
- * Gets device id of this group.
- *
- * @return the device id
- */
- public DeviceId deviceId() {
- return deviceId;
- }
-
- /**
- * Gets action profile id of this group.
- *
- * @return the action profile id
- */
- public PiActionProfileId actionProfileId() {
- return piActionProfileId;
- }
-
- /**
- * Gets group id of this group.
- *
- * @return group id
- */
- public PiActionGroupId groupId() {
- return groupId;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- P4RuntimeGroupReference that = (P4RuntimeGroupReference) o;
- return Objects.equal(deviceId, that.deviceId) &&
- Objects.equal(piActionProfileId, that.piActionProfileId) &&
- Objects.equal(groupId, that.groupId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(deviceId, piActionProfileId, groupId);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("deviceId", deviceId)
- .add("piActionProfileId", piActionProfileId)
- .add("groupId", groupId)
- .toString();
- }
-}
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeGroupWrapper.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeGroupWrapper.java
deleted file mode 100644
index 8cb4b83..0000000
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeGroupWrapper.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.p4runtime.api;
-
-import com.google.common.annotations.Beta;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.pi.runtime.PiActionGroup;
-
-/**
- * A wrapper for a ONOS group installed on a P4Runtime device.
- */
-@Beta
-public class P4RuntimeGroupWrapper {
- private final PiActionGroup piActionGroup;
- private final Group group;
- private final long installMilliSeconds;
-
- /**
- * Creates new group wrapper.
- *
- * @param piActionGroup the Pi action group
- * @param group the group
- * @param installMilliSeconds the installation time
- */
- public P4RuntimeGroupWrapper(PiActionGroup piActionGroup, Group group,
- long installMilliSeconds) {
- this.piActionGroup = piActionGroup;
- this.group = group;
- this.installMilliSeconds = installMilliSeconds;
- }
-
- /**
- * Gets PI action group from this wrapper.
- *
- * @return the PI action group
- */
- public PiActionGroup piActionGroup() {
- return piActionGroup;
- }
-
- /**
- * Gets group from this wrapper.
- *
- * @return the group
- */
- public Group group() {
- return group;
- }
-
- /**
- * Gets installation time of this wrapper.
- *
- * @return the installation time
- */
- public long installMilliSeconds() {
- return installMilliSeconds;
- }
-}
diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeTableEntryReference.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeTableEntryReference.java
deleted file mode 100644
index d345f15..0000000
--- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeTableEntryReference.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.p4runtime.api;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.pi.model.PiTableId;
-import org.onosproject.net.pi.runtime.PiMatchKey;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Class containing the reference for a table entry in P4Runtime.
- */
-public final class P4RuntimeTableEntryReference {
-
- private final DeviceId deviceId;
- private final PiTableId tableId;
- private final PiMatchKey matchKey;
-
- /**
- * Creates a new table entry reference.
- *
- * @param deviceId a device ID
- * @param tableId a table name
- * @param matchKey a match key
- */
- public P4RuntimeTableEntryReference(DeviceId deviceId, PiTableId tableId, PiMatchKey matchKey) {
- this.deviceId = checkNotNull(deviceId);
- this.tableId = checkNotNull(tableId);
- this.matchKey = checkNotNull(matchKey);
- }
-
- /**
- * Returns the device ID of this table entry reference.
- *
- * @return a device ID
- */
- public DeviceId deviceId() {
- return deviceId;
- }
-
- /**
- * Returns the table id of this table entry reference.
- *
- * @return a table name
- */
- public PiTableId tableId() {
- return tableId;
- }
-
- /**
- * Returns the match key of this table entry reference.
- *
- * @return a match key
- */
- public PiMatchKey matchKey() {
- return matchKey;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(deviceId, tableId, matchKey);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- final P4RuntimeTableEntryReference other = (P4RuntimeTableEntryReference) obj;
- return Objects.equal(this.deviceId, other.deviceId)
- && Objects.equal(this.tableId, other.tableId)
- && Objects.equal(this.matchKey, other.matchKey);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("deviceId", deviceId)
- .add("tableId", tableId)
- .add("matchKey", matchKey)
- .toString();
- }
-}
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
index a77dc33..824ec79 100644
--- a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
@@ -30,7 +30,11 @@
import org.onlab.util.ItemNotFoundException;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
@@ -110,6 +114,10 @@
public class GeneralDeviceProvider extends AbstractProvider
implements DeviceProvider {
public static final String DRIVER = "driver";
+ public static final int REACHABILITY_TIMEOUT = 10;
+ public static final String DEPLOY = "deploy-";
+ public static final String PIPECONF_TOPIC = "-pipeconf";
+
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -131,8 +139,17 @@
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PiPipeconfService piPipeconfService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 10;
@Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
label = "Configure poll frequency for port status and statistics; " +
@@ -257,17 +274,26 @@
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
- log.debug("Received role {} for device {}", newRole, deviceId);
+ log.info("Received role {} for device {}", newRole, deviceId);
CompletableFuture<MastershipRole> roleReply = getHandshaker(deviceId).roleChanged(newRole);
- roleReply.thenAcceptAsync(mastership -> providerService.receivedRoleReply(deviceId, newRole, mastership));
+ roleReply.thenAcceptAsync(mastership -> {
+ providerService.receivedRoleReply(deviceId, newRole, mastership);
+ if (!mastership.equals(MastershipRole.MASTER) && scheduledTasks.get(deviceId) != null) {
+ scheduledTasks.get(deviceId).cancel(false);
+ scheduledTasks.remove(deviceId);
+ } else if (mastership.equals(MastershipRole.MASTER) && scheduledTasks.get(deviceId) == null) {
+ scheduledTasks.put(deviceId, schedulePolling(deviceId, false));
+ updatePortStatistics(deviceId);
+ }
+ });
}
@Override
public boolean isReachable(DeviceId deviceId) {
- log.debug("Testing rechability for device {}", deviceId);
+ log.debug("Testing reachability for device {}", deviceId);
CompletableFuture<Boolean> reachable = getHandshaker(deviceId).isReachable();
try {
- return reachable.get(10, TimeUnit.SECONDS);
+ return reachable.get(REACHABILITY_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
log.error("Device {} is not reachable", deviceId, e);
return false;
@@ -368,15 +394,7 @@
return;
}
- //Storing deviceKeyId and all other config values
- // as data in the driver with protocol_<info>
- // name as the key. e.g protocol_ip
- providerConfig.protocolsInfo()
- .forEach((protocol, deviceInfoConfig) -> {
- deviceInfoConfig.configValues()
- .forEach((k, v) -> driverData.set(protocol + "_" + k, v));
- driverData.set(protocol + "_key", deviceInfoConfig.deviceKeyId());
- });
+ addConfigData(providerConfig, driverData);
//Connecting to the device
CompletableFuture<Boolean> connected = handshaker.connect();
@@ -409,7 +427,7 @@
ports = deviceDiscovery.discoverPortDetails();
}
- if (!handlePipeconf(deviceId, driver, driverData)) {
+ if (!handlePipeconf(deviceId, driver, driverData, true)) {
// Something went wrong during handling of pipeconf.
// We already logged the error.
handshaker.disconnect();
@@ -425,11 +443,37 @@
}
}
+ private void connectStandbyDevice(DeviceId deviceId) {
+
+ //if device is pipeline programmable we merge pipeconf + base driver for every other role
+ GeneralProviderDeviceConfig providerConfig =
+ cfgService.getConfig(deviceId, GeneralProviderDeviceConfig.class);
+
+ Driver driver = getDriver(deviceId);
+
+
+ DriverData driverData = new DefaultDriverData(driver, deviceId);
+ DeviceHandshaker handshaker = getBehaviour(driver, DeviceHandshaker.class, driverData);
+ if (handshaker == null) {
+ log.error("Device {}, with driver {} does not support DeviceHandshaker " +
+ "behaviour, supported behaviours={}", deviceId, driver.name(), driver.behaviours());
+ return;
+ }
+ addConfigData(providerConfig, driverData);
+
+ //Connecting to the device
+ handshaker.connect().thenAcceptAsync(result -> {
+ if (result) {
+ handlePipeconf(deviceId, driver, driverData, false);
+ }
+ });
+ }
+
/**
* Handles the case of a device that is pipeline programmable. Returns true if the operation wa successful and the
* device can be registered to the core, false otherwise.
*/
- private boolean handlePipeconf(DeviceId deviceId, Driver driver, DriverData driverData) {
+ private boolean handlePipeconf(DeviceId deviceId, Driver driver, DriverData driverData, boolean deployPipeconf) {
PiPipelineProgrammable pipelineProg = getBehaviour(driver, PiPipelineProgrammable.class,
driverData);
@@ -439,6 +483,42 @@
return true;
}
+ PiPipeconf pipeconf = getPipeconf(deviceId, pipelineProg);
+
+ if (pipeconf != null) {
+
+ PiPipeconfId pipeconfId = pipeconf.id();
+
+ try {
+ if (deployPipeconf) {
+ if (!pipelineProg.deployPipeconf(pipeconf).get()) {
+ log.error("Unable to deploy pipeconf {} to {}, aborting device discovery",
+ pipeconfId, deviceId);
+ return false;
+ }
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Exception occurred while deploying pipeconf {} to device {}", pipeconf.id(), deviceId, e);
+ return false;
+ }
+ try {
+ if (!piPipeconfService.bindToDevice(pipeconfId, deviceId).get()) {
+ log.error("Unable to merge driver {} for device {} with pipeconf {}, aborting device discovery",
+ driver.name(), deviceId, pipeconfId);
+ return false;
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ log.warn("Exception occurred while binding pipeconf {} to device {}", pipeconf.id(), deviceId, e);
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ private PiPipeconf getPipeconf(DeviceId deviceId, PiPipelineProgrammable pipelineProg) {
PiPipeconfId pipeconfId = piPipeconfService.ofDevice(deviceId).orElseGet(() -> {
// No pipeconf has been associated with this device.
// Check if device driver provides a default one.
@@ -453,33 +533,16 @@
if (pipeconfId == null) {
log.warn("Device {} is pipeline programmable but no pipeconf can be associated to it.", deviceId);
- return false;
+ return null;
}
if (!piPipeconfService.getPipeconf(pipeconfId).isPresent()) {
log.warn("Pipeconf {} is not registered", pipeconfId);
- return false;
+ return null;
}
- PiPipeconf pipeconf = piPipeconfService.getPipeconf(pipeconfId).get();
-
- try {
- if (!pipelineProg.deployPipeconf(pipeconf).get()) {
- log.error("Unable to deploy pipeconf {} to {}, aborting device discovery", pipeconfId, deviceId);
- return false;
- }
-
- if (!piPipeconfService.bindToDevice(pipeconfId, deviceId).get()) {
- log.error("Unable to merge driver {} for device {} with pipeconf {}, aborting device discovery",
- driver.name(), deviceId, pipeconfId);
- return false;
- }
- } catch (InterruptedException | ExecutionException e) {
- throw new IllegalStateException(e);
- }
-
- return true;
+ return piPipeconfService.getPipeconf(pipeconfId).get();
}
private void advertiseDevice(DeviceId deviceId, DeviceDescription description, List<PortDescription> ports) {
@@ -492,7 +555,6 @@
DeviceHandshaker handshaker = getHandshaker(deviceId);
if (handshaker != null) {
CompletableFuture<Boolean> disconnect = handshaker.disconnect();
-
disconnect.thenAcceptAsync(result -> {
if (result) {
log.info("Disconnected device {}", deviceId);
@@ -560,6 +622,28 @@
log.info("Device {} is already connected to ONOS and is available", deviceId);
return;
}
+ NodeId leaderNodeId = leadershipService.runForLeadership(DEPLOY + deviceId.toString() + PIPECONF_TOPIC)
+ .leader().nodeId();
+ NodeId localNodeId = clusterService.getLocalNode().id();
+ if (localNodeId.equals(leaderNodeId)) {
+ if (processEvent(event, deviceId)) {
+ log.debug("{} is leader for {}, initiating the connection and deploying pipeline", leaderNodeId,
+ deviceId);
+ checkAndSubmitDeviceTask(deviceId);
+ }
+ } else {
+ if (processEvent(event, deviceId)) {
+ log.debug("{} is not leader for {}, initiating connection but not deploying pipeline, {} is LEADER",
+ localNodeId, deviceId, leaderNodeId);
+ connectionExecutor.submit(exceptionSafe(() -> connectStandbyDevice(deviceId)));
+ //FIXME this will be removed when config is synced
+ cleanUpConfigInfo(deviceId);
+ }
+ }
+
+ }
+
+ private boolean processEvent(NetworkConfigEvent event, DeviceId deviceId) {
//FIXME to be removed when netcfg will issue device events in a bundle or
// ensure all configuration needed is present
Lock lock = ENTRY_LOCKS.computeIfAbsent(deviceId, key -> new ReentrantLock());
@@ -590,7 +674,7 @@
// in the pipelineConfigured
if (deviceConfigured.contains(deviceId) && driverConfigured.contains(deviceId)
&& pipelineConfigured.contains(deviceId)) {
- checkAndSubmitDeviceTask(deviceId);
+ return true;
} else {
if (deviceConfigured.contains(deviceId) && driverConfigured.contains(deviceId)) {
log.debug("Waiting for pipeline configuration for device {}", deviceId);
@@ -604,6 +688,7 @@
log.debug("Only device configuration for device {}", deviceId);
}
}
+ return false;
} finally {
lock.unlock();
}
@@ -622,10 +707,26 @@
private void checkAndSubmitDeviceTask(DeviceId deviceId) {
connectionExecutor.submit(exceptionSafe(() -> connectDevice(deviceId)));
//FIXME this will be removed when configuration is synced.
+ cleanUpConfigInfo(deviceId);
+
+ }
+
+ private void addConfigData(GeneralProviderDeviceConfig providerConfig, DriverData driverData) {
+ //Storing deviceKeyId and all other config values
+ // as data in the driver with protocol_<info>
+ // name as the key. e.g protocol_ip
+ providerConfig.protocolsInfo()
+ .forEach((protocol, deviceInfoConfig) -> {
+ deviceInfoConfig.configValues()
+ .forEach((k, v) -> driverData.set(protocol + "_" + k, v));
+ driverData.set(protocol + "_key", deviceInfoConfig.deviceKeyId());
+ });
+ }
+
+ private void cleanUpConfigInfo(DeviceId deviceId) {
deviceConfigured.remove(deviceId);
driverConfigured.remove(deviceId);
pipelineConfigured.remove(deviceId);
-
}
private ScheduledFuture<?> schedulePolling(DeviceId deviceId, boolean randomize) {
@@ -650,10 +751,13 @@
//For now this is scheduled periodically, when streaming API will
// be available we check and base it on the streaming API (e.g. gNMI)
- scheduledTasks.put(deviceId, schedulePolling(deviceId, false));
- updatePortStatistics(deviceId);
+ if (mastershipService.isLocalMaster(deviceId)) {
+ scheduledTasks.put(deviceId, schedulePolling(deviceId, false));
+ updatePortStatistics(deviceId);
+ }
} else if (type.equals(Type.DEVICE_REMOVED)) {
+
connectionExecutor.submit(exceptionSafe(() ->
disconnectDevice(deviceId)));
}
diff --git a/providers/p4runtime/packet/src/main/java/org/onosproject/provider/p4runtime/packet/impl/P4RuntimePacketProvider.java b/providers/p4runtime/packet/src/main/java/org/onosproject/provider/p4runtime/packet/impl/P4RuntimePacketProvider.java
index fb4a97a..8693eff 100644
--- a/providers/p4runtime/packet/src/main/java/org/onosproject/provider/p4runtime/packet/impl/P4RuntimePacketProvider.java
+++ b/providers/p4runtime/packet/src/main/java/org/onosproject/provider/p4runtime/packet/impl/P4RuntimePacketProvider.java
@@ -22,6 +22,7 @@
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
@@ -67,6 +68,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
private PacketProviderService providerService;
private InternalPacketListener packetListener = new InternalPacketListener();
@@ -98,7 +102,7 @@
if (packet != null) {
DeviceId deviceId = packet.sendThrough();
Device device = deviceService.getDevice(deviceId);
- if (device.is(PacketProgrammable.class)) {
+ if (device.is(PacketProgrammable.class) && mastershipService.isLocalMaster(deviceId)) {
PacketProgrammable packetProgrammable = device.as(PacketProgrammable.class);
packetProgrammable.emit(packet);
} else {
@@ -148,7 +152,10 @@
@Override
public void event(P4RuntimeEvent event) {
- if (event.type() != P4RuntimeEvent.Type.PACKET_IN) {
+ //Masterhip message is sent to everybody but picked up only by master.
+ //FIXME we need the device ID into p4RuntimeEvnetSubject to check for mastsership
+ if (!(event.subject() instanceof P4RuntimePacketIn) || event.type() != P4RuntimeEvent.Type.PACKET_IN) {
+ log.debug("Event type {}", event.type());
// Not a packet-in event, ignore it.
return;
}
@@ -163,7 +170,7 @@
if (!device.is(PiPipelineInterpreter.class)) {
log.warn("Unable to process packet-in from {}, device has no PiPipelineInterpreter behaviour",
- deviceId);
+ deviceId);
return;
}
@@ -184,7 +191,7 @@
log.debug("Processing inbound packet: {}", inPkt.toString());
OutboundPacket outPkt = new DefaultOutboundPacket(eventSubject.deviceId(), null,
- operation.data().asReadOnlyBuffer());
+ operation.data().asReadOnlyBuffer());
PacketContext pktCtx = new P4RuntimePacketContext(System.currentTimeMillis(), inPkt, outPkt, false);
// Pushing the packet context up for processing.
diff --git a/tools/dev/mininet/bmv2.py b/tools/dev/mininet/bmv2.py
index f218257..7066f61 100644
--- a/tools/dev/mininet/bmv2.py
+++ b/tools/dev/mininet/bmv2.py
@@ -35,7 +35,8 @@
def config(self, **params):
r = super(Host, self).config(**params)
for off in ["rx", "tx", "sg"]:
- cmd = "/sbin/ethtool --offload %s %s off" % (self.defaultIntf(), off)
+ cmd = "/sbin/ethtool --offload %s %s off"\
+ % (self.defaultIntf(), off)
self.cmd(cmd)
# disable IPv6
self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
@@ -52,7 +53,8 @@
def __init__(self, name, json=None, debugger=False, loglevel="warn", elogger=False,
persistent=False, grpcPort=None, thriftPort=None, netcfg=True, dryrun=False,
- pipeconfId="", pktdump=False, valgrind=False, **kwargs):
+ pipeconfId="", pktdump=False, valgrind=False, netcfgDelay=0,
+ **kwargs):
Switch.__init__(self, name, **kwargs)
self.grpcPort = ONOSBmv2Switch.pickUnusedPort() if not grpcPort else grpcPort
self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not thriftPort else thriftPort
@@ -71,6 +73,7 @@
self.netcfg = parseBoolean(netcfg)
self.dryrun = parseBoolean(dryrun)
self.valgrind = parseBoolean(valgrind)
+ self.netcfgDelay = netcfgDelay
self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId
self.pipeconfId = pipeconfId
if persistent:
@@ -229,11 +232,13 @@
out = self.cmd(cmdStr)
if out:
print out
- if self.netcfg and self.valgrind:
- # With valgrind, it takes some time before the gRPC server is available.
- # Wait before pushing the netcfg.
- info("\n*** Waiting %d seconds before pushing the config to ONOS...\n" % VALGRIND_SLEEP)
- time.sleep(VALGRIND_SLEEP)
+ if self.netcfg:
+ if self.valgrind:
+ # With valgrind, it takes some time before the gRPC server is available.
+ # Wait before pushing the netcfg.
+ info("\n*** Waiting %d seconds before pushing the config to ONOS...\n" % VALGRIND_SLEEP)
+ time.sleep(VALGRIND_SLEEP)
+ time.sleep(self.netcfgDelay)
try: # onos.py
clist = controllers[0].nodes()
diff --git a/tools/dev/mininet/onos.py b/tools/dev/mininet/onos.py
index 9b59cc7..2e7b6f7 100755
--- a/tools/dev/mininet/onos.py
+++ b/tools/dev/mininet/onos.py
@@ -364,14 +364,11 @@
time.sleep( 1 )
info( ' ssh-port' )
waitListening( server=self, port=KarafPort, callback=self.sanityCheck )
- info( ' openflow-port' )
- waitListening( server=self, port=OpenFlowPort,
- callback=self.sanityCheck )
- info( ' client' )
+ info( ' protocol' )
while True:
result = quietRun( '%s -h %s "apps -a"' %
( self.client, self.IP() ), shell=True )
- if 'openflow' in result:
+ if 'openflow' in result or 'p4runtime' in result:
break
info( '.' )
self.sanityCheck()
@@ -380,7 +377,7 @@
while True:
result = quietRun( '%s -h %s "nodes"' %
( self.client, self.IP() ), shell=True )
- nodeStr = 'id=%s, address=%s:%s, state=READY, updated' %\
+ nodeStr = 'id=%s, address=%s:%s, state=READY' %\
( self.IP(), self.IP(), CopycatPort )
if nodeStr in result:
break
diff --git a/tools/test/topos/bmv2-demo.py b/tools/test/topos/bmv2-demo.py
index 9bccc5c..ef0a124 100755
--- a/tools/test/topos/bmv2-demo.py
+++ b/tools/test/topos/bmv2-demo.py
@@ -42,8 +42,12 @@
from mininet.topo import Topo
+def getCmdBg(cmd, logfile="/dev/null"):
+ return "{} > {} 2>&1 &".format(cmd, logfile)
+
+
class ClosTopo(Topo):
- "2 stage Clos topology"
+ """2 stage Clos topology"""
def __init__(self, args, **opts):
# Initialize topology and default options
@@ -58,16 +62,17 @@
for switchId in bmv2SwitchIds:
deviceId = int(switchId[1:])
- # Use first number in device id to calculate latitude (row number)
+ # Use first number in device id to calculate latitude (row number),
+ # use second to calculate longitude (column number)
latitude = SWITCH_BASE_LATITUDE + (deviceId // 10) * BASE_SHIFT
-
- # Use second number in device id to calculate longitude (column number)
longitude = BASE_LONGITUDE + (deviceId % 10) * BASE_SHIFT
+
bmv2Switches[switchId] = self.addSwitch(switchId,
cls=ONOSBmv2Switch,
- loglevel="warn",
+ loglevel=args.log_level,
deviceId=deviceId,
- netcfg=False,
+ netcfg=True,
+ netcfgDelay=0.5,
longitude=longitude,
latitude=latitude,
pipeconfId=args.pipeconf_id)
@@ -75,14 +80,17 @@
for i in range(1, args.size + 1):
for j in range(1, args.size + 1):
if i == j:
- # 2 links
- self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
+ self.addLink(bmv2Switches["s1%d" % i],
+ bmv2Switches["s2%d" % j],
cls=TCLink, bw=DEFAULT_SW_BW)
if args.with_imbalanced_striping:
- self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
+ # 2 links
+ self.addLink(bmv2Switches["s1%d" % i],
+ bmv2Switches["s2%d" % j],
cls=TCLink, bw=DEFAULT_SW_BW)
else:
- self.addLink(bmv2Switches["s1%d" % i], bmv2Switches["s2%d" % j],
+ self.addLink(bmv2Switches["s1%d" % i],
+ bmv2Switches["s2%d" % j],
cls=TCLink, bw=DEFAULT_SW_BW)
for hostId in range(1, args.size + 1):
@@ -90,11 +98,12 @@
cls=DemoHost,
ip="10.0.0.%d/24" % hostId,
mac='00:00:00:00:00:%02x' % hostId)
- self.addLink(host, bmv2Switches["s1%d" % hostId], cls=TCLink, bw=DEFAULT_HOST_BW)
+ self.addLink(host, bmv2Switches["s1%d" % hostId],
+ cls=TCLink, bw=DEFAULT_HOST_BW)
class DemoHost(ONOSHost):
- "Demo host"
+ """Demo host"""
def __init__(self, name, **params):
ONOSHost.__init__(self, name, **params)
@@ -109,10 +118,11 @@
self.cmd(self.getInfiniteCmdBg("iperf -s -u"))
def startIperfClient(self, h, flowBw="512k", numFlows=5, duration=5):
- iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(h.IP(), flowBw, numFlows, duration)
- self.cmd(self.getInfiniteCmdBg(iperfCmd, sleep=0))
+ iperfCmd = "iperf -c{} -u -b{} -P{} -t{}".format(
+ h.IP(), flowBw, numFlows, duration)
+ self.cmd(self.getInfiniteCmdBg(iperfCmd, delay=0))
- def stop(self):
+ def stop(self, **kwargs):
self.cmd("killall iperf")
self.cmd("killall ping")
self.cmd("killall arping")
@@ -127,28 +137,27 @@
)
print "**********"
- def getInfiniteCmdBg(self, cmd, logfile="/dev/null", sleep=1):
+ def getInfiniteCmdBg(self, cmd, logfile="/dev/null", delay=1):
return "(while [ -e {} ]; " \
"do {}; " \
"sleep {}; " \
- "done;) > {} 2>&1 &".format(self.exectoken, cmd, sleep, logfile)
-
- def getCmdBg(self, cmd, logfile="/dev/null"):
- return "{} > {} 2>&1 &".format(cmd, logfile)
+ "done;) > {} 2>&1 &".format(self.exectoken, cmd, delay, logfile)
def generateNetcfg(onosIp, net, args):
netcfg = OrderedDict()
+
+ netcfg['hosts'] = {}
netcfg['devices'] = {}
netcfg['links'] = {}
- netcfg['hosts'] = {}
- # Device configs
- for sw in net.switches:
- srcIp = sw.getSourceIp(onosIp)
- netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
+
+ if args.full_netcfg:
+ # Device configs
+ for sw in net.switches:
+ srcIp = sw.getSourceIp(onosIp)
+ netcfg['devices'][sw.onosDeviceId] = sw.getDeviceConfig(srcIp)
hostLocations = {}
- # Link configs
for link in net.links:
switchPort = link.intf1.name.split('-')
sw1Name = switchPort[0] # s11
@@ -172,14 +181,18 @@
hostLocations[sw2.name] = '%s/%s' % (sw1.onosDeviceId, port1)
continue
- for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1, sw2.onosDeviceId, port2),
- '%s/%s-%s/%s' % (sw2.onosDeviceId, port2, sw1.onosDeviceId, port1)):
- netcfg['links'][linkId] = {
- 'basic': {
- 'type': 'DIRECT',
- 'bandwidth': DEFAULT_SW_BW
+ if args.full_netcfg:
+ # Link configs
+ for linkId in ('%s/%s-%s/%s' % (sw1.onosDeviceId, port1,
+ sw2.onosDeviceId, port2),
+ '%s/%s-%s/%s' % (sw2.onosDeviceId, port2,
+ sw1.onosDeviceId, port1)):
+ netcfg['links'][linkId] = {
+ 'basic': {
+ 'type': 'DIRECT',
+ 'bandwidth': DEFAULT_SW_BW
+ }
}
- }
# Host configs
longitude = BASE_LONGITUDE
@@ -203,13 +216,14 @@
}
netcfg['hosts'][hostId] = hostConfig
- netcfg["apps"] = {
- "org.onosproject.core": {
- "core": {
- "linkDiscoveryMode": "STRICT"
+ if args.full_netcfg:
+ netcfg["apps"] = {
+ "org.onosproject.core": {
+ "core": {
+ "linkDiscoveryMode": "STRICT"
+ }
}
}
- }
print "Writing network config to %s" % TEMP_NETCFG_FILE
with open(TEMP_NETCFG_FILE, 'w') as tempFile:
@@ -233,7 +247,7 @@
print "Network started"
- # Generate background traffic.
+ # Always generate background pings.
sleep(3)
for (h1, h2) in combinations(net.hosts, 2):
h1.startPingBg(h2)
@@ -246,14 +260,17 @@
print "Iperf servers started"
- # sleep(4)
- # print "Starting traffic from h1 to h3..."
- # net.hosts[0].startIperfClient(net.hosts[-1], flowBw="200k", numFlows=100, duration=10)
+ if args.bg_traffic:
+ sleep(4)
+ print "Starting iperf clients..."
+ net.hosts[0].startIperfClient(net.hosts[-1], flowBw="400k",
+ numFlows=50, duration=10)
generateNetcfg(onosIp, net, args)
if args.netcfg_sleep > 0:
- print "Waiting %d seconds before pushing config to ONOS..." % args.netcfg_sleep
+ print "Waiting %d seconds before pushing config to ONOS..." \
+ % args.netcfg_sleep
sleep(args.netcfg_sleep)
print "Pushing config to ONOS..."
@@ -275,12 +292,25 @@
type=str, action="store", required=False)
parser.add_argument('--size', help='Number of leaf/spine switches',
type=int, action="store", required=False, default=2)
- parser.add_argument('--with-imbalanced-striping', help='Topology with imbalanced striping',
- type=bool, action="store", required=False, default=False)
+ parser.add_argument('--with-imbalanced-striping',
+ help='Topology with imbalanced striping',
+ type=bool, action="store", required=False,
+ default=False)
parser.add_argument('--pipeconf-id', help='Pipeconf ID for switches',
type=str, action="store", required=False, default='')
- parser.add_argument('--netcfg-sleep', help='Seconds to wait before pushing config to ONOS',
+ parser.add_argument('--netcfg-sleep',
+ help='Seconds to wait before pushing config to ONOS',
type=int, action="store", required=False, default=5)
- args = parser.parse_args()
+ parser.add_argument('--log-level', help='BMv2 log level',
+ type=str, action="store", required=False,
+ default='warn')
+ parser.add_argument('--full-netcfg',
+ help='Generate full netcfg JSON with links and devices',
+ type=bool, action="store", required=False,
+ default=False)
+ parser.add_argument('--bg-traffic',
+ help='Starts background traffic',
+ type=bool, action="store", required=False,
+ default=False)
setLogLevel('info')
- main(args)
+ main(parser.parse_args())