Pruned old BMv2 stuff

...making space for new BMv2 support via P4Runtime.

Change-Id: Ia34c1a152c0e6e006fd3b86afc5086316264b6f6
This commit is contained in:
Carmelo Cascone 2017-06-08 15:38:32 -04:00
parent 1022a4ef8e
commit 43740a078d
109 changed files with 0 additions and 14569 deletions

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>onos-app-bmv2-demo</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<artifactId>onos-app-bmv2-demo-common</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-protocol-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,560 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.demo.app.common;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
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.app.ApplicationAdminService;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Port;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleOperations;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyEvent;
import org.onosproject.net.topology.TopologyGraph;
import org.onosproject.net.topology.TopologyListener;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.net.topology.TopologyVertex;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Stream.concat;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.device.DeviceEvent.Type.*;
import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Abstract implementation of an app providing fabric connectivity for a 2-stage Clos topology of BMv2 devices.
*/
@Component(immediate = true)
public abstract class AbstractUpgradableFabricApp {
private static final Map<String, AbstractUpgradableFabricApp> APP_HANDLES = Maps.newConcurrentMap();
private static final int NUM_LEAFS = 3;
private static final int NUM_SPINES = 3;
private static final int FLOW_PRIORITY = 100;
private static final int CLEANUP_SLEEP = 2000;
protected final Logger log = getLogger(getClass());
private final TopologyListener topologyListener = new InternalTopologyListener();
private final DeviceListener deviceListener = new InternalDeviceListener();
private final HostListener hostListener = new InternalHostListener();
private final ExecutorService executorService = Executors
.newFixedThreadPool(8, groupedThreads("onos/bmv2-demo-app", "bmv2-app-task", log));
private final String appName;
private final String configurationName;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TopologyService topologyService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private ApplicationAdminService appService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private Bmv2DeviceContextService bmv2ContextService;
private boolean appActive = false;
private boolean appFreezed = false;
private boolean otherAppFound = false;
private AbstractUpgradableFabricApp otherApp;
private boolean flowRuleGenerated = false;
private ApplicationId appId;
private Bmv2DeviceContext bmv2Context;
private Set<DeviceId> leafSwitches;
private Set<DeviceId> spineSwitches;
private Map<DeviceId, List<FlowRule>> deviceFlowRules;
private Map<DeviceId, Bmv2DeviceContext> previousContexts;
private Map<DeviceId, Boolean> contextFlags;
private Map<DeviceId, Boolean> ruleFlags;
private ConcurrentMap<DeviceId, Lock> deviceLocks = Maps.newConcurrentMap();
/**
* Creates a new BMv2 fabric app.
*
* @param appName app name
* @param configurationName a common name for the P4 program / BMv2 configuration used by this app
* @param context a BMv2 device context to be used on devices
*/
protected AbstractUpgradableFabricApp(String appName, String configurationName, Bmv2DeviceContext context) {
this.appName = checkNotNull(appName);
this.configurationName = checkNotNull(configurationName);
this.bmv2Context = checkNotNull(context);
}
@Activate
public void activate() {
log.info("Starting...");
appActive = true;
appFreezed = false;
if (APP_HANDLES.size() > 0) {
if (APP_HANDLES.size() > 1) {
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);
otherApp.setAppFreezed(true);
}
APP_HANDLES.put(appName, this);
appId = coreService.registerApplication(appName);
topologyService.addListener(topologyListener);
deviceService.addListener(deviceListener);
hostService.addListener(hostListener);
bmv2ContextService.registerInterpreterClassLoader(bmv2Context.interpreter().getClass(),
this.getClass().getClassLoader());
init();
log.info("STARTED", appId.id());
}
@Deactivate
public void deactivate() {
log.info("Stopping...");
try {
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
List<Runnable> runningTasks = executorService.shutdownNow();
log.warn("Unable to stop the following tasks: {}", runningTasks);
}
deviceService.removeListener(deviceListener);
topologyService.removeListener(topologyListener);
hostService.removeListener(hostListener);
flowRuleService.removeFlowRulesById(appId);
appActive = false;
APP_HANDLES.remove(appName);
log.info("STOPPED");
}
private void init() {
// Reset any previous state
synchronized (this) {
flowRuleGenerated = Boolean.FALSE;
leafSwitches = Sets.newHashSet();
spineSwitches = Sets.newHashSet();
deviceFlowRules = Maps.newConcurrentMap();
ruleFlags = Maps.newConcurrentMap();
contextFlags = Maps.newConcurrentMap();
}
// Start flow rules generator...
spawnTask(() -> generateFlowRules(topologyService.currentTopology(), Sets.newHashSet(hostService.getHosts())));
}
private void setAppFreezed(boolean appFreezed) {
this.appFreezed = appFreezed;
if (appFreezed) {
log.info("Freezing...");
} else {
log.info("Unfreezing...!");
}
}
/**
* Perform device initialization. Returns true if the operation was successful, false otherwise.
*
* @param deviceId a device id
* @return a boolean value
*/
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.
*
* @param leaf a leaf device id
* @param srcHost a source host
* @param dstHosts a collection of destination hosts
* @param spines a collection of spine device IDs
* @param topology a topology
* @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)
throws FlowRuleGeneratorException;
/**
* 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
* @param topology a topology
* @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)
throws FlowRuleGeneratorException;
private void deployAllDevices() {
if (otherAppFound && otherApp.appActive) {
log.info("Deactivating other app...");
appService.deactivate(otherApp.appId);
try {
Thread.sleep(CLEANUP_SLEEP);
} catch (InterruptedException e) {
log.warn("Cleanup sleep interrupted!");
Thread.interrupted();
}
}
Stream.concat(leafSwitches.stream(), spineSwitches.stream())
.map(deviceService::getDevice)
.forEach(device -> spawnTask(() -> deployDevice(device)));
}
/**
* Executes a device deploy.
*
* @param device a device
*/
public void deployDevice(Device device) {
DeviceId deviceId = device.id();
// Synchronize executions over the same device.
Lock lock = deviceLocks.computeIfAbsent(deviceId, k -> new ReentrantLock());
lock.lock();
try {
// Set context if not already done.
if (!contextFlags.getOrDefault(deviceId, false)) {
log.info("Setting context to {} for {}...", configurationName, deviceId);
bmv2ContextService.setContext(deviceId, bmv2Context);
contextFlags.put(device.id(), true);
}
// Initialize device.
if (!initDevice(deviceId)) {
log.warn("Failed to initialize device {}", deviceId);
}
// Install rules.
if (!ruleFlags.getOrDefault(deviceId, false)) {
List<FlowRule> rules = deviceFlowRules.getOrDefault(deviceId, Collections.emptyList());
if (rules.size() > 0) {
log.info("Installing rules for {}...", deviceId);
installFlowRules(rules);
ruleFlags.put(deviceId, true);
}
}
} finally {
lock.unlock();
}
}
private void spawnTask(Runnable task) {
executorService.execute(task);
}
private void installFlowRules(Collection<FlowRule> rules) {
FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
rules.forEach(opsBuilder::add);
flowRuleService.apply(opsBuilder.build());
}
private void removeFlowRules(Collection<FlowRule> rules) {
FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
rules.forEach(opsBuilder::remove);
flowRuleService.apply(opsBuilder.build());
}
/**
* Generates the flow rules to provide host-to-host connectivity for the given topology and hosts.
*
* @param topo a topology
* @param hosts a collection of hosts
*/
private synchronized void generateFlowRules(Topology topo, Collection<Host> hosts) {
if (flowRuleGenerated) {
log.debug("Flow rules have been already generated, aborting...");
return;
}
log.debug("Starting flow rules generator...");
TopologyGraph graph = topologyService.getGraph(topo);
Set<DeviceId> spines = Sets.newHashSet();
Set<DeviceId> leafs = Sets.newHashSet();
graph.getVertexes().stream()
.map(TopologyVertex::deviceId)
.forEach(did -> (isSpine(did, topo) ? spines : leafs).add(did));
if (spines.size() != NUM_SPINES || leafs.size() != NUM_LEAFS) {
log.info("Invalid leaf/spine switches count, aborting... > leafCount={}, spineCount={}",
spines.size(), leafs.size());
return;
}
for (DeviceId did : spines) {
int portCount = deviceService.getPorts(did).size();
// Expected port count: num leafs + 1 redundant leaf link
if (portCount != (NUM_LEAFS + 1)) {
log.info("Invalid port count for spine, aborting... > deviceId={}, portCount={}", did, portCount);
return;
}
}
for (DeviceId did : leafs) {
int portCount = deviceService.getPorts(did).size();
// Expected port count: num spines + host port + 1 redundant spine link
if (portCount != (NUM_SPINES + 2)) {
log.info("Invalid port count for leaf, aborting... > deviceId={}, portCount={}", did, portCount);
return;
}
}
// Check hosts, number and exactly one per leaf
Map<DeviceId, Host> hostMap = Maps.newHashMap();
hosts.forEach(h -> hostMap.put(h.location().deviceId(), h));
if (hosts.size() != NUM_LEAFS || !leafs.equals(hostMap.keySet())) {
log.info("Wrong host configuration, aborting... > hostCount={}, hostMapz={}", hosts.size(), hostMap);
return;
}
List<FlowRule> newFlowRules = Lists.newArrayList();
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));
}
for (DeviceId deviceId : spines) {
newFlowRules.addAll(generateSpineRules(deviceId, hosts, topo));
}
} catch (FlowRuleGeneratorException e) {
log.warn("Exception while executing flow rule generator: ", e.toString());
return;
}
if (newFlowRules.size() == 0) {
// Something went wrong
log.error("0 flow rules generated, BUG?");
return;
}
// All good!
// Divide flow rules per device id...
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()))
.forEach(frs -> mapBuilder.put(frs.get(0).deviceId(), frs));
this.deviceFlowRules = mapBuilder.build();
this.leafSwitches = ImmutableSet.copyOf(leafs);
this.spineSwitches = ImmutableSet.copyOf(spines);
// Avoid other executions to modify the generated flow rules.
flowRuleGenerated = true;
log.info("Generated {} flow rules for {} devices", newFlowRules.size(), spines.size() + leafs.size());
spawnTask(this::deployAllDevices);
}
/**
* Returns a new, pre-configured flow rule builder.
*
* @param did a device id
* @param tableName a table name
* @return a new flow rule builder
*/
protected FlowRule.Builder flowRuleBuilder(DeviceId did, String tableName) throws FlowRuleGeneratorException {
Map<String, Integer> tableMap = bmv2Context.interpreter().tableIdMap().inverse();
if (tableMap.get(tableName) == null) {
throw new FlowRuleGeneratorException("Unknown table " + tableName);
}
return DefaultFlowRule.builder()
.forDevice(did)
.forTable(tableMap.get(tableName))
.fromApp(appId)
.withPriority(FLOW_PRIORITY)
.makePermanent();
}
private List<Port> getHostPorts(DeviceId deviceId, Topology topology) {
// Get all non-fabric ports.
return deviceService
.getPorts(deviceId)
.stream()
.filter(p -> !isFabricPort(p, topology))
.collect(Collectors.toList());
}
private boolean isSpine(DeviceId deviceId, Topology topology) {
// True if all ports are fabric.
return getHostPorts(deviceId, topology).size() == 0;
}
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()));
}
/**
* A listener of topology events that executes a flow rule generation task each time a device is added.
*/
private class InternalTopologyListener implements TopologyListener {
@Override
public void event(TopologyEvent event) {
spawnTask(() -> generateFlowRules(event.subject(), Sets.newHashSet(hostService.getHosts())));
}
@Override
public boolean isRelevant(TopologyEvent event) {
return !appFreezed &&
// If at least one reason is of type DEVICE_ADDED.
event.reasons().stream().
filter(r -> r instanceof DeviceEvent)
.filter(r -> ((DeviceEvent) r).type() == DEVICE_ADDED)
.findAny()
.isPresent();
}
}
/**
* 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
public void event(DeviceEvent event) {
spawnTask(() -> deployDevice(event.subject()));
}
@Override
public boolean isRelevant(DeviceEvent event) {
return !appFreezed &&
(event.type() == DEVICE_ADDED ||
event.type() == DEVICE_UPDATED ||
(event.type() == DEVICE_AVAILABILITY_CHANGED &&
deviceService.isAvailable(event.subject().id())));
}
}
/**
* A listener of host events that generates flow rules each time a new host is added.
*/
private class InternalHostListener implements HostListener {
@Override
public void event(HostEvent event) {
spawnTask(() -> generateFlowRules(topologyService.currentTopology(),
Sets.newHashSet(hostService.getHosts())));
}
@Override
public boolean isRelevant(HostEvent event) {
return !appFreezed && event.type() == HOST_ADDED;
}
}
/**
* An exception occurred while generating flow rules for this fabric.
*/
public class FlowRuleGeneratorException extends Exception {
public FlowRuleGeneratorException() {
}
public FlowRuleGeneratorException(String msg) {
super(msg);
}
public FlowRuleGeneratorException(Exception cause) {
super(cause);
}
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Bmv2 demo app common classes.
*/
package org.onosproject.bmv2.demo.app.common;

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-bmv2-demo-common/${project.version}</bundle>
</feature>
</features>

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>onos-app-bmv2-demo</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<artifactId>onos-app-bmv2-demo-ecmp</artifactId>
<packaging>bundle</packaging>
<properties>
<onos.app.name>org.onosproject.bmv2-ecmp-fabric</onos.app.name>
<onos.app.title>P4/BMv2 Demo Fabric App v1 (ECMP)</onos.app.title>
<onos.app.category>Traffic Steering</onos.app.category>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.readme>P4/BMv2 demo application with ECMP support for a 2-stage clos fabric topology</onos.app.readme>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-bmv2-demo-common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,272 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.demo.app.ecmp;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.felix.scr.annotations.Component;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
import org.onosproject.bmv2.demo.app.common.AbstractUpgradableFabricApp;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Path;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.ExtensionSelector;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.topology.DefaultTopologyVertex;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyGraph;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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.util.stream.Collectors.toSet;
import static org.onlab.packet.EthType.EtherType.IPV4;
import static org.onosproject.bmv2.demo.app.ecmp.EcmpInterpreter.*;
/**
* Implementation of an upgradable fabric app for the ECMP configuration.
*/
@Component(immediate = true)
public class EcmpFabricApp extends AbstractUpgradableFabricApp {
private static final String APP_NAME = "org.onosproject.bmv2-ecmp-fabric";
private static final String MODEL_NAME = "ECMP";
private static final String JSON_CONFIG_PATH = "/ecmp.json";
private static final Bmv2Configuration ECMP_CONFIGURATION = loadConfiguration();
private static final EcmpInterpreter ECMP_INTERPRETER = new EcmpInterpreter();
protected static final Bmv2DeviceContext ECMP_CONTEXT = new Bmv2DeviceContext(ECMP_CONFIGURATION, ECMP_INTERPRETER);
private static final Map<DeviceId, Map<Set<PortNumber>, Short>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
public EcmpFabricApp() {
super(APP_NAME, MODEL_NAME, ECMP_CONTEXT);
}
@Override
public boolean initDevice(DeviceId deviceId) {
// Nothing to do.
return true;
}
@Override
public List<FlowRule> generateLeafRules(DeviceId leaf, Host srcHost, Collection<Host> dstHosts,
Collection<DeviceId> availableSpines, Topology topo)
throws FlowRuleGeneratorException {
// Get ports which connect this leaf switch to hosts.
Set<PortNumber> hostPorts = deviceService.getPorts(leaf)
.stream()
.filter(port -> !isFabricPort(port, topo))
.map(Port::number)
.collect(Collectors.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))
.stream()
.filter(e -> availableSpines.contains(e.dst().deviceId()))
.map(e -> e.link().src().port())
.collect(Collectors.toSet());
if (hostPorts.size() != 1 || fabricPorts.size() == 0) {
log.error("Leaf switch has invalid port configuration: hostPorts={}, fabricPorts={}",
hostPorts.size(), fabricPorts.size());
throw new FlowRuleGeneratorException();
}
PortNumber hostPort = hostPorts.iterator().next();
List<FlowRule> rules = Lists.newArrayList();
TrafficTreatment treatment;
if (fabricPorts.size() > 1) {
// Do ECMP.
Pair<ExtensionTreatment, List<FlowRule>> result = provisionEcmpTreatment(leaf, fabricPorts);
rules.addAll(result.getRight());
ExtensionTreatment extTreatment = result.getLeft();
treatment = DefaultTrafficTreatment.builder().extension(extTreatment, leaf).build();
} else {
// Output on port.
PortNumber outPort = fabricPorts.iterator().next();
treatment = DefaultTrafficTreatment.builder().setOutput(outPort).build();
}
// From srHost to dstHosts.
for (Host dstHost : dstHosts) {
FlowRule rule = flowRuleBuilder(leaf, TABLE0)
.withSelector(
DefaultTrafficSelector.builder()
.matchInPort(hostPort)
.matchEthType(IPV4.ethType().toShort())
.matchEthSrc(srcHost.mac())
.matchEthDst(dstHost.mac())
.build())
.withTreatment(treatment)
.build();
rules.add(rule);
}
// From fabric ports to this leaf host.
for (PortNumber port : fabricPorts) {
FlowRule rule = flowRuleBuilder(leaf, TABLE0)
.withSelector(
DefaultTrafficSelector.builder()
.matchInPort(port)
.matchEthType(IPV4.ethType().toShort())
.matchEthDst(srcHost.mac())
.build())
.withTreatment(
DefaultTrafficTreatment.builder()
.setOutput(hostPort)
.build())
.build();
rules.add(rule);
}
return rules;
}
@Override
public List<FlowRule> generateSpineRules(DeviceId deviceId, Collection<Host> dstHosts, Topology topo)
throws FlowRuleGeneratorException {
List<FlowRule> rules = Lists.newArrayList();
// for each host
for (Host dstHost : dstHosts) {
Set<Path> paths = topologyService.getPaths(topo, deviceId, dstHost.location().deviceId());
if (paths.size() == 0) {
log.warn("Can't find any path between spine {} and host {}", deviceId, dstHost);
throw new FlowRuleGeneratorException();
}
TrafficTreatment treatment;
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<ExtensionTreatment, List<FlowRule>> result = provisionEcmpTreatment(deviceId, portNumbers);
rules.addAll(result.getRight());
treatment = DefaultTrafficTreatment.builder().extension(result.getLeft(), deviceId).build();
}
FlowRule rule = flowRuleBuilder(deviceId, TABLE0)
.withSelector(
DefaultTrafficSelector.builder()
.matchEthType(IPV4.ethType().toShort())
.matchEthDst(dstHost.mac())
.build())
.withTreatment(treatment)
.build();
rules.add(rule);
}
return rules;
}
private Pair<ExtensionTreatment, List<FlowRule>> provisionEcmpTreatment(DeviceId deviceId,
Set<PortNumber> fabricPorts)
throws FlowRuleGeneratorException {
// Install ECMP group table entries that map from hash values to actual fabric ports...
int groupId = groupIdOf(deviceId, fabricPorts);
int groupSize = fabricPorts.size();
Iterator<PortNumber> portIterator = fabricPorts.iterator();
List<FlowRule> rules = Lists.newArrayList();
for (short i = 0; i < groupSize; i++) {
ExtensionSelector extSelector = buildEcmpSelector(groupId, i);
FlowRule rule = flowRuleBuilder(deviceId, ECMP_GROUP_TABLE)
.withSelector(
DefaultTrafficSelector.builder()
.extension(extSelector, deviceId)
.build())
.withTreatment(
DefaultTrafficTreatment.builder()
.setOutput(portIterator.next())
.build())
.build();
rules.add(rule);
}
ExtensionTreatment extTreatment = buildEcmpTreatment(groupId, groupSize);
return Pair.of(extTreatment, rules);
}
private Bmv2ExtensionTreatment buildEcmpTreatment(int groupId, int groupSize) {
return Bmv2ExtensionTreatment.builder()
.forConfiguration(ECMP_CONTEXT.configuration())
.setActionName(ECMP_GROUP)
.addParameter(GROUP_ID, groupId)
.addParameter(GROUP_SIZE, groupSize)
.build();
}
private Bmv2ExtensionSelector buildEcmpSelector(int groupId, int selector) {
return Bmv2ExtensionSelector.builder()
.forConfiguration(ECMP_CONTEXT.configuration())
.matchExact(ECMP_METADATA, GROUP_ID, groupId)
.matchExact(ECMP_METADATA, SELECTOR, selector)
.build();
}
public int groupIdOf(DeviceId deviceId, Set<PortNumber> ports) {
DEVICE_GROUP_ID_MAP.putIfAbsent(deviceId, Maps.newHashMap());
// Counts the number of unique portNumber sets for each deviceId.
// Each distinct set of portNumbers will have a unique ID.
return DEVICE_GROUP_ID_MAP.get(deviceId).computeIfAbsent(ports, (pp) ->
(short) (DEVICE_GROUP_ID_MAP.get(deviceId).size() + 1));
}
private static Bmv2Configuration loadConfiguration() {
try {
JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
EcmpFabricApp.class.getResourceAsStream(JSON_CONFIG_PATH)))).asObject();
return Bmv2DefaultConfiguration.parse(json);
} catch (IOException e) {
throw new RuntimeException("Unable to load configuration", e);
}
}
}

View File

