Addition of new module - ovs-driver, responsible for creating forwarding constructs... 76/52376/3
authorMarek Ryznar <marek.ryznar@amartus.com>
Mon, 13 Feb 2017 13:14:45 +0000 (14:14 +0100)
committerMarek Ryznar <marek.ryznar@amartus.com>
Wed, 1 Mar 2017 15:52:40 +0000 (16:52 +0100)
Developer's Certificate of Origin 1.1

        By making a contribution to this project, I certify that:

        (a) The contribution was created in whole or in part by me and I
            have the right to submit it under the open source license
            indicated in the file; or

        (b) The contribution is based upon previous work that, to the best
            of my knowledge, is covered under an appropriate open source
            license and I have the right under that license to submit that
            work with modifications, whether created in whole or in part
            by me, under the same open source license (unless I am
            permitted to submit under a different license), as indicated
            in the file; or

        (c) The contribution was provided directly to me by some other
            person who certified (a), (b) or (c) and I have not modified
            it.

        (d) I understand and agree that this project and the contribution
            are public and that a record of the contribution (including all
            personal information I submit with it, including my sign-off) is
            maintained indefinitely and may be redistributed consistent with
            this project or the open source license(s) involved.

Change-Id: Ieecc38e0716822932a2a61d5ed4125f4d82e0771
Signed-off-by: Marek Ryznar <marek.ryznar@amartus.com>
18 files changed:
artifacts/pom.xml
features/pom.xml
features/src/main/features/features.xml
ovs-driver/pom.xml [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/activator/OvsActivator.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/activator/OvsActivatorHelper.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/driver/OvsDriver.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/exception/VlanNotSetException.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/exception/VlanPoolExhaustedException.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/transaction/TableTransaction.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/transaction/TopologyTransaction.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/ActionUtils.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/MatchUtils.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/OpenFlowUtils.java [new file with mode: 0644]
ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/VlanUtils.java [new file with mode: 0644]
ovs-driver/src/main/resources/org/opendaylight/blueprint/ovs-driver.xml [new file with mode: 0644]
pom.xml
presto-api/src/main/yang/mef-unimgr-ext.yang

index 0959ab64f71ac60bedc8bb10cad7387c67c8fe8e..11e42914b1138c8e8da9926f5181fcdb43c4097d 100644 (file)
@@ -39,6 +39,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
         <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>
index 7bb0ae055aa76557565e38b0e4cadd3e0c9b48c3..a14003630f307c6f4356c5783909f5dd2ae97e06 100644 (file)
     <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>
index fb39f3e443ddf42bf0b930957e8b59ded4507494..9ceee68a341317f76d1cfde9109a7fe1b36d805b 100755 (executable)
@@ -16,6 +16,7 @@
   <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>
diff --git a/ovs-driver/pom.xml b/ovs-driver/pom.xml
new file mode 100644 (file)
index 0000000..d6db016
--- /dev/null
@@ -0,0 +1,120 @@
+<?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
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/activator/OvsActivator.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/activator/OvsActivator.java
new file mode 100644 (file)
index 0000000..63644b1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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);
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/activator/OvsActivatorHelper.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/activator/OvsActivatorHelper.java
new file mode 100644 (file)
index 0000000..dcedb2d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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;
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/driver/OvsDriver.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/driver/OvsDriver.java
new file mode 100644 (file)
index 0000000..549a921
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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;
+            }
+        };
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/exception/VlanNotSetException.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/exception/VlanNotSetException.java
new file mode 100644 (file)
index 0000000..3e8a7ce
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/exception/VlanPoolExhaustedException.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/exception/VlanPoolExhaustedException.java
new file mode 100644 (file)
index 0000000..a8947a9
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/transaction/TableTransaction.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/transaction/TableTransaction.java
new file mode 100644 (file)
index 0000000..84de476
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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());
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/transaction/TopologyTransaction.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/transaction/TopologyTransaction.java
new file mode 100644 (file)
index 0000000..18ed0f5
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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();
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/ActionUtils.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/ActionUtils.java
new file mode 100644 (file)
index 0000000..4e0e69c
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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));
+        }
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/MatchUtils.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/MatchUtils.java
new file mode 100644 (file)
index 0000000..3b9d1df
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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();
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/OpenFlowUtils.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/OpenFlowUtils.java
new file mode 100644 (file)
index 0000000..3da24ea
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * 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;
+    }
+}
diff --git a/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/VlanUtils.java b/ovs-driver/src/main/java/org/opendaylight/unimgr/mef/nrp/ovs/util/VlanUtils.java
new file mode 100644 (file)
index 0000000..0251a5c
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/ovs-driver/src/main/resources/org/opendaylight/blueprint/ovs-driver.xml b/ovs-driver/src/main/resources/org/opendaylight/blueprint/ovs-driver.xml
new file mode 100644 (file)
index 0000000..2a74a66
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>
diff --git a/pom.xml b/pom.xml
index d4de1f46bbad04502ea2c576547fa736f7bbea4f..d5c43f2a1593fc99f43b6adf2f1c903c202db93b 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
         <module>impl</module>
         <module>netvirt</module>
         <module>cisco-xr-driver</module>
+        <module>ovs-driver</module>
         <module>dlux</module>
         <module>cli</module>
         <module>karaf</module>
index a2a98277302626dd6e399334cf43d85d6e3288eb..fe090e1595cd1181fededb72e8d89afe59691d72 100644 (file)
@@ -6,6 +6,8 @@ module mef-unimgr-ext {
     prefix onf-cn;
   }
 
+  import ietf-inet-types { prefix "inet"; }
+
   revision 2016-07-25 {
   }
 
@@ -17,6 +19,12 @@ module mef-unimgr-ext {
     }
   }
 
+  typedef CTagVlanId {
+      type uint32 {
+        range "0..4094";
+      }
+  }
+
   augment "/onf-cn:forwarding-constructs/onf-cn:forwarding-construct" {
     container unimgr-attrs {
       leaf status {
@@ -26,4 +34,12 @@ module mef-unimgr-ext {
       }
     }
   }
+
+  augment "/onf-cn:forwarding-constructs/onf-cn:forwarding-construct/onf-cn:fcPort" {
+      leaf cTagVlanId {
+        type CTagVlanId;
+        config true;
+        default 0;
+      }
+   }
 }