<artifactId>unimgr-cisco-xr-driver</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ovs-driver</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>unimgr-features</artifactId>
<mdsal.model.version>0.10.0-SNAPSHOT</mdsal.model.version>
<netconf.version>1.2.0-SNAPSHOT</netconf.version>
<genius.version>0.2.0-SNAPSHOT</genius.version>
+ <l2switch.version>0.5.0-SNAPSHOT</l2switch.version>
<configfile.directory>etc/opendaylight/karaf</configfile.directory>
+ <openflowplugin.version>0.4.0-SNAPSHOT</openflowplugin.version>
+ <openflowjava.version>0.9.0-SNAPSHOT</openflowjava.version>
+ <ovsdb.version>1.4.0-SNAPSHOT</ovsdb.version>
</properties>
<dependencyManagement>
<dependencies>
<type>pom</type>
<scope>import</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.l2switch</groupId>
+ <artifactId>l2switch-artifacts</artifactId>
+ <version>${l2switch.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<artifactId>cisco-xrmodels</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ovs-driver</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.unimgr</groupId>
<artifactId>edgeassure-1000</artifactId>
<artifactId>unimgr-legato-api</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.l2switch</groupId>
+ <artifactId>features-l2switch</artifactId>
+ <classifier>features</classifier>
+ <version>${l2switch.version}</version>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
</dependencies>
</project>
<repository>mvn:org.opendaylight.netconf/features-netconf-connector/{{VERSION}}/xml/features</repository>
<repository>mvn:org.opendaylight.genius/genius-features/{{VERSION}}/xml/features</repository>
<repository>mvn:org.opendaylight.netvirt/vpnservice-features/{{VERSION}}/xml/features</repository>
+ <repository>mvn:org.opendaylight.l2switch/features-l2switch/{{VERSION}}/xml/features</repository>
<feature name='odl-unimgr-api' version='${project.version}'
description='OpenDaylight :: UniMgr :: api'>
<bundle>mvn:org.opendaylight.unimgr/unimgr-cisco-xr-driver/{{VERSION}}</bundle>
</feature>
+ <feature name="odl-unimgr-ovs-driver" version="${project.version}" description='OpenDaylight :: UniMgr :: OVSDB Driver'>
+ <feature version='${project.version}'>odl-unimgr</feature>
+ <feature version="${l2switch.version}">odl-l2switch-all</feature>
+ <feature version="${restconf.version}">odl-restconf</feature>
+ <feature version="${ovsdb.version}">odl-ovsdb-library</feature>
+ <feature version="${ovsdb.version}">odl-ovsdb-southbound-api</feature>
+ <feature version="${ovsdb.version}">odl-ovsdb-southbound-impl</feature>
+ <feature version="${ovsdb.version}">odl-ovsdb-southbound-impl-rest</feature>
+ <feature version="${openflowjava.version}">odl-openflowjava-protocol</feature>
+ <feature version="${openflowplugin.version}">odl-openflowplugin-flow-services-rest</feature>
+ <feature version="${openflowplugin.version}">odl-openflowplugin-flow-services</feature>
+ <feature version="${openflowplugin.version}">odl-openflowplugin-southbound</feature>
+ <bundle>mvn:org.opendaylight.unimgr/ovs-driver/{{VERSION}}</bundle>
+ </feature>
+
</features>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-parent</artifactId>
+ <version>0.6.0-SNAPSHOT</version>
+ <relativePath/>
+ </parent>
+
+ <properties>
+ <checkstyle.skip>true</checkstyle.skip>
+ <powermock.version>1.6.4</powermock.version>
+ <openflow.version>0.3.0-Boron</openflow.version>
+ </properties>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.opendaylight.unimgr</groupId>
+ <artifactId>ovs-driver</artifactId>
+ <version>0.2.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>unimgr-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- dependencies to use AbstractDataBrokerTest -->
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-support</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-reflect</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal.model</groupId>
+ <artifactId>ietf-yang-types-20130715</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ <version>${openflow.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.model</groupId>
+ <artifactId>model-flow-statistics</artifactId>
+ <version>${openflow.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.activator;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.unimgr.mef.nrp.common.ResourceActivator;
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+import org.opendaylight.unimgr.mef.nrp.ovs.transaction.TableTransaction;
+import org.opendaylight.unimgr.mef.nrp.ovs.transaction.TopologyTransaction;
+import org.opendaylight.unimgr.mef.nrp.ovs.util.OpenFlowUtils;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forwardingconstruct.FcPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author marek.ryznar@amartus.com
+ */
+public class OvsActivator implements ResourceActivator {
+
+ private final DataBroker dataBroker;
+ private static final Logger LOG = LoggerFactory.getLogger(OvsActivator.class);
+
+ public OvsActivator(DataBroker dataBroker) {
+ this.dataBroker = dataBroker;
+ }
+
+ @Override
+ public void activate(String nodeName, String outerName, String innerName, FcPort flowPoint, FcPort neighbor, long mtu) throws TransactionCommitFailedException, ResourceNotAvailableException {
+ // Transaction - Get Open vSwitch node and its flow table
+ String portName = flowPoint.getTp().getValue();
+ TopologyTransaction topologyTransaction = new TopologyTransaction(dataBroker);
+ Node node = topologyTransaction.readNode(portName);
+ Table table = OpenFlowUtils.getTable(node);
+
+ // Prepare list of flows to be added/removed
+ List<Flow> flowsToWrite = new ArrayList<>();
+ List<Flow> flowsToDelete = new ArrayList<>();
+ List<Link> interswitchLinks = topologyTransaction.readInterswitchLinks(node);
+ if (!OpenFlowUtils.isTablePreconfigured(table)) {
+ LOG.debug("Table is not preconfigured. Adding base flows.");
+ flowsToWrite.addAll(OpenFlowUtils.getBaseFlows(interswitchLinks));
+ flowsToDelete.addAll(OpenFlowUtils.getExistingFlows(table));
+ }
+ OvsActivatorHelper ovsActivatorHelper = new OvsActivatorHelper(topologyTransaction, flowPoint);
+ String openFlowPortName = ovsActivatorHelper.getOpenFlowPortName();
+ int externalVlanId = ovsActivatorHelper.getServiceVlanId();
+ int internalVlanId = ovsActivatorHelper.getInternalVlanId();
+ flowsToWrite.addAll(OpenFlowUtils.getVlanFlows(openFlowPortName, externalVlanId, internalVlanId, interswitchLinks, outerName));
+
+ // Transaction - Add flows related to service to table and remove unnecessary flows
+ TableTransaction tableTransaction = new TableTransaction(dataBroker, node, table);
+ tableTransaction.deleteFlows(flowsToDelete, true);
+ tableTransaction.writeFlows(flowsToWrite);
+ }
+
+ @Override
+ public void deactivate(String nodeName, String outerName, String innerName, FcPort flowPoint, FcPort neighbor, long mtu) throws TransactionCommitFailedException, ResourceNotAvailableException {
+ // Transaction - Get Open vSwitch node and its flow table
+ String portName = flowPoint.getTp().getValue();
+ TopologyTransaction topologyTransaction = new TopologyTransaction(dataBroker);
+ Node node = topologyTransaction.readNode(portName);
+ Table table = OpenFlowUtils.getTable(node);
+
+ // Get list of flows to be removed
+ List<Flow> flowsToDelete = OpenFlowUtils.getServiceFlows(table, outerName);
+
+ // Transaction - Remove flows related to service from table
+ TableTransaction tableTransaction = new TableTransaction(dataBroker, node, table);
+ tableTransaction.deleteFlows(flowsToDelete, false);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.activator;
+
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+import org.opendaylight.unimgr.mef.nrp.ovs.exception.VlanNotSetException;
+import org.opendaylight.unimgr.mef.nrp.ovs.transaction.TopologyTransaction;
+import org.opendaylight.unimgr.mef.nrp.ovs.util.VlanUtils;
+import org.opendaylight.unimgr.utils.NullAwareDatastoreGetter;
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.FcPort1;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forwardingconstruct.FcPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class for OvsDriver activation.
+ *
+ * @author jakub.niezgoda@amartus.com
+ */
+class OvsActivatorHelper {
+ private List<NullAwareDatastoreGetter<Node>> nodes;
+ private FcPort flowPoint;
+ private Map<String, String> portMap;
+
+ private static final String CTAG_VLAN_ID_NOT_SET_ERROR_MESSAGE = "C-Tag VLAN Id not set for termination point '%s'.";
+ private static final String FC_PORT_NOT_AUGMENTED_ERROR_MESSAGE = "Forwarding Construct port '%s' does not have '%s' augmentation.";
+
+ private static final Logger LOG = LoggerFactory.getLogger(OvsActivatorHelper.class);
+
+ OvsActivatorHelper(TopologyTransaction topologyTransaction, FcPort flowPoint) {
+ this.nodes = topologyTransaction.readNodes();
+ this.flowPoint = flowPoint;
+ this.portMap = createPortMap(nodes);
+ }
+
+ /**
+ * Returns VLAN Id of the service
+ *
+ * @return Integer with VLAN Id
+ */
+ int getServiceVlanId() throws ResourceNotAvailableException {
+ FcPort1 fcPort1 = flowPoint.getAugmentation(FcPort1.class);
+ String tpName = flowPoint.getTp().getValue();
+
+ if (fcPort1 != null) {
+ if (fcPort1.getCTagVlanId() != null) {
+ return fcPort1.getCTagVlanId().getValue().intValue();
+ } else {
+ LOG.warn(String.format(CTAG_VLAN_ID_NOT_SET_ERROR_MESSAGE, tpName));
+ throw new VlanNotSetException(String.format(CTAG_VLAN_ID_NOT_SET_ERROR_MESSAGE, tpName));
+ }
+ } else {
+ String fcPort1ClassName = FcPort1.class.toString();
+ LOG.warn(String.format(FC_PORT_NOT_AUGMENTED_ERROR_MESSAGE, tpName, fcPort1ClassName));
+ throw new ResourceNotAvailableException(String.format(FC_PORT_NOT_AUGMENTED_ERROR_MESSAGE, tpName, fcPort1ClassName));
+ }
+ }
+
+ /**
+ * Returns VLAN Id to be used internally in OvSwitch network
+ *
+ * @return Integer with VLAN Id
+ */
+ int getInternalVlanId() throws ResourceNotAvailableException {
+ VlanUtils vlanUtils = new VlanUtils(nodes);
+ int serviceVlanId = getServiceVlanId();
+
+ if (vlanUtils.isVlanInUse(serviceVlanId)) {
+ LOG.debug("VLAN ID = '" + serviceVlanId + "' already in use.");
+ return vlanUtils.generateVlanID();
+ } else {
+ LOG.debug("VLAN ID = '" + serviceVlanId + "' not in use.");
+ return serviceVlanId;
+ }
+ }
+
+ /**
+ * Returns port name in openflow plugin convention (e.g. openflow:1:4)
+ *
+ * @return String with port name
+ */
+ String getOpenFlowPortName() {
+ return portMap.get(flowPoint.getTp().getValue());
+ }
+
+ private Map<String, String> createPortMap(List<NullAwareDatastoreGetter<Node>> nodes) {
+ Map<String, String> portMap = new HashMap<>();
+ for (NullAwareDatastoreGetter<Node> node : nodes) {
+ if (node.get().isPresent()){
+ for (NodeConnector nodeConnector : node.get().get().getNodeConnector()) {
+ String ofName = nodeConnector.getId().getValue();
+ FlowCapableNodeConnector flowCapableNodeConnector = nodeConnector.getAugmentation(FlowCapableNodeConnector.class);
+ String name = flowCapableNodeConnector.getName();
+ portMap.put(name, ofName);
+ }
+ }
+ }
+ return portMap;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.driver;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.unimgr.mef.nrp.api.ActivationDriver;
+import org.opendaylight.unimgr.mef.nrp.api.ActivationDriverBuilder;
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+import org.opendaylight.unimgr.mef.nrp.ovs.activator.OvsActivator;
+import org.opendaylight.unimgr.utils.CapabilitiesService;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstruct;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forwardingconstruct.FcPort;
+
+import java.util.Optional;
+
+import static org.opendaylight.unimgr.utils.CapabilitiesService.Capability.Mode.AND;
+import static org.opendaylight.unimgr.utils.CapabilitiesService.NodeContext.NodeCapability.OVSDB;
+
+/**
+ * @author marek.ryznar@amartus.com
+ */
+public class OvsDriver implements ActivationDriverBuilder {
+
+ private OvsActivator activator;
+ private final DataBroker dataBroker;
+ private static final String GROUP_NAME = "local";
+ private static final long MTU_VALUE = 1522;
+
+ public OvsDriver(DataBroker dataBroker){
+ this.dataBroker = dataBroker;
+ activator = new OvsActivator(dataBroker);
+ }
+
+ @Override
+ public Optional<ActivationDriver> driverFor(FcPort port, BuilderContext context) {
+ CapabilitiesService capabilitiesService = new CapabilitiesService(dataBroker);
+ if(capabilitiesService.nodeByPort(port).isSupporting(AND, OVSDB)) {
+ return Optional.of(getDriver());
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<ActivationDriver> driverFor(FcPort aPort, FcPort zPort, BuilderContext context) {
+ CapabilitiesService capabilitiesService = new CapabilitiesService(dataBroker);
+ if(capabilitiesService.nodeByPort(aPort).isSupporting(AND, OVSDB) &&
+ capabilitiesService.nodeByPort(zPort).isSupporting(AND, OVSDB)) {
+ return Optional.of(getDriver());
+ }
+ return Optional.empty();
+ }
+
+ private ActivationDriver getDriver() {
+ return new ActivationDriver() {
+ private FcPort aEnd;
+ private FcPort zEnd;
+ private String uuid;
+
+ @Override
+ public void commit() {
+
+ }
+
+ @Override
+ public void rollback() {
+
+ }
+
+ @Override
+ public void initialize(FcPort from, FcPort to, ForwardingConstruct context) {
+ this.zEnd = to;
+ this.aEnd = from;
+ this.uuid = context.getUuid();
+ }
+
+ @Override
+ public void activate() throws TransactionCommitFailedException, ResourceNotAvailableException {
+ String aEndNodeName = aEnd.getNode().getValue();
+ activator.activate(aEndNodeName, uuid, GROUP_NAME, aEnd, zEnd, MTU_VALUE);
+ }
+
+ @Override
+ public void deactivate() throws TransactionCommitFailedException, ResourceNotAvailableException {
+ String aEndNodeName = aEnd.getNode().getValue();
+ activator.deactivate(aEndNodeName, uuid, GROUP_NAME, aEnd, zEnd, MTU_VALUE);
+ }
+
+ @Override
+ public int priority() {
+ return 0;
+ }
+ };
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.exception;
+
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+
+/**
+ * Exception thrown when C-Tag VLAN ID is not set for termination point
+ *
+ * @author jakub.niezgoda@amartus.com
+ */
+public class VlanNotSetException extends ResourceNotAvailableException {
+ public VlanNotSetException(String message){
+ super(message);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.exception;
+
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+
+/**
+ * Exception thrown when VLAN pool is exhausted
+ *
+ * @author marek.ryznar@amartus.com
+ */
+public class VlanPoolExhaustedException extends ResourceNotAvailableException {
+ public VlanPoolExhaustedException(String message){
+ super(message);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.transaction;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+
+/**
+ * Performs output (write/delete) transactions related to openflow flows
+ * during OvsDriver activation/deactivation.
+ *
+ * @author jakub.niezgoda@amartus.com
+ */
+public class TableTransaction {
+
+ private DataBroker dataBroker;
+ private InstanceIdentifier<Table> tableInstanceId;
+
+ private static final Logger LOG = LoggerFactory.getLogger(TableTransaction.class);
+
+ /**
+ * Creates and initialize TableTransaction object
+ *
+ * @param dataBroker access to data tree store
+ * @param node openflow network node
+ * @param table flow table of node
+ */
+ public TableTransaction(DataBroker dataBroker, Node node, Table table) {
+ this.dataBroker = dataBroker;
+ this.tableInstanceId = getTableIid(node.getKey(), table.getKey());
+ }
+
+ /**
+ * Writes flows to the flow table
+ *
+ * @param flows list of flows to be added
+ * @throws TransactionCommitFailedException if writing flows transaction fails
+ */
+ public void writeFlows(List<Flow> flows) throws TransactionCommitFailedException {
+ for (Flow flow : flows) {
+ writeFlow(flow);
+ }
+ }
+
+ /**
+ * Writes flow to the flow table
+ *
+ * @param flow flow to be added
+ * @throws TransactionCommitFailedException if writing flow transaction fails
+ */
+ public void writeFlow(Flow flow) throws TransactionCommitFailedException {
+ WriteTransaction transaction = dataBroker.newWriteOnlyTransaction();
+ LOG.debug("Writing flow '" + flow.getId().getValue() + "' to " + LogicalDatastoreType.CONFIGURATION + " data store.");
+ transaction.put(LogicalDatastoreType.CONFIGURATION, getFlowIid(flow), flow, true);
+ transaction.submit().checkedGet();
+ }
+
+ /**
+ * Deletes flows from the flow table
+ *
+ * @param flows list of flows to be deleted
+ * @param writeToConfigurationDataStoreFirst if set flows are written to CONFIGURATION data store before deletion
+ * @throws TransactionCommitFailedException if writing/deleting flows transaction fails
+ */
+ public void deleteFlows(List<Flow> flows, boolean writeToConfigurationDataStoreFirst) throws TransactionCommitFailedException {
+ if (writeToConfigurationDataStoreFirst) {
+ writeFlows(flows);
+ }
+ for (Flow flow : flows) {
+ deleteFlow(flow);
+ }
+ }
+
+ /**
+ * Deletes flow from the flow table
+ *
+ * @param flow flow to be deleted
+ * @throws TransactionCommitFailedException if deleting flow transaction fails
+ */
+ public void deleteFlow(Flow flow) throws TransactionCommitFailedException {
+ WriteTransaction deleteTransaction = dataBroker.newWriteOnlyTransaction();
+ LOG.debug("Deleting flow '" + flow.getId().getValue() + "' from " + LogicalDatastoreType.CONFIGURATION + " data store.");
+ deleteTransaction.delete(LogicalDatastoreType.CONFIGURATION, getFlowIid(flow));
+ deleteTransaction.submit().checkedGet();
+ }
+
+ private InstanceIdentifier<Table> getTableIid(NodeKey nodeKey, TableKey tableKey){
+ return InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class, nodeKey)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, tableKey)
+ .build();
+ }
+
+ private InstanceIdentifier<Flow> getFlowIid(Flow flow){
+ return tableInstanceId.child(Flow.class, flow.getKey());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.transaction;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+import org.opendaylight.unimgr.utils.MdsalUtils;
+import org.opendaylight.unimgr.utils.NullAwareDatastoreGetter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Performs reading transactions related to openflow topology
+ * during OvsDriver activation/deactivation.
+ *
+ * @author jakub.niezgoda@amartus.com
+ */
+public class TopologyTransaction {
+ private DataBroker dataBroker;
+
+ private static final String NODE_NOT_FOUND_ERROR_MESSAGE = "Node with port '%s' not found in OPERATIONAL data store.";
+ private static final String LINKS_NOT_FOUND_ERROR_MESSAGE = "Links for node '%s' not found in OPERATIONAL data store.";
+ private static final String TOPOLOGY_NOT_FOUND_ERROR_MESSAGE = "Topology '%s' not found in OPERATIONAL data store.";
+
+ private static final String FLOW_TOPOLOGY_NAME = "flow:1";
+ private static final String INTERSWITCH_LINK_ID_REGEX = "openflow:\\d+:\\d+";
+
+ private static final Logger LOG = LoggerFactory.getLogger(TopologyTransaction.class);
+
+ /**
+ * Creates and initialize TopologyTransaction object
+ *
+ * @param dataBroker access to data tree store
+ */
+ public TopologyTransaction(DataBroker dataBroker) {
+ this.dataBroker = dataBroker;
+ }
+
+ /**
+ * Returns list of nodes in openflow topology
+ *
+ * @return list of nodes
+ */
+ public List<NullAwareDatastoreGetter<Node>> readNodes() {
+ return new NullAwareDatastoreGetter<Nodes>(MdsalUtils.readOptional(dataBroker, LogicalDatastoreType.OPERATIONAL, getNodesInstanceId())).collectMany(x -> x::getNode);
+ }
+
+ /**
+ * Returns openflow node containing port portName
+ *
+ * @param portName node's port name
+ * @return node
+ * @throws ResourceNotAvailableException if node for the specified port name was not found
+ */
+ public Node readNode(String portName) throws ResourceNotAvailableException {
+ for(NullAwareDatastoreGetter<Node> node : readNodes()) {
+ if(node.get().isPresent()){
+ for(NodeConnector nodeConnector:node.get().get().getNodeConnector()) {
+ FlowCapableNodeConnector flowCapableNodeConnector
+ = nodeConnector.getAugmentation(FlowCapableNodeConnector.class);
+ if (portName.equals(flowCapableNodeConnector.getName())) {
+ return node.get().get();
+ }
+ }
+ }
+ }
+
+ LOG.warn(String.format(NODE_NOT_FOUND_ERROR_MESSAGE, portName));
+ throw new ResourceNotAvailableException(String.format(NODE_NOT_FOUND_ERROR_MESSAGE, portName));
+ }
+
+ /**
+ * Returns links associated with specified node
+ *
+ * @param node openflow node
+ * @return list of links
+ * @throws ResourceNotAvailableException if openflow topology or links for the specified node were not found
+ */
+ public List<Link> readLinks(Node node) throws ResourceNotAvailableException {
+ Optional<Topology> flowTopology
+ = MdsalUtils.readTopology(dataBroker, LogicalDatastoreType.OPERATIONAL, FLOW_TOPOLOGY_NAME);
+
+ if (flowTopology.isPresent()) {
+ String nodeId = node.getId().getValue();
+ List<Link> links = flowTopology.get().getLink()
+ .stream()
+ .filter(link -> link.getLinkId().getValue().startsWith(nodeId))
+ .collect(Collectors.toList());
+ if (links.isEmpty()) {
+ LOG.warn(String.format(LINKS_NOT_FOUND_ERROR_MESSAGE, nodeId));
+ throw new ResourceNotAvailableException(String.format(LINKS_NOT_FOUND_ERROR_MESSAGE, nodeId));
+ } else {
+ return links;
+ }
+ } else {
+ LOG.warn(String.format(TOPOLOGY_NOT_FOUND_ERROR_MESSAGE, FLOW_TOPOLOGY_NAME));
+ throw new ResourceNotAvailableException(String.format(TOPOLOGY_NOT_FOUND_ERROR_MESSAGE, FLOW_TOPOLOGY_NAME));
+ }
+ }
+
+ /**
+ * Returns interswitch links (links between openflow nodes) associated with specified node
+ *
+ * @param node openflow node
+ * @return list of links
+ * @throws ResourceNotAvailableException if openflow topology or links for the specified node were not found
+ */
+ public List<Link> readInterswitchLinks(Node node) throws ResourceNotAvailableException {
+ List<Link> links = readLinks(node);
+ return links.stream()
+ .filter(link -> link.getLinkId().getValue().matches(INTERSWITCH_LINK_ID_REGEX))
+ .collect(Collectors.toList());
+ }
+
+ private InstanceIdentifier<Nodes> getNodesInstanceId() {
+ return InstanceIdentifier.builder(Nodes.class).build();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.util;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.drop.action._case.DropActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.pop.vlan.action._case.PopVlanActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.field._case.SetFieldBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Utility class providing common operations for Instruction and Action objects.
+ *
+ * @author jakub.niezgoda@amartus.com
+ */
+
+class ActionUtils {
+ private static final int MAX_LENGTH = 65535;
+ private static final int VLAN_TAG_ETHERNET_TYPE = 33024;
+
+ private final static int NUMBER_OF_PORTNAME_PARTS = 3;
+ private final static int INDEX_OF_PORT_NUMBER = 2;
+ private final static String PORTNAME_PARTS_SEPARATOR = ":";
+
+ private final static String INVALID_PORT_NAME_FORMAT_ERROR_MESSAGE = "Port name '%s' has invalid format.";
+
+ static Instructions createInstructions(List<Action> actions) {
+ InstructionsBuilder instructionsBuilder = new InstructionsBuilder();
+
+ List<Instruction> instructions = new LinkedList<>();
+ instructions.add(createInstruction(actions));
+
+ return instructionsBuilder.setInstruction(instructions).build();
+ }
+
+ private static Instruction createInstruction(List<Action> actions) {
+ ApplyActionsBuilder applyActionsBuilder = new ApplyActionsBuilder();
+ applyActionsBuilder.setAction(actions);
+ ApplyActionsCaseBuilder applyActionsCaseBuilder = new ApplyActionsCaseBuilder();
+ applyActionsCaseBuilder.setApplyActions(applyActionsBuilder.build());
+
+ InstructionBuilder instructionBuilder = new InstructionBuilder();
+ instructionBuilder.setOrder(0);
+ instructionBuilder.setInstruction(applyActionsCaseBuilder.build());
+
+ return instructionBuilder.build();
+ }
+
+ static Action createPopVlanAction(int order) {
+ ActionBuilder actionBuilder = new ActionBuilder();
+
+ PopVlanActionCaseBuilder popVlanActionCaseBuilder = new PopVlanActionCaseBuilder();
+ PopVlanActionBuilder popVlanActionBuilder = new PopVlanActionBuilder();
+ popVlanActionCaseBuilder.setPopVlanAction(popVlanActionBuilder.build());
+
+ actionBuilder.setOrder(order);
+ actionBuilder.setAction(popVlanActionCaseBuilder.build());
+ return actionBuilder.build();
+ }
+
+ static Action createDropAction(int order) {
+ ActionBuilder actionBuilder = new ActionBuilder();
+
+ DropActionCaseBuilder dropActionCaseBuilder = new DropActionCaseBuilder();
+ dropActionCaseBuilder.setDropAction(new DropActionBuilder().build());
+
+ actionBuilder.setOrder(order);
+ actionBuilder.setAction(dropActionCaseBuilder.build());
+
+ return actionBuilder.build();
+ }
+
+
+ static Action createOutputAction(String port, int order) {
+ ActionBuilder actionBuilder = new ActionBuilder();
+
+ OutputActionCaseBuilder outputActionCaseBuilder = new OutputActionCaseBuilder();
+ OutputActionBuilder outputActionBuilder = new OutputActionBuilder();
+ outputActionBuilder.setOutputNodeConnector(new Uri(getPortNumber(port)));
+ outputActionBuilder.setMaxLength(MAX_LENGTH);
+ outputActionCaseBuilder.setOutputAction(outputActionBuilder.build());
+
+ actionBuilder.setOrder(order);
+ actionBuilder.setAction(outputActionCaseBuilder.build());
+ return actionBuilder.build();
+ }
+
+
+ static Action createSetVlanIdAction(int vlan, int order) {
+ ActionBuilder actionBuilder = new ActionBuilder();
+
+ VlanId vlanId = new VlanId(vlan);
+
+ VlanIdBuilder vlanIdBuilder = new VlanIdBuilder();
+ vlanIdBuilder.setVlanIdPresent(true);
+ vlanIdBuilder.setVlanId(vlanId);
+
+ VlanMatchBuilder vlanMatchBuilder = new VlanMatchBuilder();
+ vlanMatchBuilder.setVlanId(vlanIdBuilder.build());
+
+ SetFieldBuilder setFieldBuilder = new SetFieldBuilder();
+ setFieldBuilder.setVlanMatch(vlanMatchBuilder.build());
+
+ SetFieldCaseBuilder setFieldCaseBuilder = new SetFieldCaseBuilder();
+ setFieldCaseBuilder.setSetField(setFieldBuilder.build());
+
+ actionBuilder.setAction(setFieldCaseBuilder.build());
+ actionBuilder.setOrder(order);
+
+ return actionBuilder.build();
+ }
+
+ static Action createPushVlanAction(int order) {
+ ActionBuilder actionBuilder = new ActionBuilder();
+ actionBuilder.setOrder(order);
+
+ PushVlanActionBuilder pushVlanActionBuilder = new PushVlanActionBuilder();
+ pushVlanActionBuilder.setEthernetType(VLAN_TAG_ETHERNET_TYPE);
+ PushVlanAction pushVlanAction = pushVlanActionBuilder.build();
+
+ PushVlanActionCaseBuilder pushVlanActionCaseBuilder = new PushVlanActionCaseBuilder();
+ pushVlanActionCaseBuilder.setPushVlanAction(pushVlanAction);
+
+ actionBuilder.setAction(pushVlanActionCaseBuilder.build());
+
+ return actionBuilder.build();
+ }
+
+ /**
+ * Returns port number basing on openflow port name (e.g. 4 will be returned from 'openflow:3:4')
+ *
+ * @param portName string containing port name
+ * @return port number
+ */
+ private static String getPortNumber(String portName) {
+ String[] splittedPortName = portName.split(PORTNAME_PARTS_SEPARATOR);
+ if (splittedPortName.length == NUMBER_OF_PORTNAME_PARTS) {
+ return splittedPortName[INDEX_OF_PORT_NUMBER];
+ } else {
+ throw new IllegalArgumentException(String.format(INVALID_PORT_NAME_FORMAT_ERROR_MESSAGE, portName));
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
+
+/**
+ * Utility class providing common operations for Match objects.
+ *
+ * @author jakub.niezgoda@amartus.com
+ */
+
+class MatchUtils {
+
+ static Match createInPortMatch(String inPort) {
+ return new MatchBuilder().setInPort(new NodeConnectorId(inPort)).build();
+ }
+
+ static Match createVlanMatch(int vlanID, String port) {
+ MatchBuilder matchBuilder = new MatchBuilder();
+ VlanIdBuilder vlanIdBuilder = new VlanIdBuilder().setVlanIdPresent(true).setVlanId(new VlanId(vlanID));
+ VlanMatchBuilder vlanMatchBuilder = new VlanMatchBuilder().setVlanId(vlanIdBuilder.build());
+
+ return matchBuilder.setInPort(new NodeConnectorId(port))
+ .setVlanMatch(vlanMatchBuilder.build())
+ .build();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.util;
+
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Class responsible for managing OpenFlow rules in OVS.
+ *
+ * @author marek.ryznar@amartus.com
+ */
+public class OpenFlowUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(OpenFlowUtils.class);
+ private static final String INTERSWITCH_FLOW_ID_PREFIX = "interswitch";
+ private static final String FLOW_ID_MIDDLE_PART = "vlan";
+ private static final String DROP_FLOW_ID = "default-DROP";
+ private static final Short FLOW_TABLE_ID = 0;
+ private static final int VLAN_FLOW_PRIORITY = 20;
+ private static final int INTERSWITCH_FLOW_PRIORITY = 10;
+ private static final int DROP_FLOW_PRIORITY = 0;
+
+ private static final String FLOW_TABLE_NOT_PRESENT_ERROR_MESSAGE = "Flow table is not present in node '%s'.";
+ private static final String NODE_NOT_AUGMENTED_ERROR_MESSAGE = "Node '%s' does not have '%s' augmentation.";
+ /**
+ * Checks if flow table contains base flows
+ *
+ * @param table - openflow node's table
+ * @return true if table is preconfigured, false in other case
+ */
+ public static boolean isTablePreconfigured(Table table) {
+ boolean isTablePreconfigured = false;
+
+ for (Flow flow : table.getFlow()) {
+ if (flow.getId().getValue().startsWith(INTERSWITCH_FLOW_ID_PREFIX)) {
+ isTablePreconfigured = true;
+ break;
+ }
+ }
+
+ LOG.debug("Table is " + (isTablePreconfigured ? "" : "not") + " preconfigured with default flows.");
+ return isTablePreconfigured;
+ }
+
+ /**
+ * Returns flow table for the specified node
+ *
+ * @param node openflow node
+ * @return flow table
+ * @throws ResourceNotAvailableException if node is not augmented with FlowCapableNode class or flow table is not present in node
+ */
+ public static Table getTable(Node node) throws ResourceNotAvailableException {
+ String nodeId = node.getId().getValue();
+ FlowCapableNode flowCapableNode = node.getAugmentation(FlowCapableNode.class);
+ if (flowCapableNode == null) {
+ LOG.warn(String.format(NODE_NOT_AUGMENTED_ERROR_MESSAGE, nodeId, FlowCapableNode.class.toString()));
+ throw new ResourceNotAvailableException(String.format(NODE_NOT_AUGMENTED_ERROR_MESSAGE, nodeId, FlowCapableNode.class.toString()));
+ }
+
+ Optional<Table> flowTable = flowCapableNode.getTable()
+ .stream()
+ .filter(table -> table.getId().equals(FLOW_TABLE_ID))
+ .findFirst();
+ if (!flowTable.isPresent()) {
+ LOG.warn(String.format(FLOW_TABLE_NOT_PRESENT_ERROR_MESSAGE, nodeId));
+ throw new ResourceNotAvailableException(String.format(FLOW_TABLE_NOT_PRESENT_ERROR_MESSAGE, nodeId));
+ }
+
+ return flowTable.get();
+ }
+
+ /**
+ * Returns list of flows for passing traffic with given VLAN ID
+ *
+ * @param servicePort port on which service is activated (format: openflow:[node]:[port])
+ * @param externalVlanId VLAN ID used outside OvSwitch network
+ * @param internalVlanId VLAN ID used internally in OvSwitch network
+ * @param interswitchLinks list of interswitch links for the node on which service is activated
+ * @param serviceName service name (used as prefix for flow IDs)
+ * @return list of flows
+ */
+ public static List<Flow> getVlanFlows(String servicePort, int externalVlanId, int internalVlanId, List<Link> interswitchLinks, String serviceName) {
+ List<Flow> flows = new ArrayList<>();
+ flows.addAll(createVlanPassingFlows(servicePort, externalVlanId, internalVlanId, serviceName, interswitchLinks));
+ flows.add(createVlanIngressFlow(servicePort, externalVlanId, internalVlanId, serviceName, interswitchLinks));
+
+ return flows;
+ }
+
+ /**
+ * Returns list of flows related to service named serviceName installed in specified flow table
+ *
+ * @param table flow table
+ * @param serviceName service name
+ * @return list of flows
+ */
+ public static List<Flow> getServiceFlows(Table table, String serviceName) {
+ return table.getFlow().stream()
+ .filter(flow -> flow.getId().getValue().startsWith(serviceName))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns list of flows - full-mesh - created on the base of list of provided links and default dropping all flow
+ *
+ * @param interswitchLinks list of links
+ * @return list of flows
+ */
+ public static List<Flow> getBaseFlows(List<Link> interswitchLinks) {
+ List<Flow> baseFlows = new ArrayList<>();
+
+ baseFlows.addAll(createInterswitchFlows(interswitchLinks));
+ baseFlows.add(createDefaultFlow());
+
+ return baseFlows;
+ }
+
+ /**
+ * Returns list of flows installed in flow table
+ *
+ * @param table flow table
+ * @return list of flows
+ */
+ public static List<Flow> getExistingFlows(Table table) {
+ return table.getFlow().stream().map(x -> new FlowBuilder(x).build()).collect(Collectors.toList());
+ }
+
+ private static List<Flow> createInterswitchFlows(List<Link> interswitchLinks) {
+ List<Flow> flows = new ArrayList<>();
+
+ // ports have the same names as interswitch links
+ List<String> portIds = interswitchLinks.stream()
+ .map(link -> link.getLinkId().getValue())
+ .collect(Collectors.toList());
+
+ for (String portId : portIds) {
+ List<Action> outputActions = portIds.stream()
+ .filter(outputPortId -> !outputPortId.equalsIgnoreCase(portId))
+ .map(outputPortId -> ActionUtils.createOutputAction(outputPortId, portIds.indexOf(outputPortId)))
+ .collect(Collectors.toList());
+ FlowId flowId = new FlowId(INTERSWITCH_FLOW_ID_PREFIX + "-" + portId);
+ Flow flow = new FlowBuilder().setId(flowId)
+ .setKey(new FlowKey(flowId))
+ .setTableId(FLOW_TABLE_ID)
+ .setPriority(INTERSWITCH_FLOW_PRIORITY)
+ .setMatch(MatchUtils.createInPortMatch(portId))
+ .setInstructions(ActionUtils.createInstructions(outputActions))
+ .build();
+ flows.add(flow);
+ }
+
+ return flows;
+ }
+
+ private static Flow createDefaultFlow() {
+ FlowId dropFlowId = new FlowId(DROP_FLOW_ID);
+ return new FlowBuilder().setId(dropFlowId)
+ .setKey(new FlowKey(dropFlowId))
+ .setTableId(FLOW_TABLE_ID)
+ .setPriority(DROP_FLOW_PRIORITY)
+ .setInstructions(ActionUtils.createInstructions(Arrays.asList(ActionUtils.createDropAction(0))))
+ .build();
+ }
+
+ private static List<Flow> createVlanPassingFlows(String outputPort, int externalVlanId, int internalVlanId, String serviceName, List<Link> interswitchLinks) {
+ return interswitchLinks.stream()
+ .map(link -> createVlanPassingFlow(outputPort, link.getLinkId().getValue(), externalVlanId, internalVlanId, serviceName))
+ .collect(Collectors.toList());
+ }
+
+ private static Flow createVlanPassingFlow(String outputPort, String inputPort, int externalVlanId, int internalVlanId, String serviceName) {
+ // Create list of actions and VLAN match
+ List<Action> actions = new ArrayList<>();
+ Match vlanMatch;
+ int actionOrder = 0;
+ if (externalVlanId == internalVlanId) {
+ vlanMatch = MatchUtils.createVlanMatch(externalVlanId, inputPort);
+ } else {
+ vlanMatch = MatchUtils.createVlanMatch(internalVlanId, inputPort);
+ actions.add(ActionUtils.createPopVlanAction(actionOrder++));
+ actions.add(ActionUtils.createPushVlanAction(actionOrder++));
+ actions.add(ActionUtils.createSetVlanIdAction(externalVlanId, actionOrder++));
+ }
+ actions.add(ActionUtils.createOutputAction(outputPort, actionOrder));
+
+ FlowId flowId = new FlowId(getVlanFlowId(serviceName, inputPort));
+ return new FlowBuilder().setId(flowId)
+ .setKey(new FlowKey(flowId))
+ .setTableId(FLOW_TABLE_ID)
+ .setPriority(VLAN_FLOW_PRIORITY)
+ .setMatch(vlanMatch)
+ .setInstructions(ActionUtils.createInstructions(actions))
+ .build();
+ }
+
+ private static Flow createVlanIngressFlow(String inputPort, int externalVlanId, int internalVlanId, String serviceName, List<Link> interswitchLinks) {
+ // Create list of output port IDs
+ List<String> outputPortIds = interswitchLinks.stream()
+ .map(link -> link.getLinkId().getValue())
+ .filter(outputPort -> !outputPort.equalsIgnoreCase(inputPort))
+ .collect(Collectors.toList());
+
+ // Create list of actions
+ List<Action> actions = new ArrayList<>();
+ int actionOrder = 0;
+ // 1. Create VLAN actions performing VLAN translation when service VLAN is already used in OvSwitch network
+ if (externalVlanId != internalVlanId) {
+ actions.add(ActionUtils.createPopVlanAction(actionOrder++));
+ actions.add(ActionUtils.createPushVlanAction(actionOrder++));
+ actions.add(ActionUtils.createSetVlanIdAction(internalVlanId, actionOrder++));
+ }
+ // 2. Create output actions
+ final int outputActionOrder = actionOrder;
+ actions.addAll(outputPortIds.stream()
+ .map(outputPortId -> ActionUtils.createOutputAction(outputPortId,
+ outputActionOrder + outputPortIds.indexOf(outputPortId)))
+ .collect(Collectors.toList()));
+
+ FlowId flowId = new FlowId(getVlanFlowId(serviceName, inputPort));
+ return new FlowBuilder().setId(flowId)
+ .setKey(new FlowKey(flowId))
+ .setTableId(FLOW_TABLE_ID)
+ .setPriority(VLAN_FLOW_PRIORITY)
+ .setMatch(MatchUtils.createVlanMatch(externalVlanId, inputPort))
+ .setInstructions(ActionUtils.createInstructions(actions))
+ .build();
+ }
+
+ private static String getVlanFlowId(String serviceName, String inputPort) {
+ return serviceName + "-" + FLOW_ID_MIDDLE_PART + "-" + inputPort;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems Inc and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.unimgr.mef.nrp.ovs.util;
+
+import com.google.common.collect.Sets;
+import org.opendaylight.unimgr.mef.nrp.common.ResourceNotAvailableException;
+import org.opendaylight.unimgr.mef.nrp.ovs.exception.VlanPoolExhaustedException;
+import org.opendaylight.unimgr.utils.NullAwareDatastoreGetter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.field._case.SetField;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Class responsible for generate Vlan ID or check if given Vlan ID is not used.
+ *
+ * @author marek.ryznar@amartus.com
+ */
+public class VlanUtils {
+ private Set<Integer> usedVlans = new HashSet<>();
+
+ private final static Set<Integer> possibleVlans = IntStream.range(1, 4094).boxed().collect(Collectors.toSet());
+ private final static String VLAN_POOL_EXHAUSTED_ERROR_MESSAGE = "All VLAN IDs are in use. VLAN pool exhausted.";
+
+ private final static Logger LOG = LoggerFactory.getLogger(VlanUtils.class);
+
+ public VlanUtils(List<NullAwareDatastoreGetter<Node>> nodes) throws ResourceNotAvailableException {
+ getAllVlanIDs(nodes);
+ }
+
+ /**
+ * Method return given vlan ID (if it is not used in OVS network) or generate new one.
+ */
+ public Integer generateVlanID() throws ResourceNotAvailableException {
+ return generateVid();
+ }
+
+ public boolean isVlanInUse(Integer vlanId) {
+ return usedVlans.contains(vlanId);
+ }
+
+ private Integer generateVid() throws VlanPoolExhaustedException {
+ Set<Integer> difference = Sets.difference(possibleVlans, usedVlans);
+ if (difference.isEmpty()) {
+ LOG.warn(VLAN_POOL_EXHAUSTED_ERROR_MESSAGE);
+ throw new VlanPoolExhaustedException(VLAN_POOL_EXHAUSTED_ERROR_MESSAGE);
+ }
+ return difference.iterator().next();
+ }
+
+ private void getAllVlanIDs(List<NullAwareDatastoreGetter<Node>> nodes) throws ResourceNotAvailableException {
+ for (NullAwareDatastoreGetter<Node> node : nodes) {
+ if (node.get().isPresent()) {
+ Table table = OpenFlowUtils.getTable(node.get().get());
+ if (table != null && table.getFlow() != null) {
+ for (Flow flow : table.getFlow()) {
+ checkFlows(flow);
+ }
+ }
+ }
+ }
+ }
+
+ private void checkFlows(Flow flow) {
+ getVlanFromActions(flow);
+ Integer vid = getVlanFromMatch(flow.getMatch().getVlanMatch());
+ if (vid != null && !usedVlans.contains(vid)) {
+ usedVlans.add(vid);
+ }
+ }
+
+ private Integer getVlanFromMatch(VlanMatch vlanMatch) {
+ if (vlanMatch != null &&
+ vlanMatch.getVlanId() != null &&
+ vlanMatch.getVlanId().getVlanId() != null)
+ {
+ return vlanMatch.getVlanId().getVlanId().getValue();
+ }
+ return null;
+ }
+
+ private void getVlanFromActions(Flow flow) {
+ if (flow.getInstructions() != null &&
+ !flow.getInstructions().getInstruction().isEmpty())
+ {
+ for(Instruction instruction : flow.getInstructions().getInstruction()) {
+ ApplyActionsCase applyActionsCase = (ApplyActionsCase) instruction.getInstruction();
+ ApplyActions applyActions = applyActionsCase.getApplyActions();
+ List<Action> actions = applyActions.getAction();
+ checkActions(actions);
+ }
+ }
+ }
+
+ private void checkActions(List<Action> actions) {
+ for( Action action:actions ) {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action concreteAction = action.getAction();
+ if( concreteAction instanceof SetField ) {
+ SetField setField = (SetField) concreteAction;
+ if( setField.getVlanMatch() != null ) {
+ Integer vid = getVlanFromMatch(setField.getVlanMatch());
+ if( vid != null && !usedVlans.contains(vid)) {
+ usedVlans.add(vid);
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (c) 2015 Cisco Systems Inc. and others. All rights reserved.
+
+This program and the accompanying materials are made available under the
+terms of the Eclipse Public License v1.0 which accompanies this distribution,
+and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker" />
+
+ <service id="ovsDriverService" interface="org.opendaylight.unimgr.mef.nrp.api.ActivationDriverBuilder">
+ <bean class="org.opendaylight.unimgr.mef.nrp.ovs.driver.OvsDriver">
+ <argument ref="dataBroker" />
+ </bean>
+ </service>
+</blueprint>
<module>impl</module>
<module>netvirt</module>
<module>cisco-xr-driver</module>
+ <module>ovs-driver</module>
<module>dlux</module>
<module>cli</module>
<module>karaf</module>
prefix onf-cn;
}
+ import ietf-inet-types { prefix "inet"; }
+
revision 2016-07-25 {
}
}
}
+ typedef CTagVlanId {
+ type uint32 {
+ range "0..4094";
+ }
+ }
+
augment "/onf-cn:forwarding-constructs/onf-cn:forwarding-construct" {
container unimgr-attrs {
leaf status {
}
}
}
+
+ augment "/onf-cn:forwarding-constructs/onf-cn:forwarding-construct/onf-cn:fcPort" {
+ leaf cTagVlanId {
+ type CTagVlanId;
+ config true;
+ default 0;
+ }
+ }
}