@ -1,123 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.demo.app.ecmp;
import com.google.common.collect.ImmutableBiMap;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.bmv2.api.context.Bmv2InterpreterException;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
import static org.onosproject.net.PortNumber.CONTROLLER;
import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
/**
* Implementation of a BMv2 interpreter for the ecmp.json configuration.
*/
public class EcmpInterpreter implements Bmv2Interpreter {
protected static final String ECMP_METADATA = "ecmp_metadata";
protected static final String SELECTOR = "selector";
protected static final String GROUP_ID = "groupId";
protected static final String GROUP_SIZE = "groupSize";
protected static final String ECMP_GROUP = "ecmp_group";
protected static final String ECMP_GROUP_TABLE = "ecmp_group_table";
protected static final String TABLE0 = "table0";
protected static final String SEND_TO_CPU = "send_to_cpu";
protected static final String DROP = "_drop";
protected static final String SET_EGRESS_PORT = "set_egress_port";
protected static final String PORT = "port";
private static final ImmutableBiMap<Criterion.Type, String> CRITERION_TYPE_MAP = ImmutableBiMap.of(
Criterion.Type.IN_PORT, "standard_metadata.ingress_port",
Criterion.Type.ETH_DST, "ethernet.dstAddr",
Criterion.Type.ETH_SRC, "ethernet.srcAddr",
Criterion.Type.ETH_TYPE, "ethernet.etherType");
private static final ImmutableBiMap<Integer, String> TABLE_ID_MAP = ImmutableBiMap.of(
0, TABLE0,
1, ECMP_GROUP_TABLE);
@Override
public ImmutableBiMap<Integer, String> tableIdMap() {
return TABLE_ID_MAP;
}
@Override
public ImmutableBiMap<Criterion.Type, String> criterionTypeMap() {
return CRITERION_TYPE_MAP;
}
@Override
public Bmv2Action mapTreatment(TrafficTreatment treatment, Bmv2Configuration configuration)
throws Bmv2InterpreterException {
if (treatment.allInstructions().size() == 0) {
// No instructions means drop for us.
return actionWithName(DROP);
} else if (treatment.allInstructions().size() > 1) {
// Otherwise, we understand treatments with only 1 instruction.
throw new Bmv2InterpreterException("Treatment has multiple instructions");
}
Instruction instruction = treatment.allInstructions().get(0);
switch (instruction.type()) {
case OUTPUT:
OutputInstruction outInstruction = (OutputInstruction) instruction;
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
return buildEgressAction(port, configuration);
} else if (port.equals(CONTROLLER)) {
return actionWithName(SEND_TO_CPU);
} else {
throw new Bmv2InterpreterException("Egress on logical port not supported: " + port);
}
case NOACTION:
return actionWithName(DROP);
default:
throw new Bmv2InterpreterException("Instruction type not supported: " + instruction.type().name());
}
}
private static Bmv2Action buildEgressAction(PortNumber port, Bmv2Configuration configuration)
throws Bmv2InterpreterException {
int portBitWidth = configuration.action(SET_EGRESS_PORT).runtimeData(PORT).bitWidth();
try {
ImmutableByteSequence portBs = fitByteSequence(ImmutableByteSequence.copyFrom(port.toLong()), portBitWidth);
return Bmv2Action.builder()
.withName(SET_EGRESS_PORT)
.addParameter(portBs)
.build();
} catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
throw new Bmv2InterpreterException(e.getMessage());
}
}
private static Bmv2Action actionWithName(String name) {
return Bmv2Action.builder().withName(name).build();
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 demo app for the ECMP configuration.
*/
package org.onosproject.bmv2.demo.app.ecmp;

View File

@ -1,909 +0,0 @@
{
"header_types": [
{
"name": "standard_metadata_t",
"id": 0,
"fields": [
[
"ingress_port",
9
],
[
"packet_length",
32
],
[
"egress_spec",
9
],
[
"egress_port",
9
],
[
"egress_instance",
32
],
[
"instance_type",
32
],
[
"clone_spec",
32
],
[
"_padding",
5
]
],
"length_exp": null,
"max_length": null
},
{
"name": "intrinsic_metadata_t",
"id": 1,
"fields": [
[
"ingress_global_timestamp",
32
],
[
"lf_field_list",
32
],
[
"mcast_grp",
16
],
[
"egress_rid",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "ethernet_t",
"id": 2,
"fields": [
[
"dstAddr",
48
],
[
"srcAddr",
48
],
[
"etherType",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "ipv4_t",
"id": 3,
"fields": [
[
"version",
4
],
[
"ihl",
4
],
[
"diffserv",
8
],
[
"totalLen",
16
],
[
"identification",
16
],
[
"flags",
3
],
[
"fragOffset",
13
],
[
"ttl",
8
],
[
"protocol",
8
],
[
"hdrChecksum",
16
],
[
"srcAddr",
32
],
[
"dstAddr",
32
]
],
"length_exp": null,
"max_length": null
},
{
"name": "tcp_t",
"id": 4,
"fields": [
[
"srcPort",
16
],
[
"dstPort",
16
],
[
"seqNo",
32
],
[
"ackNo",
32
],
[
"dataOffset",
4
],
[
"res",
3
],
[
"ecn",
3
],
[
"ctrl",
6
],
[
"window",
16
],
[
"checksum",
16
],
[
"urgentPtr",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "udp_t",
"id": 5,
"fields": [
[
"srcPort",
16
],
[
"dstPort",
16
],
[
"length_",
16
],
[
"checksum",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "ecmp_metadata_t",
"id": 6,
"fields": [
[
"groupId",
16
],
[
"selector",
16
]
],
"length_exp": null,
"max_length": null
}
],
"headers": [
{
"name": "standard_metadata",
"id": 0,
"header_type": "standard_metadata_t",
"metadata": true
},
{
"name": "intrinsic_metadata",
"id": 1,
"header_type": "intrinsic_metadata_t",
"metadata": true
},
{
"name": "ethernet",
"id": 2,
"header_type": "ethernet_t",
"metadata": false
},
{
"name": "ipv4",
"id": 3,
"header_type": "ipv4_t",
"metadata": false
},
{
"name": "tcp",
"id": 4,
"header_type": "tcp_t",
"metadata": false
},
{
"name": "udp",
"id": 5,
"header_type": "udp_t",
"metadata": false
},
{
"name": "ecmp_metadata",
"id": 6,
"header_type": "ecmp_metadata_t",
"metadata": true
}
],
"header_stacks": [],
"parsers": [
{
"name": "parser",
"id": 0,
"init_state": "start",
"parse_states": [
{
"name": "start",
"id": 0,
"parser_ops": [],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": "parse_ethernet"
}
]
},
{
"name": "parse_ethernet",
"id": 1,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "ethernet"
}
]
}
],
"transition_key": [
{
"type": "field",
"value": [
"ethernet",
"etherType"
]
}
],
"transitions": [
{
"value": "0x0800",
"mask": null,
"next_state": "parse_ipv4"
},
{
"value": "default",
"mask": null,
"next_state": null
}
]
},
{
"name": "parse_ipv4",
"id": 2,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "ipv4"
}
]
}
],
"transition_key": [
{
"type": "field",
"value": [
"ipv4",
"fragOffset"
]
},
{
"type": "field",
"value": [
"ipv4",
"protocol"
]
}
],
"transitions": [
{
"value": "0x000006",
"mask": null,
"next_state": "parse_tcp"
},
{
"value": "0x000011",
"mask": null,
"next_state": "parse_udp"
},
{
"value": "default",
"mask": null,
"next_state": null
}
]
},
{
"name": "parse_tcp",
"id": 3,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "tcp"
}
]
}
],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": null
}
]
},
{
"name": "parse_udp",
"id": 4,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "udp"
}
]
}
],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": null
}
]
}
]
}
],
"deparsers": [
{
"name": "deparser",
"id": 0,
"order": [
"ethernet",
"ipv4",
"udp",
"tcp"
]
}
],
"meter_arrays": [],
"actions": [
{
"name": "_drop",
"id": 0,
"runtime_data": [],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "hexstr",
"value": "0x1ff"
}
]
}
]
},
{
"name": "ecmp_group",
"id": 1,
"runtime_data": [
{
"name": "groupId",
"bitwidth": 16
},
{
"name": "groupSize",
"bitwidth": 16
}
],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"ecmp_metadata",
"groupId"
]
},
{
"type": "runtime_data",
"value": 0
}
]
},
{
"op": "modify_field_with_hash_based_offset",
"parameters": [
{
"type": "field",
"value": [
"ecmp_metadata",
"selector"
]
},
{
"type": "hexstr",
"value": "0x0"
},
{
"type": "calculation",
"value": "ecmp_hash"
},
{
"type": "runtime_data",
"value": 1
}
]
}
]
},
{
"name": "send_to_cpu",
"id": 2,
"runtime_data": [],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "hexstr",
"value": "0xff"
}
]
}
]
},
{
"name": "count_packet",
"id": 3,
"runtime_data": [],
"primitives": [
{
"op": "count",
"parameters": [
{
"type": "counter_array",
"value": "ingress_port_counter"
},
{
"type": "field",
"value": [
"standard_metadata",
"ingress_port"
]
}
]
},
{
"op": "count",
"parameters": [
{
"type": "counter_array",
"value": "egress_port_counter"
},
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
}
]
}
]
},
{
"name": "set_egress_port",
"id": 4,
"runtime_data": [
{
"name": "port",
"bitwidth": 9
}
],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "runtime_data",
"value": 0
}
]
}
]
}
],
"pipelines": [
{
"name": "ingress",
"id": 0,
"init_table": "table0",
"tables": [
{
"name": "port_count_table",
"id": 0,
"match_type": "exact",
"type": "simple",
"max_size": 16384,
"with_counters": false,
"direct_meters": null,
"support_timeout": false,
"key": [],
"actions": [
"count_packet"
],
"next_tables": {
"count_packet": null
},
"default_action": null,
"base_default_next": null
},
{
"name": "table0",
"id": 1,
"match_type": "ternary",
"type": "simple",
"max_size": 16384,
"with_counters": true,
"direct_meters": null,
"support_timeout": true,
"key": [
{
"match_type": "ternary",
"target": [
"standard_metadata",
"ingress_port"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"dstAddr"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"srcAddr"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"etherType"
],
"mask": null
}
],
"actions": [
"set_egress_port",
"ecmp_group",
"send_to_cpu",
"_drop"
],
"next_tables": {
"set_egress_port": "_condition_0",
"ecmp_group": "ecmp_group_table",
"send_to_cpu": "_condition_0",
"_drop": "_condition_0"
},
"default_action": null,
"base_default_next": "_condition_0"
},
{
"name": "ecmp_group_table",
"id": 2,
"match_type": "exact",
"type": "simple",
"max_size": 16384,
"with_counters": true,
"direct_meters": null,
"support_timeout": false,
"key": [
{
"match_type": "exact",
"target": [
"ecmp_metadata",
"groupId"
],
"mask": null
},
{
"match_type": "exact",
"target": [
"ecmp_metadata",
"selector"
],
"mask": null
}
],
"actions": [
"set_egress_port"
],
"next_tables": {
"set_egress_port": "_condition_0"
},
"default_action": null,
"base_default_next": "_condition_0"
}
],
"conditionals": [
{
"name": "_condition_0",
"id": 0,
"expression": {
"type": "expression",
"value": {
"op": "<",
"left": {
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
"right": {
"type": "hexstr",
"value": "0xfe"
}
}
},
"true_next": "port_count_table",
"false_next": null
}
]
},
{
"name": "egress",
"id": 1,
"init_table": null,
"tables": [],
"conditionals": []
}
],
"calculations": [
{
"name": "ecmp_hash",
"id": 0,
"input": [
{
"type": "field",
"value": [
"ipv4",
"srcAddr"
]
},
{
"type": "field",
"value": [
"ipv4",
"dstAddr"
]
},
{
"type": "field",
"value": [
"ipv4",
"protocol"
]
},
{
"type": "field",
"value": [
"tcp",
"srcPort"
]
},
{
"type": "field",
"value": [
"tcp",
"dstPort"
]
},
{
"type": "field",
"value": [
"udp",
"srcPort"
]
},
{
"type": "field",
"value": [
"udp",
"dstPort"
]
}
],
"algo": "bmv2_hash"
}
],
"checksums": [],
"learn_lists": [],
"field_lists": [],
"counter_arrays": [
{
"name": "ingress_port_counter",
"id": 0,
"is_direct": false,
"size": 254
},
{
"name": "egress_port_counter",
"id": 1,
"is_direct": false,
"size": 254
},
{
"name": "table0_counter",
"id": 2,
"is_direct": true,
"binding": "table0"
},
{
"name": "ecmp_group_table_counter",
"id": 3,
"is_direct": true,
"binding": "ecmp_group_table"
}
],
"register_arrays": [],
"force_arith": [
[
"standard_metadata",
"ingress_port"
],
[
"standard_metadata",
"packet_length"
],
[
"standard_metadata",
"egress_spec"
],
[
"standard_metadata",
"egress_port"
],
[
"standard_metadata",
"egress_instance"
],
[
"standard_metadata",
"instance_type"
],
[
"standard_metadata",
"clone_spec"
],
[
"standard_metadata",
"_padding"
],
[
"intrinsic_metadata",
"ingress_global_timestamp"
],
[
"intrinsic_metadata",
"lf_field_list"
],
[
"intrinsic_metadata",
"mcast_grp"
],
[
"intrinsic_metadata",
"egress_rid"
]
]
}

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<artifactId>onos-app-bmv2-demo</artifactId>
<packaging>pom</packaging>
<modules>
<module>common</module>
<module>ecmp</module>
<module>wcmp</module>
</modules>
</project>

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-app-bmv2-demo-common/${project.version}</bundle>
</feature>
</features>

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>onos-app-bmv2-demo</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<artifactId>onos-app-bmv2-demo-wcmp</artifactId>
<packaging>bundle</packaging>
<properties>
<onos.app.name>org.onosproject.bmv2-wcmp-fabric</onos.app.name>
<onos.app.title>P4/BMv2 Demo Fabric App v2 (WCMP)</onos.app.title>
<onos.app.category>Traffic Steering</onos.app.category>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.readme>P4/BMv2 demo application with WCMP support for a 2-stage clos fabric topology</onos.app.readme>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-bmv2-demo-common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,359 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.demo.app.wcmp;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.bmv2.demo.app.common.AbstractUpgradableFabricApp;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.Path;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.ExtensionSelector;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.topology.DefaultTopologyVertex;
import org.onosproject.net.topology.Topology;
import org.onosproject.net.topology.TopologyGraph;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.onlab.packet.EthType.EtherType.IPV4;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.roundToBytes;
import static org.onosproject.bmv2.demo.app.wcmp.WcmpInterpreter.*;
/**
* Implementation of an upgradable fabric app for the WCMP configuration.
*/
@Component(immediate = true)
public class WcmpFabricApp extends AbstractUpgradableFabricApp {
private static final String APP_NAME = "org.onosproject.bmv2-wcmp-fabric";
private static final String MODEL_NAME = "WCMP";
private static final String JSON_CONFIG_PATH = "/wcmp.json";
private static final double MULTI_PORT_WEIGHT_COEFFICIENT = 0.85;
private static final Bmv2Configuration WCMP_CONFIGURATION = loadConfiguration();
private static final WcmpInterpreter WCMP_INTERPRETER = new WcmpInterpreter();
protected static final Bmv2DeviceContext WCMP_CONTEXT = new Bmv2DeviceContext(WCMP_CONFIGURATION, WCMP_INTERPRETER);
private static final Map<DeviceId, Map<Map<PortNumber, Double>, Integer>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private Bmv2Controller bmv2Controller;
/**
* TODO.
*/
public WcmpFabricApp() {
super(APP_NAME, MODEL_NAME, WCMP_CONTEXT);
}
@Override
public boolean initDevice(DeviceId deviceId) {
try {
Bmv2DeviceAgent agent = bmv2Controller.getAgent(deviceId);
for (Map.Entry<String, Bmv2Action> entry : WCMP_INTERPRETER.defaultActionsMap().entrySet()) {
agent.setTableDefaultAction(entry.getKey(), entry.getValue());
}
return true;
} catch (Bmv2RuntimeException e) {
log.debug("Exception while initializing device {}: {}", deviceId, e.explain());
return false;
}
}
@Override
public List<FlowRule> generateLeafRules(DeviceId deviceId, Host srcHost, Collection<Host> dstHosts,
Collection<DeviceId> availableSpines, Topology topo)
throws FlowRuleGeneratorException {
Set<PortNumber> hostPortNumbers = Sets.newHashSet();
Set<PortNumber> fabricPortNumbers = Sets.newHashSet();
deviceService.getPorts(deviceId)
.forEach(p -> (isFabricPort(p, topo) ? fabricPortNumbers : hostPortNumbers).add(p.number()));
if (hostPortNumbers.size() != 1 || fabricPortNumbers.size() == 0) {
log.error("Leaf switch has invalid port configuration: hostPorts={}, fabricPorts={}",
hostPortNumbers.size(), fabricPortNumbers.size());
throw new FlowRuleGeneratorException();
}
PortNumber hostPort = hostPortNumbers.iterator().next();
TopologyGraph graph = topologyService.getGraph(topo);
// Map key: spine device id, value: leaf switch ports which connect to spine in the key.
Map<DeviceId, Set<PortNumber>> spineToPortsMap = Maps.newHashMap();
graph.getEdgesFrom(new DefaultTopologyVertex(deviceId)).forEach(edge -> {
spineToPortsMap.putIfAbsent(edge.dst().deviceId(), Sets.newHashSet());
spineToPortsMap.get(edge.dst().deviceId()).add(edge.link().src().port());
});
double baseWeight = 1d / spineToPortsMap.size();
int numSinglePorts = (int) spineToPortsMap.values().stream().filter(s -> s.size() == 1).count();
int numMultiPorts = spineToPortsMap.size() - numSinglePorts;
// Reduce weight portion assigned to multi-ports to mitigate flow assignment imbalance (measured empirically).
double multiPortBaseWeight = baseWeight * MULTI_PORT_WEIGHT_COEFFICIENT;
double excess = (baseWeight - multiPortBaseWeight) * numMultiPorts;
double singlePortBaseWeight = baseWeight + (excess / numSinglePorts);
Map<PortNumber, Double> weighedPortNumbers = Maps.newHashMap();
spineToPortsMap.forEach((did, portSet) -> {
double base = (portSet.size() == 1) ? singlePortBaseWeight : multiPortBaseWeight;
double weight = base / portSet.size();
portSet.forEach(portNumber -> weighedPortNumbers.put(portNumber, weight));
});
List<FlowRule> rules = Lists.newArrayList();
Pair<ExtensionTreatment, List<FlowRule>> result = provisionWcmpTreatment(deviceId, weighedPortNumbers);
ExtensionTreatment wcmpTreatment = result.getLeft();
rules.addAll(result.getRight());
// From src host to dst hosts, WCMP to all fabric ports.
for (Host dstHost : dstHosts) {
FlowRule rule = flowRuleBuilder(deviceId, TABLE0)
.withSelector(
DefaultTrafficSelector.builder()
.matchInPort(hostPort)
.matchEthType(IPV4.ethType().toShort())
.matchEthSrc(srcHost.mac())
.matchEthDst(dstHost.mac())
.build())
.withTreatment(
DefaultTrafficTreatment.builder()
.extension(wcmpTreatment, deviceId)
.build())
.build();
rules.add(rule);
}
// From fabric ports to src host.
for (PortNumber port : fabricPortNumbers) {
FlowRule rule = flowRuleBuilder(deviceId, TABLE0)
.withSelector(
DefaultTrafficSelector.builder()
.matchInPort(port)
.matchEthType(IPV4.ethType().toShort())
.matchEthDst(srcHost.mac())
.build())
.withTreatment(
DefaultTrafficTreatment.builder()
.setOutput(hostPort)
.build())
.build();
rules.add(rule);
}
return rules;
}
@Override
public List<FlowRule> generateSpineRules(DeviceId deviceId, Collection<Host> dstHosts, Topology topo)
throws FlowRuleGeneratorException {
List<FlowRule> rules = Lists.newArrayList();
for (Host dstHost : dstHosts) {
Set<Path> paths = topologyService.getPaths(topo, deviceId, dstHost.location().deviceId());
if (paths.size() == 0) {
log.warn("Can't find any path between spine {} and host {}", deviceId, dstHost);
throw new FlowRuleGeneratorException();
}
TrafficTreatment treatment;
if (paths.size() == 1) {
// Only one path.
PortNumber port = paths.iterator().next().src().port();
treatment = DefaultTrafficTreatment.builder().setOutput(port).build();
} else {
// Multiple paths, do WCMP.
Set<PortNumber> portNumbers = paths.stream().map(p -> p.src().port()).collect(toSet());
double weight = 1d / portNumbers.size();
// Same weight for all ports.
Map<PortNumber, Double> weightedPortNumbers = portNumbers.stream()
.collect(Collectors.toMap(p -> p, p -> weight));
Pair<ExtensionTreatment, List<FlowRule>> result = provisionWcmpTreatment(deviceId, weightedPortNumbers);
rules.addAll(result.getRight());
treatment = DefaultTrafficTreatment.builder().extension(result.getLeft(), deviceId).build();
}
FlowRule rule = flowRuleBuilder(deviceId, TABLE0)
.withSelector(
DefaultTrafficSelector.builder()
.matchEthType(IPV4.ethType().toShort())
.matchEthDst(dstHost.mac())
.build())
.withTreatment(treatment)
.build();
rules.add(rule);
}
return rules;
}
private Pair<ExtensionTreatment, List<FlowRule>> provisionWcmpTreatment(DeviceId deviceId,
Map<PortNumber, Double> weightedFabricPorts)
throws FlowRuleGeneratorException {
// Install WCMP group table entries that map from hash values to fabric ports.
int groupId = groupIdOf(deviceId, weightedFabricPorts);
List<PortNumber> portNumbers = Lists.newArrayList();
List<Double> weights = Lists.newArrayList();
weightedFabricPorts.forEach((p, w) -> {
portNumbers.add(p);
weights.add(w);
});
List<Integer> prefixLengths = toPrefixLengths(weights);
List<FlowRule> rules = Lists.newArrayList();
for (int i = 0; i < portNumbers.size(); i++) {
ExtensionSelector extSelector = buildWcmpSelector(groupId, prefixLengths.get(i));
FlowRule rule = flowRuleBuilder(deviceId, WCMP_GROUP_TABLE)
.withSelector(DefaultTrafficSelector.builder()
.extension(extSelector, deviceId)
.build())
.withTreatment(
DefaultTrafficTreatment.builder()
.setOutput(portNumbers.get(i))
.build())
.build();
rules.add(rule);
}
ExtensionTreatment extTreatment = buildWcmpTreatment(groupId);
return Pair.of(extTreatment, rules);
}
private Bmv2ExtensionSelector buildWcmpSelector(int groupId, int prefixLength) {
byte[] ones = new byte[roundToBytes(prefixLength)];
Arrays.fill(ones, (byte) 0xFF);
return Bmv2ExtensionSelector.builder()
.forConfiguration(WCMP_CONTEXT.configuration())
.matchExact(WCMP_META, GROUP_ID, groupId)
.matchLpm(WCMP_META, SELECTOR, ones, prefixLength)
.build();
}
private Bmv2ExtensionTreatment buildWcmpTreatment(int groupId) {
return Bmv2ExtensionTreatment.builder()
.forConfiguration(WCMP_CONTEXT.configuration())
.setActionName(WCMP_GROUP)
.addParameter(GROUP_ID, groupId)
.build();
}
public int groupIdOf(DeviceId did, Map<PortNumber, Double> weightedPorts) {
DEVICE_GROUP_ID_MAP.putIfAbsent(did, Maps.newHashMap());
// Counts the number of unique portNumber sets for each device ID.
// Each distinct set of portNumbers will have a unique ID.
return DEVICE_GROUP_ID_MAP.get(did).computeIfAbsent(weightedPorts,
(pp) -> DEVICE_GROUP_ID_MAP.get(did).size() + 1);
}
public List<Integer> toPrefixLengths(List<Double> weigths) {
final double weightSum = weigths.stream()
.mapToDouble(Double::doubleValue)
.map(this::roundDouble)
.sum();
if (Math.abs(weightSum - 1) > 0.0001) {
throw new RuntimeException("WCMP weights sum is expected to be 1, found was " + weightSum);
}
final int selectorBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(SELECTOR).bitWidth();
final int availableBits = selectorBitWidth - 1;
List<Long> prefixDiffs = weigths.stream().map(w -> Math.round(w * availableBits)).collect(toList());
final long bitSum = prefixDiffs.stream().mapToLong(Long::longValue).sum();
final long error = availableBits - bitSum;
if (error != 0) {
// Lazy intuition here is that the error can be absorbed by the longest prefixDiff with the minor impact.
Long maxDiff = Collections.max(prefixDiffs);
int idx = prefixDiffs.indexOf(maxDiff);
prefixDiffs.remove(idx);
prefixDiffs.add(idx, maxDiff + error);
}
List<Integer> prefixLengths = Lists.newArrayList();
int prefix = 1;
for (Long p : prefixDiffs) {
prefixLengths.add(prefix);
prefix += p;
}
return ImmutableList.copyOf(prefixLengths);
}
private double roundDouble(double n) {
// 5 digits precision.
return (double) Math.round(n * 100000d) / 100000d;
}
private static Bmv2Configuration loadConfiguration() {
try {
JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
WcmpFabricApp.class.getResourceAsStream(JSON_CONFIG_PATH)))).asObject();
return Bmv2DefaultConfiguration.parse(json);
} catch (IOException e) {
throw new RuntimeException("Unable to load configuration", e);
}
}
}

View File

