mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-12-11 04:12:10 +01:00
Pruned old BMv2 stuff
...making space for new BMv2 support via P4Runtime. Change-Id: Ia34c1a152c0e6e006fd3b86afc5086316264b6f6
This commit is contained in:
parent
1022a4ef8e
commit
43740a078d
@ -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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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"
|
||||
]
|
||||
]
|
||||
}
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
@ -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>
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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)));
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
]
|
||||
]
|
||||
}
|
||||
@ -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>
|
||||
@ -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_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -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"
|
||||
]
|
||||
]
|
||||
}
|
||||
@ -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)));
|
||||
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user