@ -1,134 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.demo.app.wcmp;
import com.google.common.collect.ImmutableBiMap;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.bmv2.api.context.Bmv2InterpreterException;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
import java.util.Map;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
import static org.onosproject.net.PortNumber.CONTROLLER;
/**
* Implementation of a BMv2 interpreter for the wcmp.json configuration.
*/
public final class WcmpInterpreter implements Bmv2Interpreter {
protected static final String WCMP_META_T = "wcmp_meta_t";
protected static final String WCMP_META = "wcmp_meta";
protected static final String SELECTOR = "selector";
protected static final String GROUP_ID = "groupId";
protected static final String WCMP_GROUP = "wcmp_group";
protected static final String WCMP_SET_SELECTOR = "wcmp_set_selector";
protected static final String WCMP_SET_SELECTOR_TABLE = "wcmp_set_selector_table";
protected static final String WCMP_GROUP_TABLE = "wcmp_group_table";
protected static final String TABLE0 = "table0";
protected static final String SEND_TO_CPU = "send_to_cpu";
protected static final String DROP = "_drop";
protected static final String SET_EGRESS_PORT = "set_egress_port";
protected static final String PORT = "port";
private static final ImmutableBiMap<Criterion.Type, String> CRITERION_TYPE_MAP = ImmutableBiMap.of(
Criterion.Type.IN_PORT, "standard_metadata.ingress_port",
Criterion.Type.ETH_DST, "ethernet.dstAddr",
Criterion.Type.ETH_SRC, "ethernet.srcAddr",
Criterion.Type.ETH_TYPE, "ethernet.etherType");
private static final ImmutableBiMap<Integer, String> TABLE_ID_MAP = ImmutableBiMap.of(
0, TABLE0,
1, WCMP_GROUP_TABLE);
private static final Map<String, Bmv2Action> DEFAULT_ACTIONS_MAP = ImmutableBiMap.of(
WCMP_SET_SELECTOR_TABLE, actionWithName(WCMP_SET_SELECTOR));
@Override
public ImmutableBiMap<Integer, String> tableIdMap() {
return TABLE_ID_MAP;
}
@Override
public ImmutableBiMap<Criterion.Type, String> criterionTypeMap() {
return CRITERION_TYPE_MAP;
}
public Map<String, Bmv2Action> defaultActionsMap() {
return DEFAULT_ACTIONS_MAP;
}
@Override
public Bmv2Action mapTreatment(TrafficTreatment treatment, Bmv2Configuration configuration)
throws Bmv2InterpreterException {
if (treatment.allInstructions().size() == 0) {
// No instructions means drop for us.
return actionWithName(DROP);
} else if (treatment.allInstructions().size() > 1) {
// Otherwise, we understand treatments with only 1 instruction.
throw new Bmv2InterpreterException("Treatment has multiple instructions");
}
Instruction instruction = treatment.allInstructions().get(0);
switch (instruction.type()) {
case OUTPUT:
OutputInstruction outInstruction = (OutputInstruction) instruction;
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
return buildEgressAction(port, configuration);
} else if (port.equals(CONTROLLER)) {
return actionWithName(SEND_TO_CPU);
} else {
throw new Bmv2InterpreterException("Egress on logical port not supported: " + port);
}
case NOACTION:
return actionWithName(DROP);
default:
throw new Bmv2InterpreterException("Instruction type not supported: " + instruction.type().name());
}
}
private static Bmv2Action buildEgressAction(PortNumber port, Bmv2Configuration configuration)
throws Bmv2InterpreterException {
int portBitWidth = configuration.action(SET_EGRESS_PORT).runtimeData(PORT).bitWidth();
try {
ImmutableByteSequence portBs = fitByteSequence(ImmutableByteSequence.copyFrom(port.toLong()), portBitWidth);
return Bmv2Action.builder()
.withName(SET_EGRESS_PORT)
.addParameter(portBs)
.build();
} catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
throw new Bmv2InterpreterException(e.getMessage());
}
}
private static Bmv2Action actionWithName(String name) {
return Bmv2Action.builder().withName(name).build();
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 demo app for the WCMP configuration.
*/
package org.onosproject.bmv2.demo.app.wcmp;

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,6 @@
<module>gangliametrics</module>
<module>graphitemetrics</module>
<module>scalablegateway</module>
<module>bmv2-demo</module>
<module>yms</module>
<module>tetopology</module>
<module>rabbitmq</module>

View File

@ -47,7 +47,6 @@ public class ExtensionSelectorType {
OFDPA_MATCH_OVID(17),
OFDPA_MATCH_MPLS_L2_PORT(18),
EXT_MATCH_FLOW_TYPE(20),
BMV2_MATCH_PARAMS(128),
UNRESOLVED_TYPE(200);

View File

@ -72,7 +72,6 @@ public final class ExtensionTreatmentType {
NICIRA_ENCAP_ETH_SRC(121),
NICIRA_ENCAP_ETH_DST(122),
NICIRA_ENCAP_ETH_TYPE(123),
BMV2_ACTION(128),
OPLINK_ATTENUATION(130),
UNRESOLVED_TYPE(200);

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-protocol-api/${project.version}</bundle>
</feature>
</features>

View File

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-drivers-general</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-drivers-bmv2</artifactId>
<packaging>bundle</packaging>
<description>Device drivers for p4.org reference softswitch BMv2</description>
<properties>
<onos.app.name>org.onosproject.drivers.bmv2</onos.app.name>
<onos.app.origin>ON.Lab</onos.app.origin>
<onos.app.category>Drivers</onos.app.category>
<onos.app.title>BMv2 Drivers</onos.app.title>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.requires>
org.onosproject.bmv2
</onos.app.requires>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-protocol-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-drivers</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,142 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.onlab.osgi.ServiceNotFoundException;
import org.onlab.packet.ChassisId;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DefaultPortDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.util.List;
import static org.onosproject.bmv2.api.runtime.Bmv2Device.*;
import static org.onosproject.net.Device.Type.SWITCH;
/**
* Implementation of the device description discovery behaviour for BMv2.
*/
public class Bmv2DeviceDescriptionDiscovery extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
private static final String JSON_CONFIG_MD5 = "bmv2JsonConfigMd5";
private static final String PROCESS_INSTANCE_ID = "bmv2ProcessInstanceId";
private final Logger log = LoggerFactory.getLogger(this.getClass());
private Bmv2Controller controller;
private boolean init() {
try {
controller = handler().get(Bmv2Controller.class);
return true;
} catch (ServiceNotFoundException e) {
log.warn(e.getMessage());
return false;
}
}
@Override
public DeviceDescription discoverDeviceDetails() {
if (!init()) {
return null;
}
DeviceId deviceId = handler().data().deviceId();
Bmv2DeviceAgent deviceAgent;
try {
deviceAgent = controller.getAgent(deviceId);
} catch (Bmv2RuntimeException e) {
log.error("Failed to connect to Bmv2 device", e);
return null;
}
DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder();
try {
String md5 = deviceAgent.getJsonConfigMd5();
BigInteger i = new BigInteger(1, md5.getBytes());
annotationsBuilder.set(JSON_CONFIG_MD5, String.format("%1$032X", i).toLowerCase());
} catch (Bmv2RuntimeException e) {
log.warn("Unable to dump JSON configuration from {}: {}", deviceId, e.explain());
}
try {
int instanceId = deviceAgent.getProcessInstanceId();
annotationsBuilder.set(PROCESS_INSTANCE_ID, String.valueOf(instanceId));
} catch (Bmv2RuntimeException e) {
log.warn("Unable to get process instance ID from {}: {}", deviceId, e.explain());
}
annotationsBuilder.set(AnnotationKeys.PROTOCOL, PROTOCOL);
return new DefaultDeviceDescription(deviceId.uri(),
SWITCH,
MANUFACTURER,
HW_VERSION,
SW_VERSION,
SERIAL_NUMBER,
new ChassisId(),
annotationsBuilder.build());
}
@Override
public List<PortDescription> discoverPortDetails() {
if (!init()) {
return null;
}
DeviceId deviceId = handler().data().deviceId();
Bmv2DeviceAgent deviceAgent;
try {
deviceAgent = controller.getAgent(deviceId);
} catch (Bmv2RuntimeException e) {
log.error("Failed to connect to Bmv2 device", e);
return null;
}
List<PortDescription> portDescriptions = Lists.newArrayList();
try {
deviceAgent.getPortsInfo().forEach(p -> {
PortNumber portNumber = PortNumber.portNumber((long) p.number(), p.ifaceName());
portDescriptions.add(new DefaultPortDescription(portNumber, p.isUp(), DefaultAnnotations.EMPTY));
});
} catch (Bmv2RuntimeException e) {
log.error("Unable to get port descriptions of {}: {}", deviceId, e);
}
return ImmutableList.copyOf(portDescriptions);
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import org.apache.felix.scr.annotations.Component;
import org.onosproject.net.driver.AbstractDriverLoader;
/**
* Loader for BMv2 drivers from xml file.
*/
@Component(immediate = true)
public class Bmv2DriversLoader extends AbstractDriverLoader {
private static final String DRIVERS_XML = "/bmv2-drivers.xml";
public Bmv2DriversLoader() {
super(DRIVERS_XML);
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
import org.onosproject.net.behaviour.ExtensionSelectorResolver;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.criteria.ExtensionSelector;
import org.onosproject.net.flow.criteria.ExtensionSelectorType;
import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS;
/**
* Implementation of the extension selector resolver behaviour for BMv2.
*/
public class Bmv2ExtensionSelectorResolver extends AbstractHandlerBehaviour implements ExtensionSelectorResolver {
@Override
public ExtensionSelector getExtensionSelector(ExtensionSelectorType type) {
if (type.equals(BMV2_MATCH_PARAMS.type())) {
return Bmv2ExtensionSelector.empty();
}
return null;
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
/**
* Implementation of the extension treatment resolver behavior for BMv2.
*/
public class Bmv2ExtensionTreatmentResolver extends AbstractHandlerBehaviour implements ExtensionTreatmentResolver {
@Override
public ExtensionTreatment getExtensionInstruction(ExtensionTreatmentType type) {
if (type.equals(BMV2_ACTION.type())) {
return Bmv2ExtensionTreatment.empty();
}
return null;
}
}

View File

@ -1,340 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
import org.onlab.osgi.ServiceNotFoundException;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslatorException;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.bmv2.api.context.Bmv2TableModel;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
import org.onosproject.bmv2.api.runtime.Bmv2ParsedTableEntry;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.DefaultFlowEntry;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleProgrammable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static org.onosproject.bmv2.api.runtime.Bmv2RuntimeException.Code.*;
import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
/**
* Implementation of the flow rule programmable behaviour for BMv2.
*/
public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
private final Logger log = LoggerFactory.getLogger(this.getClass());
// Needed to synchronize operations over the same table entry.
private static final ConcurrentMap<Bmv2TableEntryReference, Lock> ENTRY_LOCKS = Maps.newConcurrentMap();
private Bmv2Controller controller;
private Bmv2TableEntryService tableEntryService;
private Bmv2DeviceContextService contextService;
private boolean init() {
try {
controller = handler().get(Bmv2Controller.class);
tableEntryService = handler().get(Bmv2TableEntryService.class);
contextService = handler().get(Bmv2DeviceContextService.class);
return true;
} catch (ServiceNotFoundException e) {
log.warn(e.getMessage());
return false;
}
}
@Override
public Collection<FlowEntry> getFlowEntries() {
if (!init()) {
return Collections.emptyList();
}
DeviceId deviceId = handler().data().deviceId();
Bmv2DeviceAgent deviceAgent;
try {
deviceAgent = controller.getAgent(deviceId);
} catch (Bmv2RuntimeException e) {
log.error("Failed to get BMv2 device agent: {}", e.explain());
return Collections.emptyList();
}
Bmv2DeviceContext context = contextService.getContext(deviceId);
if (context == null) {
log.warn("Unable to get device context for {}", deviceId);
return Collections.emptyList();
}
Bmv2Interpreter interpreter = context.interpreter();
Bmv2Configuration configuration = context.configuration();
List<FlowEntry> entryList = Lists.newArrayList();
for (Bmv2TableModel table : configuration.tables()) {
// For each table in the configuration AND exposed by the interpreter.
if (!interpreter.tableIdMap().inverse().containsKey(table.name())) {
continue; // next table
}
List<Bmv2ParsedTableEntry> installedEntries;
try {
installedEntries = deviceAgent.getTableEntries(table.name());
} catch (Bmv2RuntimeException e) {
log.warn("Failed to get table entries of table {} of {}: {}", table.name(), deviceId, e.explain());
continue; // next table
}
for (Bmv2ParsedTableEntry parsedEntry : installedEntries) {
Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, table.name(),
parsedEntry.matchKey());
Lock lock = ENTRY_LOCKS.computeIfAbsent(entryRef, key -> new ReentrantLock());
lock.lock();
try {
Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
if (frWrapper == null) {
log.debug("Missing reference from table entry service. Deleting it. BUG? " +
"deviceId={}, tableName={}, matchKey={}",
deviceId, table.name(), entryRef.matchKey());
try {
doRemove(deviceAgent, table.name(), parsedEntry.entryId(), parsedEntry.matchKey());
} catch (Bmv2RuntimeException e) {
log.warn("Unable to remove inconsistent flow rule: {}", e.explain());
}
continue; // next entry
}
long remoteEntryId = parsedEntry.entryId();
long localEntryId = frWrapper.entryId();
if (remoteEntryId != localEntryId) {
log.debug("getFlowEntries(): inconsistent entry id! BUG? Updating it... remote={}, local={}",
remoteEntryId, localEntryId);
frWrapper = new Bmv2FlowRuleWrapper(frWrapper.rule(), remoteEntryId,
frWrapper.installedOnMillis());
tableEntryService.bind(entryRef, frWrapper);
}
long bytes = 0L;
long packets = 0L;
if (table.hasCounters()) {
// Read counter values from device.
try {
Pair<Long, Long> counterValue = deviceAgent.readTableEntryCounter(table.name(),
remoteEntryId);
bytes = counterValue.getLeft();
packets = counterValue.getRight();
} catch (Bmv2RuntimeException e) {
log.warn("Unable to get counters for entry {}/{} of device {}: {}",
table.name(), remoteEntryId, deviceId, e.explain());
}
}
FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
packets, bytes);
entryList.add(entry);
} finally {
lock.unlock();
}
}
}
return Collections.unmodifiableCollection(entryList);
}
@Override
public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
return processFlowRules(rules, Operation.APPLY);
}
@Override
public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
return processFlowRules(rules, Operation.REMOVE);
}
private Collection<FlowRule> processFlowRules(Collection<FlowRule> rules, Operation operation) {
if (!init()) {
return Collections.emptyList();
}
DeviceId deviceId = handler().data().deviceId();
Bmv2DeviceAgent deviceAgent;
try {
deviceAgent = controller.getAgent(deviceId);
} catch (Bmv2RuntimeException e) {
log.error("Failed to get BMv2 device agent: {}", e.explain());
return Collections.emptyList();
}
Bmv2DeviceContext context = contextService.getContext(deviceId);
if (context == null) {
log.error("Unable to get device context for {}", deviceId);
return Collections.emptyList();
}
Bmv2FlowRuleTranslator translator = tableEntryService.getFlowRuleTranslator();
List<FlowRule> processedFlowRules = Lists.newArrayList();
for (FlowRule rule : rules) {
Bmv2TableEntry bmv2Entry;
try {
bmv2Entry = translator.translate(rule, context);
} catch (Bmv2FlowRuleTranslatorException e) {
log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
continue; // next rule
}
String tableName = bmv2Entry.tableName();
Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey());
Lock lock = ENTRY_LOCKS.computeIfAbsent(entryRef, k -> new ReentrantLock());
lock.lock();
try {
// Get from store
Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
try {
if (operation == Operation.APPLY) {
// Apply entry
long entryId;
if (frWrapper != null) {
// Existing entry.
entryId = frWrapper.entryId();
// Tentatively delete entry before re-adding.
// It might not exist on device due to inconsistencies.
silentlyRemove(deviceAgent, entryRef.tableName(), entryId);
}
// Add entry.
entryId = doAddEntry(deviceAgent, bmv2Entry);
frWrapper = new Bmv2FlowRuleWrapper(rule, entryId, System.currentTimeMillis());
} else {
// Remove entry
if (frWrapper == null) {
// Entry not found in map, how come?
forceRemove(deviceAgent, entryRef.tableName(), entryRef.matchKey());
} else {
long entryId = frWrapper.entryId();
doRemove(deviceAgent, entryRef.tableName(), entryId, entryRef.matchKey());
}
frWrapper = null;
}
// If here, no exceptions... things went well :)
processedFlowRules.add(rule);
} catch (Bmv2RuntimeException e) {
log.warn("Unable to {} flow rule: {}", operation.name(), e.explain());
}
// Update entryRef binding in table entry service.
if (frWrapper != null) {
tableEntryService.bind(entryRef, frWrapper);
} else {
tableEntryService.unbind(entryRef);
}
} finally {
lock.unlock();
}
}
return processedFlowRules;
}
private long doAddEntry(Bmv2DeviceAgent agent, Bmv2TableEntry entry) throws Bmv2RuntimeException {
try {
return agent.addTableEntry(entry);
} catch (Bmv2RuntimeException e) {
if (e.getCode().equals(TABLE_DUPLICATE_ENTRY)) {
forceRemove(agent, entry.tableName(), entry.matchKey());
return agent.addTableEntry(entry);
} else {
throw e;
}
}
}
private void doRemove(Bmv2DeviceAgent agent, String tableName, long entryId, Bmv2MatchKey matchKey)
throws Bmv2RuntimeException {
try {
agent.deleteTableEntry(tableName, entryId);
} catch (Bmv2RuntimeException e) {
if (e.getCode().equals(TABLE_INVALID_HANDLE) || e.getCode().equals(TABLE_EXPIRED_HANDLE)) {
// entry is not there with the declared ID, try with a forced remove.
forceRemove(agent, tableName, matchKey);
} else {
throw e;
}
}
}
private void forceRemove(Bmv2DeviceAgent agent, String tableName, Bmv2MatchKey matchKey)
throws Bmv2RuntimeException {
// Find the entryID (expensive call!)
for (Bmv2ParsedTableEntry pEntry : agent.getTableEntries(tableName)) {
if (pEntry.matchKey().equals(matchKey)) {
// Remove entry and drop exceptions.
silentlyRemove(agent, tableName, pEntry.entryId());
break;
}
}
}
private void silentlyRemove(Bmv2DeviceAgent agent, String tableName, long entryId) {
try {
agent.deleteTableEntry(tableName, entryId);
} catch (Bmv2RuntimeException e) {
// do nothing
}
}
private enum Operation {
APPLY, REMOVE
}
}

View File

@ -1,105 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import org.onlab.osgi.ServiceNotFoundException;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.net.DeviceId;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketProgrammable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import static java.lang.Math.toIntExact;
import static java.util.stream.Collectors.toList;
import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
/**
* Implementation of the packet programmable behaviour for BMv2.
*/
public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public void emit(OutboundPacket packet) {
TrafficTreatment treatment = packet.treatment();
// BMv2 supports only OUTPUT instructions.
List<OutputInstruction> outInstructions = treatment.allInstructions()
.stream()
.filter(i -> i.type().equals(OUTPUT))
.map(i -> (OutputInstruction) i)
.collect(toList());
if (treatment.allInstructions().size() != outInstructions.size()) {
// There are other instructions that are not of type OUTPUT
log.warn("Dropping emit request, treatment nor supported: {}", treatment);
return;
}
outInstructions.forEach(outInst -> {
if (outInst.port().isLogical()) {
log.warn("Dropping emit request, logical port not supported: {}", outInst.port());
} else {
try {
int portNumber = toIntExact(outInst.port().toLong());
send(portNumber, packet);
} catch (ArithmeticException e) {
log.error("Dropping emit request, port number too big: {}", outInst.port().toLong());
}
}
});
}
private void send(int port, OutboundPacket packet) {
DeviceId deviceId = handler().data().deviceId();
Bmv2Controller controller;
try {
controller = handler().get(Bmv2Controller.class);
} catch (ServiceNotFoundException e) {
log.warn(e.getMessage());
return;
}
Bmv2DeviceAgent deviceAgent;
try {
deviceAgent = controller.getAgent(deviceId);
} catch (Bmv2RuntimeException e) {
log.error("Failed to get Bmv2 device agent for {}: {}", deviceId, e.explain());
return;
}
ImmutableByteSequence bs = ImmutableByteSequence.copyFrom(packet.data());
try {
deviceAgent.transmitPacket(port, bs);
} catch (Bmv2RuntimeException e) {
log.warn("Unable to emit packet trough {}: {}", deviceId, e.explain());
}
}
}

View File

@ -1,65 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.drivers.bmv2;
import org.onosproject.driver.pipeline.DefaultSingleTablePipeline;
import org.onosproject.net.DeviceId;
import org.onosproject.net.behaviour.NextGroup;
import org.onosproject.net.behaviour.Pipeliner;
import org.onosproject.net.behaviour.PipelinerContext;
import org.onosproject.net.driver.AbstractHandlerBehaviour;
import org.onosproject.net.flowobjective.FilteringObjective;
import org.onosproject.net.flowobjective.ForwardingObjective;
import org.onosproject.net.flowobjective.NextObjective;
import java.util.List;
/**
* Pipeliner device behaviour implementation for BMv2.
*/
public class Bmv2Pipeliner extends AbstractHandlerBehaviour implements Pipeliner {
private Pipeliner pipeliner;
@Override
public void init(DeviceId deviceId, PipelinerContext context) {
// TODO: get multi-table pipeliner dynamically based on BMv2 device running model (hard).
// Right now we are able to map flow objectives only in the first table of the pipeline.
pipeliner = new DefaultSingleTablePipeline();
pipeliner.init(deviceId, context);
}
@Override
public void filter(FilteringObjective filterObjective) {
pipeliner.filter(filterObjective);
}
@Override
public void forward(ForwardingObjective forwardObjective) {
pipeliner.forward(forwardObjective);
}
@Override
public void next(NextObjective nextObjective) {
pipeliner.next(nextObjective);
}
@Override
public List<String> getNextMappings(NextGroup nextGroup) {
return pipeliner.getNextMappings(nextGroup);
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 driver implementation.
*/
package org.onosproject.drivers.bmv2;

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<drivers>
<driver name="bmv2-thrift" manufacturer="p4.org" hwVersion="bmv2" swVersion="1.0.0">
<behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
impl="org.onosproject.drivers.bmv2.Bmv2DeviceDescriptionDiscovery"/>
<behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
impl="org.onosproject.drivers.bmv2.Bmv2FlowRuleProgrammable"/>
<behaviour api="org.onosproject.net.behaviour.Pipeliner"
impl="org.onosproject.drivers.bmv2.Bmv2Pipeliner"/>
<behaviour api="org.onosproject.net.packet.PacketProgrammable"
impl="org.onosproject.drivers.bmv2.Bmv2PacketProgrammable"/>
<behaviour api="org.onosproject.net.behaviour.ExtensionSelectorResolver"
impl="org.onosproject.drivers.bmv2.Bmv2ExtensionSelectorResolver"/>
<behaviour api="org.onosproject.net.behaviour.ExtensionTreatmentResolver"
impl="org.onosproject.drivers.bmv2.Bmv2ExtensionTreatmentResolver"/>
</driver>
</drivers>

View File

@ -41,7 +41,6 @@
<module>lumentum</module>
<!-- TODO ONOS-5554 excluding from the build -->
<!--<module>bti</module>-->
<module>bmv2</module>
<module>corsa</module>
<module>optical</module>
<module>arista</module>

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-bmv2-protocol</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>bundle</packaging>
<artifactId>onos-bmv2-protocol-api</artifactId>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,118 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* A BMv2 action model.
*/
@Beta
public final class Bmv2ActionModel {
private final String name;
private final int id;
private final LinkedHashMap<String, Bmv2RuntimeDataModel> runtimeDatas = Maps.newLinkedHashMap();
/**
* Creates a new action model.
*
* @param name name
* @param id id
* @param runtimeDatas list of runtime data
*/
protected Bmv2ActionModel(String name, int id, List<Bmv2RuntimeDataModel> runtimeDatas) {
this.name = name;
this.id = id;
runtimeDatas.forEach(r -> this.runtimeDatas.put(r.name(), r));
}
/**
* Returns the name of this action.
*
* @return a string value
*/
public String name() {
return name;
}
/**
* Returns the id of this action.
*
* @return an integer value
*/
public int id() {
return id;
}
/**
* Returns this action's runtime data defined by the given name, null
* if not present.
*
* @return runtime data or null
*/
public Bmv2RuntimeDataModel runtimeData(String name) {
return runtimeDatas.get(name);
}
/**
* Returns an immutable list of runtime data for this action.
* The list is ordered according to the values defined in the configuration.
*
* @return list of runtime data.
*/
public List<Bmv2RuntimeDataModel> runtimeDatas() {
return ImmutableList.copyOf(runtimeDatas.values());
}
@Override
public int hashCode() {
return Objects.hash(name, id, runtimeDatas);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2ActionModel other = (Bmv2ActionModel) obj;
return Objects.equals(this.name, other.name)
&& Objects.equals(this.id, other.id)
&& Objects.equals(this.runtimeDatas, other.runtimeDatas);
}
@Override
public String toString() {
return toStringHelper(this)
.add("name", name)
.add("id", id)
.add("runtimeDatas", runtimeDatas)
.toString();
}
}

View File

@ -1,136 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.eclipsesource.json.JsonObject;
import com.google.common.annotations.Beta;
import java.util.List;
/**
* BMv2 packet processing configuration. Such a configuration is used to define the way BMv2 should process packets
* (i.e. it defines the device ingress/egress pipelines, parser, tables, actions, etc.). It must be noted that this
* class exposes only a subset of the configuration properties of a BMv2 device (only those that are needed for the
* purpose of translating ONOS structures to BMv2 structures). Such a configuration is backed by a JSON object.
* BMv2 JSON configuration files are usually generated using a P4 frontend compiler such as p4c-bmv2.
*/
@Beta
public interface Bmv2Configuration {
/**
* Return an unmodifiable view of the JSON backing this configuration.
*
* @return a JSON object.
*/
JsonObject json();
/**
* Returns the header type associated with the given numeric ID, null if there's no such an ID in the configuration.
*
* @param id integer value
* @return header type object or null
*/
Bmv2HeaderTypeModel headerType(int id);
/**
* Returns the header type associated with the given name, null if there's no such a name in the configuration.
*
* @param name string value
* @return header type object or null
*/
Bmv2HeaderTypeModel headerType(String name);
/**
* Returns the list of all the header types defined by in this configuration. Values returned are sorted in
* ascending order based on the numeric ID.
*
* @return list of header types
*/
List<Bmv2HeaderTypeModel> headerTypes();
/**
* Returns the header associated with the given numeric ID, null if there's no such an ID in the configuration.
*
* @param id integer value
* @return header object or null
*/
Bmv2HeaderModel header(int id);
/**
* Returns the header associated with the given name, null if there's no such a name in the configuration.
*
* @param name string value
* @return header object or null
*/
Bmv2HeaderModel header(String name);
/**
* Returns the list of all the header instances defined in this configuration. Values returned are sorted in
* ascending order based on the numeric ID.
*
* @return list of header types
*/
List<Bmv2HeaderModel> headers();
/**
* Returns the action associated with the given numeric ID, null if there's no such an ID in the configuration.
*
* @param id integer value
* @return action object or null
*/
Bmv2ActionModel action(int id);
/**
* Returns the action associated with the given name, null if there's no such a name in the configuration.
*
* @param name string value
* @return action object or null
*/
Bmv2ActionModel action(String name);
/**
* Returns the list of all the actions defined by in this configuration. Values returned are sorted in ascending
* order based on the numeric ID.
*
* @return list of actions
*/
List<Bmv2ActionModel> actions();
/**
* Returns the table associated with the given numeric ID, null if there's no such an ID in the configuration.
*
* @param id integer value
* @return table object or null
*/
Bmv2TableModel table(int id);
/**
* Returns the table associated with the given name, null if there's no such a name in the configuration.
*
* @param name string value
* @return table object or null
*/
Bmv2TableModel table(String name);
/**
* Returns the list of all the tables defined by in this configuration. Values returned are sorted in ascending
* order based on the numeric ID.
*
* @return list of actions
*/
List<Bmv2TableModel> tables();
}

View File

@ -1,352 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Default implementation of a BMv2 configuration backed by a JSON object.
*/
@Beta
public final class Bmv2DefaultConfiguration implements Bmv2Configuration {
private final JsonObject json;
private final DualKeySortedMap<Bmv2HeaderTypeModel> headerTypes = new DualKeySortedMap<>();
private final DualKeySortedMap<Bmv2HeaderModel> headers = new DualKeySortedMap<>();
private final DualKeySortedMap<Bmv2ActionModel> actions = new DualKeySortedMap<>();
private final DualKeySortedMap<Bmv2TableModel> tables = new DualKeySortedMap<>();
private Bmv2DefaultConfiguration(JsonObject json) {
this.json = JsonObject.unmodifiableObject(json);
}
/**
* Returns a new BMv2 configuration object by parsing the passed JSON.
*
* @param json json
* @return a new BMv2 configuration object
* @see <a href="https://github.com/p4lang/behavioral-configuration/blob/master/docs/JSON_format.md">
* BMv2 JSON specification</a>
*/
public static Bmv2DefaultConfiguration parse(JsonObject json) {
checkArgument(json != null, "json cannot be null");
// TODO: implement caching, no need to parse a json if we already have the configuration
Bmv2DefaultConfiguration configuration = new Bmv2DefaultConfiguration(json);
configuration.doParse();
return configuration;
}
@Override
public Bmv2HeaderTypeModel headerType(int id) {
return headerTypes.get(id);
}
@Override
public Bmv2HeaderTypeModel headerType(String name) {
return headerTypes.get(name);
}
@Override
public List<Bmv2HeaderTypeModel> headerTypes() {
return ImmutableList.copyOf(headerTypes.sortedMap().values());
}
@Override
public Bmv2HeaderModel header(int id) {
return headers.get(id);
}
@Override
public Bmv2HeaderModel header(String name) {
return headers.get(name);
}
@Override
public List<Bmv2HeaderModel> headers() {
return ImmutableList.copyOf(headers.sortedMap().values());
}
@Override
public Bmv2ActionModel action(int id) {
return actions.get(id);
}
@Override
public Bmv2ActionModel action(String name) {
return actions.get(name);
}
@Override
public List<Bmv2ActionModel> actions() {
return ImmutableList.copyOf(actions.sortedMap().values());
}
@Override
public Bmv2TableModel table(int id) {
return tables.get(id);
}
@Override
public Bmv2TableModel table(String name) {
return tables.get(name);
}
@Override
public List<Bmv2TableModel> tables() {
return ImmutableList.copyOf(tables.sortedMap().values());
}
@Override
public JsonObject json() {
return this.json;
}
/**
* Generates a hash code for this BMv2 configuration. The hash function is based solely on the JSON backing this
* configuration.
*/
@Override
public int hashCode() {
return json.hashCode();
}
/**
* Indicates whether some other BMv2 configuration is equal to this one.
* Equality is based solely on the low-level JSON representation.
*
* @param obj other object
* @return true if equals, false elsewhere
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2DefaultConfiguration other = (Bmv2DefaultConfiguration) obj;
return Objects.equal(this.json, other.json);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("jsonHash", json.hashCode())
.toString();
}
/**
* Parse the JSON object and build the corresponding objects.
*/
private void doParse() {
// parse header types
json.get("header_types").asArray().forEach(val -> {
JsonObject jHeaderType = val.asObject();
// populate fields list
List<Bmv2FieldTypeModel> fieldTypes = Lists.newArrayList();
jHeaderType.get("fields").asArray().forEach(x -> fieldTypes.add(
new Bmv2FieldTypeModel(
x.asArray().get(0).asString(),
x.asArray().get(1).asInt())));
// add header type instance
String name = jHeaderType.get("name").asString();
int id = jHeaderType.get("id").asInt();
Bmv2HeaderTypeModel headerType = new Bmv2HeaderTypeModel(name,
id,
fieldTypes);
headerTypes.put(name, id, headerType);
});
// parse headers
json.get("headers").asArray().forEach(val -> {
JsonObject jHeader = val.asObject();
String name = jHeader.get("name").asString();
int id = jHeader.get("id").asInt();
String typeName = jHeader.get("header_type").asString();
Bmv2HeaderModel header = new Bmv2HeaderModel(name,
id,
headerTypes.get(typeName),
jHeader.get("metadata").asBoolean());
// add instance
headers.put(name, id, header);
});
// parse actions
json.get("actions").asArray().forEach(val -> {
JsonObject jAction = val.asObject();
// populate runtime data list
List<Bmv2RuntimeDataModel> runtimeDatas = Lists.newArrayList();
jAction.get("runtime_data").asArray().forEach(jData -> runtimeDatas.add(
new Bmv2RuntimeDataModel(
jData.asObject().get("name").asString(),
jData.asObject().get("bitwidth").asInt()
)));
// add action instance
String name = jAction.get("name").asString();
int id = jAction.get("id").asInt();
Bmv2ActionModel action = new Bmv2ActionModel(name,
id,
runtimeDatas);
actions.put(name, id, action);
});
// parse tables
json.get("pipelines").asArray().forEach(pipeline -> {
pipeline.asObject().get("tables").asArray().forEach(val -> {
JsonObject jTable = val.asObject();
// populate keys
List<Bmv2TableKeyModel> keys = Lists.newArrayList();
jTable.get("key").asArray().forEach(jKey -> {
JsonArray target = jKey.asObject().get("target").asArray();
Bmv2HeaderModel header = header(target.get(0).asString());
String typeName = target.get(1).asString();
Bmv2FieldModel field = new Bmv2FieldModel(
header, header.type().field(typeName));
String matchTypeStr = jKey.asObject().get("match_type").asString();
Bmv2MatchParam.Type matchType;
switch (matchTypeStr) {
case "ternary":
matchType = Bmv2MatchParam.Type.TERNARY;
break;
case "exact":
matchType = Bmv2MatchParam.Type.EXACT;
break;
case "lpm":
matchType = Bmv2MatchParam.Type.LPM;
break;
case "valid":
matchType = Bmv2MatchParam.Type.VALID;
break;
default:
throw new RuntimeException(
"Unable to parse match type: " + matchTypeStr);
}
keys.add(new Bmv2TableKeyModel(matchType, field));
});
// populate actions set
Set<Bmv2ActionModel> actionzz = Sets.newHashSet();
jTable.get("actions").asArray().forEach(
jAction -> actionzz.add(action(jAction.asString())));
// add table instance
String name = jTable.get("name").asString();
int id = jTable.get("id").asInt();
Bmv2TableModel table = new Bmv2TableModel(name,
id,
jTable.get("match_type").asString(),
jTable.get("type").asString(),
jTable.get("max_size").asInt(),
jTable.get("with_counters").asBoolean(),
jTable.get("support_timeout").asBoolean(),
keys,
actionzz);
tables.put(name, id, table);
});
});
}
/**
* Handy class for a map indexed by two keys, a string and an integer.
*
* @param <T> type of value stored by the map
*/
private class DualKeySortedMap<T> {
private final SortedMap<Integer, T> intMap = Maps.newTreeMap();
private final Map<String, Integer> strToIntMap = Maps.newHashMap();
private void put(String name, int id, T object) {
strToIntMap.put(name, id);
intMap.put(id, object);
}
private T get(int id) {
return intMap.get(id);
}
private T get(String name) {
return strToIntMap.get(name) == null ? null : get(strToIntMap.get(name));
}
private SortedMap<Integer, T> sortedMap() {
return intMap;
}
@Override
public int hashCode() {
return Objects.hashCode(intMap, strToIntMap);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final DualKeySortedMap other = (DualKeySortedMap) obj;
return Objects.equal(this.intMap, other.intMap)
&& Objects.equal(this.strToIntMap, other.strToIntMap);
}
}
}

View File

@ -1,88 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A BMv2 device context, defined by a configuration and an interpreter.
*/
@Beta
public final class Bmv2DeviceContext {
private final Bmv2Configuration configuration;
private final Bmv2Interpreter interpreter;
/**
* Creates a new BMv2 device context.
*
* @param configuration a configuration
* @param interpreter an interpreter
*/
public Bmv2DeviceContext(Bmv2Configuration configuration, Bmv2Interpreter interpreter) {
this.configuration = checkNotNull(configuration, "configuration cannot be null");
this.interpreter = checkNotNull(interpreter, "interpreter cannot be null");
}
/**
* Returns the BMv2 configuration of this context.
*
* @return a configuration
*/
public Bmv2Configuration configuration() {
return configuration;
}
/**
* Returns the BMv2 interpreter of this context.
*
* @return an interpreter
*/
public Bmv2Interpreter interpreter() {
return interpreter;
}
@Override
public int hashCode() {
return Objects.hashCode(configuration, interpreter);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2DeviceContext other = (Bmv2DeviceContext) obj;
return Objects.equal(this.configuration, other.configuration)
&& Objects.equal(this.interpreter, other.interpreter);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("configuration", configuration)
.add("interpreter", interpreter)
.toString();
}
}

View File

@ -1,81 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* A BMv2 header field model.
*/
@Beta
public final class Bmv2FieldModel {
private final Bmv2HeaderModel header;
private final Bmv2FieldTypeModel type;
protected Bmv2FieldModel(Bmv2HeaderModel header, Bmv2FieldTypeModel type) {
this.header = header;
this.type = type;
}
/**
* Returns the header instance of this field instance.
*
* @return a header instance
*/
public Bmv2HeaderModel header() {
return header;
}
/**
* Returns the type of this field instance.
*
* @return a field type value
*/
public Bmv2FieldTypeModel type() {
return type;
}
@Override
public int hashCode() {
return Objects.hashCode(header, type);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2FieldModel other = (Bmv2FieldModel) obj;
return Objects.equal(this.header, other.header)
&& Objects.equal(this.type, other.type);
}
@Override
public String toString() {
return toStringHelper(this)
.add("header", header)
.add("type", type)
.toString();
}
}

View File

@ -1,81 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* A BMv2 header type field model.
*/
@Beta
public final class Bmv2FieldTypeModel {
private final String name;
private final int bitWidth;
protected Bmv2FieldTypeModel(String name, int bitWidth) {
this.name = name;
this.bitWidth = bitWidth;
}
/**
* Returns the name of this header type field.
*
* @return a string value
*/
public String name() {
return name;
}
/**
* Returns the bit width of this header type field.
*
* @return an integer value
*/
public int bitWidth() {
return bitWidth;
}
@Override
public int hashCode() {
return Objects.hashCode(name, bitWidth);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2FieldTypeModel other = (Bmv2FieldTypeModel) obj;
return Objects.equal(this.name, other.name)
&& Objects.equal(this.bitWidth, other.bitWidth);
}
@Override
public String toString() {
return toStringHelper(this)
.add("name", name)
.add("bitWidth", bitWidth)
.toString();
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
import org.onosproject.net.flow.FlowRule;
/**
* Translator of ONOS flow rules to BMv2 table entries.
*/
@Beta
public interface Bmv2FlowRuleTranslator {
/**
* Returns a BMv2 table entry equivalent to the given flow rule for the given context.
* <p>
* Translation is performed according to the following logic:
* <ul>
* <li> table name: obtained from the context interpreter {@link Bmv2Interpreter#tableIdMap() table ID map}.
* <li> match key: is built using both the context interpreter {@link Bmv2Interpreter#criterionTypeMap() criterion
* map} and all {@link org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector extension selectors} (if any).
* <li> action: is built using the context interpreter
* {@link Bmv2Interpreter#mapTreatment(org.onosproject.net.flow.TrafficTreatment, Bmv2Configuration)
* treatment mapping function} or the flow rule
* {@link org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment extension treatment} (if any).
* <li> timeout: if the table supports timeout, use the same as the flow rule, otherwise none (i.e. returns a
* permanent entry).
* </ul>
*
* @param rule a flow rule
* @param context a context
* @return a BMv2 table entry
* @throws Bmv2FlowRuleTranslatorException if the flow rule cannot be translated
*/
Bmv2TableEntry translate(FlowRule rule, Bmv2DeviceContext context) throws Bmv2FlowRuleTranslatorException;
}

View File

@ -1,30 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
/**
* BMv2 flow rule translator exception.
*/
@Beta
public final class Bmv2FlowRuleTranslatorException extends Exception {
public Bmv2FlowRuleTranslatorException(String msg) {
super(msg);
}
}

View File

@ -1,115 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* BMv2 header instance model.
*/
@Beta
public final class Bmv2HeaderModel {
private final String name;
private final int id;
private final Bmv2HeaderTypeModel type;
private final boolean isMetadata;
/**
* Creates a new header instance model.
*
* @param name name
* @param id id
* @param type header type
* @param metadata if is metadata
*/
protected Bmv2HeaderModel(String name, int id, Bmv2HeaderTypeModel type, boolean metadata) {
this.name = name;
this.id = id;
this.type = type;
this.isMetadata = metadata;
}
/**
* Returns the name of this header instance.
*
* @return a string value
*/
public String name() {
return name;
}
/**
* Return the id of this header instance.
*
* @return an integer value
*/
public int id() {
return id;
}
/**
* Return the type of this header instance.
*
* @return a header type value
*/
public Bmv2HeaderTypeModel type() {
return type;
}
/**
* Return true if this header instance is a metadata, false elsewhere.
*
* @return a boolean value
*/
public boolean isMetadata() {
return isMetadata;
}
@Override
public int hashCode() {
return Objects.hashCode(name, id, type, isMetadata);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2HeaderModel other = (Bmv2HeaderModel) obj;
return Objects.equal(this.name, other.name)
&& Objects.equal(this.id, other.id)
&& Objects.equal(this.type, other.type)
&& Objects.equal(this.isMetadata, other.isMetadata);
}
@Override
public String toString() {
return toStringHelper(this)
.add("name", name)
.add("id", id)
.add("type", type)
.add("isMetadata", isMetadata)
.toString();
}
}

View File

@ -1,119 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.util.LinkedHashMap;
import java.util.List;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* BMv2 header type model.
*/
@Beta
public final class Bmv2HeaderTypeModel {
private final String name;
private final int id;
private final LinkedHashMap<String, Bmv2FieldTypeModel> fields = Maps.newLinkedHashMap();
/**
* Creates a new header type model.
*
* @param name name
* @param id id
* @param fieldTypes fields
*/
protected Bmv2HeaderTypeModel(String name, int id, List<Bmv2FieldTypeModel> fieldTypes) {
this.name = name;
this.id = id;
fieldTypes.forEach(f -> this.fields.put(f.name(), f));
}
/**
* Returns this header type name.
*
* @return name
*/
public String name() {
return name;
}
/**
* Returns this header type id.
*
* @return id
*/
public int id() {
return id;
}
/**
* Returns this header type's field defined by the passed name, null if
* not present.
*
* @param fieldName field name
* @return field or null
*/
public Bmv2FieldTypeModel field(String fieldName) {
return fields.get(fieldName);
}
/**
* Return and immutable list of header fields for this header
* type. The list is ordered according to the values defined in the
* model.
*
* @return list of fields
*/
public List<Bmv2FieldTypeModel> fields() {
return ImmutableList.copyOf(fields.values());
}
@Override
public int hashCode() {
return Objects.hashCode(name, id, fields);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2HeaderTypeModel other = (Bmv2HeaderTypeModel) obj;
return Objects.equal(this.name, other.name)
&& Objects.equal(this.id, other.id)
&& Objects.equal(this.fields, other.fields);
}
@Override
public String toString() {
return toStringHelper(this)
.add("name", name)
.add("id", id)
.add("fields", fields)
.toString();
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableBiMap;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
/**
* A BMv2 configuration interpreter.
*/
@Beta
public interface Bmv2Interpreter {
/**
* Returns a bi-map describing a one-to-one relationship between ONOS flow rule table IDs and BMv2 table names.
*
* @return a {@link com.google.common.collect.BiMap} where the key is a ONOS flow rule table id and
* the value is a BMv2 table names
*/
ImmutableBiMap<Integer, String> tableIdMap();
/**
* Returns a bi-map describing a one-to-one relationship between ONOS criterion types and BMv2 header field names.
* Header field names are formatted using the notation {@code header_name.field_member_name}.
*
* @return a {@link com.google.common.collect.BiMap} where the keys are ONOS criterion types and the values are
* BMv2 header field names
*/
ImmutableBiMap<Criterion.Type, String> criterionTypeMap();
/**
* Return a BMv2 action that is functionally equivalent to the given ONOS traffic treatment for the given
* configuration.
*
* @param treatment a ONOS traffic treatment
* @param configuration a BMv2 configuration
* @return a BMv2 action object
* @throws Bmv2InterpreterException if the treatment cannot be mapped to any BMv2 action
*/
Bmv2Action mapTreatment(TrafficTreatment treatment, Bmv2Configuration configuration)
throws Bmv2InterpreterException;
}

View File

@ -1,30 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
/**
* A BMv2 interpreter exception.
*/
@Beta
public final class Bmv2InterpreterException extends Exception {
public Bmv2InterpreterException(String message) {
super(message);
}
}

View File

@ -1,88 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* A BMv2 action runtime data model.
*/
@Beta
public final class Bmv2RuntimeDataModel {
private final String name;
private final int bitWidth;
/**
* Creates a new runtime data model.
*
* @param name name
* @param bitWidth bitwidth
*/
protected Bmv2RuntimeDataModel(String name, int bitWidth) {
this.name = name;
this.bitWidth = bitWidth;
}
/**
* Return the name of this runtime data.
*
* @return a string value
*/
public String name() {
return name;
}
/**
* Return the bit width of this runtime data.
*
* @return an integer value
*/
public int bitWidth() {
return bitWidth;
}
@Override
public int hashCode() {
return Objects.hash(name, bitWidth);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2RuntimeDataModel other = (Bmv2RuntimeDataModel) obj;
return Objects.equals(this.name, other.name)
&& Objects.equals(this.bitWidth, other.bitWidth);
}
@Override
public String toString() {
return toStringHelper(this)
.add("name", name)
.add("bitWidth", bitWidth)
.toString();
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* A BMv2 table key model.
*/
@Beta
public final class Bmv2TableKeyModel {
private final Bmv2MatchParam.Type matchType;
private final Bmv2FieldModel field;
/**
* Creates a new table key model.
*
* @param matchType match type
* @param field field instance
*/
protected Bmv2TableKeyModel(Bmv2MatchParam.Type matchType, Bmv2FieldModel field) {
this.matchType = matchType;
this.field = field;
}
/**
* Returns the match type of this key.
*
* @return a string value
* TODO returns enum of match type
*/
public Bmv2MatchParam.Type matchType() {
return matchType;
}
/**
* Returns the header field instance matched by this key.
*
* @return a header field value
*/
public Bmv2FieldModel field() {
return field;
}
@Override
public int hashCode() {
return Objects.hashCode(matchType, field);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2TableKeyModel other = (Bmv2TableKeyModel) obj;
return Objects.equal(this.matchType, other.matchType)
&& Objects.equal(this.field, other.field);
}
@Override
public String toString() {
return toStringHelper(this)
.add("matchType", matchType)
.add("field", field)
.toString();
}
}

View File

@ -1,193 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import java.util.List;
import java.util.Set;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* A BMv2 table model.
*/
@Beta
public final class Bmv2TableModel {
private final String name;
private final int id;
private final String matchType;
private final String type;
private final int maxSize;
private final boolean hasCounters;
private final boolean hasTimeouts;
private final List<Bmv2TableKeyModel> keys;
private final Set<Bmv2ActionModel> actions;
/**
* Creates a new table model.
*
* @param name name
* @param id id
* @param matchType match type
* @param type type
* @param maxSize max number of entries
* @param withCounters if table has counters
* @param supportTimeout if table supports aging
* @param keys list of match keys
* @param actions list of actions
*/
protected Bmv2TableModel(String name, int id, String matchType, String type,
int maxSize, boolean withCounters, boolean supportTimeout,
List<Bmv2TableKeyModel> keys, Set<Bmv2ActionModel> actions) {
this.name = name;
this.id = id;
this.matchType = matchType;
this.type = type;
this.maxSize = maxSize;
this.hasCounters = withCounters;
this.hasTimeouts = supportTimeout;
this.keys = keys;
this.actions = actions;
}
/**
* Returns the name of this table.
*
* @return a string value
*/
public String name() {
return name;
}
/**
* Returns the id of this table.
*
* @return an integer value
*/
public int id() {
return id;
}
/**
* Return the match type of this table.
*
* @return a string value
*/
public String matchType() {
return matchType;
}
/**
* Return the match type of this table.
*
* @return a string value
*/
public String type() {
return type;
}
/**
* Returns the maximum number of entries supported by this table.
*
* @return an integer value
*/
public int maxSize() {
return maxSize;
}
/**
* Returns true if this table has counters, false otherwise.
*
* @return a boolean value
*/
public boolean hasCounters() {
return hasCounters;
}
/**
* Returns true if this table supports aging, false otherwise.
*
* @return a boolean value
*/
public boolean hasTimeouts() {
return hasTimeouts;
}
/**
* Returns the list of match keys supported by this table.
* The list is ordered accordingly to the model's table definition.
*
* @return a list of match keys
*/
public List<Bmv2TableKeyModel> keys() {
return keys;
}
/**
* Returns the set of actions supported by this table.
*
* @return a list of actions
*/
public Set<Bmv2ActionModel> actions() {
return actions;
}
@Override
public int hashCode() {
return Objects.hashCode(name, id, matchType, type, maxSize, hasCounters,
hasTimeouts, keys, actions);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2TableModel other = (Bmv2TableModel) obj;
return Objects.equal(this.name, other.name)
&& Objects.equal(this.id, other.id)
&& Objects.equal(this.matchType, other.matchType)
&& Objects.equal(this.type, other.type)
&& Objects.equal(this.maxSize, other.maxSize)
&& Objects.equal(this.hasCounters, other.hasCounters)
&& Objects.equal(this.hasTimeouts, other.hasTimeouts)
&& Objects.equal(this.keys, other.keys)
&& Objects.equal(this.actions, other.actions);
}
@Override
public String toString() {
return toStringHelper(this)
.add("name", name)
.add("id", id)
.add("matchType", matchType)
.add("type", type)
.add("maxSize", maxSize)
.add("hasCounters", hasCounters)
.add("hasTimeouts", hasTimeouts)
.add("keys", keys)
.add("actions", actions)
.toString();
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 device context API.
*/
package org.onosproject.bmv2.api.context;

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 protocol API.
*/
package org.onosproject.bmv2.api;

View File

@ -1,141 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import org.onlab.util.ImmutableByteSequence;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* An action of a BMv2 match-action table entry.
*/
@Beta
public final class Bmv2Action {
private final String name;
private final List<ImmutableByteSequence> parameters;
protected Bmv2Action(String name, List<ImmutableByteSequence> parameters) {
// hide constructor
this.name = name;
this.parameters = parameters;
}
/**
* Returns a new action builder.
*/
public static Builder builder() {
return new Builder();
}
/**
* Return the name of this action.
*
* @return action name
*/
public final String name() {
return name;
}
/**
* Returns an immutable view of the list of parameters of this action.
*
* @return list of byte sequence
*/
public final List<ImmutableByteSequence> parameters() {
return Collections.unmodifiableList(parameters);
}
@Override
public final int hashCode() {
return Objects.hash(name, parameters);
}
@Override
public final boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2Action other = (Bmv2Action) obj;
return Objects.equals(this.name, other.name)
&& Objects.equals(this.parameters, other.parameters);
}
@Override
public final String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.add("parameters", parameters)
.toString();
}
/**
* A BMv2 action builder.
*/
public static final class Builder {
private String name = null;
private List<ImmutableByteSequence> parameters;
private Builder() {
this.parameters = Lists.newArrayList();
}
/**
* Sets the action name.
*
* @param actionName a string value
* @return this
*/
public Builder withName(String actionName) {
this.name = checkNotNull(actionName);
return this;
}
/**
* Adds a parameter at the end of the parameters list.
*
* @param parameter a ByteBuffer value
* @return this
*/
public Builder addParameter(ImmutableByteSequence parameter) {
parameters.add(checkNotNull(parameter));
return this;
}
/**
* Builds a BMv2 action object.
*
* @return a BMv2 action
*/
public Bmv2Action build() {
checkState(name != null, "action name not set");
return new Bmv2Action(name, parameters);
}
}
}

View File

@ -1,154 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import org.onosproject.net.DeviceId;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A BMv2 device.
*/
@Beta
public final class Bmv2Device {
public static final String N_A = "n/a";
public static final String SCHEME = "bmv2";
public static final String PROTOCOL = "bmv2-thrift";
public static final String MANUFACTURER = "p4.org";
public static final String HW_VERSION = "bmv2";
public static final String SW_VERSION = "1.0.0";
public static final String SERIAL_NUMBER = N_A;
private final String thriftServerHost;
private final int thriftServerPort;
private final int internalDeviceId;
/**
* Creates a new BMv2 device object.
*
* @param thriftServerHost the hostname or IP address of the Thrift RPC server running on the device
* @param thriftServerPort the listening port used by the device Thrift RPC server
* @param internalDeviceId the internal numeric device ID
*/
public Bmv2Device(String thriftServerHost, int thriftServerPort, int internalDeviceId) {
this.thriftServerHost = checkNotNull(thriftServerHost, "host cannot be null");
this.thriftServerPort = checkNotNull(thriftServerPort, "port cannot be null");
this.internalDeviceId = internalDeviceId;
}
/**
* Returns a Bmv2Device representing the given deviceId.
*
* @param deviceId a deviceId
* @return
*/
public static Bmv2Device of(DeviceId deviceId) {
return DeviceIdParser.parse(checkNotNull(deviceId, "deviceId cannot be null"));
}
/**
* Returns the hostname or IP address of the Thrift RPC server running on the device.
*
* @return a string value
*/
public String thriftServerHost() {
return thriftServerHost;
}
/**
* Returns the listening port of the Thrift RPC server running on the device.
*
* @return an integer value
*/
public int thriftServerPort() {
return thriftServerPort;
}
/**
* Returns the BMv2-internal device ID, which is an integer arbitrary chosen at device boot.
* Such an ID must not be confused with the ONOS-internal {@link org.onosproject.net.DeviceId}.
*
* @return an integer value
*/
public int internalDeviceId() {
return internalDeviceId;
}
/**
* Returns a new ONOS device ID for this device.
*
* @return a new device ID
*/
public DeviceId asDeviceId() {
try {
return DeviceId.deviceId(new URI(SCHEME, this.thriftServerHost + ":" + this.thriftServerPort,
String.valueOf(this.internalDeviceId)));
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Unable to build deviceID for device " + this.toString(), e);
}
}
@Override
public int hashCode() {
return Objects.hashCode(thriftServerHost, thriftServerPort, internalDeviceId);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2Device other = (Bmv2Device) obj;
return Objects.equal(this.thriftServerHost, other.thriftServerHost)
&& Objects.equal(this.thriftServerPort, other.thriftServerPort)
&& Objects.equal(this.internalDeviceId, other.internalDeviceId);
}
@Override
public String toString() {
return asDeviceId().toString();
}
private static class DeviceIdParser {
private static final Pattern REGEX = Pattern.compile(SCHEME + ":(.+):(\\d+)#(\\d+)");
public static Bmv2Device parse(DeviceId deviceId) {
Matcher matcher = REGEX.matcher(deviceId.toString());
if (matcher.find()) {
String host = matcher.group(1);
int port = Integer.valueOf(matcher.group(2));
int internalDeviceId = Integer.valueOf(matcher.group(3));
return new Bmv2Device(host, port, internalDeviceId);
} else {
throw new RuntimeException("Unable to parse bmv2 device id string: " + deviceId.toString());
}
}
}
}

View File

@ -1,177 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import org.apache.commons.lang3.tuple.Pair;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.net.DeviceId;
import java.util.Collection;
import java.util.List;
/**
* An agent to control a BMv2 device.
*/
@Beta
public interface Bmv2DeviceAgent {
/**
* Returns the device ID of this agent.
*
* @return a device id
*/
DeviceId deviceId();
/**
* Pings the device, returns true if the device is reachable, false otherwise.
*
* @return true if reachable, false otherwise
*/
boolean ping();
/**
* Adds a new table entry. If successful returns a table-specific identifier of the installed entry.
*
* @param entry a table entry
* @return a long value
* @throws Bmv2RuntimeException if any error occurs
*/
long addTableEntry(Bmv2TableEntry entry) throws Bmv2RuntimeException;
/**
* Modifies an existing entry at by updating its action.
*
* @param tableName a string value
* @param entryId a long value
* @param action an action
* @throws Bmv2RuntimeException if any error occurs
*/
void modifyTableEntry(String tableName, long entryId, Bmv2Action action) throws Bmv2RuntimeException;
/**
* Deletes currently installed entry.
*
* @param tableName a string value
* @param entryId a long value
* @throws Bmv2RuntimeException if any error occurs
*/
void deleteTableEntry(String tableName, long entryId) throws Bmv2RuntimeException;
/**
* Sets a default action for the given table.
*
* @param tableName a string value
* @param action an action value
* @throws Bmv2RuntimeException if any error occurs
*/
void setTableDefaultAction(String tableName, Bmv2Action action) throws Bmv2RuntimeException;
/**
* Returns information on the ports currently configured in the switch.
*
* @return collection of port information
* @throws Bmv2RuntimeException if any error occurs
*/
Collection<Bmv2PortInfo> getPortsInfo() throws Bmv2RuntimeException;
/**
* Returns a list of table entries installed in the given table.
*
* @param tableName a string value
* @return a list of parsed table entries
* @throws Bmv2RuntimeException if any error occurs
*/
List<Bmv2ParsedTableEntry> getTableEntries(String tableName) throws Bmv2RuntimeException;
/**
* Requests the device to transmit a given packet over the given port.
*
* @param portNumber a port number
* @param packet a byte sequence
* @throws Bmv2RuntimeException
*/
void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException;
/**
* Resets the state of the switch.
*
* @throws Bmv2RuntimeException if any error occurs
*/
void resetState() throws Bmv2RuntimeException;
/**
* Returns the JSON configuration currently used to process packets.
*
* @return a JSON-formatted string value
* @throws Bmv2RuntimeException if any error occurs
*/
String dumpJsonConfig() throws Bmv2RuntimeException;
/**
* Returns the MD5 sum of the JSON-formatted configuration currently used to process packets.
*
* @return a string value
* @throws Bmv2RuntimeException if any error occurs
*/
String getJsonConfigMd5() throws Bmv2RuntimeException;
/**
* Returns the counter values for a given table and entry.
*
* @param tableName a table name
* @param entryId an entry id
* @return a pair of long values, where the left value is the number of bytes and the right the number of packets
* @throws Bmv2RuntimeException if any error occurs
*/
Pair<Long, Long> readTableEntryCounter(String tableName, long entryId) throws Bmv2RuntimeException;
/**
* Returns the values of a given counter instance.
*
* @param counterName a counter name
* @param index an integer value
* @return a pair of long values, where the left value is the number of bytes and the right value is the number of
* packets
* @throws Bmv2RuntimeException if any error occurs
*/
Pair<Long, Long> readCounter(String counterName, int index) throws Bmv2RuntimeException;
/**
* Returns the ID of the current BMv2 process instance (used to distinguish between different executions of the
* same BMv2 device).
*
* @return an integer value
* @throws Bmv2RuntimeException if any error occurs
*/
int getProcessInstanceId() throws Bmv2RuntimeException;
/**
* Uploads a new JSON configuration on the device.
*
* @param jsonString a string value
* @throws Bmv2RuntimeException if any error occurs
*/
void uploadNewJsonConfig(String jsonString) throws Bmv2RuntimeException;
/**
* Triggers a configuration swap on the device.
*
* @throws Bmv2RuntimeException
*/
void swapJsonConfig() throws Bmv2RuntimeException;
}

View File

@ -1,81 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import org.onlab.util.ImmutableByteSequence;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A BMv2 exact match parameter.
*/
@Beta
public final class Bmv2ExactMatchParam implements Bmv2MatchParam {
private final ImmutableByteSequence value;
/**
* Creates a new match parameter object that matches exactly on the
* given byte sequence.
*
* @param value a byte sequence value
*/
public Bmv2ExactMatchParam(ImmutableByteSequence value) {
this.value = checkNotNull(value, "value cannot be null");
}
@Override
public Type type() {
return Type.EXACT;
}
/**
* Return the byte sequence matched by this parameter.
*
* @return an immutable byte buffer value
*/
public ImmutableByteSequence value() {
return this.value;
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2ExactMatchParam other = (Bmv2ExactMatchParam) obj;
return Objects.equal(this.value, other.value);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("value", value)
.toString();
}
}

View File

@ -1,475 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
import org.onlab.util.KryoNamespace;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2FieldTypeModel;
import org.onosproject.bmv2.api.context.Bmv2HeaderModel;
import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
import org.onosproject.net.flow.AbstractExtension;
import org.onosproject.net.flow.criteria.ExtensionSelector;
import org.onosproject.net.flow.criteria.ExtensionSelectorType;
import org.onosproject.store.serializers.KryoNamespaces;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Map;
import static com.google.common.base.Preconditions.*;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
/**
* Extension selector for BMv2 used as a wrapper for multiple BMv2 match parameters. Match parameters are
* encoded using a map where the keys are expected to be field names formatted as {@code headerName.fieldName}
* (e.g. {@code ethernet.dstAddr}).
*/
@Beta
public final class Bmv2ExtensionSelector extends AbstractExtension implements ExtensionSelector {
private static final KryoNamespace APP_KRYO = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(Bmv2ExactMatchParam.class)
.register(Bmv2TernaryMatchParam.class)
.register(Bmv2LpmMatchParam.class)
.register(Bmv2ValidMatchParam.class)
.build();
private Map<String, Bmv2MatchParam> parameterMap;
/**
* Creates a new BMv2 extension selector for the given match parameters map.
*
* @param paramMap a map
*/
private Bmv2ExtensionSelector(Map<String, Bmv2MatchParam> paramMap) {
this.parameterMap = paramMap;
}
/**
* Returns the match parameters map of this selector.
*
* @return a match parameter map
*/
public Map<String, Bmv2MatchParam> parameterMap() {
return parameterMap;
}
@Override
public ExtensionSelectorType type() {
return ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS.type();
}
@Override
public byte[] serialize() {
return APP_KRYO.serialize(parameterMap);
}
@Override
public void deserialize(byte[] data) {
this.parameterMap = APP_KRYO.deserialize(data);
}
@Override
public int hashCode() {
return Objects.hashCode(parameterMap);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2ExtensionSelector other = (Bmv2ExtensionSelector) obj;
return Objects.equal(this.parameterMap, other.parameterMap);
}
@Override
public String toString() {
MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this);
parameterMap.forEach((name, param) -> {
switch (param.type()) {
case EXACT:
Bmv2ExactMatchParam e = (Bmv2ExactMatchParam) param;
helper.add(name, e.value());
break;
case TERNARY:
Bmv2TernaryMatchParam t = (Bmv2TernaryMatchParam) param;
helper.add(name, t.value() + "&&&" + t.mask());
break;
case LPM:
Bmv2LpmMatchParam l = (Bmv2LpmMatchParam) param;
helper.add(name, l.value() + "/" + String.valueOf(l.prefixLength()));
break;
case VALID:
Bmv2ValidMatchParam v = (Bmv2ValidMatchParam) param;
helper.add(name, v.flag() ? "VALID" : "NOT_VALID");
break;
default:
helper.add(name, param);
break;
}
});
return helper.toString();
}
/**
* Returns a new, empty BMv2 extension selector.
*
* @return a BMv2 extension treatment
*/
public static Bmv2ExtensionSelector empty() {
return new Bmv2ExtensionSelector(Collections.emptyMap());
}
/**
* Returns a new builder of BMv2 extension selectors.
*
* @return a builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder of BMv2 extension selectors.
* <p>
* Match parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
* {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
* configuration.
*/
public static final class Builder {
private final Map<Pair<String, String>, Bmv2MatchParam> parameterMap = Maps.newHashMap();
private Bmv2Configuration configuration;
private Builder() {
// ban constructor.
}
/**
* Sets the BMv2 configuration to format the match parameters of the selector.
*
* @param config a BMv2 configuration
* @return this
*/
public Builder forConfiguration(Bmv2Configuration config) {
this.configuration = config;
return this;
}
/**
* Adds an exact match parameter for the given header field and value.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a short value
* @return this
*/
public Builder matchExact(String headerName, String fieldName, short value) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
exact(value));
return this;
}
/**
* Adds an exact match parameter for the given header field and value.
*
* @param headerName a string value
* @param fieldName a string value
* @param value an integer value
* @return this
*/
public Builder matchExact(String headerName, String fieldName, int value) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
exact(value));
return this;
}
/**
* Adds an exact match parameter for the given header field and value.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a long value
* @return this
*/
public Builder matchExact(String headerName, String fieldName, long value) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
exact(value));
return this;
}
/**
* Adds an exact match parameter for the given header field and value.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a byte array
* @return this
*/
public Builder matchExact(String headerName, String fieldName, byte[] value) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
exact(value));
return this;
}
/**
* Adds a ternary match parameter for the given header field, value and mask.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a short value
* @param mask a short value
* @return this
*/
public Builder matchTernary(String headerName, String fieldName, short value, short mask) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
ternary(value, mask));
return this;
}
/**
* Adds a ternary match parameter for the given header field, value and mask.
*
* @param headerName a string value
* @param fieldName a string value
* @param value an integer value
* @param mask an integer value
* @return this
*/
public Builder matchTernary(String headerName, String fieldName, int value, int mask) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
ternary(value, mask));
return this;
}
/**
* Adds a ternary match parameter for the given header field, value and mask.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a long value
* @param mask a long value
* @return this
*/
public Builder matchTernary(String headerName, String fieldName, long value, long mask) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
ternary(value, mask));
return this;
}
/**
* Adds a ternary match parameter for the given header field, value and mask.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a byte array
* @param mask a byte array
* @return this
*/
public Builder matchTernary(String headerName, String fieldName, byte[] value, byte[] mask) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
ternary(value, mask));
return this;
}
/**
* Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a short value
* @param prefixLength an integer value
* @return this
*/
public Builder matchLpm(String headerName, String fieldName, short value, int prefixLength) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
lpm(value, prefixLength));
return this;
}
/**
* Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
*
* @param headerName a string value
* @param fieldName a string value
* @param value an integer value
* @param prefixLength an integer value
* @return this
*/
public Builder matchLpm(String headerName, String fieldName, int value, int prefixLength) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
lpm(value, prefixLength));
return this;
}
/**
* Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a long value
* @param prefixLength an integer value
* @return this
*/
public Builder matchLpm(String headerName, String fieldName, long value, int prefixLength) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
lpm(value, prefixLength));
return this;
}
/**
* Adds a longest-prefix match (LPM) parameter for the given header field, value and prefix length.
*
* @param headerName a string value
* @param fieldName a string value
* @param value a byte array
* @param prefixLength an integer value
* @return this
*/
public Builder matchLpm(String headerName, String fieldName, byte[] value, int prefixLength) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
lpm(value, prefixLength));
return this;
}
/**
* Adds a valid match parameter for the given header field.
*
* @param headerName a string value
* @param fieldName a string value
* @param flag a boolean value
* @return this
*/
public Builder matchValid(String headerName, String fieldName, boolean flag) {
parameterMap.put(Pair.of(checkNotNull(headerName, "header name cannot be null"),
checkNotNull(fieldName, "field name cannot be null")),
new Bmv2ValidMatchParam(flag));
return this;
}
/**
* Returns a new BMv2 extension selector.
*
* @return a BMv2 extension selector
* @throws NullPointerException if a given header or field name is not defined in the given configuration
* @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
* when trying to fit an integer value into a smaller, fixed-length parameter
* produces overflow.
*/
public Bmv2ExtensionSelector build() {
checkNotNull(configuration, "configuration cannot be null");
checkState(parameterMap.size() > 0, "parameter map cannot be empty");
final Map<String, Bmv2MatchParam> newParameterMap = Maps.newHashMap();
for (Pair<String, String> key : parameterMap.keySet()) {
String headerName = key.getLeft();
String fieldName = key.getRight();
Bmv2HeaderModel headerModel = configuration.header(headerName);
checkNotNull(headerModel, "no such a header in configuration", headerName);
Bmv2FieldTypeModel fieldModel = headerModel.type().field(fieldName);
checkNotNull(fieldModel, "no such a field in configuration", key);
int bitWidth = fieldModel.bitWidth();
Bmv2MatchParam oldParam = parameterMap.get(key);
Bmv2MatchParam newParam = null;
try {
switch (oldParam.type()) {
case EXACT:
Bmv2ExactMatchParam e = (Bmv2ExactMatchParam) oldParam;
newParam = new Bmv2ExactMatchParam(fitByteSequence(e.value(), bitWidth));
break;
case TERNARY:
Bmv2TernaryMatchParam t = (Bmv2TernaryMatchParam) oldParam;
newParam = new Bmv2TernaryMatchParam(fitByteSequence(t.value(), bitWidth),
fitByteSequence(t.mask(), bitWidth));
break;
case LPM:
Bmv2LpmMatchParam l = (Bmv2LpmMatchParam) oldParam;
checkArgument(l.prefixLength() <= bitWidth, "LPM parameter has prefix length too long",
key);
newParam = new Bmv2LpmMatchParam(fitByteSequence(l.value(), bitWidth),
l.prefixLength());
break;
case VALID:
newParam = oldParam;
break;
default:
throw new RuntimeException("Match parameter type not supported: " + oldParam.type());
}
} catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
throw new IllegalArgumentException(e.getMessage() + " [" + key + "]");
}
// FIXME: should put the pair object instead of building a new string for the key.
newParameterMap.put(headerName + "." + fieldName, newParam);
}
return new Bmv2ExtensionSelector(newParameterMap);
}
private static Bmv2MatchParam exact(Object value) {
return new Bmv2ExactMatchParam(copyFrom(bb(value)));
}
private static Bmv2MatchParam ternary(Object value, Object mask) {
return new Bmv2TernaryMatchParam(copyFrom(bb(value)), copyFrom(bb(mask)));
}
private static Bmv2MatchParam lpm(Object value, int prefixLength) {
return new Bmv2LpmMatchParam(copyFrom(bb(value)), prefixLength);
}
private static ByteBuffer bb(Object value) {
if (value instanceof Short) {
return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
} else if (value instanceof Integer) {
return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
} else if (value instanceof Long) {
return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
} else if (value instanceof byte[]) {
byte[] bytes = (byte[]) value;
return ByteBuffer.allocate(bytes.length).put(bytes);
} else {
// Never here.
return null;
}
}
}
}

View File

@ -1,292 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.Maps;
import org.onlab.util.ImmutableByteSequence;
import org.onlab.util.KryoNamespace;
import org.onosproject.bmv2.api.context.Bmv2ActionModel;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2RuntimeDataModel;
import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
import org.onosproject.net.flow.AbstractExtension;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
import org.onosproject.store.serializers.KryoNamespaces;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.BMV2_ACTION;
/**
* Extension treatment for BMv2 used as a wrapper for a {@link Bmv2Action}.
*/
@Beta
public final class Bmv2ExtensionTreatment extends AbstractExtension implements ExtensionTreatment {
private static final KryoNamespace APP_KRYO = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(Bmv2ExtensionTreatment.class)
.register(Bmv2Action.class)
.build();
private List<String> parameterNames;
private Bmv2Action action;
/**
* Creates a new extension treatment for the given BMv2 action.
* The list of action parameters name is also required for visualization purposes (i.e. nicer toString()).
*
* @param action an action
* @param parameterNames a list of strings
*/
private Bmv2ExtensionTreatment(Bmv2Action action, List<String> parameterNames) {
this.action = action;
this.parameterNames = parameterNames;
}
/**
* Returns the action contained by this extension selector.
*
* @return an action
*/
public Bmv2Action action() {
return action;
}
@Override
public ExtensionTreatmentType type() {
return BMV2_ACTION.type();
}
@Override
public byte[] serialize() {
return APP_KRYO.serialize(this);
}
@Override
public void deserialize(byte[] data) {
Bmv2ExtensionTreatment other = APP_KRYO.deserialize(data);
action = other.action;
parameterNames = other.parameterNames;
}
@Override
public int hashCode() {
return Objects.hashCode(action);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2ExtensionTreatment other = (Bmv2ExtensionTreatment) obj;
return Objects.equal(this.action, other.action);
}
@Override
public String toString() {
StringJoiner stringJoiner = new StringJoiner(", ", "(", ")");
for (int i = 0; i < parameterNames.size(); i++) {
stringJoiner.add(parameterNames.get(i) + "=" + action.parameters().get(i));
}
return MoreObjects.toStringHelper(this)
.addValue(action.name() + stringJoiner.toString())
.toString();
}
/**
* Returns a new, empty BMv2 extension treatment.
*
* @return a BMv2 extension treatment
*/
public static Bmv2ExtensionTreatment empty() {
return new Bmv2ExtensionTreatment(null, Collections.emptyList());
}
/**
* Returns a new BMv2 extension treatment builder.
*
* @return a builder
*/
public static Builder builder() {
return new Builder();
}
/**
* A builder of BMv2 extension treatments.
*
* BMv2 action parameters are built from primitive data types ({@code short}, {@code int}, {@code long} or
* {@code byte[]}) and automatically casted to fixed-length byte sequences according to the given BMv2
* configuration.
*/
public static final class Builder {
private Bmv2Configuration configuration;
private String actionName;
private final Map<String, ImmutableByteSequence> parameters = Maps.newHashMap();
private Builder() {
// Ban constructor.
}
/**
* Sets the BMv2 configuration to format the action parameters.
*
* @param config a BMv2 configuration
* @return this
*/
public Builder forConfiguration(Bmv2Configuration config) {
this.configuration = config;
return this;
}
/**
* Sets the action name.
*
* @param actionName a string value
* @return this
*/
public Builder setActionName(String actionName) {
this.actionName = actionName;
return this;
}
/**
* Adds an action parameter.
*
* @param parameterName a string value
* @param value a short value
* @return this
*/
public Builder addParameter(String parameterName, short value) {
this.parameters.put(parameterName, copyFrom(bb(value)));
return this;
}
/**
* Adds an action parameter.
*
* @param parameterName a string value
* @param value an integer value
* @return this
*/
public Builder addParameter(String parameterName, int value) {
this.parameters.put(parameterName, copyFrom(bb(value)));
return this;
}
/**
* Adds an action parameter.
*
* @param parameterName a string value
* @param value a long value
* @return this
*/
public Builder addParameter(String parameterName, long value) {
this.parameters.put(parameterName, copyFrom(bb(value)));
return this;
}
/**
* Adds an action parameter.
*
* @param parameterName a string value
* @param value a byte array
* @return this
*/
public Builder addParameter(String parameterName, byte[] value) {
this.parameters.put(parameterName, copyFrom(bb(value)));
return this;
}
/**
* Returns a new BMv2 extension treatment.
*
* @return a BMv2 extension treatment
* @throws NullPointerException if the given action or parameter names are not defined in the given
* configuration
* @throws IllegalArgumentException if a given parameter cannot be casted for the given configuration, e.g.
* when trying to fit an integer value into a smaller, fixed-length parameter
* produces overflow.
*/
public Bmv2ExtensionTreatment build() {
checkNotNull(configuration, "configuration cannot be null");
checkNotNull(actionName, "action name cannot be null");
Bmv2ActionModel actionModel = configuration.action(actionName);
checkNotNull(actionModel, "no such an action in configuration", actionName);
checkArgument(actionModel.runtimeDatas().size() == parameters.size(),
"invalid number of parameters", actionName);
List<ImmutableByteSequence> newParameters = new ArrayList<>(parameters.size());
List<String> parameterNames = new ArrayList<>(parameters.size());
for (String parameterName : parameters.keySet()) {
Bmv2RuntimeDataModel runtimeData = actionModel.runtimeData(parameterName);
checkNotNull(runtimeData, "no such an action parameter in configuration",
actionName + "->" + runtimeData.name());
int bitWidth = runtimeData.bitWidth();
try {
ImmutableByteSequence newSequence = fitByteSequence(parameters.get(parameterName), bitWidth);
int idx = actionModel.runtimeDatas().indexOf(runtimeData);
newParameters.add(idx, newSequence);
parameterNames.add(idx, parameterName);
} catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
throw new IllegalArgumentException(e.getMessage() +
" [" + actionName + "->" + runtimeData.name() + "]");
}
}
return new Bmv2ExtensionTreatment(new Bmv2Action(actionName, newParameters), parameterNames);
}
private static ByteBuffer bb(Object value) {
if (value instanceof Short) {
return ByteBuffer.allocate(Short.BYTES).putShort((short) value);
} else if (value instanceof Integer) {
return ByteBuffer.allocate(Integer.BYTES).putInt((int) value);
} else if (value instanceof Long) {
return ByteBuffer.allocate(Long.BYTES).putLong((long) value);
} else if (value instanceof byte[]) {
byte[] bytes = (byte[]) value;
return ByteBuffer.allocate(bytes.length).put(bytes);
} else {
// Never here.
return null;
}
}
}
}

View File

@ -1,107 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import org.onosproject.net.flow.FlowRule;
/**
* A wrapper for a ONOS flow rule installed on a BMv2 device.
*/
@Beta
public final class Bmv2FlowRuleWrapper {
private final FlowRule rule;
private final long entryId;
private final long installedOnMillis;
/**
* Creates a new flow rule wrapper.
*
* @param rule a flow rule
* @param entryId a BMv2 table entry ID
* @param installedOnMillis the time (in milliseconds, since January 1, 1970 UTC) when the flow rule was installed
* on the device
*/
public Bmv2FlowRuleWrapper(FlowRule rule, long entryId, long installedOnMillis) {
this.rule = rule;
this.entryId = entryId;
this.installedOnMillis = installedOnMillis;
}
/**
* Returns the flow rule contained by this wrapper.
*
* @return a flow rule
*/
public FlowRule rule() {
return rule;
}
/**
* 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;
}
/**
* Returns the the time (in milliseconds, since January 1, 1970 UTC) when the flow rule was installed on
* the device.
*
* @return a long value
*/
public long installedOnMillis() {
return installedOnMillis;
}
/**
* Returns the BMv2 entry ID of this flow rule.
*
* @return a long value
*/
public long entryId() {
return entryId;
}
@Override
public int hashCode() {
return Objects.hashCode(rule, entryId, installedOnMillis);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2FlowRuleWrapper other = (Bmv2FlowRuleWrapper) obj;
return Objects.equal(this.rule, other.rule)
&& Objects.equal(this.entryId, other.entryId)
&& Objects.equal(this.installedOnMillis, other.installedOnMillis);
}
@Override
public String toString() {
return installedOnMillis + "-" + rule.hashCode();
}
}

View File

@ -1,97 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import org.onlab.util.ImmutableByteSequence;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A BMv2 longest prefix match (LPM) parameter.
*/
@Beta
public final class Bmv2LpmMatchParam implements Bmv2MatchParam {
private final ImmutableByteSequence value;
private final int prefixLength;
/**
* Creates a new LPM parameter using the given byte sequence value and
* prefix length.
*
* @param value a byte sequence value
* @param prefixLength an integer value
*/
public Bmv2LpmMatchParam(ImmutableByteSequence value, int prefixLength) {
checkArgument(prefixLength >= 0, "prefix length cannot be negative");
this.value = checkNotNull(value);
this.prefixLength = prefixLength;
}
@Override
public Bmv2MatchParam.Type type() {
return Type.LPM;
}
/**
* Returns the byte sequence value of this parameter.
*
* @return a byte sequence value
*/
public ImmutableByteSequence value() {
return this.value;
}
/**
* Returns the prefix length of this parameter.
*
* @return an integer value
*/
public int prefixLength() {
return this.prefixLength;
}
@Override
public int hashCode() {
return Objects.hashCode(value, prefixLength);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2LpmMatchParam other = (Bmv2LpmMatchParam) obj;
return Objects.equal(this.value, other.value)
&& Objects.equal(this.prefixLength, other.prefixLength);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("value", value)
.add("prefixLength", prefixLength)
.toString();
}
}

View File

@ -1,131 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import org.onlab.util.ImmutableByteSequence;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A match key of a BMv2 match-action table entry.
*/
@Beta
public final class Bmv2MatchKey {
private final List<Bmv2MatchParam> matchParams;
private Bmv2MatchKey(List<Bmv2MatchParam> matchParams) {
// ban constructor
this.matchParams = matchParams;
}
/**
* Returns a new match key builder.
*
* @return a match key builder
*/
public static Builder builder() {
return new Builder();
}
/**
* Returns the list of match parameters of this match key.
*
* @return list match parameters
*/
public final List<Bmv2MatchParam> matchParams() {
return Collections.unmodifiableList(matchParams);
}
@Override
public final int hashCode() {
return Objects.hashCode(matchParams);
}
@Override
public final boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2MatchKey other = (Bmv2MatchKey) obj;
return Objects.equals(this.matchParams, other.matchParams);
}
@Override
public final String toString() {
return MoreObjects.toStringHelper(this)
.addValue(matchParams)
.toString();
}
/**
* Builder of a BMv2 match key.
*/
public static final class Builder {
private List<Bmv2MatchParam> matchParams;
private Builder() {
this.matchParams = Lists.newArrayList();
}
/**
* Adds a match parameter to the match key.
*
* @param param a match parameter
* @return this
*/
public Builder add(Bmv2MatchParam param) {
this.matchParams.add(checkNotNull(param));
return this;
}
/**
* Adds a ternary match parameter where all bits are don't-care.
*
* @param byteLength length in bytes of the parameter
* @return this
*/
public Builder withWildcard(int byteLength) {
checkArgument(byteLength > 0, "length must be a positive integer");
return add(new Bmv2TernaryMatchParam(
ImmutableByteSequence.ofZeros(byteLength),
ImmutableByteSequence.ofZeros(byteLength)));
}
/**
* Builds a new match key object.
*
* @return match key
*/
public Bmv2MatchKey build() {
return new Bmv2MatchKey(this.matchParams);
}
}
}

View File

@ -1,55 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
/**
* Representation of a BMv2 match parameter.
*/
@Beta
public interface Bmv2MatchParam {
/**
* Returns the match type of this parameter.
*
* @return a match type value
*/
Type type();
/**
* BMv2 match types.
*/
enum Type {
/**
* Exact match type.
*/
EXACT,
/**
* Ternary match type.
*/
TERNARY,
/**
* Longest-prefix match type.
*/
LPM,
/**
* Valid match type.
*/
VALID
}
}

View File

@ -1,112 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
/**
* Representation of a table entry installed on a BMv2 device.
*/
@Beta
public final class Bmv2ParsedTableEntry {
private final long entryId;
private final Bmv2MatchKey matchKey;
private final Bmv2Action action;
private final int priority;
/**
* Creates a new parsed table entry.
*
* @param entryId a long value
* @param matchKey a match key
* @param action an action
* @param priority an integer value
*/
public Bmv2ParsedTableEntry(long entryId, Bmv2MatchKey matchKey, Bmv2Action action, int priority) {
this.entryId = entryId;
this.matchKey = matchKey;
this.action = action;
this.priority = priority;
}
/**
* Returns the entry ID.
*
* @return a long value
*/
public long entryId() {
return entryId;
}
/**
* Returns the match key.
*
* @return a match key object
*/
public Bmv2MatchKey matchKey() {
return matchKey;
}
/**
* Returns the action.
*
* @return an action object
*/
public Bmv2Action action() {
return action;
}
/**
* Returns the priority.
*
* @return an integer value
*/
public int getPriority() {
return priority;
}
@Override
public int hashCode() {
return Objects.hashCode(entryId, matchKey, action, priority);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2ParsedTableEntry other = (Bmv2ParsedTableEntry) obj;
return Objects.equal(this.entryId, other.entryId)
&& Objects.equal(this.matchKey, other.matchKey)
&& Objects.equal(this.action, other.action)
&& Objects.equal(this.priority, other.priority);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("entryId", entryId)
.add("matchKey", matchKey)
.add("action", action)
.toString();
}
}

View File

@ -1,100 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
/**
* Information of a port of a BMv2 device.
*/
@Beta
public final class Bmv2PortInfo {
private final String ifaceName;
private final int number;
private final boolean isUp;
/**
* Creates a new port description.
*
* @param ifaceName the common name of the network interface
* @param number a port number
* @param isUp interface status
*/
public Bmv2PortInfo(String ifaceName, int number, boolean isUp) {
this.ifaceName = ifaceName;
this.number = number;
this.isUp = isUp;
}
/**
* Returns the common name the network interface used by this port.
*
* @return a string value
*/
public String ifaceName() {
return ifaceName;
}
/**
* Returns the number of this port.
*
* @return an integer value
*/
public int number() {
return number;
}
/**
* Returns true if the port is up, false otherwise.
*
* @return a boolean value
*/
public boolean isUp() {
return isUp;
}
@Override
public int hashCode() {
return Objects.hashCode(ifaceName, number, isUp);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2PortInfo other = (Bmv2PortInfo) obj;
return Objects.equal(this.ifaceName, other.ifaceName)
&& Objects.equal(this.number, other.number)
&& Objects.equal(this.isUp, other.isUp);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("ifaceName", ifaceName)
.add("number", number)
.add("isUp", isUp)
.toString();
}
}

View File

@ -1,102 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
/**
* General exception of the BMv2 runtime APIs.
*/
@Beta
public final class Bmv2RuntimeException extends Exception {
private final Code code;
private String codeString;
public Bmv2RuntimeException(String message) {
super(message);
this.code = Code.OTHER;
this.codeString = message;
}
public Bmv2RuntimeException(Throwable cause) {
super(cause);
this.code = Code.OTHER;
this.codeString = cause.toString();
}
public Bmv2RuntimeException(Code code) {
super(code.name());
this.code = code;
}
public Code getCode() {
return this.code;
}
public String explain() {
return (codeString == null) ? code.name() : code.name() + " " + codeString;
}
@Override
public String toString() {
return getClass().getSimpleName() + " " + explain();
}
public enum Code {
TABLE_FULL,
TABLE_INVALID_HANDLE,
TABLE_EXPIRED_HANDLE,
TABLE_COUNTERS_DISABLED,
TABLE_METERS_DISABLED,
TABLE_AGEING_DISABLED,
TABLE_INVALID_TABLE_NAME,
TABLE_INVALID_ACTION_NAME,
TABLE_WRONG_TABLE_TYPE,
TABLE_INVALID_MBR_HANDLE,
TABLE_MBR_STILL_USED,
TABLE_MBR_ALREADY_IN_GRP,
TABLE_MBR_NOT_IN_GRP,
TABLE_INVALID_GRP_HANDLE,
TABLE_GRP_STILL_USED,
TABLE_EMPTY_GRP,
TABLE_DUPLICATE_ENTRY,
TABLE_BAD_MATCH_KEY,
TABLE_INVALID_METER_OPERATION,
TABLE_DEFAULT_ACTION_IS_CONST,
TABLE_DEFAULT_ENTRY_IS_CONST,
TABLE_GENERAL_ERROR,
TABLE_UNKNOWN_ERROR,
DEV_MGR_ERROR_GENERAL,
DEV_MGR_UNKNOWN,
COUNTER_INVALID_NAME,
COUNTER_INVALID_INDEX,
COUNTER_ERROR_GENERAL,
COUNTER_ERROR_UNKNOWN,
SWAP_CONFIG_DISABLED,
SWAP_ONGOING,
SWAP_NO_ONGOING,
SWAP_ERROR_UKNOWN,
// TODO: add other codes based on autogenerated Thrift APIs
OTHER
}
}

View File

@ -1,226 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An entry of a match-action table in a BMv2 device.
*/
@Beta
public final class Bmv2TableEntry {
private static final int NO_PRIORITY_VALUE = -1;
private static final int NO_TIMEOUT_VALUE = -1;
private final String tableName;
private final Bmv2MatchKey matchKey;
private final Bmv2Action action;
private final int priority;
private final double timeout;
private Bmv2TableEntry(String tableName, Bmv2MatchKey matchKey,
Bmv2Action action, int priority, double timeout) {
this.tableName = tableName;
this.matchKey = matchKey;
this.action = action;
this.priority = priority;
this.timeout = timeout;
}
/**
* Returns a new BMv2 table entry builder.
*
* @return a new builder.
*/
public static Builder builder() {
return new Builder();
}
/**
* Returns the name of the table where this entry is installed.
*
* @return table name
*/
public final String tableName() {
return this.tableName;
}
/**
* Returns the match key of this table entry.
*
* @return match key
*/
public final Bmv2MatchKey matchKey() {
return matchKey;
}
/**
* Returns the action of this table entry.
*
* @return action
*/
public final Bmv2Action action() {
return action;
}
/**
* Returns true is the entry has a valid priority.
*
* @return true if priority is set, false elsewhere
*/
public final boolean hasPriority() {
return this.priority != NO_PRIORITY_VALUE;
}
/**
* Return the priority of this table entry.
*
* @return priority
*/
public final int priority() {
return priority;
}
/**
* Returns true is this table entry has a valid timeout.
*
* @return true if timeout is set, false elsewhere
*/
public final boolean hasTimeout() {
return this.timeout != NO_PRIORITY_VALUE;
}
/**
* Returns the timeout (in fractional seconds) of this table entry.
*
* @return a timeout vale (in fractional seconds)
*/
public final double timeout() {
return timeout;
}
@Override
public final int hashCode() {
return Objects.hash(matchKey, action, priority, timeout);
}
@Override
public final boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2TableEntry other = (Bmv2TableEntry) obj;
return Objects.equals(this.matchKey, other.matchKey)
&& Objects.equals(this.action, other.action)
&& Objects.equals(this.priority, other.priority)
&& Objects.equals(this.timeout, other.timeout);
}
@Override
public final String toString() {
return com.google.common.base.MoreObjects.toStringHelper(this)
.addValue(matchKey)
.addValue(action)
.add("priority", priority)
.add("timeout", timeout)
.toString();
}
public static final class Builder {
private String tableName;
private Bmv2MatchKey matchKey;
private Bmv2Action action;
private int priority = NO_PRIORITY_VALUE;
private double timeout = NO_TIMEOUT_VALUE;
private Builder() {
// hide constructor
}
/**
* Sets the table name.
*
* @param tableName a string value
* @return this
*/
public Builder withTableName(String tableName) {
this.tableName = checkNotNull(tableName, "table name cannot be null");
return this;
}
/**
* Sets the match key.
*
* @param matchKey a match key value
* @return this
*/
public Builder withMatchKey(Bmv2MatchKey matchKey) {
this.matchKey = checkNotNull(matchKey, "match key cannot be null");
return this;
}
/**
* Sets the action.
*
* @param action an action value
* @return this
*/
public Builder withAction(Bmv2Action action) {
this.action = checkNotNull(action, "action cannot be null");
return this;
}
public Builder withPriority(int priority) {
checkArgument(priority >= 0, "priority cannot be negative");
this.priority = priority;
return this;
}
/**
* Sets the timeout.
*
* @param timeout a timeout value in fractional seconds
* @return this
*/
public Builder withTimeout(double timeout) {
checkArgument(timeout > 0, "timeout must be a positive non-zero value");
this.timeout = timeout;
return this;
}
/**
* Build the table entry.
*
* @return a new table entry object
*/
public Bmv2TableEntry build() {
return new Bmv2TableEntry(tableName, matchKey, action, priority,
timeout);
}
}
}

View File

@ -1,102 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import org.onosproject.net.DeviceId;
/**
* A reference to a table entry installed on a BMv2 device.
*/
@Beta
public final class Bmv2TableEntryReference {
private final DeviceId deviceId;
private final String tableName;
private final Bmv2MatchKey matchKey;
/**
* Creates a new table entry reference.
*
* @param deviceId a device ID
* @param tableName a table name
* @param matchKey a match key
*/
public Bmv2TableEntryReference(DeviceId deviceId, String tableName, Bmv2MatchKey matchKey) {
this.deviceId = deviceId;
this.tableName = tableName;
this.matchKey = matchKey;
}
/**
* Returns the device ID of this table entry reference.
*
* @return a device ID
*/
public DeviceId deviceId() {
return deviceId;
}
/**
* Returns the name of the table of this table entry reference.
*
* @return a table name
*/
public String tableName() {
return tableName;
}
/**
* Returns the match key of this table entry reference.
*
* @return a match key
*/
public Bmv2MatchKey matchKey() {
return matchKey;
}
@Override
public int hashCode() {
return Objects.hashCode(deviceId, tableName, matchKey);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2TableEntryReference other = (Bmv2TableEntryReference) obj;
return Objects.equal(this.deviceId, other.deviceId)
&& Objects.equal(this.tableName, other.tableName)
&& Objects.equal(this.matchKey, other.matchKey);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("deviceId", deviceId)
.add("tableName", tableName)
.add("matchKey", matchKey)
.toString();
}
}

View File

@ -1,99 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import org.onlab.util.ImmutableByteSequence;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* Representation of a BMv2 ternary match parameter.
*/
@Beta
public final class Bmv2TernaryMatchParam implements Bmv2MatchParam {
private final ImmutableByteSequence value;
private final ImmutableByteSequence mask;
/**
* Creates a new ternary match parameter using the given byte sequences of
* value and mask.
*
* @param value a byte sequence value
* @param mask a byte sequence value
*/
public Bmv2TernaryMatchParam(ImmutableByteSequence value,
ImmutableByteSequence mask) {
this.value = checkNotNull(value, "value cannot be null");
this.mask = checkNotNull(mask, "value cannot be null");
checkState(value.size() == mask.size(),
"value and mask must have equal size");
}
@Override
public Type type() {
return Type.TERNARY;
}
/**
* Returns the byte sequence value of by this parameter.
*
* @return a byte sequence value
*/
public ImmutableByteSequence value() {
return this.value;
}
/**
* Returns the byte sequence mask of by this parameter.
*
* @return a byte sequence value
*/
public ImmutableByteSequence mask() {
return this.mask;
}
@Override
public int hashCode() {
return Objects.hashCode(value, mask);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2TernaryMatchParam other = (Bmv2TernaryMatchParam) obj;
return Objects.equal(this.value, other.value)
&& Objects.equal(this.mask, other.mask);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("value", value)
.add("mask", mask)
.toString();
}
}

View File

@ -1,79 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import java.util.Objects;
/**
* Representation of a BMv2 valid match parameter.
*/
@Beta
public final class Bmv2ValidMatchParam implements Bmv2MatchParam {
private final boolean flag;
/**
* Creates a new valid match parameter using the given boolean flag.
*
* @param flag a boolean value
*/
public Bmv2ValidMatchParam(boolean flag) {
this.flag = flag;
}
@Override
public Type type() {
return Type.VALID;
}
/**
* Returns the boolean flag of this parameter.
*
* @return a boolean value
*/
public boolean flag() {
return flag;
}
@Override
public int hashCode() {
return Objects.hashCode(flag);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final Bmv2ValidMatchParam other = (Bmv2ValidMatchParam) obj;
return this.flag == other.flag;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("flag", flag)
.toString();
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 runtime API.
*/
package org.onosproject.bmv2.api.runtime;

View File

@ -1,79 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.service;
import com.google.common.annotations.Beta;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.net.DeviceId;
/**
* A controller of BMv2 devices.
*/
@Beta
public interface Bmv2Controller {
/**
* Default port.
*/
int DEFAULT_PORT = 40123;
/**
* Return an agent to operate on the given device.
*
* @param deviceId a device ID
* @return a BMv2 agent
* @throws Bmv2RuntimeException if the agent is not available
*/
Bmv2DeviceAgent getAgent(DeviceId deviceId) throws Bmv2RuntimeException;
/**
* Returns true if the given device is reachable from this controller, false otherwise.
*
* @param deviceId a device ID
* @return a boolean value
*/
boolean isReacheable(DeviceId deviceId);
/**
* Register the given device listener.
*
* @param listener a device listener
*/
void addDeviceListener(Bmv2DeviceListener listener);
/**
* Unregister the given device listener.
*
* @param listener a device listener
*/
void removeDeviceListener(Bmv2DeviceListener listener);
/**
* Register the given packet listener.
*
* @param listener a packet listener
*/
void addPacketListener(Bmv2PacketListener listener);
/**
* Unregister the given packet listener.
*
* @param listener a packet listener
*/
void removePacketListener(Bmv2PacketListener listener);
}

View File

@ -1,66 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.service;
import com.google.common.annotations.Beta;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.net.DeviceId;
/**
* A service for managing BMv2 device contexts.
*/
@Beta
public interface Bmv2DeviceContextService {
/**
* Returns the context of the given device, null if no context has been previously set.
*
* @param deviceId a device ID
* @return a BMv2 device context
*/
Bmv2DeviceContext getContext(DeviceId deviceId);
/**
* Sets the context for the given device.
*
* @param deviceId a device ID
* @param context a BMv2 device context
*/
void setContext(DeviceId deviceId, Bmv2DeviceContext context);
/**
* Binds the given interpreter with the given class loader so that other ONOS instances in the cluster can properly
* load the interpreter.
*
* @param interpreterClass an interpreter class
* @param loader a class loader
*/
void registerInterpreterClassLoader(Class<? extends Bmv2Interpreter> interpreterClass, ClassLoader loader);
/**
* Returns the default context.
*
* @return a BMv2 device context
*/
Bmv2DeviceContext defaultContext();
/**
* Sets the default context for the given device.
*/
void setDefaultContext(DeviceId deviceId);
}

View File

@ -1,36 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.service;
import com.google.common.annotations.Beta;
import org.onosproject.bmv2.api.runtime.Bmv2Device;
/**
* A listener of BMv2 device events.
*/
@Beta
public interface Bmv2DeviceListener {
/**
* Handles a hello message.
*
* @param device the BMv2 device that originated the message
* @param instanceId the ID of the BMv2 process instance
* @param jsonConfigMd5 the MD5 sum of the JSON configuration currently running on the device
*/
void handleHello(Bmv2Device device, int instanceId, String jsonConfigMd5);
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.service;
import com.google.common.annotations.Beta;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.runtime.Bmv2Device;
/**
* A listener of BMv2 packet events.
*/
@Beta
public interface Bmv2PacketListener {
/**
* Handles a packet-in message.
*
* @param device the BMv2 device that originated the message
* @param inputPort the device port where the packet was received
* @param packet the packet raw data
*/
void handlePacketIn(Bmv2Device device, int inputPort, ImmutableByteSequence packet);
}

View File

@ -1,69 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.service;
import com.google.common.annotations.Beta;
import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
import org.onosproject.net.DeviceId;
/**
* A service for managing BMv2 table entries.
*/
@Beta
public interface Bmv2TableEntryService {
/**
* Returns a flow rule translator.
*
* @return a flow rule translator
*/
Bmv2FlowRuleTranslator getFlowRuleTranslator();
/**
* Binds the given ONOS flow rule with a BMv2 table entry reference.
*
* @param entryRef a table entry reference
* @param rule a BMv2 flow rule wrapper
*/
void bind(Bmv2TableEntryReference entryRef, Bmv2FlowRuleWrapper rule);
/**
* Returns the ONOS flow rule associated with the given BMv2 table entry reference, or null if there's no such a
* mapping.
*
* @param entryRef a table entry reference
* @return a BMv2 flow rule wrapper
*/
Bmv2FlowRuleWrapper lookup(Bmv2TableEntryReference entryRef);
/**
* Removes any flow rule previously bound with a given BMv2 table entry reference.
*
* @param entryRef a table entry reference
*/
void unbind(Bmv2TableEntryReference entryRef);
/**
* Removes all bindings for a given device.
*
* @param deviceId a device ID
*/
void unbindAll(DeviceId deviceId);
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 service API.
*/
package org.onosproject.bmv2.api.service;

View File

@ -1,115 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.utils;
import com.google.common.annotations.Beta;
import org.onlab.util.HexString;
import org.onlab.util.ImmutableByteSequence;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Collection of utility methods to deal with flow rule translation.
*/
@Beta
public final class Bmv2TranslatorUtils {
private Bmv2TranslatorUtils() {
// Ban constructor.
}
/**
* Returns the number of bytes necessary to contain the given bit-width.
*
* @param bitWidth an integer value
* @return an integer value
*/
public static int roundToBytes(int bitWidth) {
return (int) Math.ceil((double) bitWidth / 8);
}
/**
* Trims or expands the given byte sequence so to fit a given bit-width.
*
* @param original a byte sequence
* @param bitWidth an integer value
* @return a new byte sequence
* @throws ByteSequenceFitException if the byte sequence cannot be fitted in the given bit-width
*/
public static ImmutableByteSequence fitByteSequence(ImmutableByteSequence original, int bitWidth)
throws ByteSequenceFitException {
checkNotNull(original, "byte sequence cannot be null");
checkArgument(bitWidth > 0, "byte width must a non-zero positive integer");
int newByteWidth = roundToBytes(bitWidth);
if (original.size() == newByteWidth) {
// nothing to do
return original;
}
byte[] originalBytes = original.asArray();
if (newByteWidth > original.size()) {
// pad missing bytes with zeros
return ImmutableByteSequence.copyFrom(Arrays.copyOf(originalBytes, newByteWidth));
}
byte[] newBytes = new byte[newByteWidth];
// ImmutableByteSequence is always big-endian, hence check the array in reverse order
int diff = originalBytes.length - newByteWidth;
for (int i = originalBytes.length - 1; i > 0; i--) {
byte ob = originalBytes[i]; // original byte
byte nb; // new byte
if (i > diff) {
// no need to truncate, copy as is
nb = ob;
} else if (i == diff) {
// truncate this byte, check if we're loosing something
byte mask = (byte) ((1 >> ((bitWidth % 8) + 1)) - 1);
if ((ob & ~mask) != 0) {
throw new ByteSequenceFitException(originalBytes, bitWidth);
} else {
nb = (byte) (ob & mask);
}
} else {
// drop this byte, check if we're loosing something
if (originalBytes[i] != 0) {
throw new ByteSequenceFitException(originalBytes, bitWidth);
} else {
continue;
}
}
newBytes[i - diff] = nb;
}
return ImmutableByteSequence.copyFrom(newBytes);
}
/**
* A byte sequence fit exception.
*/
public static class ByteSequenceFitException extends Exception {
public ByteSequenceFitException(byte[] bytes, int bitWidth) {
super("cannot fit " + HexString.toHexString(bytes) + " into a " + bitWidth + " bits value");
}
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 utils.
*/
package org.onosproject.bmv2.api.utils;

View File

@ -1,162 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.context;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.google.common.testing.EqualsTester;
import org.hamcrest.collection.IsIterableContainingInOrder;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsEqual.equalTo;
/**
* BMv2 JSON configuration parser test.
*/
public class Bmv2ConfigurationTest {
private JsonObject json;
private JsonObject json2;
@Before
public void setUp() throws Exception {
json = Json.parse(new BufferedReader(new InputStreamReader(
this.getClass().getResourceAsStream("/simple.json")))).asObject();
json2 = Json.parse(new BufferedReader(new InputStreamReader(
this.getClass().getResourceAsStream("/simple.json")))).asObject();
}
@Test
public void testParse() throws Exception {
Bmv2Configuration config = Bmv2DefaultConfiguration.parse(json);
Bmv2Configuration config2 = Bmv2DefaultConfiguration.parse(json2);
new EqualsTester()
.addEqualityGroup(config, config2)
.testEquals();
/* Check header types */
Bmv2HeaderTypeModel stdMetaT = config.headerType("standard_metadata_t");
Bmv2HeaderTypeModel ethernetT = config.headerType("ethernet_t");
Bmv2HeaderTypeModel intrinsicMetaT = config.headerType("intrinsic_metadata_t");
Bmv2HeaderTypeModel stdMetaT2 = config2.headerType("standard_metadata_t");
Bmv2HeaderTypeModel ethernetT2 = config2.headerType("ethernet_t");
Bmv2HeaderTypeModel intrinsicMetaT2 = config2.headerType("intrinsic_metadata_t");
new EqualsTester()
.addEqualityGroup(stdMetaT, stdMetaT2)
.addEqualityGroup(ethernetT, ethernetT2)
.addEqualityGroup(intrinsicMetaT, intrinsicMetaT2)
.testEquals();
// existence
assertThat("Json parsed value is null", stdMetaT, notNullValue());
assertThat("Json parsed value is null", ethernetT, notNullValue());
assertThat("Json parsed value is null", intrinsicMetaT, notNullValue());
// fields size
assertThat("Incorrect size for header type fields",
stdMetaT.fields(), hasSize(8));
assertThat("Incorrect size for header type fields",
ethernetT.fields(), hasSize(3));
assertThat("Incorrect size for header type fields",
intrinsicMetaT.fields(), hasSize(4));
// check that fields are in order
assertThat("Incorrect order for header type fields",
stdMetaT.fields(), IsIterableContainingInOrder.contains(
stdMetaT.field("ingress_port"),
stdMetaT.field("packet_length"),
stdMetaT.field("egress_spec"),
stdMetaT.field("egress_port"),
stdMetaT.field("egress_instance"),
stdMetaT.field("instance_type"),
stdMetaT.field("clone_spec"),
stdMetaT.field("_padding")));
/* Check actions */
Bmv2ActionModel floodAction = config.action("flood");
Bmv2ActionModel dropAction = config.action("_drop");
Bmv2ActionModel fwdAction = config.action("set_egress_port");
Bmv2ActionModel floodAction2 = config2.action("flood");
Bmv2ActionModel dropAction2 = config2.action("_drop");
Bmv2ActionModel fwdAction2 = config2.action("set_egress_port");
new EqualsTester()
.addEqualityGroup(floodAction, floodAction2)
.addEqualityGroup(dropAction, dropAction2)
.addEqualityGroup(fwdAction, fwdAction2)
.testEquals();
// existence
assertThat("Json parsed value is null", floodAction, notNullValue());
assertThat("Json parsed value is null", dropAction, notNullValue());
assertThat("Json parsed value is null", fwdAction, notNullValue());
// runtime data size
assertThat("Incorrect size for action runtime data",
floodAction.runtimeDatas().size(), is(equalTo(0)));
assertThat("Incorrect size for action runtime data",
dropAction.runtimeDatas().size(), is(equalTo(0)));
assertThat("Incorrect size for action runtime data",
fwdAction.runtimeDatas().size(), is(equalTo(1)));
// runtime data existence and parsing
assertThat("Parsed Json value is null",
fwdAction.runtimeData("port"), notNullValue());
assertThat("Incorrect value for action runtime data bitwidth",
fwdAction.runtimeData("port").bitWidth(), is(equalTo(9)));
/* Check tables */
Bmv2TableModel table0 = config.table(0);
Bmv2TableModel table02 = config2.table(0);
new EqualsTester()
.addEqualityGroup(table0, table02)
.testEquals();
// existence
assertThat("Parsed Json value is null", table0, notNullValue());
// id and name correspondence
assertThat("Incorrect value for table name",
table0.name(), is(equalTo("table0")));
// keys size
assertThat("Incorrect size for table keys",
table0.keys().size(), is(equalTo(4)));
// key match type
assertThat("Incorrect value for table key match type",
table0.keys().get(0).matchType(), is(equalTo(Bmv2MatchParam.Type.TERNARY)));
// header type
assertThat("Incorrect value for table key header type",
table0.keys().get(0).field().header().type(), is(equalTo(stdMetaT)));
}
}

View File

@ -1,137 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.api.runtime;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.google.common.testing.EqualsTester;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.MacAddress;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class Bmv2ExtensionsTest {
private Bmv2Configuration config;
@Before
public void setUp() throws Exception {
JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
this.getClass().getResourceAsStream("/simple.json")))).asObject();
config = Bmv2DefaultConfiguration.parse(json);
}
@Test
public void testExtensionSelectorBuilder() throws Exception {
Bmv2ExtensionSelector extSelectorExact = Bmv2ExtensionSelector.builder()
.forConfiguration(config)
.matchExact("standard_metadata", "ingress_port", (short) 255)
.matchExact("ethernet", "etherType", 512)
.matchExact("ethernet", "dstAddr", 1024L)
.matchExact("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes())
.build();
Bmv2ExtensionSelector extSelectorTernary = Bmv2ExtensionSelector.builder()
.forConfiguration(config)
.matchTernary("standard_metadata", "ingress_port", (short) 255, (short) 255)
.matchTernary("ethernet", "etherType", 512, 512)
.matchTernary("ethernet", "dstAddr", 1024L, 1024L)
.matchTernary("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes(), MacAddress.NONE.toBytes())
.build();
Bmv2ExtensionSelector extSelectorLpm = Bmv2ExtensionSelector.builder()
.forConfiguration(config)
.matchLpm("standard_metadata", "ingress_port", (short) 255, 1)
.matchLpm("ethernet", "etherType", 512, 2)
.matchLpm("ethernet", "dstAddr", 1024L, 3)
.matchLpm("ethernet", "srcAddr", MacAddress.BROADCAST.toBytes(), 4)
.build();
Bmv2ExtensionSelector extSelectorValid = Bmv2ExtensionSelector.builder()
.forConfiguration(config)
.matchValid("standard_metadata", "ingress_port", true)
.matchValid("ethernet", "etherType", true)
.matchValid("ethernet", "dstAddr", false)
.matchValid("ethernet", "srcAddr", false)
.build();
assertThat(extSelectorExact.parameterMap().size(), is(4));
assertThat(extSelectorTernary.parameterMap().size(), is(4));
assertThat(extSelectorLpm.parameterMap().size(), is(4));
assertThat(extSelectorValid.parameterMap().size(), is(4));
// TODO add more tests, e.g. check for byte sequences content and size.
}
@Test
public void testExtensionTreatmentBuilder() throws Exception {
Bmv2ExtensionTreatment treatment = Bmv2ExtensionTreatment.builder()
.forConfiguration(config)
.setActionName("set_egress_port")
.addParameter("port", 1)
.build();
assertThat(treatment.action().parameters().size(), is(1));
// TODO add more tests, e.g. check for byte sequences content and size.
}
@Test
public void testExtensionSelectorSerialization() throws Exception {
Bmv2ExtensionSelector original = Bmv2ExtensionSelector.builder()
.forConfiguration(config)
.matchExact("standard_metadata", "ingress_port", (short) 255)
.matchLpm("ethernet", "etherType", 512, 4)
.matchTernary("ethernet", "dstAddr", 1024L, 512L)
.matchValid("ethernet", "srcAddr", true)
.build();
Bmv2ExtensionSelector other = Bmv2ExtensionSelector.empty();
other.deserialize(original.serialize());
new EqualsTester()
.addEqualityGroup(original, other)
.testEquals();
}
@Test
public void testExtensionTreatmentSerialization() throws Exception {
Bmv2ExtensionTreatment original = Bmv2ExtensionTreatment.builder()
.forConfiguration(config)
.setActionName("set_egress_port")
.addParameter("port", 1)
.build();
Bmv2ExtensionTreatment other = Bmv2ExtensionTreatment.empty();
other.deserialize(original.serialize());
new EqualsTester()
.addEqualityGroup(original, other)
.testEquals();
}
}

View File

@ -1,397 +0,0 @@
{
"header_types": [
{
"name": "standard_metadata_t",
"id": 0,
"fields": [
[
"ingress_port",
9
],
[
"packet_length",
32
],
[
"egress_spec",
9
],
[
"egress_port",
9
],
[
"egress_instance",
32
],
[
"instance_type",
32
],
[
"clone_spec",
32
],
[
"_padding",
5
]
],
"length_exp": null,
"max_length": null
},
{
"name": "ethernet_t",
"id": 1,
"fields": [
[
"dstAddr",
48
],
[
"srcAddr",
48
],
[
"etherType",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "intrinsic_metadata_t",
"id": 2,
"fields": [
[
"ingress_global_timestamp",
32
],
[
"lf_field_list",
32
],
[
"mcast_grp",
16
],
[
"egress_rid",
16
]
],
"length_exp": null,
"max_length": null
}
],
"headers": [
{
"name": "standard_metadata",
"id": 0,
"header_type": "standard_metadata_t",
"metadata": true
},
{
"name": "ethernet",
"id": 1,
"header_type": "ethernet_t",
"metadata": false
},
{
"name": "intrinsic_metadata",
"id": 2,
"header_type": "intrinsic_metadata_t",
"metadata": true
}
],
"header_stacks": [],
"parsers": [
{
"name": "parser",
"id": 0,
"init_state": "start",
"parse_states": [
{
"name": "start",
"id": 0,
"parser_ops": [],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": "parse_ethernet"
}
]
},
{
"name": "parse_ethernet",
"id": 1,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "ethernet"
}
]
}
],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": null
}
]
}
]
}
],
"deparsers": [
{
"name": "deparser",
"id": 0,
"order": [
"ethernet"
]
}
],
"meter_arrays": [],
"actions": [
{
"name": "set_egress_port",
"id": 0,
"runtime_data": [
{
"name": "port",
"bitwidth": 9
}
],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "runtime_data",
"value": 0
}
]
}
]
},
{
"name": "_drop",
"id": 1,
"runtime_data": [],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "hexstr",
"value": "0x1ff"
}
]
}
]
},
{
"name": "flood",
"id": 2,
"runtime_data": [],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"intrinsic_metadata",
"mcast_grp"
]
},
{
"type": "field",
"value": [
"standard_metadata",
"ingress_port"
]
}
]
}
]
},
{
"name": "send_to_cpu",
"id": 3,
"runtime_data": [],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "hexstr",
"value": "0xff"
}
]
}
]
}
],
"pipelines": [
{
"name": "ingress",
"id": 0,
"init_table": "table0",
"tables": [
{
"name": "table0",
"id": 0,
"match_type": "ternary",
"type": "simple",
"max_size": 16384,
"with_counters": false,
"direct_meters": null,
"support_timeout": false,
"key": [
{
"match_type": "ternary",
"target": [
"standard_metadata",
"ingress_port"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"dstAddr"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"srcAddr"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"etherType"
],
"mask": null
}
],
"actions": [
"set_egress_port",
"flood",
"send_to_cpu",
"_drop"
],
"next_tables": {
"set_egress_port": null,
"flood": null,
"send_to_cpu": null,
"_drop": null
},
"default_action": null,
"base_default_next": null
}
],
"conditionals": []
},
{
"name": "egress",
"id": 1,
"init_table": null,
"tables": [],
"conditionals": []
}
],
"calculations": [],
"checksums": [],
"learn_lists": [],
"field_lists": [],
"counter_arrays": [],
"register_arrays": [],
"force_arith": [
[
"standard_metadata",
"ingress_port"
],
[
"standard_metadata",
"packet_length"
],
[
"standard_metadata",
"egress_spec"
],
[
"standard_metadata",
"egress_port"
],
[
"standard_metadata",
"egress_instance"
],
[
"standard_metadata",
"instance_type"
],
[
"standard_metadata",
"clone_spec"
],
[
"standard_metadata",
"_padding"
],
[
"intrinsic_metadata",
"ingress_global_timestamp"
],
[
"intrinsic_metadata",
"lf_field_list"
],
[
"intrinsic_metadata",
"mcast_grp"
],
[
"intrinsic_metadata",
"egress_rid"
]
]
}

View File

@ -1,73 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-bmv2-protocol</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-protocol-ctl</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.3</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-protocol-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-protocol-thrift-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.onosproject</groupId>
<artifactId>onos-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,165 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.thrift.TProcessor;
import org.apache.thrift.server.TThreadedSelectorServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TNonblockingServerTransport;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
/**
* A Thrift TThreadedSelectorServer that keeps track of the clients' IP address.
*/
final class Bmv2ControlPlaneThriftServer extends TThreadedSelectorServer {
private static final int MAX_WORKER_THREADS = 20;
private static final int MAX_SELECTOR_THREADS = 4;
private static final int ACCEPT_QUEUE_LEN = 8;
private final Map<TTransport, InetAddress> clientAddresses = Maps.newConcurrentMap();
private final Set<TrackingSelectorThread> selectorThreads = Sets.newHashSet();
private AcceptThread acceptThread;
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* Creates a new server.
*
* @param port a listening port
* @param processor a processor
* @param executorService an executor service
* @throws TTransportException
*/
public Bmv2ControlPlaneThriftServer(int port, TProcessor processor, ExecutorService executorService)
throws TTransportException {
super(new TThreadedSelectorServer.Args(new TNonblockingServerSocket(port))
.workerThreads(MAX_WORKER_THREADS)
.selectorThreads(MAX_SELECTOR_THREADS)
.acceptQueueSizePerThread(ACCEPT_QUEUE_LEN)
.executorService(executorService)
.processor(processor));
}
/**
* Returns the IP address of the client associated with the given input framed transport.
*
* @param inputTransport a framed transport instance
* @return the IP address of the client or null
*/
InetAddress getClientAddress(TFramedTransport inputTransport) {
return clientAddresses.get(inputTransport);
}
@Override
protected boolean startThreads() {
try {
for (int i = 0; i < MAX_SELECTOR_THREADS; ++i) {
selectorThreads.add(new TrackingSelectorThread(ACCEPT_QUEUE_LEN));
}
acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_,
createSelectorThreadLoadBalancer(selectorThreads));
selectorThreads.forEach(Thread::start);
acceptThread.start();
return true;
} catch (IOException e) {
log.error("Failed to start threads!", e);
return false;
}
}
@Override
protected void joinThreads() throws InterruptedException {
// Wait until the io threads exit.
acceptThread.join();
for (TThreadedSelectorServer.SelectorThread thread : selectorThreads) {
thread.join();
}
}
@Override
public void stop() {
stopped_ = true;
// Stop queuing connect attempts asap.
stopListening();
if (acceptThread != null) {
acceptThread.wakeupSelector();
}
if (selectorThreads != null) {
selectorThreads.stream()
.filter(thread -> thread != null)
.forEach(TrackingSelectorThread::wakeupSelector);
}
}
private class TrackingSelectorThread extends TThreadedSelectorServer.SelectorThread {
TrackingSelectorThread(int maxPendingAccepts) throws IOException {
super(maxPendingAccepts);
}
@Override
protected FrameBuffer createFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey,
AbstractSelectThread selectThread) {
TrackingFrameBuffer frameBuffer = new TrackingFrameBuffer(trans, selectionKey, selectThread);
if (trans instanceof TNonblockingSocket) {
try {
SocketChannel socketChannel = ((TNonblockingSocket) trans).getSocketChannel();
InetAddress addr = ((InetSocketAddress) socketChannel.getRemoteAddress()).getAddress();
clientAddresses.put(frameBuffer.getInputFramedTransport(), addr);
} catch (IOException e) {
log.warn("Exception while tracking client address", e);
clientAddresses.remove(frameBuffer.getInputFramedTransport());
}
} else {
log.warn("Unknown TNonblockingTransport instance: {}", trans.getClass().getName());
clientAddresses.remove(frameBuffer.getInputFramedTransport());
}
return frameBuffer;
}
}
private class TrackingFrameBuffer extends FrameBuffer {
TrackingFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey,
AbstractSelectThread selectThread) {
super(trans, selectionKey, selectThread);
}
TTransport getInputFramedTransport() {
return this.inTrans_;
}
}
}

View File

@ -1,326 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.tuple.Pair;
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.apache.felix.scr.annotations.Service;
import org.apache.thrift.TException;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TMultiplexedProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.runtime.Bmv2Device;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.bmv2.api.service.Bmv2DeviceListener;
import org.onosproject.bmv2.api.service.Bmv2PacketListener;
import org.onosproject.bmv2.thriftapi.BmConfig;
import org.onosproject.bmv2.thriftapi.ControlPlaneService;
import org.onosproject.bmv2.thriftapi.SimpleSwitch;
import org.onosproject.bmv2.thriftapi.Standard;
import org.onosproject.core.CoreService;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.bmv2.thriftapi.ControlPlaneService.Processor;
/**
* Default implementation of a BMv2 controller.
*/
@Component(immediate = true)
@Service
public class Bmv2ControllerImpl implements Bmv2Controller {
private static final String APP_ID = "org.onosproject.bmv2";
// Seconds after a client is expired (and connection closed) in the cache.
private static final int CLIENT_CACHE_TIMEOUT = 60;
// Number of connection retries after a network error.
private static final int NUM_CONNECTION_RETRIES = 2;
// Time between retries in milliseconds.
private static final int TIME_BETWEEN_RETRIES = 10;
private final Logger log = LoggerFactory.getLogger(this.getClass());
// Cache where clients are removed after a predefined timeout.
private final LoadingCache<DeviceId, Pair<TTransport, Bmv2DeviceThriftClient>> agentCache =
CacheBuilder.newBuilder()
.expireAfterAccess(CLIENT_CACHE_TIMEOUT, TimeUnit.SECONDS)
.removalListener(new ClientRemovalListener())
.build(new ClientLoader());
private final TProcessor trackingProcessor = new TrackingProcessor();
private final ExecutorService executorService = Executors
.newFixedThreadPool(32, groupedThreads("onos/bmv2", "controller", log));
private final Set<Bmv2DeviceListener> deviceListeners = new CopyOnWriteArraySet<>();
private final Set<Bmv2PacketListener> packetListeners = new CopyOnWriteArraySet<>();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private Bmv2ControlPlaneThriftServer server;
// TODO: make it configurable trough component config
private int serverPort = DEFAULT_PORT;
@Activate
public void activate() {
coreService.registerApplication(APP_ID);
startServer(serverPort);
log.info("Activated");
}
@Deactivate
public void deactivate() {
stopServer();
log.info("Deactivated");
}
private void startServer(int port) {
try {
log.info("Starting server on port {}...", port);
this.server = new Bmv2ControlPlaneThriftServer(port, trackingProcessor, executorService);
executorService.execute(server::serve);
} catch (TTransportException e) {
log.error("Unable to start server", e);
}
}
private void stopServer() {
// Stop the server if running...
if (server != null && !server.isServing()) {
server.setShouldStop(true);
server.stop();
}
try {
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
List<Runnable> runningTasks = executorService.shutdownNow();
log.warn("Unable to stop server threads: {}", runningTasks);
}
}
@Override
public Bmv2DeviceAgent getAgent(DeviceId deviceId) throws Bmv2RuntimeException {
try {
checkNotNull(deviceId, "deviceId cannot be null");
return agentCache.get(deviceId).getRight();
} catch (ExecutionException e) {
throw new Bmv2RuntimeException(e);
}
}
@Override
public boolean isReacheable(DeviceId deviceId) {
try {
Bmv2DeviceThriftClient client = (Bmv2DeviceThriftClient) getAgent(deviceId);
BmConfig config = client.standardClient.bm_mgmt_get_info();
// The BMv2 instance running at this thrift IP and port might have a different BMv2 internal ID.
return config.getDevice_id() == Integer.valueOf(deviceId.uri().getFragment());
} catch (Bmv2RuntimeException | TException e) {
return false;
}
}
@Override
public void addDeviceListener(Bmv2DeviceListener listener) {
if (!deviceListeners.contains(listener)) {
deviceListeners.add(listener);
}
}
@Override
public void removeDeviceListener(Bmv2DeviceListener listener) {
deviceListeners.remove(listener);
}
@Override
public void addPacketListener(Bmv2PacketListener listener) {
if (!packetListeners.contains(listener)) {
packetListeners.add(listener);
}
}
@Override
public void removePacketListener(Bmv2PacketListener listener) {
packetListeners.remove(listener);
}
/**
* Client cache removal listener. Close the connection on cache removal.
*/
private static class ClientRemovalListener implements
RemovalListener<DeviceId, Pair<TTransport, Bmv2DeviceThriftClient>> {
@Override
public void onRemoval(RemovalNotification<DeviceId, Pair<TTransport, Bmv2DeviceThriftClient>> notification) {
// close the transport connection
Bmv2DeviceThriftClient client = notification.getValue().getRight();
TTransport transport = notification.getValue().getLeft();
// Locking here is ugly, but needed (see SafeThriftClient).
synchronized (transport) {
if (transport.isOpen()) {
transport.close();
}
}
}
}
/**
* Handles requests from BMv2 devices using the registered listeners.
*/
private final class ServiceHandler implements ControlPlaneService.Iface {
private final InetAddress clientAddress;
private Bmv2Device remoteDevice;
ServiceHandler(InetAddress clientAddress) {
this.clientAddress = clientAddress;
}
@Override
public boolean ping() {
return true;
}
@Override
public void hello(int thriftServerPort, int deviceId, int instanceId, String jsonConfigMd5) {
// Store a reference to the remote device for future uses.
String host = clientAddress.getHostAddress();
remoteDevice = new Bmv2Device(host, thriftServerPort, deviceId);
if (deviceListeners.size() == 0) {
log.debug("Received hello, but there's no listener registered.");
} else {
deviceListeners.forEach(l -> l.handleHello(remoteDevice, instanceId, jsonConfigMd5));
}
}
@Override
public void packet_in(int port, ByteBuffer data, int dataLength) {
if (remoteDevice == null) {
log.debug("Received packet-in, but the remote device is still unknown. Need a hello first...");
return;
}
if (packetListeners.size() == 0) {
log.debug("Received packet-in, but there's no listener registered.");
} else {
byte[] bytes = new byte[dataLength];
data.get(bytes);
ImmutableByteSequence pkt = ImmutableByteSequence.copyFrom(bytes);
packetListeners.forEach(l -> l.handlePacketIn(remoteDevice, port, pkt));
}
}
}
/**
* Decorator of a Thrift processor needed in order to keep track of the client's IP address that originated the
* request.
*/
private final class TrackingProcessor implements TProcessor {
// Map transports to processors.
private final ConcurrentMap<TTransport, Processor<ServiceHandler>> processors = Maps.newConcurrentMap();
@Override
public boolean process(final TProtocol in, final TProtocol out) throws TException {
// Get the client address for this request.
InetAddress clientAddress = server.getClientAddress((TFramedTransport) in.getTransport());
if (clientAddress != null) {
// Get or create a processor for this input transport, i.e. the client on the other side.
Processor<ServiceHandler> processor = processors.computeIfAbsent(
in.getTransport(), t -> new Processor<>(new ServiceHandler(clientAddress)));
// Delegate to the processor we are decorating.
return processor.process(in, out);
} else {
log.warn("Unable to retrieve client IP address of incoming request");
return false;
}
}
}
/**
* Cache loader of BMv2 Thrift clients.
*/
private class ClientLoader extends CacheLoader<DeviceId, Pair<TTransport, Bmv2DeviceThriftClient>> {
private final SafeThriftClient.Options options = new SafeThriftClient.Options(NUM_CONNECTION_RETRIES,
TIME_BETWEEN_RETRIES);
@Override
public Pair<TTransport, Bmv2DeviceThriftClient> load(DeviceId deviceId)
throws TTransportException {
log.debug("Instantiating new client... > deviceId={}", deviceId);
// Make the expensive call
Bmv2Device device = Bmv2Device.of(deviceId);
TTransport transport = new TSocket(device.thriftServerHost(), device.thriftServerPort());
TProtocol protocol = new TBinaryProtocol(transport);
// Our BMv2 device implements multiple Thrift services, create a client for each one on the same transport.
Standard.Client standardClient = new Standard.Client(
new TMultiplexedProtocol(protocol, "standard"));
SimpleSwitch.Client simpleSwitch = new SimpleSwitch.Client(
new TMultiplexedProtocol(protocol, "simple_switch"));
// Wrap clients so to automatically have synchronization and resiliency to connectivity errors
Standard.Iface safeStandardClient = SafeThriftClient.wrap(standardClient,
Standard.Iface.class,
options);
SimpleSwitch.Iface safeSimpleSwitchClient = SafeThriftClient.wrap(simpleSwitch,
SimpleSwitch.Iface.class,
options);
Bmv2DeviceThriftClient client = new Bmv2DeviceThriftClient(deviceId,
transport,
safeStandardClient,
safeSimpleSwitchClient);
return Pair.of(transport, client);
}
}
}

View File

@ -1,117 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import com.google.common.collect.ImmutableBiMap;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.bmv2.api.context.Bmv2InterpreterException;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.instructions.Instruction;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.ByteSequenceFitException;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
import static org.onosproject.net.PortNumber.CONTROLLER;
import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
/**
* Implementation of a BMv2 interpreter for the BMv2 default.json configuration.
*/
public final class Bmv2DefaultInterpreterImpl implements Bmv2Interpreter {
public static final String TABLE0 = "table0";
public static final String SEND_TO_CPU = "send_to_cpu";
public static final String PORT = "port";
public static final String DROP = "_drop";
public static final String SET_EGRESS_PORT = "set_egress_port";
private static final ImmutableBiMap<Criterion.Type, String> CRITERION_MAP = ImmutableBiMap.of(
Criterion.Type.IN_PORT, "standard_metadata.ingress_port",
Criterion.Type.ETH_DST, "ethernet.dstAddr",
Criterion.Type.ETH_SRC, "ethernet.srcAddr",
Criterion.Type.ETH_TYPE, "ethernet.etherType");
private static final ImmutableBiMap<Integer, String> TABLE_MAP = ImmutableBiMap.of(
0, TABLE0);
@Override
public ImmutableBiMap<Criterion.Type, String> criterionTypeMap() {
return CRITERION_MAP;
}
@Override
public ImmutableBiMap<Integer, String> tableIdMap() {
return TABLE_MAP;
}
@Override
public Bmv2Action mapTreatment(TrafficTreatment treatment, Bmv2Configuration configuration)
throws Bmv2InterpreterException {
if (treatment.allInstructions().size() == 0) {
// No instructions means drop for us.
return actionWithName(DROP);
} else if (treatment.allInstructions().size() > 1) {
// Otherwise, we understand treatments with only 1 instruction.
throw new Bmv2InterpreterException("Treatment has multiple instructions");
}
Instruction instruction = treatment.allInstructions().get(0);
switch (instruction.type()) {
case OUTPUT:
OutputInstruction outInstruction = (OutputInstruction) instruction;
PortNumber port = outInstruction.port();
if (!port.isLogical()) {
return buildEgressAction(port, configuration);
} else if (port.equals(CONTROLLER)) {
return actionWithName(SEND_TO_CPU);
} else {
throw new Bmv2InterpreterException("Egress on logical port not supported: " + port);
}
case NOACTION:
return actionWithName(DROP);
default:
throw new Bmv2InterpreterException("Instruction type not supported: " + instruction.type().name());
}
}
private Bmv2Action buildEgressAction(PortNumber port, Bmv2Configuration configuration)
throws Bmv2InterpreterException {
int portBitWidth = configuration.action(SET_EGRESS_PORT).runtimeData(PORT).bitWidth();
try {
ImmutableByteSequence portBs = fitByteSequence(copyFrom(port.toLong()), portBitWidth);
return Bmv2Action.builder()
.withName(SET_EGRESS_PORT)
.addParameter(portBs)
.build();
} catch (ByteSequenceFitException e) {
throw new Bmv2InterpreterException(e.getMessage());
}
}
private Bmv2Action actionWithName(String name) {
return Bmv2Action.builder().withName(name).build();
}
}

View File

@ -1,285 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.google.common.collect.Maps;
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.apache.felix.scr.annotations.Service;
import org.onlab.util.KryoNamespace;
import org.onlab.util.SharedScheduledExecutors;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.ConsistentMap;
import org.onosproject.store.service.ConsistentMapException;
import org.onosproject.store.service.MapEvent;
import org.onosproject.store.service.MapEventListener;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.Versioned;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration.parse;
import static org.onosproject.store.service.MapEvent.Type.INSERT;
import static org.onosproject.store.service.MapEvent.Type.UPDATE;
@Component(immediate = true)
@Service
public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService {
private static final String JSON_DEFAULT_CONFIG_PATH = "/default.json";
private static final long CHECK_INTERVAL = 5_000; // milliseconds
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private Bmv2Controller controller;
private final ScheduledExecutorService scheduledExecutor = SharedScheduledExecutors.getPoolThreadExecutor();
private final MapEventListener<DeviceId, Bmv2DeviceContext> contextListener = new ContextMapEventListener();
private final ConcurrentMap<DeviceId, Lock> deviceLocks = Maps.newConcurrentMap();
private ConsistentMap<DeviceId, Bmv2DeviceContext> contexts;
private Map<String, ClassLoader> interpreterClassLoaders;
private Bmv2DeviceContext defaultContext;
private ScheduledFuture<?> configChecker = null;
private final Logger log = LoggerFactory.getLogger(getClass());
@Activate
public void activate() {
KryoNamespace kryo = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(new BmvDeviceContextSerializer(), Bmv2DeviceContext.class)
.build();
this.contexts = storageService.<DeviceId, Bmv2DeviceContext>consistentMapBuilder()
.withSerializer(Serializer.using(kryo))
.withName("onos-bmv2-contexts")
.build();
Bmv2Configuration defaultConfiguration = loadDefaultConfiguration();
Bmv2Interpreter defaultInterpreter = new Bmv2DefaultInterpreterImpl();
defaultContext = new Bmv2DeviceContext(defaultConfiguration, defaultInterpreter);
interpreterClassLoaders = Maps.newConcurrentMap();
registerInterpreterClassLoader(defaultInterpreter.getClass(), this.getClass().getClassLoader());
contexts.addListener(contextListener, scheduledExecutor);
if (configChecker != null && configChecker.isCancelled()) {
configChecker.cancel(false);
}
configChecker = scheduledExecutor.scheduleAtFixedRate(this::checkDevices, 0, CHECK_INTERVAL,
TimeUnit.MILLISECONDS);
log.info("Started");
}
@Deactivate
public void deactivate() {
contexts.removeListener(contextListener);
if (configChecker != null) {
configChecker.cancel(false);
}
log.info("Stopped");
}
@Override
public Bmv2DeviceContext getContext(DeviceId deviceId) {
checkNotNull(deviceId, "device id cannot be null");
Versioned<Bmv2DeviceContext> versionedContext = contexts.get(deviceId);
return (versionedContext == null) ? null : versionedContext.value();
}
@Override
public void setContext(DeviceId deviceId, Bmv2DeviceContext context) {
checkNotNull(deviceId, "device id cannot be null");
checkNotNull(context, "context cannot be null");
if (!interpreterClassLoaders.containsKey(context.interpreter().getClass().getName())) {
log.error("Unable to set context, missing class loader for interpreter '{}'. " +
"Please register it with registerInterpreterClassLoader()",
context.interpreter().getClass().getName());
} else {
try {
contexts.put(deviceId, context);
} catch (ConsistentMapException.ConcurrentModification e) {
log.error("Detected concurrent modification on context map");
}
}
}
@Override
public void registerInterpreterClassLoader(Class<? extends Bmv2Interpreter> interpreterClass, ClassLoader loader) {
interpreterClassLoaders.put(interpreterClass.getName(), loader);
}
@Override
public Bmv2DeviceContext defaultContext() {
return defaultContext;
}
@Override
public void setDefaultContext(DeviceId deviceId) {
Versioned<Bmv2DeviceContext> previous = contexts.put(deviceId, defaultContext);
if (mastershipService.getMasterFor(deviceId) == null) {
// Checking for who is the master here is ugly but necessary, as this method is called by Bmv2DeviceProvider
// prior to master election. A solution could be to use a separate leadership contest instead of the
// mastership service.
triggerConfigCheck(deviceId, defaultContext);
}
}
private void configCheck(DeviceId deviceId, Bmv2DeviceContext storedContext) {
if (storedContext == null) {
return;
}
// Synchronize executions over the same deviceId.
Lock lock = deviceLocks.computeIfAbsent(deviceId, did -> new ReentrantLock());
lock.lock();
try {
log.trace("Executing configuration check on {}...", deviceId);
try {
// FIXME: JSON dump is heavy, can we use the JSON MD5 to check the running configuration?
String jsonString = controller.getAgent(deviceId).dumpJsonConfig();
Bmv2Configuration deviceConfiguration = parse(Json.parse(jsonString).asObject());
if (!storedContext.configuration().equals(deviceConfiguration)) {
log.info("Triggering configuration swap on {}...", deviceId);
try {
Bmv2DeviceAgent agent = controller.getAgent(deviceId);
String newJsonString = storedContext.configuration().json().toString();
agent.uploadNewJsonConfig(newJsonString);
agent.swapJsonConfig();
} catch (Bmv2RuntimeException e) {
log.error("Unable to swap configuration on {}: {}", deviceId, e.explain());
}
}
} catch (Bmv2RuntimeException e) {
log.warn("Unable to dump JSON configuration from {}: {}", deviceId, e.explain());
}
} finally {
lock.unlock();
}
}
private void triggerConfigCheck(DeviceId deviceId, Bmv2DeviceContext context) {
scheduledExecutor.schedule(() -> configCheck(deviceId, context), 0, TimeUnit.SECONDS);
}
private void checkDevices() {
deviceService.getAvailableDevices().forEach(device -> {
if (mastershipService.isLocalMaster(device.id())) {
triggerConfigCheck(device.id(), getContext(device.id()));
}
});
}
protected static Bmv2DefaultConfiguration loadDefaultConfiguration() {
try {
JsonObject json = Json.parse(new BufferedReader(new InputStreamReader(
Bmv2DeviceContextServiceImpl.class.getResourceAsStream(JSON_DEFAULT_CONFIG_PATH)))).asObject();
return parse(json);
} catch (IOException e) {
throw new RuntimeException("Unable to load default configuration", e);
}
}
/**
* Listener of context changes that immediately triggers config checks (to swap the config if necessary).
*/
private class ContextMapEventListener implements MapEventListener<DeviceId, Bmv2DeviceContext> {
@Override
public void event(MapEvent<DeviceId, Bmv2DeviceContext> event) {
DeviceId deviceId = event.key();
if (event.type().equals(INSERT) || event.type().equals(UPDATE)) {
if (mastershipService.isLocalMaster(deviceId)) {
log.trace("Context {} for {}", event.type().name(), deviceId);
triggerConfigCheck(deviceId, event.newValue().value());
}
}
}
}
/**
* Context serializer.
*/
private class BmvDeviceContextSerializer extends com.esotericsoftware.kryo.Serializer<Bmv2DeviceContext> {
@Override
public void write(Kryo kryo, Output output, Bmv2DeviceContext context) {
kryo.writeObject(output, context.configuration().json().toString());
kryo.writeObject(output, context.interpreter().getClass().getName());
}
@Override
public Bmv2DeviceContext read(Kryo kryo, Input input, Class<Bmv2DeviceContext> type) {
String jsonStr = kryo.readObject(input, String.class);
String interpreterClassName = kryo.readObject(input, String.class);
Bmv2Configuration configuration = parse(Json.parse(jsonStr).asObject());
ClassLoader loader = interpreterClassLoaders.get(interpreterClassName);
if (loader == null) {
throw new IllegalStateException("No class loader registered for interpreter: " + interpreterClassName);
}
try {
Bmv2Interpreter interpreter = (Bmv2Interpreter) loader.loadClass(interpreterClassName).newInstance();
return new Bmv2DeviceContext(configuration, interpreter);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
throw new RuntimeException("Unable to load interpreter class", e);
}
}
}
}

View File

@ -1,486 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransport;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2ParsedTableEntry;
import org.onosproject.bmv2.api.runtime.Bmv2PortInfo;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam;
import org.onosproject.bmv2.thriftapi.BmActionEntry;
import org.onosproject.bmv2.thriftapi.BmAddEntryOptions;
import org.onosproject.bmv2.thriftapi.BmCounterValue;
import org.onosproject.bmv2.thriftapi.BmMatchParam;
import org.onosproject.bmv2.thriftapi.BmMatchParamExact;
import org.onosproject.bmv2.thriftapi.BmMatchParamLPM;
import org.onosproject.bmv2.thriftapi.BmMatchParamTernary;
import org.onosproject.bmv2.thriftapi.BmMatchParamType;
import org.onosproject.bmv2.thriftapi.BmMatchParamValid;
import org.onosproject.bmv2.thriftapi.BmMtEntry;
import org.onosproject.bmv2.thriftapi.SimpleSwitch;
import org.onosproject.bmv2.thriftapi.Standard;
import org.onosproject.net.DeviceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import static org.onlab.util.ImmutableByteSequence.copyFrom;
import static org.onosproject.bmv2.ctl.Bmv2TExceptionParser.parseTException;
/**
* Implementation of a Thrift client to control a BMv2 device.
*/
public final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent {
private final Logger log = LoggerFactory.getLogger(this.getClass());
// FIXME: make context_id arbitrary for each call
// See: https://github.com/p4lang/behavioral-model/blob/master/modules/bm_sim/include/bm_sim/context.h
private static final int CONTEXT_ID = 0;
protected final Standard.Iface standardClient;
private final SimpleSwitch.Iface simpleSwitchClient;
private final TTransport transport;
private final DeviceId deviceId;
// ban constructor
protected Bmv2DeviceThriftClient(DeviceId deviceId, TTransport transport, Standard.Iface standardClient,
SimpleSwitch.Iface simpleSwitchClient) {
this.deviceId = deviceId;
this.transport = transport;
this.standardClient = standardClient;
this.simpleSwitchClient = simpleSwitchClient;
}
@Override
public DeviceId deviceId() {
return deviceId;
}
@Override
public boolean ping() {
try {
return this.simpleSwitchClient.ping();
} catch (TException e) {
return false;
}
}
@Override
public final long addTableEntry(Bmv2TableEntry entry) throws Bmv2RuntimeException {
log.debug("Adding table entry... > deviceId={}, entry={}", deviceId, entry);
long entryId = -1;
try {
BmAddEntryOptions options = new BmAddEntryOptions();
if (entry.hasPriority()) {
options.setPriority(entry.priority());
}
entryId = standardClient.bm_mt_add_entry(
CONTEXT_ID,
entry.tableName(),
buildMatchParamsList(entry.matchKey()),
entry.action().name(),
buildActionParamsList(entry.action()),
options);
if (entry.hasTimeout()) {
/* bmv2 accepts timeouts in milliseconds */
int msTimeout = (int) Math.round(entry.timeout() * 1_000);
standardClient.bm_mt_set_entry_ttl(
CONTEXT_ID, entry.tableName(), entryId, msTimeout);
}
log.debug("Table entry added! > deviceId={}, entryId={}/{}", deviceId, entry.tableName(), entryId);
return entryId;
} catch (TException e) {
log.debug("Exception while adding table entry: {} > deviceId={}, tableName={}",
e, deviceId, entry.tableName());
if (entryId != -1) {
// entry is in inconsistent state (unable to add timeout), remove it
try {
deleteTableEntry(entry.tableName(), entryId);
} catch (Bmv2RuntimeException e1) {
log.debug("Unable to remove failed table entry: {} > deviceId={}, tableName={}",
e1, deviceId, entry.tableName());
}
}
throw parseTException(e);
}
}
@Override
public final void modifyTableEntry(String tableName,
long entryId, Bmv2Action action)
throws Bmv2RuntimeException {
log.debug("Modifying table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
try {
standardClient.bm_mt_modify_entry(
CONTEXT_ID,
tableName,
entryId,
action.name(),
buildActionParamsList(action));
log.debug("Table entry modified! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
} catch (TException e) {
log.debug("Exception while modifying table entry: {} > deviceId={}, entryId={}/{}",
e, deviceId, tableName, entryId);
throw parseTException(e);
}
}
@Override
public final void deleteTableEntry(String tableName,
long entryId) throws Bmv2RuntimeException {
log.debug("Deleting table entry... > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
try {
standardClient.bm_mt_delete_entry(CONTEXT_ID, tableName, entryId);
log.debug("Table entry deleted! > deviceId={}, entryId={}/{}", deviceId, tableName, entryId);
} catch (TException e) {
log.debug("Exception while deleting table entry: {} > deviceId={}, entryId={}/{}",
e, deviceId, tableName, entryId);
throw parseTException(e);
}
}
@Override
public final void setTableDefaultAction(String tableName, Bmv2Action action)
throws Bmv2RuntimeException {
log.debug("Setting table default... > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
try {
standardClient.bm_mt_set_default_action(
CONTEXT_ID,
tableName,
action.name(),
buildActionParamsList(action));
log.debug("Table default set! > deviceId={}, tableName={}, action={}", deviceId, tableName, action);
} catch (TException e) {
log.debug("Exception while setting table default : {} > deviceId={}, tableName={}, action={}",
e, deviceId, tableName, action);
throw parseTException(e);
}
}
@Override
public Collection<Bmv2PortInfo> getPortsInfo() throws Bmv2RuntimeException {
log.debug("Retrieving port info... > deviceId={}", deviceId);
try {
return standardClient.bm_dev_mgr_show_ports().stream()
.map(p -> new Bmv2PortInfo(p.getIface_name(), p.getPort_num(), p.isIs_up()))
.collect(Collectors.toList());
} catch (TException e) {
log.debug("Exception while retrieving port info: {} > deviceId={}", e, deviceId);
throw parseTException(e);
}
}
@Override
public List<Bmv2ParsedTableEntry> getTableEntries(String tableName) throws Bmv2RuntimeException {
log.debug("Retrieving table entries... > deviceId={}, tableName={}", deviceId, tableName);
List<BmMtEntry> bmEntries;
try {
bmEntries = standardClient.bm_mt_get_entries(CONTEXT_ID, tableName);
} catch (TException e) {
log.debug("Exception while retrieving table entries: {} > deviceId={}, tableName={}",
e, deviceId, tableName);
throw parseTException(e);
}
List<Bmv2ParsedTableEntry> parsedEntries = Lists.newArrayList();
entryLoop:
for (BmMtEntry bmEntry : bmEntries) {
Bmv2MatchKey.Builder matchKeyBuilder = Bmv2MatchKey.builder();
for (BmMatchParam bmParam : bmEntry.getMatch_key()) {
Bmv2MatchParam param;
switch (bmParam.getType()) {
case EXACT:
param = new Bmv2ExactMatchParam(copyFrom(bmParam.getExact().getKey()));
break;
case LPM:
param = new Bmv2LpmMatchParam(copyFrom(bmParam.getLpm().getKey()),
bmParam.getLpm().getPrefix_length());
break;
case TERNARY:
param = new Bmv2TernaryMatchParam(copyFrom(bmParam.getTernary().getKey()),
copyFrom(bmParam.getTernary().getMask()));
break;
case VALID:
param = new Bmv2ValidMatchParam(bmParam.getValid().isKey());
break;
default:
log.warn("Parsing of match type {} unsupported, skipping table entry.",
bmParam.getType().name());
continue entryLoop;
}
matchKeyBuilder.add(param);
}
Bmv2Action.Builder actionBuilder = Bmv2Action.builder();
BmActionEntry bmActionEntry = bmEntry.getAction_entry();
switch (bmActionEntry.getAction_type()) {
case ACTION_DATA:
actionBuilder.withName(bmActionEntry.getAction_name());
bmActionEntry.getAction_data()
.stream()
.map(ImmutableByteSequence::copyFrom)
.forEach(actionBuilder::addParameter);
break;
default:
log.warn("Parsing of action action type {} unsupported, skipping table entry.",
bmActionEntry.getAction_type().name());
continue entryLoop;
}
parsedEntries.add(new Bmv2ParsedTableEntry(bmEntry.getEntry_handle(), matchKeyBuilder.build(),
actionBuilder.build(), bmEntry.getOptions().getPriority()));
}
return parsedEntries;
}
@Override
public void transmitPacket(int portNumber, ImmutableByteSequence packet) throws Bmv2RuntimeException {
log.debug("Requesting packet transmission... > portNumber={}, packetSize={}", portNumber, packet.size());
try {
simpleSwitchClient.packet_out(portNumber, ByteBuffer.wrap(packet.asArray()));
log.debug("Packet transmission requested! > portNumber={}, packetSize={}", portNumber, packet.size());
} catch (TException e) {
log.debug("Exception while requesting packet transmission: {} > portNumber={}, packetSize={}",
e, portNumber, packet.size());
throw parseTException(e);
}
}
@Override
public void resetState() throws Bmv2RuntimeException {
log.debug("Resetting device state... > deviceId={}", deviceId);
try {
standardClient.bm_reset_state();
log.debug("Device state reset! > deviceId={}", deviceId);
} catch (TException e) {
log.debug("Exception while resetting device state: {} > deviceId={}", e, deviceId);
throw parseTException(e);
}
}
@Override
public String dumpJsonConfig() throws Bmv2RuntimeException {
log.debug("Dumping device config... > deviceId={}", deviceId);
try {
String config = standardClient.bm_get_config();
log.debug("Device config dumped! > deviceId={}, configLength={}", deviceId, config.length());
return config;
} catch (TException e) {
log.debug("Exception while dumping device config: {} > deviceId={}", e, deviceId);
throw parseTException(e);
}
}
@Override
public Pair<Long, Long> readTableEntryCounter(String tableName, long entryId) throws Bmv2RuntimeException {
log.debug("Reading table entry counters... > deviceId={}, tableName={}, entryId={}",
deviceId, tableName, entryId);
try {
BmCounterValue counterValue = standardClient.bm_mt_read_counter(CONTEXT_ID, tableName, entryId);
log.debug("Table entry counters retrieved! > deviceId={}, tableName={}, entryId={}, bytes={}, packets={}",
deviceId, tableName, entryId, counterValue.bytes, counterValue.packets);
return Pair.of(counterValue.bytes, counterValue.packets);
} catch (TException e) {
log.debug("Exception while reading table counters: {} > deviceId={}, tableName={}, entryId={}",
e.toString(), deviceId);
throw parseTException(e);
}
}
@Override
public Pair<Long, Long> readCounter(String counterName, int index) throws Bmv2RuntimeException {
log.debug("Reading table entry counters... > deviceId={}, counterName={}, index={}",
deviceId, counterName, index);
try {
BmCounterValue counterValue = standardClient.bm_counter_read(CONTEXT_ID, counterName, index);
log.debug("Table entry counters retrieved! >deviceId={}, counterName={}, index={}, bytes={}, packets={}",
deviceId, counterName, index, counterValue.bytes, counterValue.packets);
return Pair.of(counterValue.bytes, counterValue.packets);
} catch (TException e) {
log.debug("Exception while reading table counters: {} > deviceId={}, counterName={}, index={}",
e.toString(), deviceId);
throw parseTException(e);
}
}
@Override
public int getProcessInstanceId() throws Bmv2RuntimeException {
log.debug("Getting process instance ID... > deviceId={}", deviceId);
try {
int instanceId = simpleSwitchClient.get_process_instance_id();
log.debug("TProcess instance ID retrieved! > deviceId={}, instanceId={}",
deviceId, instanceId);
return instanceId;
} catch (TException e) {
log.debug("Exception while getting process instance ID: {} > deviceId={}", e.toString(), deviceId);
throw parseTException(e);
}
}
@Override
public String getJsonConfigMd5() throws Bmv2RuntimeException {
log.debug("Getting device config md5... > deviceId={}", deviceId);
try {
String md5 = standardClient.bm_get_config_md5();
log.debug("Device config md5 received! > deviceId={}, configMd5={}", deviceId, md5);
return md5;
} catch (TException e) {
log.debug("Exception while getting device config md5: {} > deviceId={}", e, deviceId);
throw parseTException(e);
}
}
@Override
public void uploadNewJsonConfig(String jsonString) throws Bmv2RuntimeException {
log.debug("Loading new JSON config on device... > deviceId={}, jsonStringLength={}",
deviceId, jsonString.length());
try {
standardClient.bm_load_new_config(jsonString);
log.debug("JSON config loaded! > deviceId={}", deviceId);
} catch (TException e) {
log.debug("Exception while loading JSON config: {} > deviceId={}", e, deviceId);
throw parseTException(e);
}
}
@Override
public void swapJsonConfig() throws Bmv2RuntimeException {
log.debug("Swapping JSON config on device... > deviceId={}", deviceId);
try {
standardClient.bm_swap_configs();
simpleSwitchClient.force_swap();
log.debug("JSON config swapped! > deviceId={}", deviceId);
} catch (TException e) {
log.debug("Exception while swapping JSON config: {} > deviceId={}", e, deviceId);
throw parseTException(e);
}
}
/**
* Builds a list of Bmv2/Thrift compatible match parameters.
*
* @param matchKey a bmv2 matchKey
* @return list of thrift-compatible bm match parameters
*/
private static List<BmMatchParam> buildMatchParamsList(Bmv2MatchKey matchKey) {
List<BmMatchParam> paramsList = Lists.newArrayList();
matchKey.matchParams().forEach(x -> {
ByteBuffer value;
ByteBuffer mask;
switch (x.type()) {
case EXACT:
value = ByteBuffer.wrap(((Bmv2ExactMatchParam) x).value().asArray());
paramsList.add(
new BmMatchParam(BmMatchParamType.EXACT)
.setExact(new BmMatchParamExact(value)));
break;
case TERNARY:
value = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).value().asArray());
mask = ByteBuffer.wrap(((Bmv2TernaryMatchParam) x).mask().asArray());
paramsList.add(
new BmMatchParam(BmMatchParamType.TERNARY)
.setTernary(new BmMatchParamTernary(value, mask)));
break;
case LPM:
value = ByteBuffer.wrap(((Bmv2LpmMatchParam) x).value().asArray());
int prefixLength = ((Bmv2LpmMatchParam) x).prefixLength();
paramsList.add(
new BmMatchParam(BmMatchParamType.LPM)
.setLpm(new BmMatchParamLPM(value, prefixLength)));
break;
case VALID:
boolean flag = ((Bmv2ValidMatchParam) x).flag();
paramsList.add(
new BmMatchParam(BmMatchParamType.VALID)
.setValid(new BmMatchParamValid(flag)));
break;
default:
// should never be here
throw new RuntimeException("Unknown match param type " + x.type().name());
}
});
return paramsList;
}
/**
* Build a list of Bmv2/Thrift compatible action parameters.
*
* @param action an action object
* @return list of ByteBuffers
*/
private static List<ByteBuffer> buildActionParamsList(Bmv2Action action) {
List<ByteBuffer> buffers = Lists.newArrayList();
action.parameters().forEach(p -> buffers.add(ByteBuffer.wrap(p.asArray())));
return buffers;
}
}

View File

@ -1,394 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import com.google.common.annotations.Beta;
import com.google.common.collect.Sets;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.bmv2.api.context.Bmv2ActionModel;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.context.Bmv2FieldModel;
import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslatorException;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.bmv2.api.context.Bmv2InterpreterException;
import org.onosproject.bmv2.api.context.Bmv2RuntimeDataModel;
import org.onosproject.bmv2.api.context.Bmv2TableKeyModel;
import org.onosproject.bmv2.api.context.Bmv2TableModel;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionSelector;
import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
import org.onosproject.bmv2.api.runtime.Bmv2MatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criterion;
import org.onosproject.net.flow.criteria.EthCriterion;
import org.onosproject.net.flow.criteria.EthTypeCriterion;
import org.onosproject.net.flow.criteria.ExtensionCriterion;
import org.onosproject.net.flow.criteria.PortCriterion;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
import org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes;
import org.onosproject.net.flow.instructions.Instruction;
import org.onosproject.net.flow.instructions.Instructions;
import org.onosproject.net.flow.instructions.Instructions.ExtensionInstructionWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.roundToBytes;
import static org.onosproject.net.flow.criteria.Criterion.Type.EXTENSION;
import static org.onosproject.net.flow.criteria.ExtensionSelectorType.ExtensionSelectorTypes.BMV2_MATCH_PARAMS;
/**
* Default implementation of a BMv2 flow rule translator.
*/
@Beta
public class Bmv2FlowRuleTranslatorImpl implements Bmv2FlowRuleTranslator {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public Bmv2TableEntry translate(FlowRule rule, Bmv2DeviceContext context)
throws Bmv2FlowRuleTranslatorException {
Bmv2Configuration configuration = context.configuration();
Bmv2Interpreter interpreter = context.interpreter();
int tableId = rule.tableId();
String tableName = interpreter.tableIdMap().get(tableId);
Bmv2TableModel table = (tableName == null) ? configuration.table(tableId) : configuration.table(tableName);
if (table == null) {
throw new Bmv2FlowRuleTranslatorException("Unknown table ID: " + tableId);
}
/* Translate selector */
Bmv2MatchKey bmv2MatchKey = buildMatchKey(interpreter, rule.selector(), table);
/* Translate treatment */
TrafficTreatment treatment = rule.treatment();
Bmv2Action bmv2Action = null;
// If treatment has only 1 instruction of type extension, use that
for (Instruction inst : treatment.allInstructions()) {
if (inst.type() == Instruction.Type.EXTENSION) {
if (treatment.allInstructions().size() == 1) {
bmv2Action = getActionFromExtension((ExtensionInstructionWrapper) inst);
} else {
throw new Bmv2FlowRuleTranslatorException("Unable to translate traffic treatment, found multiple " +
"instructions of which one is an extension: " +
treatment.toString());
}
}
}
if (bmv2Action == null) {
// No extension, use interpreter to build action.
try {
bmv2Action = interpreter.mapTreatment(treatment, configuration);
} catch (Bmv2InterpreterException e) {
throw new Bmv2FlowRuleTranslatorException("Unable to translate treatment. " + e.toString());
}
}
if (bmv2Action == null) {
// Interpreter returned null.
throw new Bmv2FlowRuleTranslatorException("Unable to translate treatment");
}
// Check action
Bmv2ActionModel actionModel = configuration.action(bmv2Action.name());
if (actionModel == null) {
throw new Bmv2FlowRuleTranslatorException("Unknown action " + bmv2Action.name());
}
if (!table.actions().contains(actionModel)) {
throw new Bmv2FlowRuleTranslatorException("Action " + bmv2Action.name()
+ " is not defined for table " + tableName);
}
if (actionModel.runtimeDatas().size() != bmv2Action.parameters().size()) {
throw new Bmv2FlowRuleTranslatorException("Wrong number of parameters for action "
+ actionModel.name() + ", expected "
+ actionModel.runtimeDatas().size() + ", but found "
+ bmv2Action.parameters().size());
}
for (int i = 0; i < actionModel.runtimeDatas().size(); i++) {
Bmv2RuntimeDataModel data = actionModel.runtimeDatas().get(i);
ImmutableByteSequence param = bmv2Action.parameters().get(i);
if (param.size() != roundToBytes(data.bitWidth())) {
throw new Bmv2FlowRuleTranslatorException("Wrong byte-width for parameter " + data.name()
+ " of action " + actionModel.name()
+ ", expected " + roundToBytes(data.bitWidth())
+ " bytes, but found " + param.size());
}
}
Bmv2TableEntry.Builder tableEntryBuilder = Bmv2TableEntry.builder();
// In BMv2 0 is the highest priority.
int newPriority = Integer.MAX_VALUE - rule.priority();
tableEntryBuilder
.withTableName(table.name())
.withPriority(newPriority)
.withMatchKey(bmv2MatchKey)
.withAction(bmv2Action);
if (!rule.isPermanent()) {
if (table.hasTimeouts()) {
tableEntryBuilder.withTimeout((double) rule.timeout());
} else {
log.warn("Flow rule is temporary but table {} doesn't support timeouts, translating to permanent",
table.name());
}
}
return tableEntryBuilder.build();
}
private Bmv2TernaryMatchParam buildTernaryParam(Bmv2FieldModel field, Criterion criterion, int bitWidth)
throws Bmv2FlowRuleTranslatorException {
// Value and mask will be filled according to criterion type
ImmutableByteSequence value;
ImmutableByteSequence mask = null;
int byteWidth = roundToBytes(bitWidth);
switch (criterion.type()) {
case IN_PORT:
long port = ((PortCriterion) criterion).port().toLong();
value = ImmutableByteSequence.copyFrom(port);
break;
case ETH_DST:
EthCriterion c = (EthCriterion) criterion;
value = ImmutableByteSequence.copyFrom(c.mac().toBytes());
if (c.mask() != null) {
mask = ImmutableByteSequence.copyFrom(c.mask().toBytes());
}
break;
case ETH_SRC:
EthCriterion c2 = (EthCriterion) criterion;
value = ImmutableByteSequence.copyFrom(c2.mac().toBytes());
if (c2.mask() != null) {
mask = ImmutableByteSequence.copyFrom(c2.mask().toBytes());
}
break;
case ETH_TYPE:
short ethType = ((EthTypeCriterion) criterion).ethType().toShort();
value = ImmutableByteSequence.copyFrom(ethType);
break;
// TODO: implement building for other criterion types (easy with DefaultCriterion of ONOS-4034)
default:
throw new Bmv2FlowRuleTranslatorException("Feature not implemented, ternary builder for criterion" +
"type: " + criterion.type().name());
}
// Fit byte sequence in field model bit-width.
try {
value = Bmv2TranslatorUtils.fitByteSequence(value, bitWidth);
} catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
throw new Bmv2FlowRuleTranslatorException(
"Fit exception for criterion " + criterion.type().name() + " value, " + e.getMessage());
}
if (mask == null) {
// no mask, all ones
mask = ImmutableByteSequence.ofOnes(byteWidth);
} else {
try {
mask = Bmv2TranslatorUtils.fitByteSequence(mask, bitWidth);
} catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
throw new Bmv2FlowRuleTranslatorException(
"Fit exception for criterion " + criterion.type().name() + " mask, " + e.getMessage());
}
}
return new Bmv2TernaryMatchParam(value, mask);
}
private Bmv2Action getActionFromExtension(Instructions.ExtensionInstructionWrapper inst)
throws Bmv2FlowRuleTranslatorException {
ExtensionTreatment extTreatment = inst.extensionInstruction();
if (extTreatment.type() == ExtensionTreatmentTypes.BMV2_ACTION.type()) {
if (extTreatment instanceof Bmv2ExtensionTreatment) {
return ((Bmv2ExtensionTreatment) extTreatment).action();
} else {
throw new Bmv2FlowRuleTranslatorException("Unable to decode treatment extension: " + extTreatment);
}
} else {
throw new Bmv2FlowRuleTranslatorException("Unsupported treatment extension type: " + extTreatment.type());
}
}
private Bmv2MatchKey buildMatchKey(Bmv2Interpreter interpreter, TrafficSelector selector, Bmv2TableModel tableModel)
throws Bmv2FlowRuleTranslatorException {
// Find a bmv2 extension selector (if any) and get the parameter map.
Optional<Bmv2ExtensionSelector> extSelector = selector.criteria().stream()
.filter(c -> c.type().equals(EXTENSION))
.map(c -> (ExtensionCriterion) c)
.map(ExtensionCriterion::extensionSelector)
.filter(c -> c.type().equals(BMV2_MATCH_PARAMS.type()))
.map(c -> (Bmv2ExtensionSelector) c)
.findFirst();
Map<String, Bmv2MatchParam> extParamMap =
(extSelector.isPresent()) ? extSelector.get().parameterMap() : Collections.emptyMap();
Set<Criterion> translatedCriteria = Sets.newHashSet();
Set<String> usedExtParams = Sets.newHashSet();
Bmv2MatchKey.Builder matchKeyBuilder = Bmv2MatchKey.builder();
keysLoop:
for (Bmv2TableKeyModel keyModel : tableModel.keys()) {
// use fieldName dot notation (e.g. ethernet.dstAddr)
String fieldName = keyModel.field().header().name() + "." + keyModel.field().type().name();
int bitWidth = keyModel.field().type().bitWidth();
int byteWidth = roundToBytes(bitWidth);
Criterion.Type criterionType = interpreter.criterionTypeMap().inverse().get(fieldName);
if (!extParamMap.containsKey(fieldName) &&
(criterionType == null || selector.getCriterion(criterionType) == null)) {
// Neither an extension nor a mapping / criterion is available for this field.
switch (keyModel.matchType()) {
case TERNARY:
// Wildcard field
matchKeyBuilder.withWildcard(byteWidth);
break;
case LPM:
// LPM with prefix 0
matchKeyBuilder.add(new Bmv2LpmMatchParam(ImmutableByteSequence.ofZeros(byteWidth), 0));
break;
default:
throw new Bmv2FlowRuleTranslatorException("No value found for required match field "
+ fieldName);
}
// Next key
continue keysLoop;
}
Bmv2MatchParam matchParam;
if (extParamMap.containsKey(fieldName)) {
// Parameter found in extension
if (criterionType != null && selector.getCriterion(criterionType) != null) {
// Found also a criterion that can be mapped. This is bad.
throw new Bmv2FlowRuleTranslatorException("Both an extension and a criterion mapping are defined " +
"for match field " + fieldName);
}
matchParam = extParamMap.get(fieldName);
usedExtParams.add(fieldName);
// Check parameter type and size
if (!keyModel.matchType().equals(matchParam.type())) {
throw new Bmv2FlowRuleTranslatorException("Wrong match type for parameter " + fieldName
+ ", expected " + keyModel.matchType().name()
+ ", but found " + matchParam.type().name());
}
int foundByteWidth;
switch (keyModel.matchType()) {
case EXACT:
Bmv2ExactMatchParam m1 = (Bmv2ExactMatchParam) matchParam;
foundByteWidth = m1.value().size();
break;
case TERNARY:
Bmv2TernaryMatchParam m2 = (Bmv2TernaryMatchParam) matchParam;
foundByteWidth = m2.value().size();
break;
case LPM:
Bmv2LpmMatchParam m3 = (Bmv2LpmMatchParam) matchParam;
foundByteWidth = m3.value().size();
break;
case VALID:
foundByteWidth = -1;
break;
default:
// should never be her
throw new RuntimeException("Unrecognized match type " + keyModel.matchType().name());
}
if (foundByteWidth != -1 && foundByteWidth != byteWidth) {
throw new Bmv2FlowRuleTranslatorException("Wrong byte-width for match parameter " + fieldName
+ ", expected " + byteWidth + ", but found "
+ foundByteWidth);
}
} else {
// A criterion mapping is available for this key
Criterion criterion = selector.getCriterion(criterionType);
translatedCriteria.add(criterion);
switch (keyModel.matchType()) {
case TERNARY:
matchParam = buildTernaryParam(keyModel.field(), criterion, bitWidth);
break;
default:
// TODO: implement other match param builders (exact, LPM, etc.)
throw new Bmv2FlowRuleTranslatorException("Feature not yet implemented, match param builder: "
+ keyModel.matchType().name());
}
}
matchKeyBuilder.add(matchParam);
}
// Check if all criteria have been translated
Set<Criterion> ignoredCriteria = selector.criteria()
.stream()
.filter(c -> !c.type().equals(EXTENSION))
.filter(c -> !translatedCriteria.contains(c))
.collect(Collectors.toSet());
if (ignoredCriteria.size() > 0) {
throw new Bmv2FlowRuleTranslatorException("The following criteria cannot be translated for table "
+ tableModel.name() + ": " + ignoredCriteria.toString());
}
// Check is all extension parameters have been used
Set<String> ignoredExtParams = extParamMap.keySet()
.stream()
.filter(k -> !usedExtParams.contains(k))
.collect(Collectors.toSet());
if (ignoredExtParams.size() > 0) {
throw new Bmv2FlowRuleTranslatorException("The following extension match parameters cannot be used for " +
"table " + tableModel.name() + ": "
+ ignoredExtParams.toString());
}
return matchKeyBuilder.build();
}
}

View File

@ -1,158 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import org.apache.thrift.TException;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.thriftapi.InvalidCounterOperation;
import org.onosproject.bmv2.thriftapi.InvalidDevMgrOperation;
import org.onosproject.bmv2.thriftapi.InvalidLearnOperation;
import org.onosproject.bmv2.thriftapi.InvalidMcOperation;
import org.onosproject.bmv2.thriftapi.InvalidMeterOperation;
import org.onosproject.bmv2.thriftapi.InvalidRegisterOperation;
import org.onosproject.bmv2.thriftapi.InvalidSwapOperation;
import org.onosproject.bmv2.thriftapi.InvalidTableOperation;
import static org.onosproject.bmv2.api.runtime.Bmv2RuntimeException.Code;
/**
* Utility class to translate a Thrift exception into a Bmv2RuntimeException.
*/
final class Bmv2TExceptionParser {
private Bmv2TExceptionParser() {
// ban constructor.
}
static Bmv2RuntimeException parseTException(TException cause) {
try {
return new Bmv2RuntimeException(getCode(cause));
} catch (ParserException e) {
return new Bmv2RuntimeException(e.codeString);
}
}
private static Code getCode(TException e) throws ParserException {
if (e instanceof InvalidTableOperation) {
InvalidTableOperation t = (InvalidTableOperation) e;
switch (t.getCode()) {
case TABLE_FULL:
return Code.TABLE_FULL;
case INVALID_HANDLE:
return Code.TABLE_INVALID_HANDLE;
case EXPIRED_HANDLE:
return Code.TABLE_EXPIRED_HANDLE;
case COUNTERS_DISABLED:
return Code.TABLE_COUNTERS_DISABLED;
case METERS_DISABLED:
return Code.TABLE_METERS_DISABLED;
case AGEING_DISABLED:
return Code.TABLE_AGEING_DISABLED;
case INVALID_TABLE_NAME:
return Code.TABLE_INVALID_TABLE_NAME;
case INVALID_ACTION_NAME:
return Code.TABLE_INVALID_ACTION_NAME;
case WRONG_TABLE_TYPE:
return Code.TABLE_WRONG_TABLE_TYPE;
case INVALID_MBR_HANDLE:
return Code.TABLE_INVALID_MBR_HANDLE;
case MBR_STILL_USED:
return Code.TABLE_MBR_STILL_USED;
case MBR_ALREADY_IN_GRP:
return Code.TABLE_MBR_ALREADY_IN_GRP;
case MBR_NOT_IN_GRP:
return Code.TABLE_MBR_NOT_IN_GRP;
case INVALID_GRP_HANDLE:
return Code.TABLE_INVALID_GRP_HANDLE;
case GRP_STILL_USED:
return Code.TABLE_GRP_STILL_USED;
case EMPTY_GRP:
return Code.TABLE_EMPTY_GRP;
case DUPLICATE_ENTRY:
return Code.TABLE_DUPLICATE_ENTRY;
case BAD_MATCH_KEY:
return Code.TABLE_BAD_MATCH_KEY;
case INVALID_METER_OPERATION:
return Code.TABLE_INVALID_METER_OPERATION;
case DEFAULT_ACTION_IS_CONST:
return Code.TABLE_DEFAULT_ACTION_IS_CONST;
case DEFAULT_ENTRY_IS_CONST:
return Code.TABLE_DEFAULT_ENTRY_IS_CONST;
case ERROR:
return Code.TABLE_GENERAL_ERROR;
default:
return Code.TABLE_UNKNOWN_ERROR;
}
} else if (e instanceof InvalidCounterOperation) {
InvalidCounterOperation t = (InvalidCounterOperation) e;
switch (t.getCode()) {
case INVALID_COUNTER_NAME:
return Code.COUNTER_INVALID_NAME;
case INVALID_INDEX:
return Code.COUNTER_INVALID_INDEX;
case ERROR:
return Code.COUNTER_ERROR_GENERAL;
default:
return Code.COUNTER_ERROR_UNKNOWN;
}
} else if (e instanceof InvalidDevMgrOperation) {
InvalidDevMgrOperation t = (InvalidDevMgrOperation) e;
switch (t.getCode()) {
case ERROR:
return Code.DEV_MGR_ERROR_GENERAL;
default:
return Code.DEV_MGR_UNKNOWN;
}
} else if (e instanceof InvalidSwapOperation) {
InvalidSwapOperation t = (InvalidSwapOperation) e;
switch (t.getCode()) {
case CONFIG_SWAP_DISABLED:
return Code.SWAP_CONFIG_DISABLED;
case ONGOING_SWAP:
return Code.SWAP_ONGOING;
case NO_ONGOING_SWAP:
return Code.SWAP_NO_ONGOING;
default:
return Code.SWAP_ERROR_UKNOWN;
}
} else if (e instanceof InvalidMeterOperation) {
// TODO
throw new ParserException(e.toString());
} else if (e instanceof InvalidRegisterOperation) {
// TODO
throw new ParserException(e.toString());
} else if (e instanceof InvalidLearnOperation) {
// TODO
throw new ParserException(e.toString());
} else if (e instanceof InvalidMcOperation) {
// TODO
throw new ParserException(e.toString());
} else {
throw new ParserException(e.toString());
}
}
private static class ParserException extends Exception {
private String codeString;
public ParserException(String codeString) {
this.codeString = codeString;
}
}
}

View File

@ -1,128 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
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.apache.felix.scr.annotations.Service;
import org.onlab.util.KryoNamespace;
import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
import org.onosproject.bmv2.api.runtime.Bmv2ExactMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
import org.onosproject.bmv2.api.runtime.Bmv2LpmMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
import org.onosproject.net.DeviceId;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.WallClockTimestamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implementation of the Bmv2TableEntryService.
*/
@Component(immediate = true)
@Service
public class Bmv2TableEntryServiceImpl implements Bmv2TableEntryService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final Bmv2FlowRuleTranslator translator = new Bmv2FlowRuleTranslatorImpl();
private EventuallyConsistentMap<Bmv2TableEntryReference, Bmv2FlowRuleWrapper> flowRules;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected Bmv2Controller controller;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected Bmv2DeviceContextService contextService;
@Activate
public void activate() {
KryoNamespace kryo = new KryoNamespace.Builder()
.register(KryoNamespaces.API)
.register(Bmv2TableEntryReference.class)
.register(Bmv2MatchKey.class)
.register(Bmv2ExactMatchParam.class)
.register(Bmv2TernaryMatchParam.class)
.register(Bmv2LpmMatchParam.class)
.register(Bmv2ValidMatchParam.class)
.register(Bmv2FlowRuleWrapper.class)
.build();
flowRules = storageService.<Bmv2TableEntryReference, Bmv2FlowRuleWrapper>eventuallyConsistentMapBuilder()
.withSerializer(kryo)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.withName("onos-bmv2-flowrules")
.build();
log.info("Started");
}
@Deactivate
public void deactivate() {
log.info("Stopped");
}
@Override
public Bmv2FlowRuleTranslator getFlowRuleTranslator() {
return translator;
}
@Override
public Bmv2FlowRuleWrapper lookup(Bmv2TableEntryReference entryRef) {
checkNotNull(entryRef, "table entry reference cannot be null");
return flowRules.get(entryRef);
}
@Override
public void bind(Bmv2TableEntryReference entryRef, Bmv2FlowRuleWrapper rule) {
checkNotNull(entryRef, "table entry reference cannot be null");
checkNotNull(rule, "bmv2 flow rule cannot be null");
flowRules.put(entryRef, rule);
}
@Override
public void unbind(Bmv2TableEntryReference entryRef) {
checkNotNull(entryRef, "table entry reference cannot be null");
flowRules.remove(entryRef);
}
@Override
public void unbindAll(DeviceId deviceId) {
flowRules.keySet()
.stream()
.filter(entryRef -> entryRef.deviceId().equals(deviceId))
.forEach(flowRules::remove);
}
}

View File

@ -1,250 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Most of the code of this class was copied from:
* http://liveramp.com/engineering/reconnecting-thrift-client/
*/
package org.onosproject.bmv2.ctl;
import com.google.common.collect.ImmutableSet;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set;
/**
* Thrift client wrapper that attempts a few reconnects before giving up a method call execution. It also provides
* synchronization between calls over the same transport.
*/
public final class SafeThriftClient {
private static final Logger LOG = LoggerFactory.getLogger(SafeThriftClient.class);
/**
* List of causes which suggest a restart might fix things (defined as constants in {@link TTransportException}).
*/
private static final Set<Integer> RESTARTABLE_CAUSES = ImmutableSet.of(TTransportException.NOT_OPEN,
TTransportException.END_OF_FILE,
TTransportException.TIMED_OUT,
TTransportException.UNKNOWN);
private SafeThriftClient() {
// ban constructor.
}
/**
* Reflectively wraps an already existing Thrift client.
*
* @param baseClient the client to wrap
* @param clientInterface the interface that the client implements
* @param options options that control behavior of the reconnecting client
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient, Class<C> clientInterface, Options options) {
Object proxyObject = Proxy.newProxyInstance(clientInterface.getClassLoader(),
new Class<?>[]{clientInterface},
new ReconnectingClientProxy<T>(baseClient,
options.getNumRetries(),
options.getTimeBetweenRetries()));
return (C) proxyObject;
}
/**
* Reflectively wraps an already existing Thrift client.
*
* @param baseClient the client to wrap
* @param options options that control behavior of the reconnecting client
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient, Options options) {
Class<?>[] interfaces = baseClient.getClass().getInterfaces();
for (Class<?> iface : interfaces) {
if (iface.getSimpleName().equals("Iface")
&& iface.getEnclosingClass().equals(baseClient.getClass().getEnclosingClass())) {
return (C) wrap(baseClient, iface, options);
}
}
throw new RuntimeException("Class needs to implement Iface directly. Use wrap(TServiceClient, Class) instead.");
}
/**
* Reflectively wraps an already existing Thrift client.
*
* @param baseClient the client to wrap
* @param clientInterface the interface that the client implements
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient, Class<C> clientInterface) {
return wrap(baseClient, clientInterface, Options.defaults());
}
/**
* Reflectively wraps an already existing Thrift client.
*
* @param baseClient the client to wrap
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient) {
return wrap(baseClient, Options.defaults());
}
/**
* Reconnection options for {@link SafeThriftClient}.
*/
public static class Options {
private int numRetries;
private long timeBetweenRetries;
/**
* Creates new options with the given parameters.
*
* @param numRetries the maximum number of times to try reconnecting before giving up and throwing an
* exception
* @param timeBetweenRetries the number of milliseconds to wait in between reconnection attempts.
*/
public Options(int numRetries, long timeBetweenRetries) {
this.numRetries = numRetries;
this.timeBetweenRetries = timeBetweenRetries;
}
private static Options defaults() {
return new Options(5, 10000L);
}
private int getNumRetries() {
return numRetries;
}
private long getTimeBetweenRetries() {
return timeBetweenRetries;
}
}
/**
* Helper proxy class. Attempts to call method on proxy object wrapped in try/catch. If it fails, it attempts a
* reconnect and tries the method again.
*
* @param <T>
*/
private static class ReconnectingClientProxy<T extends TServiceClient> implements InvocationHandler {
private final T baseClient;
private final TTransport transport;
private final int maxRetries;
private final long timeBetweenRetries;
public ReconnectingClientProxy(T baseClient, int maxRetries, long timeBetweenRetries) {
this.baseClient = baseClient;
this.transport = baseClient.getInputProtocol().getTransport();
this.maxRetries = maxRetries;
this.timeBetweenRetries = timeBetweenRetries;
}
private void reconnectOrThrowException()
throws TTransportException {
int errors = 0;
try {
if (transport.isOpen()) {
transport.close();
}
} catch (Exception e) {
// Thrift seems to have a bug where if the transport is already closed a SocketException is thrown.
// However, such an exception is not advertised by .close(), hence the general-purpose catch.
LOG.debug("Exception while closing transport", e);
}
while (errors < maxRetries) {
try {
LOG.debug("Attempting to reconnect...");
transport.open();
LOG.debug("Reconnection successful");
break;
} catch (TTransportException e) {
LOG.debug("Error while reconnecting:", e);
errors++;
if (errors < maxRetries) {
try {
LOG.debug("Sleeping for {} milliseconds before retrying", timeBetweenRetries);
Thread.sleep(timeBetweenRetries);
} catch (InterruptedException e2) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}
}
if (errors >= maxRetries) {
throw new TTransportException("Failed to reconnect");
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Thrift transport layer is not thread-safe (it's a wrapper on a socket), hence we need locking.
synchronized (transport) {
LOG.debug("Invoking method... > fromThread={}, method={}, args={}",
Thread.currentThread().getId(), method.getName(), args);
try {
return method.invoke(baseClient, args);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof TTransportException) {
TTransportException cause = (TTransportException) e.getTargetException();
if (RESTARTABLE_CAUSES.contains(cause.getType())) {
// Try to reconnect. If fail, a TTransportException will be thrown.
reconnectOrThrowException();
try {
// If here, transport has been successfully open, hence new exceptions will be thrown.
return method.invoke(baseClient, args);
} catch (InvocationTargetException e1) {
LOG.debug("Exception: {}", e1.getTargetException());
throw e1.getTargetException();
}
}
}
// Target exception is neither a TTransportException nor a restartable cause.
LOG.debug("Exception: {}", e.getTargetException());
throw e.getTargetException();
}
}
}
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* BMv2 Thrift client implementation.
*/
package org.onosproject.bmv2.ctl;

View File

@ -1,730 +0,0 @@
{
"header_types": [
{
"name": "standard_metadata_t",
"id": 0,
"fields": [
[
"ingress_port",
9
],
[
"packet_length",
32
],
[
"egress_spec",
9
],
[
"egress_port",
9
],
[
"egress_instance",
32
],
[
"instance_type",
32
],
[
"clone_spec",
32
],
[
"_padding",
5
]
],
"length_exp": null,
"max_length": null
},
{
"name": "intrinsic_metadata_t",
"id": 1,
"fields": [
[
"ingress_global_timestamp",
32
],
[
"lf_field_list",
32
],
[
"mcast_grp",
16
],
[
"egress_rid",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "ethernet_t",
"id": 2,
"fields": [
[
"dstAddr",
48
],
[
"srcAddr",
48
],
[
"etherType",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "ipv4_t",
"id": 3,
"fields": [
[
"version",
4
],
[
"ihl",
4
],
[
"diffserv",
8
],
[
"totalLen",
16
],
[
"identification",
16
],
[
"flags",
3
],
[
"fragOffset",
13
],
[
"ttl",
8
],
[
"protocol",
8
],
[
"hdrChecksum",
16
],
[
"srcAddr",
32
],
[
"dstAddr",
32
]
],
"length_exp": null,
"max_length": null
},
{
"name": "tcp_t",
"id": 4,
"fields": [
[
"srcPort",
16
],
[
"dstPort",
16
],
[
"seqNo",
32
],
[
"ackNo",
32
],
[
"dataOffset",
4
],
[
"res",
3
],
[
"ecn",
3
],
[
"ctrl",
6
],
[
"window",
16
],
[
"checksum",
16
],
[
"urgentPtr",
16
]
],
"length_exp": null,
"max_length": null
},
{
"name": "udp_t",
"id": 5,
"fields": [
[
"srcPort",
16
],
[
"dstPort",
16
],
[
"length_",
16
],
[
"checksum",
16
]
],
"length_exp": null,
"max_length": null
}
],
"headers": [
{
"name": "standard_metadata",
"id": 0,
"header_type": "standard_metadata_t",
"metadata": true
},
{
"name": "intrinsic_metadata",
"id": 1,
"header_type": "intrinsic_metadata_t",
"metadata": true
},
{
"name": "ethernet",
"id": 2,
"header_type": "ethernet_t",
"metadata": false
},
{
"name": "ipv4",
"id": 3,
"header_type": "ipv4_t",
"metadata": false
},
{
"name": "tcp",
"id": 4,
"header_type": "tcp_t",
"metadata": false
},
{
"name": "udp",
"id": 5,
"header_type": "udp_t",
"metadata": false
}
],
"header_stacks": [],
"parsers": [
{
"name": "parser",
"id": 0,
"init_state": "start",
"parse_states": [
{
"name": "start",
"id": 0,
"parser_ops": [],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": "parse_ethernet"
}
]
},
{
"name": "parse_ethernet",
"id": 1,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "ethernet"
}
]
}
],
"transition_key": [
{
"type": "field",
"value": [
"ethernet",
"etherType"
]
}
],
"transitions": [
{
"value": "0x0800",
"mask": null,
"next_state": "parse_ipv4"
},
{
"value": "default",
"mask": null,
"next_state": null
}
]
},
{
"name": "parse_ipv4",
"id": 2,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "ipv4"
}
]
}
],
"transition_key": [
{
"type": "field",
"value": [
"ipv4",
"fragOffset"
]
},
{
"type": "field",
"value": [
"ipv4",
"protocol"
]
}
],
"transitions": [
{
"value": "0x000006",
"mask": null,
"next_state": "parse_tcp"
},
{
"value": "0x000011",
"mask": null,
"next_state": "parse_udp"
},
{
"value": "default",
"mask": null,
"next_state": null
}
]
},
{
"name": "parse_tcp",
"id": 3,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "tcp"
}
]
}
],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": null
}
]
},
{
"name": "parse_udp",
"id": 4,
"parser_ops": [
{
"op": "extract",
"parameters": [
{
"type": "regular",
"value": "udp"
}
]
}
],
"transition_key": [],
"transitions": [
{
"value": "default",
"mask": null,
"next_state": null
}
]
}
]
}
],
"deparsers": [
{
"name": "deparser",
"id": 0,
"order": [
"ethernet",
"ipv4",
"udp",
"tcp"
]
}
],
"meter_arrays": [],
"actions": [
{
"name": "_drop",
"id": 0,
"runtime_data": [],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "hexstr",
"value": "0x1ff"
}
]
}
]
},
{
"name": "count_packet",
"id": 1,
"runtime_data": [],
"primitives": [
{
"op": "count",
"parameters": [
{
"type": "counter_array",
"value": "ingress_port_counter"
},
{
"type": "field",
"value": [
"standard_metadata",
"ingress_port"
]
}
]
},
{
"op": "count",
"parameters": [
{
"type": "counter_array",
"value": "egress_port_counter"
},
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
}
]
}
]
},
{
"name": "send_to_cpu",
"id": 2,
"runtime_data": [],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "hexstr",
"value": "0xff"
}
]
}
]
},
{
"name": "set_egress_port",
"id": 3,
"runtime_data": [
{
"name": "port",
"bitwidth": 9
}
],
"primitives": [
{
"op": "modify_field",
"parameters": [
{
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
{
"type": "runtime_data",
"value": 0
}
]
}
]
}
],
"pipelines": [
{
"name": "ingress",
"id": 0,
"init_table": "table0",
"tables": [
{
"name": "port_count_table",
"id": 0,
"match_type": "exact",
"type": "simple",
"max_size": 16384,
"with_counters": false,
"direct_meters": null,
"support_timeout": false,
"key": [],
"actions": [
"count_packet"
],
"next_tables": {
"count_packet": null
},
"default_action": null,
"base_default_next": null
},
{
"name": "table0",
"id": 1,
"match_type": "ternary",
"type": "simple",
"max_size": 16384,
"with_counters": true,
"direct_meters": null,
"support_timeout": true,
"key": [
{
"match_type": "ternary",
"target": [
"standard_metadata",
"ingress_port"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"dstAddr"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"srcAddr"
],
"mask": null
},
{
"match_type": "ternary",
"target": [
"ethernet",
"etherType"
],
"mask": null
}
],
"actions": [
"set_egress_port",
"send_to_cpu",
"_drop"
],
"next_tables": {
"set_egress_port": "_condition_0",
"send_to_cpu": "_condition_0",
"_drop": "_condition_0"
},
"default_action": null,
"base_default_next": "_condition_0"
}
],
"conditionals": [
{
"name": "_condition_0",
"id": 0,
"expression": {
"type": "expression",
"value": {
"op": "<",
"left": {
"type": "field",
"value": [
"standard_metadata",
"egress_spec"
]
},
"right": {
"type": "hexstr",
"value": "0xfe"
}
}
},
"true_next": "port_count_table",
"false_next": null
}
]
},
{
"name": "egress",
"id": 1,
"init_table": null,
"tables": [],
"conditionals": []
}
],
"calculations": [],
"checksums": [],
"learn_lists": [],
"field_lists": [],
"counter_arrays": [
{
"name": "ingress_port_counter",
"id": 0,
"is_direct": false,
"size": 254
},
{
"name": "egress_port_counter",
"id": 1,
"is_direct": false,
"size": 254
},
{
"name": "table0_counter",
"id": 2,
"is_direct": true,
"binding": "table0"
}
],
"register_arrays": [],
"force_arith": [
[
"standard_metadata",
"ingress_port"
],
[
"standard_metadata",
"packet_length"
],
[
"standard_metadata",
"egress_spec"
],
[
"standard_metadata",
"egress_port"
],
[
"standard_metadata",
"egress_instance"
],
[
"standard_metadata",
"instance_type"
],
[
"standard_metadata",
"clone_spec"
],
[
"standard_metadata",
"_padding"
],
[
"intrinsic_metadata",
"ingress_global_timestamp"
],
[
"intrinsic_metadata",
"lf_field_list"
],
[
"intrinsic_metadata",
"mcast_grp"
],
[
"intrinsic_metadata",
"egress_rid"
]
]
}

View File

@ -1,140 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bmv2.ctl;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import org.onlab.packet.MacAddress;
import org.onosproject.bmv2.api.context.Bmv2Configuration;
import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
import org.onosproject.bmv2.api.context.Bmv2Interpreter;
import org.onosproject.bmv2.api.runtime.Bmv2TableEntry;
import org.onosproject.bmv2.api.runtime.Bmv2TernaryMatchParam;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import java.util.Random;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.onosproject.bmv2.ctl.Bmv2DefaultInterpreterImpl.TABLE0;
/**
* Tests for {@link Bmv2FlowRuleTranslatorImpl}.
*/
public class Bmv2FlowRuleTranslatorImplTest {
private Random random = new Random();
private Bmv2Configuration configuration = Bmv2DeviceContextServiceImpl.loadDefaultConfiguration();
private Bmv2Interpreter interpreter = new Bmv2DefaultInterpreterImpl();
private Bmv2DeviceContext context = new Bmv2DeviceContext(configuration, interpreter);
private Bmv2FlowRuleTranslator translator = new Bmv2FlowRuleTranslatorImpl();
@Test
public void testTranslate() throws Exception {
DeviceId deviceId = DeviceId.NONE;
ApplicationId appId = new DefaultApplicationId(1, "test");
int tableId = 0;
MacAddress ethDstMac = MacAddress.valueOf(random.nextLong());
MacAddress ethSrcMac = MacAddress.valueOf(random.nextLong());
short ethType = (short) (0x0000FFFF & random.nextInt());
short outPort = (short) random.nextInt(65);
short inPort = (short) random.nextInt(65);
int timeout = random.nextInt(100);
int priority = random.nextInt(100);
TrafficSelector matchInPort1 = DefaultTrafficSelector
.builder()
.matchInPort(PortNumber.portNumber(inPort))
.matchEthDst(ethDstMac)
.matchEthSrc(ethSrcMac)
.matchEthType(ethType)
.build();
TrafficTreatment outPort2 = DefaultTrafficTreatment
.builder()
.setOutput(PortNumber.portNumber(outPort))
.build();
FlowRule rule1 = DefaultFlowRule.builder()
.forDevice(deviceId)
.forTable(tableId)
.fromApp(appId)
.withSelector(matchInPort1)
.withTreatment(outPort2)
.makeTemporary(timeout)
.withPriority(priority)
.build();
FlowRule rule2 = DefaultFlowRule.builder()
.forDevice(deviceId)
.forTable(tableId)
.fromApp(appId)
.withSelector(matchInPort1)
.withTreatment(outPort2)
.makeTemporary(timeout)
.withPriority(priority)
.build();
Bmv2TableEntry entry1 = translator.translate(rule1, context);
Bmv2TableEntry entry2 = translator.translate(rule1, context);
// check equality, i.e. same rules must produce same entries
new EqualsTester()
.addEqualityGroup(rule1, rule2)
.addEqualityGroup(entry1, entry2)
.testEquals();
int numMatchParams = configuration.table(TABLE0).keys().size();
// parse values stored in entry1
Bmv2TernaryMatchParam inPortParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(0);
Bmv2TernaryMatchParam ethDstParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(1);
Bmv2TernaryMatchParam ethSrcParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(2);
Bmv2TernaryMatchParam ethTypeParam = (Bmv2TernaryMatchParam) entry1.matchKey().matchParams().get(3);
double expectedTimeout = (double) (configuration.table(TABLE0).hasTimeouts() ? rule1.timeout() : -1);
// check that the number of parameters in the entry is the same as the number of table keys
assertThat("Incorrect number of match parameters",
entry1.matchKey().matchParams().size(), is(equalTo(numMatchParams)));
// check that values stored in entry are the same used for the flow rule
assertThat("Incorrect inPort match param value",
inPortParam.value().asReadOnlyBuffer().getShort(), is(equalTo(inPort)));
assertThat("Incorrect ethDestMac match param value",
ethDstParam.value().asArray(), is(equalTo(ethDstMac.toBytes())));
assertThat("Incorrect ethSrcMac match param value",
ethSrcParam.value().asArray(), is(equalTo(ethSrcMac.toBytes())));
assertThat("Incorrect ethType match param value",
ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
assertThat("Incorrect priority value",
entry1.priority(), is(equalTo(Integer.MAX_VALUE - rule1.priority())));
assertThat("Incorrect timeout value",
entry1.timeout(), is(equalTo(expectedTimeout)));
}
}

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-protocols</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-protocol</artifactId>
<modules>
<module>api</module>
<module>ctl</module>
<module>thrift-api</module>
</modules>
<packaging>pom</packaging>
<description>BMv2 protocol subsystem</description>
</project>

View File

@ -1,239 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-bmv2-protocol</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-protocol-thrift-api</artifactId>
<packaging>bundle</packaging>
<properties>
<!-- BMv2 Commit ID and Thrift version -->
<bmv2.commit>8f675d0284e9e014f1b8ed502ba54e61d68108cf</bmv2.commit>
<bmv2.thrift.version>0.9.3</bmv2.thrift.version>
<bmv2.baseurl>https://cdn.rawgit.com/opennetworkinglab/onos-bmv2/${bmv2.commit}</bmv2.baseurl>
<bmv2.thrift.javanamespace>org.onosproject.bmv2.thriftapi</bmv2.thrift.javanamespace>
<bmv2.thrift.srcdir>${project.build.directory}/thrift-sources/${bmv2.commit}/</bmv2.thrift.srcdir>
<thrift.exedir>${project.build.directory}/thrift-compiler/</thrift.exedir>
<thrift.exefilename>thrift-${os.detected.classifier}.exe</thrift.exefilename>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.3</version>
</dependency>
</dependencies>
<repositories>
<!-- Needed for thrift-compiler, which is hosted on GitHub -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.4.0.Final</version>
</extension>
</extensions>
<plugins>
<!-- Download Thrift source files from BMv2 Github repo -->
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>download-maven-plugin</artifactId>
<version>1.3.0</version>
<executions>
<execution>
<id>download-bmv2-thrift-standard</id>
<phase>initialize</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${bmv2.baseurl}/thrift_src/standard.thrift</url>
<outputDirectory>${bmv2.thrift.srcdir}</outputDirectory>
</configuration>
</execution>
<execution>
<id>download-bmv2-thrift-simple_pre</id>
<phase>initialize</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${bmv2.baseurl}/thrift_src/simple_pre.thrift</url>
<outputDirectory>${bmv2.thrift.srcdir}</outputDirectory>
</configuration>
</execution>
<execution>
<id>download-bmv2-thrift-simple_pre_lag</id>
<phase>initialize</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${bmv2.baseurl}/thrift_src/simple_pre_lag.thrift</url>
<outputDirectory>${bmv2.thrift.srcdir}</outputDirectory>
</configuration>
</execution>
<execution>
<id>download-bmv2-thrift-simple_switch</id>
<phase>initialize</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${bmv2.baseurl}/targets/simple_switch/thrift/simple_switch.thrift</url>
<outputDirectory>${bmv2.thrift.srcdir}</outputDirectory>
</configuration>
</execution>
<execution>
<id>download-bmv2-thrift-simple_switch-cpservice</id>
<phase>initialize</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${bmv2.baseurl}/targets/simple_switch/thrift/control_plane.thrift</url>
<outputDirectory>${bmv2.thrift.srcdir}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<!-- Extract Thrift compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>initialize</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.github.ccascone</groupId>
<artifactId>mvn-thrift-compiler</artifactId>
<version>1.1_${bmv2.thrift.version}</version>
<type>jar</type>
<includes>${thrift.exefilename}</includes>
<outputDirectory>${project.build.directory}/thrift-compiler</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<!-- Add missing java namespace to Thrift files -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<id>add-bmv2-thrift-java-namespace</id>
<phase>initialize</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${project.basedir}/src/patch.sh</executable>
<arguments>
<argument>${bmv2.thrift.srcdir}</argument>
<argument>${bmv2.thrift.javanamespace}</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>set-thrift-compiler-permissions</id>
<phase>initialize</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>chmod</executable>
<arguments>
<argument>+x</argument>
<argument>${thrift.exedir}/${thrift.exefilename}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<!-- Compile Thrift files -->
<plugin>
<groupId>org.apache.thrift.tools</groupId>
<artifactId>maven-thrift-plugin</artifactId>
<version>0.1.11</version>
<configuration>
<thriftSourceRoot>${bmv2.thrift.srcdir}</thriftSourceRoot>
<thriftExecutable>${thrift.exedir}/${thrift.exefilename}</thriftExecutable>
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
</configuration>
<executions>
<execution>
<id>thrift-sources</id>
<phase>initialize</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Make generated sources visible -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>add-thrift-sources-to-path</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>
${project.build.directory}/generated-sources
</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,29 +0,0 @@
#! /bin/bash
#
# Copyright 2014-2016 Open Networking Laboratory
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# exit on errors
set -e
srcdir=$1
ns=$2
# add java namespace at beginning of file
for f in ${srcdir}/*.thrift
do
if ! grep -q ${ns} ${f}; then
echo "namespace java ${ns}" | cat - ${f} > temp && mv temp ${f}
fi
done

View File

@ -40,7 +40,6 @@
<module>ospf</module>
<module>isis</module>
<module>snmp</module>
<module>bmv2</module>
<module>lisp</module>
<module>restconf</module>
<module>tl1</module>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<feature>onos-drivers-bmv2</feature>
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-provider-device/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-provider-packet/${project.version}</bundle>
<bundle>mvn:org.apache.thrift/libthrift/0.9.3</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-protocol-api/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-protocol-ctl/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-bmv2-protocol-thrift-api/${project.version}</bundle>
</feature>
</features>

View File

@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-bmv2-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-app</artifactId>
<packaging>bundle</packaging>
<description>ONOS BMv2 southbound providers</description>
<properties>
<onos.app.name>org.onosproject.bmv2</onos.app.name>
<onos.app.title>BMv2 Providers</onos.app.title>
<onos.app.category>Provider</onos.app.category>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-provider-device</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-provider-packet</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>onos-bmv2-providers</artifactId>
<groupId>org.onosproject</groupId>
<version>1.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>onos-bmv2-provider-device</artifactId>
<packaging>bundle</packaging>
<description>ONOS BMv2 device provider</description>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-common</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-bmv2-protocol-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,377 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.bmv2.device.impl;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.util.SharedScheduledExecutors;
import org.onosproject.bmv2.api.runtime.Bmv2Device;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.bmv2.api.service.Bmv2Controller;
import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
import org.onosproject.bmv2.api.service.Bmv2DeviceListener;
import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
import org.onosproject.common.net.AbstractDeviceProvider;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.config.basics.ConfigException;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortDescription;
import org.onosproject.net.device.PortStatistics;
import org.onosproject.net.driver.DefaultDriverData;
import org.onosproject.net.driver.DefaultDriverHandler;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.bmv2.api.runtime.Bmv2Device.*;
import static org.onosproject.net.Device.Type.SWITCH;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.getPortStatistics;
import static org.onosproject.provider.bmv2.device.impl.Bmv2PortStatisticsGetter.initCounters;
import static org.slf4j.LoggerFactory.getLogger;
/**
* BMv2 device provider.
*/
@Component(immediate = true)
public class Bmv2DeviceProvider extends AbstractDeviceProvider {
private static final String APP_NAME = "org.onosproject.bmv2";
private static final int POLL_PERIOD = 5_000; // milliseconds
private final Logger log = getLogger(this.getClass());
private final ExecutorService executorService = Executors
.newFixedThreadPool(16, groupedThreads("onos/bmv2", "device-discovery", log));
private final ScheduledExecutorService scheduledExecutorService = SharedScheduledExecutors.getPoolThreadExecutor();
private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
private final ConfigFactory cfgFactory = new InternalConfigFactory();
private final Map<DeviceId, DeviceDescription> lastDescriptions = Maps.newHashMap();
private final ConcurrentMap<DeviceId, Lock> deviceLocks = Maps.newConcurrentMap();
private final InternalDeviceListener deviceListener = new InternalDeviceListener();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry netCfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected Bmv2Controller controller;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected Bmv2DeviceContextService contextService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected Bmv2TableEntryService tableEntryService;
private ApplicationId appId;
private ScheduledFuture<?> poller;
/**
* Creates a Bmv2 device provider with the supplied identifier.
*/
public Bmv2DeviceProvider() {
super(new ProviderId("bmv2", "org.onosproject.provider.device"));
}
@Override
protected void activate() {
appId = coreService.registerApplication(APP_NAME);
netCfgService.registerConfigFactory(cfgFactory);
netCfgService.addListener(cfgListener);
controller.addDeviceListener(deviceListener);
if (poller != null) {
poller.cancel(false);
}
poller = scheduledExecutorService.scheduleAtFixedRate(this::pollDevices, 1_000, POLL_PERIOD, MILLISECONDS);
super.activate();
}
@Override
protected void deactivate() {
if (poller != null) {
poller.cancel(false);
}
controller.removeDeviceListener(deviceListener);
try {
lastDescriptions.forEach((did, value) -> {
executorService.execute(() -> disconnectDevice(did));
});
executorService.awaitTermination(1000, MILLISECONDS);
} catch (InterruptedException e) {
log.error("Device discovery threads did not terminate");
}
executorService.shutdownNow();
netCfgService.unregisterConfigFactory(cfgFactory);
netCfgService.removeListener(cfgListener);
super.deactivate();
}
@Override
public void triggerProbe(DeviceId deviceId) {
// Asynchronously trigger probe task.
executorService.execute(() -> executeProbe(deviceId));
}
private void executeProbe(DeviceId did) {
boolean reachable = isReachable(did);
log.debug("Probed device: id={}, reachable={}", did.toString(), reachable);
if (reachable) {
discoverDevice(did);
} else {
disconnectDevice(did);
}
}
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
log.debug("roleChanged() is not yet implemented");
// TODO: implement mastership handling
}
@Override
public boolean isReachable(DeviceId deviceId) {
return controller.isReacheable(deviceId);
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
log.warn("changePortState() not supported");
}
private void discoverDevice(DeviceId did) {
// Serialize discovery for the same device.
Lock lock = deviceLocks.computeIfAbsent(did, k -> new ReentrantLock());
lock.lock();
try {
log.debug("Starting device discovery... deviceId={}", did);
if (contextService.getContext(did) == null) {
// Device is a first timer.
log.info("Setting DEFAULT context for {}", did);
// It is important to do this before creating the device in the core
// so other services won't find a null context.
contextService.setDefaultContext(did);
// Abort discovery, we'll receive a new hello once the swap has been performed.
return;
}
DeviceDescription lastDescription = lastDescriptions.get(did);
DeviceDescription thisDescription = getDeviceDescription(did);
if (thisDescription != null) {
boolean descriptionChanged = lastDescription == null ||
(!Objects.equals(thisDescription, lastDescription) ||
!Objects.equals(thisDescription.annotations(), lastDescription.annotations()));
if (descriptionChanged || !deviceService.isAvailable(did)) {
resetDeviceState(did);
initPortCounters(did);
providerService.deviceConnected(did, thisDescription);
updatePortsAndStats(did);
}
lastDescriptions.put(did, thisDescription);
} else {
log.warn("Unable to get device description for {}", did);
lastDescriptions.put(did, lastDescription);
}
} finally {
lock.unlock();
}
}
private DeviceDescription getDeviceDescription(DeviceId did) {
Device device = deviceService.getDevice(did);
DeviceDescriptionDiscovery discovery = null;
if (device == null) {
// Device not yet in the core. Manually get a driver.
Driver driver = driverService.getDriver(MANUFACTURER, HW_VERSION, SW_VERSION);
if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
discovery = driver.createBehaviour(new DefaultDriverHandler(new DefaultDriverData(driver, did)),
DeviceDescriptionDiscovery.class);
}
} else if (device.is(DeviceDescriptionDiscovery.class)) {
discovery = device.as(DeviceDescriptionDiscovery.class);
}
if (discovery == null) {
log.warn("No DeviceDescriptionDiscovery behavior for device {}", did);
return null;
} else {
return discovery.discoverDeviceDetails();
}
}
private void resetDeviceState(DeviceId did) {
try {
controller.getAgent(did).resetState();
// Tables emptied. Reset all bindings.
tableEntryService.unbindAll(did);
} catch (Bmv2RuntimeException e) {
log.warn("Unable to reset {}: {}", did, e.toString());
}
}
private void initPortCounters(DeviceId did) {
try {
initCounters(controller.getAgent(did));
} catch (Bmv2RuntimeException e) {
log.warn("Unable to init counter on {}: {}", did, e.explain());
}
}
private void updatePortsAndStats(DeviceId did) {
Device device = deviceService.getDevice(did);
if (device.is(DeviceDescriptionDiscovery.class)) {
DeviceDescriptionDiscovery discovery = device.as(DeviceDescriptionDiscovery.class);
List<PortDescription> portDescriptions = discovery.discoverPortDetails();
if (portDescriptions != null) {
providerService.updatePorts(did, portDescriptions);
}
} else {
log.warn("No DeviceDescriptionDiscovery behavior for device {}", did);
}
try {
Collection<PortStatistics> portStats = getPortStatistics(controller.getAgent(did),
deviceService.getPorts(did));
providerService.updatePortStatistics(did, portStats);
} catch (Bmv2RuntimeException e) {
log.warn("Unable to get port statistics for {}: {}", did, e.explain());
}
}
private void disconnectDevice(DeviceId did) {
log.debug("Disconnecting device from core... deviceId={}", did);
providerService.deviceDisconnected(did);
lastDescriptions.remove(did);
}
private void pollDevices() {
for (Device device: deviceService.getAvailableDevices(SWITCH)) {
if (device.id().uri().getScheme().equals(SCHEME) &&
mastershipService.isLocalMaster(device.id())) {
executorService.execute(() -> pollingTask(device.id()));
}
}
}
private void pollingTask(DeviceId deviceId) {
log.debug("Polling device {}...", deviceId);
if (isReachable(deviceId)) {
updatePortsAndStats(deviceId);
} else {
disconnectDevice(deviceId);
}
}
/**
* Internal net-cfg config factory.
*/
private class InternalConfigFactory extends ConfigFactory<ApplicationId, Bmv2ProviderConfig> {
InternalConfigFactory() {
super(APP_SUBJECT_FACTORY, Bmv2ProviderConfig.class, "devices", true);
}
@Override
public Bmv2ProviderConfig createConfig() {
return new Bmv2ProviderConfig();
}
}
/**
* Internal net-cfg event listener.
*/
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
Bmv2ProviderConfig cfg = netCfgService.getConfig(appId, Bmv2ProviderConfig.class);
if (cfg != null) {
try {
cfg.getDevicesInfo().stream().forEach(info -> {
// FIXME: require also bmv2 internal device id from net-cfg (now is default 0)
Bmv2Device bmv2Device = new Bmv2Device(info.ip().toString(), info.port(), 0);
triggerProbe(bmv2Device.asDeviceId());
});
} catch (ConfigException e) {
log.error("Unable to read config: " + e);
}
} else {
log.error("Unable to read config (was null)");
}
}
@Override
public boolean isRelevant(NetworkConfigEvent event) {
return event.configClass().equals(Bmv2ProviderConfig.class) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
}
}
/**
* Listener triggered by the BMv2 controller each time a hello message is received.
*/
private class InternalDeviceListener implements Bmv2DeviceListener {
@Override
public void handleHello(Bmv2Device device, int instanceId, String jsonConfigMd5) {
log.debug("Received hello from {}", device);
triggerProbe(device.asDeviceId());
}
}
}

View File

@ -1,95 +0,0 @@
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.bmv2.device.impl;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.tuple.Pair;
import org.onosproject.bmv2.api.runtime.Bmv2Action;
import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
import org.onosproject.net.Port;
import org.onosproject.net.device.DefaultPortStatistics;
import org.onosproject.net.device.PortStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
/**
* Utility class to read port statistics from a BMv2 device.
*/
final class Bmv2PortStatisticsGetter {
// TODO: make counters configuration dependent
private static final String TABLE_NAME = "port_count_table";
private static final String ACTION_NAME = "count_packet";
private static final String EGRESS_COUNTER = "egress_port_counter";
private static final String INGRESS_COUNTER = "ingress_port_counter";
private static final Logger log = LoggerFactory.getLogger(Bmv2PortStatisticsGetter.class);
private Bmv2PortStatisticsGetter() {
// ban constructor.
}
/**
* Returns a collection of port statistics for given ports using the given BMv2 device agent.
*
* @param deviceAgent a device agent
* @param ports a collection of ports
* @return a collection of port statistics
*/
static Collection<PortStatistics> getPortStatistics(Bmv2DeviceAgent deviceAgent, Collection<Port> ports) {
List<PortStatistics> ps = Lists.newArrayList();
for (Port port : ports) {
int portNumber = (int) port.number().toLong();
try {
Pair<Long, Long> egressCounter = deviceAgent.readCounter(EGRESS_COUNTER, portNumber);
Pair<Long, Long> ingressCounter = deviceAgent.readCounter(INGRESS_COUNTER, portNumber);
ps.add(DefaultPortStatistics.builder()
.setPort(portNumber)
.setBytesSent(egressCounter.getLeft())
.setPacketsSent(egressCounter.getRight())
.setBytesReceived(ingressCounter.getLeft())
.setPacketsReceived(ingressCounter.getRight())
.build());
} catch (Bmv2RuntimeException e) {
log.info("Unable to read port statistics from {}: {}", port, e.explain());
}
}
return ps;
}
/**
* Initialize port counters on the given device agent.
*
* @param deviceAgent a device agent.
*/
static void initCounters(Bmv2DeviceAgent deviceAgent) {
try {
deviceAgent.setTableDefaultAction(TABLE_NAME, Bmv2Action.builder().withName(ACTION_NAME).build());
} catch (Bmv2RuntimeException e) {
log.debug("Failed to provision counters on {}: {}", deviceAgent.deviceId(), e.explain());
}
}
}

Some files were not shown because too many files have changed in this diff Show More