From 8a9245a725a9a72a6dae890903e636dd11cf7aa7 Mon Sep 17 00:00:00 2001 From: Sam Hague Date: Mon, 20 Mar 2017 20:06:04 -0400 Subject: [PATCH] Port sfc-translator to new netvirt. Change-Id: I6786f38ae9237ba189a2e0921fdf1699e27ac731 Signed-off-by: Sam Hague --- vpnservice/features/pom.xml | 23 ++ .../features/src/main/features/features.xml | 9 + vpnservice/pom.xml | 1 + vpnservice/sfc/classifier/api/pom.xml | 50 +++ .../api/src/main/yang/netvirt-sfc-acl.yang | 42 +++ vpnservice/sfc/classifier/pom.xml | 63 ++++ vpnservice/sfc/pom.xml | 64 ++++ vpnservice/sfc/translator/pom.xml | 84 +++++ .../DelegatingDataTreeListener.java | 103 ++++++ .../translator/INeutronSfcDataProcessor.java | 46 +++ .../netvirt/sfc/translator/MdsalUtils.java | 133 ++++++++ .../sfc/translator/NeutronMdsalHelper.java | 92 ++++++ .../OpenStackSFCTranslatorProvider.java | 53 +++ .../sfc/translator/OvsdbMdsalHelper.java | 132 ++++++++ .../sfc/translator/OvsdbPortMetadata.java | 47 +++ .../sfc/translator/SfcMdsalHelper.java | 190 +++++++++++ .../FlowClassifierTranslator.java | 170 ++++++++++ .../NeutronFlowClassifierListener.java | 79 +++++ .../portchain/NeutronPortChainListener.java | 306 ++++++++++++++++++ .../NeutronPortPairGroupListener.java | 68 ++++ .../portchain/NeutronPortPairListener.java | 133 ++++++++ .../portchain/PortChainTranslator.java | 112 +++++++ .../portchain/PortPairGroupTranslator.java | 204 ++++++++++++ .../portchain/PortPairTranslator.java | 190 +++++++++++ .../opendaylight/blueprint/sfc-translator.xml | 18 ++ 25 files changed, 2412 insertions(+) create mode 100644 vpnservice/sfc/classifier/api/pom.xml create mode 100644 vpnservice/sfc/classifier/api/src/main/yang/netvirt-sfc-acl.yang create mode 100644 vpnservice/sfc/classifier/pom.xml create mode 100644 vpnservice/sfc/pom.xml create mode 100644 vpnservice/sfc/translator/pom.xml create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/DelegatingDataTreeListener.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/INeutronSfcDataProcessor.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/MdsalUtils.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/NeutronMdsalHelper.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OpenStackSFCTranslatorProvider.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbMdsalHelper.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbPortMetadata.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/SfcMdsalHelper.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/FlowClassifierTranslator.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/NeutronFlowClassifierListener.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortChainListener.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairGroupListener.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairListener.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortChainTranslator.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairGroupTranslator.java create mode 100644 vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairTranslator.java create mode 100644 vpnservice/sfc/translator/src/main/resources/org/opendaylight/blueprint/sfc-translator.xml diff --git a/vpnservice/features/pom.xml b/vpnservice/features/pom.xml index fd834a641e..7d3b585545 100644 --- a/vpnservice/features/pom.xml +++ b/vpnservice/features/pom.xml @@ -45,6 +45,7 @@ etc/opendaylight/karaf 0.5.0-SNAPSHOT 0.2.0-SNAPSHOT + 0.5.0-SNAPSHOT @@ -377,6 +378,28 @@ ovsdb-ui-bundle ${ovsdb.version} + + ${project.groupId} + sfc.classifier-api + ${vpnservices.version} + + + ${project.groupId} + sfc.translator + ${vpnservices.version} + + + org.opendaylight.sfc + features-sfc + ${sfc.version} + features + xml + + + org.opendaylight.sfc + sfc-provider + ${sfc.version} + diff --git a/vpnservice/features/src/main/features/features.xml b/vpnservice/features/src/main/features/features.xml index 70555eb5d1..d2707bc9ac 100644 --- a/vpnservice/features/src/main/features/features.xml +++ b/vpnservice/features/src/main/features/features.xml @@ -23,6 +23,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html mvn:org.opendaylight.infrautils/infrautils-features/{{VERSION}}/xml/features mvn:org.opendaylight.dlux/features-dlux/{{VERSION}}/xml/features mvn:org.opendaylight.federation/federation-features/{{VERSION}}/xml/features + mvn:org.opendaylight.sfc/features-sfc/{{VERSION}}/xml/features odl-mdsal-broker @@ -102,4 +103,12 @@ and is available at http://www.eclipse.org/legal/epl-v10.html odl-dlux-core mvn:org.opendaylight.netvirt/ovsdb-ui-bundle/{{VERSION}} + + + odl-sfc-provider + odl-netvirt-openstack + mvn:org.opendaylight.netvirt/sfc.classifier-api/{{VERSION}} + mvn:org.opendaylight.netvirt/sfc.translator/{{VERSION}} + + diff --git a/vpnservice/pom.xml b/vpnservice/pom.xml index d425045ded..378697335c 100644 --- a/vpnservice/pom.xml +++ b/vpnservice/pom.xml @@ -38,6 +38,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL vpnservice-artifacts statemanager statistics + sfc it diff --git a/vpnservice/sfc/classifier/api/pom.xml b/vpnservice/sfc/classifier/api/pom.xml new file mode 100644 index 0000000000..64632b5304 --- /dev/null +++ b/vpnservice/sfc/classifier/api/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.opendaylight.netvirt + binding-parent + 0.4.0-SNAPSHOT + ../../../commons/binding-parent + + + sfc.classifier-api + ODL :: netvirt :: ${project.artifactId} + bundle + + + + org.opendaylight.mdsal.model + ietf-access-control-list + + + org.opendaylight.mdsal.model + yang-ext + + + + + ${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/ + + + + opendaylight-site + ${nexus.site.url}/${project.artifactId}/ + + + diff --git a/vpnservice/sfc/classifier/api/src/main/yang/netvirt-sfc-acl.yang b/vpnservice/sfc/classifier/api/src/main/yang/netvirt-sfc-acl.yang new file mode 100644 index 0000000000..54746c0c5d --- /dev/null +++ b/vpnservice/sfc/classifier/api/src/main/yang/netvirt-sfc-acl.yang @@ -0,0 +1,42 @@ +module netvirt-sfc-acl { + yang-version 1; + namespace "urn:opendaylight:netvirt:sfc:acl"; + prefix "acl"; + + import ietf-access-control-list { prefix ietf-acl; revision-date 2016-02-18; } + import yang-ext { prefix ext; } + + revision "2015-01-05" { + description "Initial revision of netvirt extensions to ietf-acl model"; + } + + augment "/ietf-acl:access-lists/ietf-acl:acl/ietf-acl:access-list-entries/ietf-acl:ace/ietf-acl:matches" { + description "Neutron network uuid"; + ext:augment-identifier "neutron-network"; + leaf network-uuid { + type string; + } + } + + grouping netvirtsfc-acl-actions { + leaf sfc-name { + type string; + } + leaf sfp-name { + type string; + } + leaf rsp-name { + type string; + } + leaf render-rsp { + type boolean; + default "false"; + } + } + + augment "/ietf-acl:access-lists/ietf-acl:acl/ietf-acl:access-list-entries/ietf-acl:ace/ietf-acl:actions" { + description "Redirect traffic to SFC identified by either SFC, SFP or RSP"; + ext:augment-identifier "redirect-to-sfc"; + uses netvirtsfc-acl-actions; + } +} diff --git a/vpnservice/sfc/classifier/pom.xml b/vpnservice/sfc/classifier/pom.xml new file mode 100644 index 0000000000..61025f69b7 --- /dev/null +++ b/vpnservice/sfc/classifier/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + + org.opendaylight.odlparent + odlparent-lite + 1.8.0-SNAPSHOT + + + + org.opendaylight.netvirt + sfc.classifier + 0.4.0-SNAPSHOT + ODL :: netvirt :: ${project.artifactId} + pom + + api + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + + + ${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/ + + + + opendaylight-site + ${nexus.site.url}/${project.artifactId}/ + + + diff --git a/vpnservice/sfc/pom.xml b/vpnservice/sfc/pom.xml new file mode 100644 index 0000000000..3dc2d99f76 --- /dev/null +++ b/vpnservice/sfc/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + + + org.opendaylight.odlparent + odlparent-lite + 1.8.0-SNAPSHOT + + + + org.opendaylight.netvirt + sfc + 0.4.0-SNAPSHOT + ODL :: netvirt :: ${project.artifactId} + pom + + classifier + translator + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + + + ${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/ + + + + opendaylight-site + ${nexus.site.url}/${project.artifactId}/ + + + diff --git a/vpnservice/sfc/translator/pom.xml b/vpnservice/sfc/translator/pom.xml new file mode 100644 index 0000000000..21d0cef264 --- /dev/null +++ b/vpnservice/sfc/translator/pom.xml @@ -0,0 +1,84 @@ + + + +4.0.0 + + + org.opendaylight.netvirt + config-parent + 0.4.0-SNAPSHOT + ../../commons/config-parent + + + sfc.translator + ODL :: netvirt :: ${project.artifactId} + bundle + + + 0.5.0-SNAPSHOT + + + + + org.opendaylight.infrautils + inject + ${infrautils.version} + + + org.opendaylight.mdsal.model + ietf-access-control-list + + + org.opendaylight.netvirt + sfc.classifier-api + ${project.version} + + + org.opendaylight.neutron + model + ${neutron.version} + + + org.opendaylight.sfc + sfc-model + ${sfc.version} + + + org.opendaylight.sfc + sfc-provider + ${sfc.version} + + + + + + + org.apache.aries.blueprint + blueprint-maven-plugin + + + + + + ${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/ + + + + opendaylight-site + ${nexus.site.url}/${project.artifactId}/ + + + diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/DelegatingDataTreeListener.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/DelegatingDataTreeListener.java new file mode 100644 index 0000000000..2d9a708b53 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/DelegatingDataTreeListener.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.Collection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Data-tree listener which delegates data processing to a {@link INeutronSfcDataProcessor}. + */ +public abstract class DelegatingDataTreeListener implements AutoCloseable, + DataTreeChangeListener, + INeutronSfcDataProcessor { + private static final Logger LOG = LoggerFactory.getLogger(DelegatingDataTreeListener.class); + private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder() + .setNameFormat("NeutronSfcListener-%d").build(); + private final ExecutorService executorService = Executors.newFixedThreadPool(1, THREAD_FACTORY); + private final INeutronSfcDataProcessor dataProcessor; + private ListenerRegistration> listenerRegistration; + + public DelegatingDataTreeListener(DataBroker db, DataTreeIdentifier treeId) { + this.dataProcessor = Preconditions.checkNotNull(this, "Data processor can not be null!"); + registerListener(Preconditions.checkNotNull(db, "Data broker can not be null!"), + Preconditions.checkNotNull(treeId, "Tree identifier can not be null!")); + } + + // TODO Clean up the exception handling + @SuppressWarnings("checkstyle:IllegalCatch") + private void registerListener(final DataBroker db, DataTreeIdentifier treeId) { + try { + LOG.info("Registering Data Change Listener for {}", getClass().getSimpleName()); + listenerRegistration = db.registerDataTreeChangeListener(treeId, this); + } catch (final Exception e) { + LOG.warn("{} DataChange listener registration fail!", getClass().getSimpleName(), e); + throw new IllegalStateException("DataTreeListener startup fail! System needs restart.", e); + } + } + + private void processChanges(Collection> changes) { + LOG.info("onDataTreeChanged: Received Data Tree Changed {}", changes); + for (DataTreeModification change : changes) { + final InstanceIdentifier key = change.getRootPath().getRootIdentifier(); + final DataObjectModification mod = change.getRootNode(); + LOG.info("onDataTreeChanged: Received Data Tree Changed Update of Type={} for Key={}", + mod.getModificationType(), key); + switch (mod.getModificationType()) { + case DELETE: + dataProcessor.remove(key, mod.getDataBefore()); + break; + case SUBTREE_MODIFIED: + dataProcessor.update(key, mod.getDataBefore(), mod.getDataAfter()); + break; + case WRITE: + if (mod.getDataBefore() == null) { + dataProcessor.add(key, mod.getDataAfter()); + } else { + dataProcessor.update(key, mod.getDataBefore(), mod.getDataAfter()); + } + break; + default: + throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType()); + } + } + } + + @Override + public void onDataTreeChanged(@Nonnull final Collection> changes) { + Preconditions.checkNotNull(changes, "Changes may not be null!"); + executorService.submit(() -> processChanges(changes)); + } + + @Override + public void close() { + if (listenerRegistration != null) { + listenerRegistration.close(); + listenerRegistration = null; + } + if (executorService != null) { + executorService.shutdownNow(); + } + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/INeutronSfcDataProcessor.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/INeutronSfcDataProcessor.java new file mode 100644 index 0000000000..58203eaca7 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/INeutronSfcDataProcessor.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Neutron SFC yang model processor. + */ +public interface INeutronSfcDataProcessor { + + /** + * Method removes DataObject which is identified by InstanceIdentifier. + * + * @param identifier - the whole path to DataObject + * @param del - DataObject for removing + */ + void remove(InstanceIdentifier identifier, D del); + + /** + * Method updates the original DataObject to the update DataObject. + * Both are identified by same InstanceIdentifier. + * + * @param identifier - the whole path to DataObject + * @param original - original DataObject (for update) + * @param update - changed DataObject (contain updates) + */ + void update(InstanceIdentifier identifier, D original, D update); + + /** + * Method adds the DataObject which is identified by InstanceIdentifier + * to device. + * + * @param identifier - the whole path to new DataObject + * @param add - new DataObject + */ + void add(InstanceIdentifier identifier, D add); + +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/MdsalUtils.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/MdsalUtils.java new file mode 100644 index 0000000000..f77d28ba5b --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/MdsalUtils.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +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.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MdsalUtils { + private static final Logger LOG = LoggerFactory.getLogger(MdsalUtils.class); + private DataBroker databroker = null; + + /** + * Class constructor setting the data broker. + * + * @param dataBroker the {@link DataBroker} + */ + public MdsalUtils(DataBroker dataBroker) { + this.databroker = dataBroker; + } + + /** + * Executes delete as a blocking transaction. + * + * @param store {@link LogicalDatastoreType} which should be modified + * @param path {@link InstanceIdentifier} to read from + * @param the data object type + * @return the result of the request + */ + public boolean delete( + final LogicalDatastoreType store, final InstanceIdentifier path) { + boolean result = false; + final WriteTransaction transaction = databroker.newWriteOnlyTransaction(); + transaction.delete(store, path); + CheckedFuture future = transaction.submit(); + try { + future.checkedGet(); + result = true; + } catch (TransactionCommitFailedException e) { + LOG.warn("Failed to delete {} ", path, e); + } + return result; + } + + /** + * Executes merge as a blocking transaction. + * + * @param logicalDatastoreType {@link LogicalDatastoreType} which should be modified + * @param path {@link InstanceIdentifier} for path to read + * @param the data object type + * @return the result of the request + */ + public boolean merge( + final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier path, D data) { + boolean result = false; + final WriteTransaction transaction = databroker.newWriteOnlyTransaction(); + transaction.merge(logicalDatastoreType, path, data, true); + CheckedFuture future = transaction.submit(); + try { + future.checkedGet(); + result = true; + } catch (TransactionCommitFailedException e) { + LOG.warn("Failed to merge {} ", path, e); + } + return result; + } + + /** + * Executes put as a blocking transaction. + * + * @param logicalDatastoreType {@link LogicalDatastoreType} which should be modified + * @param path {@link InstanceIdentifier} for path to read + * @param the data object type + * @return the result of the request + */ + public boolean put( + final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier path, D data) { + boolean result = false; + final WriteTransaction transaction = databroker.newWriteOnlyTransaction(); + transaction.put(logicalDatastoreType, path, data, true); + CheckedFuture future = transaction.submit(); + try { + future.checkedGet(); + result = true; + } catch (TransactionCommitFailedException e) { + LOG.warn("Failed to put {} ", path, e); + } + return result; + } + + /** + * Executes read as a blocking transaction. + * + * @param store {@link LogicalDatastoreType} to read + * @param path {@link InstanceIdentifier} for path to read + * @param the data object type + * @return the result as the data object requested + */ + public D read( + final LogicalDatastoreType store, final InstanceIdentifier path) { + D result = null; + final ReadOnlyTransaction transaction = databroker.newReadOnlyTransaction(); + Optional optionalDataObject; + CheckedFuture, ReadFailedException> future = transaction.read(store, path); + try { + optionalDataObject = future.checkedGet(); + if (optionalDataObject.isPresent()) { + result = optionalDataObject.get(); + } else { + LOG.debug("{}: Failed to read {}", + Thread.currentThread().getStackTrace()[1], path); + } + } catch (ReadFailedException e) { + LOG.warn("Failed to read {} ", path, e); + } + transaction.close(); + return result; + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/NeutronMdsalHelper.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/NeutronMdsalHelper.java new file mode 100644 index 0000000000..8d4bb2f801 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/NeutronMdsalHelper.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.SfcFlowClassifiers; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.sfc.flow.classifiers.SfcFlowClassifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.sfc.flow.classifiers.SfcFlowClassifierKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.PortPairGroups; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.PortPairs; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pair.groups.PortPairGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pair.groups.PortPairGroupKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pairs.PortPair; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pairs.PortPairKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility functions to read Neutron models (e.g network, subnet, port, sfc flow classifier + * port pair, port group, port chain) from md-sal data store. + */ +public class NeutronMdsalHelper { + private static final Logger LOG = LoggerFactory.getLogger(NeutronMdsalHelper.class); + private static final InstanceIdentifier FC_IID = + InstanceIdentifier.create(Neutron.class).child(SfcFlowClassifiers.class); + private static final InstanceIdentifier PORTS_IID = + InstanceIdentifier.create(Neutron.class).child(Ports.class); + private static final InstanceIdentifier PORT_PAIRS_IID = + InstanceIdentifier.create(Neutron.class).child(PortPairs.class); + private static final InstanceIdentifier PORT_PAIR_GROUPS_IID = + InstanceIdentifier.create(Neutron.class).child(PortPairGroups.class); + + private final DataBroker dataBroker; + private final MdsalUtils mdsalUtils; + + public NeutronMdsalHelper(DataBroker dataBroker) { + this.dataBroker = dataBroker; + mdsalUtils = new MdsalUtils(this.dataBroker); + } + + public Port getNeutronPort(Uuid portId) { + Port neutronPort = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION , getNeutronPortPath(portId)); + return neutronPort; + } + + public PortPair getNeutronPortPair(Uuid portPairId) { + PortPair neutronPortPair + = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION , getNeutronPortPairPath(portPairId)); + return neutronPortPair; + } + + public PortPairGroup getNeutronPortPairGroup(Uuid portPairGroupId) { + PortPairGroup neutronPortPairGroup + = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION , getNeutronPortPairGroupPath(portPairGroupId)); + return neutronPortPairGroup; + } + + public SfcFlowClassifier getNeutronFlowClassifier(Uuid flowClassifierId) { + SfcFlowClassifier sfcFlowClassifier = + mdsalUtils.read(LogicalDatastoreType.CONFIGURATION , getNeutronSfcFlowClassifierPath(flowClassifierId)); + return sfcFlowClassifier; + } + + private InstanceIdentifier getNeutronPortPath(Uuid portId) { + return PORTS_IID.builder().child(Port.class, new PortKey(portId)).build(); + } + + private InstanceIdentifier getNeutronPortPairPath(Uuid portPairId) { + return PORT_PAIRS_IID.builder().child(PortPair.class, new PortPairKey(portPairId)).build(); + } + + private InstanceIdentifier getNeutronPortPairGroupPath(Uuid portPairGroupId) { + return PORT_PAIR_GROUPS_IID.builder().child(PortPairGroup.class, new PortPairGroupKey(portPairGroupId)).build(); + } + + private InstanceIdentifier getNeutronSfcFlowClassifierPath(Uuid portId) { + return FC_IID.builder().child(SfcFlowClassifier.class, new SfcFlowClassifierKey(portId)).build(); + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OpenStackSFCTranslatorProvider.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OpenStackSFCTranslatorProvider.java new file mode 100644 index 0000000000..1224fbb262 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OpenStackSFCTranslatorProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import javax.inject.Inject; +import javax.inject.Singleton; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.infrautils.inject.AbstractLifecycle; +import org.opendaylight.netvirt.sfc.translator.flowclassifier.NeutronFlowClassifierListener; +import org.opendaylight.netvirt.sfc.translator.portchain.NeutronPortChainListener; +import org.opendaylight.netvirt.sfc.translator.portchain.NeutronPortPairGroupListener; +import org.opendaylight.netvirt.sfc.translator.portchain.NeutronPortPairListener; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePathService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public class OpenStackSFCTranslatorProvider extends AbstractLifecycle { + + private static final Logger LOG = LoggerFactory.getLogger(OpenStackSFCTranslatorProvider.class); + private final DataBroker dataBroker; + private final RenderedServicePathService rspService; + + @Inject + public OpenStackSFCTranslatorProvider(final DataBroker dataBroker, final RenderedServicePathService rspService) { + LOG.info("OpenStackSFCTranslatorProvider2 constructor"); + this.dataBroker = dataBroker; + this.rspService = rspService; + } + + @Override + protected void start() { + LOG.info("{} start", getClass().getSimpleName()); + new NeutronFlowClassifierListener(dataBroker); + new NeutronPortPairListener(dataBroker); + new NeutronPortPairGroupListener(dataBroker); + new NeutronPortChainListener(dataBroker, rspService); + if (this.rspService == null) { + LOG.warn("RenderedServicePath Service is not available. Translation layer might not work as expected."); + } + } + + @Override + protected void stop() { + LOG.info("{} close", getClass().getSimpleName()); + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbMdsalHelper.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbMdsalHelper.java new file mode 100644 index 0000000000..9441064367 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbMdsalHelper.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import java.util.List; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.port._interface.attributes.InterfaceExternalIds; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; +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.TopologyKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility methods to read OpenDaylight OVSDB models. + */ +public class OvsdbMdsalHelper { + private static final Logger LOG = LoggerFactory.getLogger(SfcMdsalHelper.class); + private static final String OVSDB_TOPOLOGY_ID = "ovsdb:1"; + private static final InstanceIdentifier TOPOLOGY_PATH + = InstanceIdentifier.create(NetworkTopology.class) + .child(Topology.class, new TopologyKey(new TopologyId(OVSDB_TOPOLOGY_ID))); + + private final DataBroker dataBroker; + private final MdsalUtils mdsalUtils; + + public OvsdbMdsalHelper(DataBroker dataBroker) { + this.dataBroker = dataBroker; + mdsalUtils = new MdsalUtils(this.dataBroker); + } + + public Topology getOvsdbTopologyTree() { + LOG.info("Reading OVSDB Topolog Tree (ovsdb:1)"); + return mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, TOPOLOGY_PATH); + } + + public OvsdbPortMetadata getOvsdbPortMetadata(Uuid ingressPort) { + LOG.info("Extract ovsdb port details for neutron port {}", ingressPort.getValue()); + Topology ovsdbTopology = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, TOPOLOGY_PATH); + return getOvsdbPortMetadata(ingressPort, ovsdbTopology); + } + + public OvsdbPortMetadata getOvsdbPortMetadata(Uuid ingressPort, Topology ovsdbTopology) { + LOG.debug("Extract ovsdb port details for neutron port {} from Topology {}", + ingressPort.getValue(), ovsdbTopology); + OvsdbPortMetadata ovsdbPortMetadata = new OvsdbPortMetadata(); + OvsdbBridgeAugmentation bridgeAugmentation = null; + if (ovsdbTopology != null) { + List nodes = ovsdbTopology.getNode(); + for (Node node : nodes) { + if (node.getTerminationPoint() != null) { + for (TerminationPoint tp : node.getTerminationPoint()) { + OvsdbTerminationPointAugmentation tpAugmentation + = tp.getAugmentation(OvsdbTerminationPointAugmentation.class); + List externalIds = tpAugmentation.getInterfaceExternalIds(); + if (externalIds != null) { + for (InterfaceExternalIds externalId : externalIds) { + if (externalId.getExternalIdValue().equals(ingressPort.getValue())) { + LOG.info("OVSDB port found for neutron port {} : {}", ingressPort, tpAugmentation); + ovsdbPortMetadata.setOvsdbPort(tpAugmentation); + break; + } + } + if (ovsdbPortMetadata.getOvsdbPort() != null) { + break; + } + } + } + } + if (ovsdbPortMetadata.getOvsdbPort() != null) { + bridgeAugmentation = node.getAugmentation(OvsdbBridgeAugmentation.class); + if (bridgeAugmentation != null) { + ovsdbPortMetadata.setOvsdbBridgeNode(bridgeAugmentation); + } else { + LOG.warn("Brige augmentation is not present for the termination point {}", + ovsdbPortMetadata.getOvsdbPort()); + return null; + } + break; + } + } + OvsdbNodeRef ovsdbNode = bridgeAugmentation.getManagedBy(); + if (ovsdbNode != null) { + NodeKey ovsdbNodeKey = ovsdbNode.getValue().firstKeyOf(Node.class); + for (Node node : nodes) { + if (node.getKey().equals(ovsdbNodeKey)) { + OvsdbNodeAugmentation nodeAugmentation = node.getAugmentation(OvsdbNodeAugmentation.class); + ovsdbPortMetadata.setOvsdbNode(nodeAugmentation); + break; + } + } + } else { + LOG.warn("Ovsdb Node not found for ovsdb bridge {}",bridgeAugmentation); + } + + } else { + LOG.warn("OVSDB Operational topology not avaialble."); + } + LOG.info("Neutron port's {} respective Ovsdb metadata {}", ingressPort, ovsdbPortMetadata); + return ovsdbPortMetadata; + } + + public static String getOvsdbPortName(OvsdbTerminationPointAugmentation ovsdbPort) { + return ovsdbPort.getName(); + } + + public static String getNodeIpAddress(OvsdbNodeAugmentation ovsdbNode) { + //Currently we support only ipv4 + return ovsdbNode.getConnectionInfo().getRemoteIp().getIpv4Address().getValue(); + } + + public static String getNodeKey(InstanceIdentifier node) { + return node.firstKeyOf(Node.class).getNodeId().getValue(); + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbPortMetadata.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbPortMetadata.java new file mode 100644 index 0000000000..cfe2bef78d --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/OvsdbPortMetadata.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation; + +public class OvsdbPortMetadata { + private OvsdbTerminationPointAugmentation ovsdbPort; + private OvsdbNodeAugmentation ovsdbNode; + private OvsdbBridgeAugmentation ovsdbBridgeNode; + + public OvsdbPortMetadata() { + + } + + public OvsdbBridgeAugmentation getOvsdbBridgeNode() { + return ovsdbBridgeNode; + } + + public void setOvsdbBridgeNode(OvsdbBridgeAugmentation ovsdbBridgeNode) { + this.ovsdbBridgeNode = ovsdbBridgeNode; + } + + public OvsdbNodeAugmentation getOvsdbNode() { + return ovsdbNode; + } + + public void setOvsdbNode(OvsdbNodeAugmentation ovsdbNode) { + this.ovsdbNode = ovsdbNode; + } + + public OvsdbTerminationPointAugmentation getOvsdbPort() { + return ovsdbPort; + } + + public void setOvsdbPort(OvsdbTerminationPointAugmentation ovsdbPort) { + this.ovsdbPort = ovsdbPort; + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/SfcMdsalHelper.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/SfcMdsalHelper.java new file mode 100644 index 0000000000..83895db0f8 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/SfcMdsalHelper.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator; + +import java.util.List; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.ServiceFunctions; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.ServiceFunctionChains; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChainKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.ServiceFunctionForwarders; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPathKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.AccessLists; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility methods to read OpenDaylight SFC models. + */ +public class SfcMdsalHelper { + private static final Logger LOG = LoggerFactory.getLogger(SfcMdsalHelper.class); + private static InstanceIdentifier accessListIid = InstanceIdentifier.create(AccessLists.class); + private static InstanceIdentifier sfIid = InstanceIdentifier.create(ServiceFunctions.class); + private static InstanceIdentifier sffIid = + InstanceIdentifier.create(ServiceFunctionForwarders.class); + private static InstanceIdentifier sfcIid = + InstanceIdentifier.create(ServiceFunctionChains.class); + private static InstanceIdentifier sfpIid + = InstanceIdentifier.create(ServiceFunctionPaths.class); + + private final DataBroker dataBroker; + private final MdsalUtils mdsalUtils; + + public SfcMdsalHelper(DataBroker dataBroker) { + this.dataBroker = dataBroker; + mdsalUtils = new MdsalUtils(this.dataBroker); + } + + //ACL Flow Classifier data store utility methods + public void addAclFlowClassifier(Acl aclFlowClassifier) { + InstanceIdentifier aclIid = getAclPath(aclFlowClassifier.getKey()); + LOG.info("Write ACL FlowClassifier {} to config data store at {}",aclFlowClassifier, aclIid); + mdsalPutWrapper(LogicalDatastoreType.CONFIGURATION, aclIid, aclFlowClassifier); + } + + public void updateAclFlowClassifier(Acl aclFlowClassifier) { + InstanceIdentifier aclIid = getAclPath(aclFlowClassifier.getKey()); + LOG.info("Update ACL FlowClassifier {} in config data store at {}",aclFlowClassifier, aclIid); + mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, aclIid, aclFlowClassifier); + } + + public void removeAclFlowClassifier(Acl aclFlowClassifier) { + InstanceIdentifier aclIid = getAclPath(aclFlowClassifier.getKey()); + LOG.info("Remove ACL FlowClassifier {} from config data store at {}",aclFlowClassifier, aclIid); + mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, aclIid); + } + + //Service Function + public ServiceFunction readServiceFunction(ServiceFunctionKey sfKey) { + InstanceIdentifier sfIid = getSFPath(sfKey); + LOG.info("Read Service Function {} from config data store at {}",sfKey, sfIid); + return mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, sfIid); + } + + public void addServiceFunction(ServiceFunction sf) { + InstanceIdentifier sfIid = getSFPath(sf.getKey()); + LOG.info("Write Service Function {} to config data store at {}",sf, sfIid); + mdsalPutWrapper(LogicalDatastoreType.CONFIGURATION, sfIid, sf); + } + + public void updateServiceFunction(ServiceFunction sf) { + InstanceIdentifier sfIid = getSFPath(sf.getKey()); + LOG.info("Update Service Function {} in config data store at {}",sf, sfIid); + mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, sfIid, sf); + } + + public void removeServiceFunction(ServiceFunctionKey sfKey) { + InstanceIdentifier sfIid = getSFPath(sfKey); + LOG.info("Remove Service Function {} from config data store at {}",sfKey, sfIid); + mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, sfIid); + } + + //Service Function Forwarder + public ServiceFunctionForwarder readServiceFunctionForwarder(ServiceFunctionForwarderKey sffKey) { + InstanceIdentifier sffIid = getSFFPath(sffKey); + LOG.info("Read Service Function Forwarder from config data store at {}", sffIid); + return mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, sffIid); + } + + public void addServiceFunctionForwarder(ServiceFunctionForwarder sff) { + InstanceIdentifier sffIid = getSFFPath(sff.getKey()); + LOG.info("Write Service Function Forwarder {} to config data store at {}",sff, sffIid); + mdsalPutWrapper(LogicalDatastoreType.CONFIGURATION, sffIid, sff); + } + + public void deleteServiceFunctionForwarder(ServiceFunctionForwarderKey sffKey) { + InstanceIdentifier sffIid = getSFFPath(sffKey); + LOG.info("Delete Service Function Forwarder from config data store at {}", sffIid); + mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, sffIid); + } + + public void addServiceFunctionChain(ServiceFunctionChain sfc) { + InstanceIdentifier sfcIid = getSFCPath(sfc.getKey()); + LOG.info("Write Service Function Chain {} to config data store at {}",sfc, sfcIid); + mdsalPutWrapper(LogicalDatastoreType.CONFIGURATION, sfcIid, sfc); + } + + public void deleteServiceFunctionChain(ServiceFunctionChainKey sfcKey) { + InstanceIdentifier sfcIid = getSFCPath(sfcKey); + LOG.info("Remove Service Function Chain {} from config data store at {}",sfcKey, sfcIid); + mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, sfcIid); + } + + public void addServiceFunctionPath(ServiceFunctionPath sfp) { + InstanceIdentifier sfpIid = getSFPPath(sfp.getKey()); + LOG.info("Write Service Function Path {} to config data store at {}",sfp, sfpIid); + mdsalPutWrapper(LogicalDatastoreType.CONFIGURATION, sfpIid, sfp); + } + + public void deleteServiceFunctionPath(ServiceFunctionPathKey sfpKey) { + InstanceIdentifier sfpIid = getSFPPath(sfpKey); + LOG.info("Delete Service Function Path from config data store at {}", sfpIid); + mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, sfpIid); + } + + private InstanceIdentifier getAclPath(AclKey aclKey) { + return accessListIid.builder().child(Acl.class, aclKey).build(); + } + + private InstanceIdentifier getSFPath(ServiceFunctionKey key) { + return sfIid.builder().child(ServiceFunction.class, key).build(); + } + + private InstanceIdentifier getSFFPath(ServiceFunctionForwarderKey key) { + return sffIid.builder().child(ServiceFunctionForwarder.class, key).build(); + } + + private InstanceIdentifier getSFCPath(ServiceFunctionChainKey key) { + return sfcIid.builder().child(ServiceFunctionChain.class, key).build(); + } + + private InstanceIdentifier getSFPPath(ServiceFunctionPathKey key) { + return sfpIid.builder().child(ServiceFunctionPath.class, key).build(); + } + + public ServiceFunctionForwarder getExistingSFF(String ipAddress) { + ServiceFunctionForwarders existingSffs = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, sffIid); + if (existingSffs != null + && existingSffs.getServiceFunctionForwarder() != null + && !existingSffs.getServiceFunctionForwarder().isEmpty()) { + + List existingSffList = existingSffs.getServiceFunctionForwarder(); + for (ServiceFunctionForwarder sff : existingSffList) { + if (sff.getIpMgmtAddress().getIpv4Address().equals(new Ipv4Address(ipAddress))) { + return sff; + } + } + } + return null; + } + + // TODO Clean up the exception handling + @SuppressWarnings("checkstyle:IllegalCatch") + private void mdsalPutWrapper(LogicalDatastoreType dataStore, + InstanceIdentifier iid, D data) { + try { + mdsalUtils.put(dataStore, iid, data); + } catch (Exception e) { + LOG.error("Exception while putting data in data store {} : {}",iid, data, e); + } + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/FlowClassifierTranslator.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/FlowClassifierTranslator.java new file mode 100644 index 0000000000..53abbaebea --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/FlowClassifierTranslator.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.flowclassifier; + +import java.util.ArrayList; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.Ipv4Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.Ipv6Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntriesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.ActionsBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.MatchesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRangeBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.SourcePortRangeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.RedirectToSfc; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.RedirectToSfcBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.EthertypeV4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.EthertypeV6; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.ProtocolTcp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.ProtocolUdp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.sfc.flow.classifiers.SfcFlowClassifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class will convert OpenStack Flow Classifier API yang models to + * OpenDaylight ACL yang models. + */ +public class FlowClassifierTranslator { + private static final Logger LOG = LoggerFactory.getLogger(FlowClassifierTranslator.class); + private static final Short PROTO_TCP = 6; + private static final Short PROTO_UDP = 17; + private static final String RULE = "_rule"; + + public static Acl buildAcl(SfcFlowClassifier flowClassifier) { + return buildAcl(flowClassifier, null); + } + + public static Acl buildAcl(SfcFlowClassifier flowClassifier, String rspName) { + LOG.info("OpenStack Networking SFC pushed Flow classifier : {}", flowClassifier); + AclBuilder aclBuilder = new AclBuilder(); + AceBuilder aceBuilder = new AceBuilder(); + + ActionsBuilder actionsBuilder = new ActionsBuilder(); + RedirectToSfcBuilder redirectToSfcBuilder = new RedirectToSfcBuilder(); + + AceIpBuilder aceIpBuilder = new AceIpBuilder(); + DestinationPortRangeBuilder destinationPortRange = new DestinationPortRangeBuilder(); + SourcePortRangeBuilder sourcePortRangeBuilder = new SourcePortRangeBuilder(); + + if (flowClassifier.getUuid() != null) { + if (flowClassifier.getName() != null) { + aclBuilder.setAclName(flowClassifier.getUuid().getValue() + "_" + flowClassifier.getName()); + } else { + aclBuilder.setAclName(flowClassifier.getUuid().getValue()); + } + + } + if (flowClassifier.getEthertype() != null) { + IpPrefix sourceIp = null; + IpPrefix destinationIp = null; + if (flowClassifier.getSourceIpPrefix() != null) { + sourceIp = flowClassifier.getSourceIpPrefix(); + } + if (flowClassifier.getDestinationIpPrefix() != null) { + destinationIp = flowClassifier.getDestinationIpPrefix(); + } + if (flowClassifier.getEthertype() == EthertypeV4.class) { + AceIpv4Builder aceIpv4Builder = new AceIpv4Builder(); + if (sourceIp != null && sourceIp.getIpv4Prefix() != null) { + aceIpv4Builder.setSourceIpv4Network(sourceIp.getIpv4Prefix()); + } + if (destinationIp != null && destinationIp.getIpv4Prefix() != null) { + aceIpv4Builder.setDestinationIpv4Network(destinationIp.getIpv4Prefix()); + } + aceIpBuilder.setAceIpVersion(aceIpv4Builder.build()); + aclBuilder.setAclType(Ipv4Acl.class); + } + if (flowClassifier.getEthertype() == EthertypeV6.class) { + AceIpv6Builder aceIpv6Builder = new AceIpv6Builder(); + if (sourceIp != null && sourceIp.getIpv6Prefix() != null) { + aceIpv6Builder.setSourceIpv6Network(sourceIp.getIpv6Prefix()); + } + if (sourceIp != null && destinationIp.getIpv6Prefix() != null) { + aceIpv6Builder.setDestinationIpv6Network(destinationIp.getIpv6Prefix()); + } + aceIpBuilder.setAceIpVersion(aceIpv6Builder.build()); + aclBuilder.setAclType(Ipv6Acl.class); + } + } + if (flowClassifier.getProtocol() != null) { + if (flowClassifier.getProtocol() == ProtocolTcp.class) { + aceIpBuilder.setProtocol(PROTO_TCP); + } + if (flowClassifier.getProtocol() == ProtocolUdp.class) { + aceIpBuilder.setProtocol(PROTO_UDP); + } + } + if (flowClassifier.getSourcePortRangeMin() != null) { + sourcePortRangeBuilder.setLowerPort(new PortNumber(flowClassifier.getSourcePortRangeMin())); + } + if (flowClassifier.getSourcePortRangeMax() != null) { + sourcePortRangeBuilder.setUpperPort(new PortNumber(flowClassifier.getSourcePortRangeMax())); + } + if (flowClassifier.getDestinationPortRangeMin() != null) { + destinationPortRange.setLowerPort(new PortNumber(flowClassifier.getDestinationPortRangeMin())); + } + if (flowClassifier.getDestinationPortRangeMax() != null) { + destinationPortRange.setUpperPort(new PortNumber(flowClassifier.getDestinationPortRangeMax())); + } + if (flowClassifier.getLogicalSourcePort() != null) { + // No respective ACL construct for it. + } + if (flowClassifier.getLogicalDestinationPort() != null) { + // No respective ACL construct for it. + } + if (flowClassifier.getL7Parameter() != null) { + //It's currently not supported. + } + + aceIpBuilder.setSourcePortRange(sourcePortRangeBuilder.build()); + aceIpBuilder.setDestinationPortRange(destinationPortRange.build()); + + MatchesBuilder matchesBuilder = new MatchesBuilder(); + matchesBuilder.setAceType(aceIpBuilder.build()); + + //Set redirect-to-rsp action if rsp name is provided + if (rspName != null) { + redirectToSfcBuilder.setRspName(rspName); + actionsBuilder.addAugmentation(RedirectToSfc.class, redirectToSfcBuilder.build()); + aceBuilder.setActions(actionsBuilder.build()); + } + aceBuilder.setMatches(matchesBuilder.build()); + + //OpenStack networking-sfc don't pass action information + //with flow classifier. It need to be determined using the + //Port Chain data and then flow calssifier need to be updated + //with the actions. + + aceBuilder.setRuleName(aclBuilder.getAclName() + RULE); + aceBuilder.setKey(new AceKey(aceBuilder.getRuleName())); + + ArrayList aceList = new ArrayList<>(); + aceList.add(aceBuilder.build()); + AccessListEntriesBuilder accessListEntriesBuilder = new AccessListEntriesBuilder(); + accessListEntriesBuilder.setAce(aceList); + + aclBuilder.setAccessListEntries(accessListEntriesBuilder.build()); + aclBuilder.setKey(new AclKey(aclBuilder.getAclName(),aclBuilder.getAclType())); + + LOG.info("Translated ACL Flow classfier : {}", aclBuilder.toString()); + + return aclBuilder.build(); + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/NeutronFlowClassifierListener.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/NeutronFlowClassifierListener.java new file mode 100644 index 0000000000..1d49ffdf0e --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/flowclassifier/NeutronFlowClassifierListener.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.flowclassifier; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netvirt.sfc.translator.DelegatingDataTreeListener; +import org.opendaylight.netvirt.sfc.translator.SfcMdsalHelper; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.SfcFlowClassifiers; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.sfc.flow.classifiers.SfcFlowClassifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * OpenDaylight Neutron Flow Classifier yang models data change listener. + */ +public class NeutronFlowClassifierListener extends DelegatingDataTreeListener { + + private static final InstanceIdentifier FLOW_CLASSIFIERS_IID = + InstanceIdentifier.create(Neutron.class).child(SfcFlowClassifiers.class).child(SfcFlowClassifier.class); + + private final SfcMdsalHelper sfcMdsalHelper; + + public NeutronFlowClassifierListener(DataBroker db) { + super(db, new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, FLOW_CLASSIFIERS_IID)); + sfcMdsalHelper = new SfcMdsalHelper(db); + + } + + /** + * Method removes Acl respective to SfcFlowClassifier which is identified by InstanceIdentifier. + * + * @param path - the whole path to SfcFlowClassifier + * @param deletedSfcFlowClassifier - SfcFlowClassifier for removing + */ + @Override + public void remove(InstanceIdentifier path, SfcFlowClassifier deletedSfcFlowClassifier) { + Acl aclFlowClassifier = FlowClassifierTranslator.buildAcl(deletedSfcFlowClassifier); + sfcMdsalHelper.removeAclFlowClassifier(aclFlowClassifier); + } + + /** + * Method updates the original SfcFlowClassifier to the update SfcFlowClassifier. + * Both are identified by same InstanceIdentifier. + * + * @param path - the whole path to SfcFlowClassifier + * @param originalSfcFlowClassifier - original SfcFlowClassifier (for update) + * @param updatedSfcFlowClassifier - changed SfcFlowClassifier (contain updates) + */ + @Override + public void update(InstanceIdentifier path, + SfcFlowClassifier originalSfcFlowClassifier, + SfcFlowClassifier updatedSfcFlowClassifier) { + + Acl aclFlowClassifier = FlowClassifierTranslator.buildAcl(updatedSfcFlowClassifier); + sfcMdsalHelper.updateAclFlowClassifier(aclFlowClassifier); + } + + /** + * Method adds the SfcFlowClassifier which is identified by InstanceIdentifier + * to device. + * + * @param path - the whole path to new SfcFlowClassifier + * @param sfcFlowClassifier - new SfcFlowClassifier + */ + @Override + public void add(InstanceIdentifier path, SfcFlowClassifier sfcFlowClassifier) { + // Respective ACL classifier will be written in data store, once the chain is created. + } + +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortChainListener.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortChainListener.java new file mode 100644 index 0000000000..9b72f178c9 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortChainListener.java @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.portchain; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netvirt.sfc.translator.DelegatingDataTreeListener; +import org.opendaylight.netvirt.sfc.translator.NeutronMdsalHelper; +import org.opendaylight.netvirt.sfc.translator.OvsdbMdsalHelper; +import org.opendaylight.netvirt.sfc.translator.OvsdbPortMetadata; +import org.opendaylight.netvirt.sfc.translator.SfcMdsalHelper; +import org.opendaylight.netvirt.sfc.translator.flowclassifier.FlowClassifierTranslator; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathOutput; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.DeleteRenderedPathInput; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePathService; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.flow.classifier.rev160511.sfc.flow.classifiers.attributes.sfc.flow.classifiers.SfcFlowClassifier; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.PortChains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.chains.PortChain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pair.groups.PortPairGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pairs.PortPair; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OpenDaylight Neutron Port Chain yang models data change listener. + */ +public class NeutronPortChainListener extends DelegatingDataTreeListener { + private static final Logger LOG = LoggerFactory.getLogger(NeutronPortChainListener.class); + + private static final InstanceIdentifier PORT_CHAIN_IID = + InstanceIdentifier.create(Neutron.class).child(PortChains.class).child(PortChain.class); + private final ExecutorService eventProcessor; + private final SfcMdsalHelper sfcMdsalHelper; + private final NeutronMdsalHelper neutronMdsalHelper; + private final OvsdbMdsalHelper ovsdbMdsalHelper; + private RenderedServicePathService rspService; + + public NeutronPortChainListener(DataBroker db, RenderedServicePathService rspService) { + super(db,new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, PORT_CHAIN_IID)); + this.sfcMdsalHelper = new SfcMdsalHelper(db); + this.neutronMdsalHelper = new NeutronMdsalHelper(db); + this.ovsdbMdsalHelper = new OvsdbMdsalHelper(db); + this.rspService = rspService; + ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Port-Chain-Event-Processor").build(); + this.eventProcessor = Executors.newSingleThreadExecutor(threadFactory); + } + + /** + * Method removes PortChain which is identified by InstanceIdentifier. + * + * @param path - the whole path to PortChain + * @param deletedPortChain - PortChain for removing + */ + @Override + public void remove(InstanceIdentifier path, PortChain deletedPortChain) { + if (this.rspService != null) { + DeleteRenderedPathInput deleteRenderedPathInput = + PortChainTranslator.buildDeleteRenderedServicePathInput(PortChainTranslator + .getSFPKey(deletedPortChain)); + if (deleteRenderedPathInput != null) { + this.rspService.deleteRenderedPath(deleteRenderedPathInput); + } + } + sfcMdsalHelper.deleteServiceFunctionPath(PortChainTranslator.getSFPKey(deletedPortChain)); + sfcMdsalHelper.deleteServiceFunctionChain(PortChainTranslator.getSFCKey(deletedPortChain)); + } + + /** + * Method updates the original PortChain to the update PortChain. + * Both are identified by same InstanceIdentifier. + * + * @param path - the whole path to PortChain + * @param originalPortChain - original PortChain (for update) + * @param updatePortChain - changed PortChain (contain updates) + */ + @Override + public void update(InstanceIdentifier path, PortChain originalPortChain, PortChain updatePortChain) { + //TODO: Add support for chain update + } + + /** + * Method adds the PortChain which is identified by InstanceIdentifier + * to device. + * + * @param path - the whole path to new PortChain + * @param newPortChain - new PortChain + */ + @Override + public void add(final InstanceIdentifier path, final PortChain newPortChain) { + processPortChain(newPortChain); + eventProcessor.submit(() -> processPortChain(newPortChain)); + } + + private void processPortChain(PortChain newPortChain) { + //List of Port Pair Group attached to the Port Chain + List portPairGroupList = new ArrayList<>(); + //Port Pair Group and associated Port Pair + Map> groupPortPairsList = new HashMap<>(); + //Map of Port Pair uuid and Port pair ingress port related Neutron Port + Map portPairToNeutronPortMap = new HashMap<>(); + + //Mapping of Port Pair UUID and OvsdbPortMetadata of the port pair ingress port + Map portPairOvsdbMetadata = new HashMap<>(); + + Map portPairGroupToSFFMap = new HashMap<>(); + List portChainServiceFunctionList = new ArrayList<>(); + + //Read chain related port pair group, port pair and neutron port from neutron data store + for (Uuid ppgUuid : newPortChain.getPortPairGroups()) { + PortPairGroup ppg = neutronMdsalHelper.getNeutronPortPairGroup(ppgUuid); + if (ppg != null) { + List portPairList = new ArrayList<>(); + portPairGroupList.add(ppg); + for (Uuid ppUuid : ppg.getPortPairs()) { + PortPair pp = neutronMdsalHelper.getNeutronPortPair(ppUuid); + if (pp != null) { + portPairList.add(pp); + //NOTE:Assuming that ingress and egress port is same. + Port neutronPort = neutronMdsalHelper.getNeutronPort(pp.getIngress()); + if (neutronPort != null) { + portPairToNeutronPortMap.put(pp.getIngress(), neutronPort); + } + } + } + groupPortPairsList.put(ppgUuid, portPairList); + } + } + + Topology ovsdbTopology = ovsdbMdsalHelper.getOvsdbTopologyTree(); + + //Read ovsdb port details related to neutron port. Each Port pair has two neutron port + //With the current implementation, i am assuming that we support SF only with single port + //that act as a ingress as well as egress. + for (Map.Entry neutronPortEntry : portPairToNeutronPortMap.entrySet()) { + OvsdbPortMetadata ovsdbPortMetadata = + ovsdbMdsalHelper.getOvsdbPortMetadata( + neutronPortEntry.getValue().getKey().getUuid(), + ovsdbTopology); + + if (ovsdbPortMetadata != null) { + portPairOvsdbMetadata.put(neutronPortEntry.getKey(), ovsdbPortMetadata); + } + } + + //For each port pair group + for (PortPairGroup ppg : portPairGroupList) { + List portPairSFList = new ArrayList<>(); + + List portPairList = groupPortPairsList.get(ppg.getUuid()); + Map metadataList = new HashMap<>(); + //Generate OvsdbPortMetadata for list of all the port pair + for (PortPair portPair : portPairList) { + OvsdbPortMetadata metadata = portPairOvsdbMetadata.get(portPair.getIngress()); + + if (metadata != null) { + metadataList.put(portPair.getIngress(), metadata); + } + } + + //Build the SFF Builder from port pair group + ServiceFunctionForwarderBuilder sffBuilder = + PortPairGroupTranslator.buildServiceFunctionForwarder(ppg,portPairList, metadataList); + LOG.info("SFF generated for Port Pair Group {} :: {}",ppg, sffBuilder); + //Check if SFF already exist + ServiceFunctionForwarder existingSff = + sfcMdsalHelper.getExistingSFF(sffBuilder.getIpMgmtAddress().getIpv4Address().getValue()); + if (existingSff != null) { + LOG.info("SFF already exist for Port Pair Group {}. Existing SFF is {}",ppg, existingSff); + sffBuilder = new ServiceFunctionForwarderBuilder(existingSff); + } + //Add SFF builder to the map for later reference + portPairGroupToSFFMap.put(ppg.getUuid(), sffBuilder); + + //Generate all the SF and write it to SFC data store + for (PortPair portPair : portPairList) { + OvsdbPortMetadata metadata = portPairOvsdbMetadata.get(portPair.getIngress()); + //Build the service function for the given port pair. + ServiceFunctionBuilder sfBuilder = PortPairTranslator.buildServiceFunction(portPair, + ppg, + portPairToNeutronPortMap.get(portPair.getIngress()), + metadata, + sffBuilder.build()); + + if (sfBuilder != null) { + LOG.info("Service Function generated for the Port Pair {} :: {}", portPair, sfBuilder); + //Write the Service Function to SFC data store. + sfcMdsalHelper.addServiceFunction(sfBuilder.build()); + + //Add to the list, to populated SFF Service Function Dictionary + portPairSFList.add(sfBuilder); + + //Add the SF to Port Chain related SF list + portChainServiceFunctionList.add(sfBuilder.build()); + } else { + LOG.warn("Service Function building failed for Port Pair {}", portPair); + } + } + + //Update the Service Function Dictionary of SFF + for (ServiceFunctionBuilder sf : portPairSFList) { + PortPairGroupTranslator.buildServiceFunctionDictonary(sffBuilder, sf.build()); + LOG.info("Updating Service Function dictionary of SFF {} for SF {}", sffBuilder, sf); + } + // Send SFF create request + LOG.info("Add Service Function Forwarder {} for Port Pair Group {}", sffBuilder.build(), ppg); + sfcMdsalHelper.addServiceFunctionForwarder(sffBuilder.build()); + } + //Build Service Function Chain Builder + ServiceFunctionChain sfc = + PortChainTranslator.buildServiceFunctionChain(newPortChain, portChainServiceFunctionList); + + //Write SFC to data store + if (sfc != null) { + LOG.info("Add service function chain {}", sfc); + sfcMdsalHelper.addServiceFunctionChain(sfc); + } else { + LOG.warn("Service Function Chain building failed for Port Chain {}", newPortChain); + } + + // Build Service Function Path Builder + ServiceFunctionPath sfp = PortChainTranslator.buildServiceFunctionPath(sfc); + //Write SFP to data store + if (sfp != null) { + LOG.info("Add service function path {}", sfp); + sfcMdsalHelper.addServiceFunctionPath(sfp); + } else { + LOG.warn("Service Function Path building failed for Service Chain {}", sfc); + } + + //TODO:Generate Flow Classifiers and augment RSP on it. + + if (this.rspService != null) { + // Build Create Rendered Service Path input + CreateRenderedPathInput rpInput = PortChainTranslator.buildCreateRenderedServicePathInput(sfp); + + //Call Create Rendered Service Path RPC call + if (rpInput != null) { + LOG.info("Call RPC for creating RSP :{}", rpInput); + Future> result = this.rspService.createRenderedPath(rpInput); + try { + if (result.get() != null) { + CreateRenderedPathOutput output = result.get().getResult(); + LOG.debug("RSP name received from SFC : {}", output.getName()); + processFlowClassifiers(newPortChain, newPortChain.getFlowClassifiers(), output.getName()); + } else { + LOG.error("RSP creation failed : {}", rpInput); + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("Error occurred during creating Rendered Service Path using RPC call", e); + } + } + } else { + LOG.error("Rendered Path Service is not available, can't create Rendered Path for Port Chain", + newPortChain); + } + } + + private void processFlowClassifiers(PortChain pc, List flowClassifiers, String rspName) { + for (Uuid uuid : flowClassifiers) { + SfcFlowClassifier fc = neutronMdsalHelper.getNeutronFlowClassifier(uuid); + if (fc != null) { + Acl acl = FlowClassifierTranslator.buildAcl(fc, rspName); + if (acl != null) { + sfcMdsalHelper.addAclFlowClassifier(acl); + } else { + LOG.warn("Acl building failed for flow classifier {}. Traffic might not be redirected to RSP", fc); + } + + } else { + LOG.error("Neutron Flow Classifier {} attached to Port Chain {} is not present in the neutron data " + + "store", uuid, pc); + } + } + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairGroupListener.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairGroupListener.java new file mode 100644 index 0000000000..c335fc2311 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairGroupListener.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.portchain; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netvirt.sfc.translator.DelegatingDataTreeListener; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.PortPairGroups; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pair.groups.PortPairGroup; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * OpenDaylight Neutron Port Pair Group yang models data change listener. + */ +public class NeutronPortPairGroupListener extends DelegatingDataTreeListener { + private static final InstanceIdentifier PORT_PAIR_GROUP_IID = + InstanceIdentifier.create(Neutron.class).child(PortPairGroups.class).child(PortPairGroup.class); + + public NeutronPortPairGroupListener(DataBroker db) { + super(db,new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, PORT_PAIR_GROUP_IID)); + } + + /** + * Method removes PortPairGroup which is identified by InstanceIdentifier. + * + * @param path - the whole path to PortPairGroup + * @param deletedPortPairGroup - PortPairGroup for removing + */ + @Override + public void remove(InstanceIdentifier path, PortPairGroup deletedPortPairGroup) { + //NO-OP + } + + /** + * Method updates the original PortPairGroup to the update PortPairGroup. + * Both are identified by same InstanceIdentifier. + * + * @param path - the whole path to PortPairGroup + * @param originalPortPairGroup - original PortPairGroup (for update) + * @param updatePortPairGroup - changed PortPairGroup (contain updates) + */ + @Override + public void update(InstanceIdentifier path, + PortPairGroup originalPortPairGroup, + PortPairGroup updatePortPairGroup) { + //NO-OP + } + + /** + * Method adds the PortPairGroup which is identified by InstanceIdentifier + * to device. + * + * @param path - the whole path to new PortPairGroup + * @param newPortPairGroup - new PortPairGroup + */ + @Override + public void add(InstanceIdentifier path, PortPairGroup newPortPairGroup) { + //NO-OP + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairListener.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairListener.java new file mode 100644 index 0000000000..098efe5a85 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/NeutronPortPairListener.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.portchain; + +import java.util.List; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netvirt.sfc.translator.DelegatingDataTreeListener; +import org.opendaylight.netvirt.sfc.translator.NeutronMdsalHelper; +import org.opendaylight.netvirt.sfc.translator.OvsdbMdsalHelper; +import org.opendaylight.netvirt.sfc.translator.SfcMdsalHelper; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.service.function.forwarder.ServiceFunctionDictionary; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.PortPairs; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pairs.PortPair; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OpenDaylight Neutron Port Pair yang models data change listener. + */ +public class NeutronPortPairListener extends DelegatingDataTreeListener { + private static final Logger LOG = LoggerFactory.getLogger(NeutronPortPairListener.class); + + private static final InstanceIdentifier PORT_PAIR_IID = + InstanceIdentifier.create(Neutron.class).child(PortPairs.class).child(PortPair.class); + + private final DataBroker db; + private final SfcMdsalHelper sfcMdsalHelper; + private final NeutronMdsalHelper neutronMdsalHelper; + private final OvsdbMdsalHelper ovsdbMdsalHelper; + + public NeutronPortPairListener(DataBroker db) { + super(db,new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, PORT_PAIR_IID)); + this.db = db; + sfcMdsalHelper = new SfcMdsalHelper(db); + neutronMdsalHelper = new NeutronMdsalHelper(db); + ovsdbMdsalHelper = new OvsdbMdsalHelper(db); + } + + /** + * Method removes PortPair which is identified by InstanceIdentifier. + * + * @param path - the whole path to PortPair + * @param deletedPortPair - PortPair for removing + */ + @Override + public void remove(InstanceIdentifier path, PortPair deletedPortPair) { + ServiceFunctionKey sfKey = PortPairTranslator.getSFKey(deletedPortPair); + ServiceFunction serviceFunction = sfcMdsalHelper.readServiceFunction(sfKey); + if (serviceFunction != null) { + //Current support is for one Service Function connected to one data plane locator + SffName sffName = serviceFunction.getSfDataPlaneLocator().get(0).getServiceFunctionForwarder(); + if (sffName != null) { + ServiceFunctionForwarderKey sffKey = new ServiceFunctionForwarderKey(sffName); + ServiceFunctionForwarder sff = sfcMdsalHelper.readServiceFunctionForwarder(sffKey); + if (sff != null) { + List sfDictionary = sff.getServiceFunctionDictionary(); + if (sfDictionary != null) { + ServiceFunctionDictionary matchingSfd = null; + for (ServiceFunctionDictionary sfd : sfDictionary) { + if (sfd.getName().equals(sfKey.getName())) { + matchingSfd = sfd; + break; + } + } + if (matchingSfd != null) { + sfDictionary.remove(matchingSfd); + if (sfDictionary.isEmpty()) { + //If the current SF is last SF attach to SFF, delete the SFF + sfcMdsalHelper.deleteServiceFunctionForwarder(sffKey); + } else { + //If there are SF attached to the SFF, just update the SFF dictionary by removing + // this deleted SF. + ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder(sff); + LOG.info("Remove the SF {} from SFF {} dictionary and update in data store.", sfKey + .getName(), sffName); + sfcMdsalHelper.addServiceFunctionForwarder(sffBuilder.build()); + } + } else { + LOG.info("Service Function {} not present in connected Service Function Forwarder {} " + + "dictionary", sfKey.getName(), sffName); + } + } + } + } + } else { + LOG.info("Service Function related to Port Pair {} don't exist.", deletedPortPair); + } + sfcMdsalHelper.removeServiceFunction(sfKey); + } + + /** + * Method updates the original PortPair to the update PortPair. + * Both are identified by same InstanceIdentifier. + * + * @param path - the whole path to PortPair + * @param originalPortPair - original PortPair (for update) + * @param updatePortPair - changed PortPair (contain updates) + */ + @Override + public void update(InstanceIdentifier path, PortPair originalPortPair, PortPair updatePortPair) { + //NO-OP + } + + /** + * Method adds the PortPair which is identified by InstanceIdentifier + * to device. + * + * @param path - the whole path to new PortPair + * @param newPortPair - new PortPair + */ + @Override + public void add(InstanceIdentifier path, PortPair newPortPair) { + //NO-OP + // Port Pair data written in neutron data store will be used + // When user will create port chain. + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortChainTranslator.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortChainTranslator.java new file mode 100644 index 0000000000..b88e96f507 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortChainTranslator.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.portchain; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.List; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfpName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.DeleteRenderedPathInput; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.DeleteRenderedPathInputBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChain; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChainBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.ServiceFunctionChainKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.service.function.chain.SfcServiceFunction; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.service.function.chain.SfcServiceFunctionBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chain.grouping.service.function.chain.SfcServiceFunctionKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPathBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPathKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.port.chain.attributes.ChainParameters; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.chains.PortChain; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class will convert OpenStack Port Chain API yang models present in + * neutron northbound project to OpenDaylight SFC yang models. + */ +public class PortChainTranslator { + private static final Logger LOG = LoggerFactory.getLogger(PortChainTranslator.class); + private static final String SYMMETRIC_PARAM = "symmetric"; + private static final String SFP_NAME_PREFIX = "Path-"; + + public static ServiceFunctionChain buildServiceFunctionChain( + PortChain portChain, List sfList) { + ServiceFunctionChainBuilder sfcBuilder = new ServiceFunctionChainBuilder(); + sfcBuilder.setName(new SfcName(portChain.getName())); + sfcBuilder.setKey(new ServiceFunctionChainKey(sfcBuilder.getName())); + + //By default set it to false. If user specify it in chain parameters, it + //will be overridden. + sfcBuilder.setSymmetric(false); + + //Set service functions + List sfcSfList = new ArrayList<>(); + for (ServiceFunction sf : sfList) { + SfcServiceFunctionBuilder sfcSfBuilder = new SfcServiceFunctionBuilder(); + sfcSfBuilder.setName(sf.getName().getValue()); + sfcSfBuilder.setType(sf.getType()); + sfcSfBuilder.setKey(new SfcServiceFunctionKey(sfcSfBuilder.getName())); + + //NOTE: no explicit order is set. + sfcSfList.add(sfcSfBuilder.build()); + } + List cpList = portChain.getChainParameters(); + if (cpList != null && !cpList.isEmpty()) { + for (ChainParameters cp : cpList) { + if (cp.getChainParameter().equals(SYMMETRIC_PARAM)) { + //Override the symmetric default value. + sfcBuilder.setSymmetric(Boolean.valueOf(cp.getChainParameterValue())); + break; + } + } + } + sfcBuilder.setSfcServiceFunction(sfcSfList); + return sfcBuilder.build(); + } + + public static ServiceFunctionPath buildServiceFunctionPath(ServiceFunctionChain sfc) { + Preconditions.checkNotNull(sfc, "Service Function Chain must not be null"); + ServiceFunctionPathBuilder sfpBuilder = new ServiceFunctionPathBuilder(); + + //Set the name + sfpBuilder.setName(new SfpName(SFP_NAME_PREFIX + sfc.getName().getValue())); + + sfpBuilder.setSymmetric(sfc.isSymmetric()); + //Set related SFC name + sfpBuilder.setServiceChainName(sfc.getName()); + return sfpBuilder.build(); + } + + public static CreateRenderedPathInput buildCreateRenderedServicePathInput(ServiceFunctionPath sfp) { + CreateRenderedPathInputBuilder rpInputBuilder = new CreateRenderedPathInputBuilder(); + rpInputBuilder.setSymmetric(sfp.isSymmetric()); + rpInputBuilder.setParentServiceFunctionPath(sfp.getName().getValue()); + return rpInputBuilder.build(); + } + + public static DeleteRenderedPathInput buildDeleteRenderedServicePathInput(ServiceFunctionPathKey sfpKey) { + DeleteRenderedPathInputBuilder rpInputBuilder = new DeleteRenderedPathInputBuilder(); + rpInputBuilder.setName(sfpKey.getName().getValue()); + return rpInputBuilder.build(); + } + + public static ServiceFunctionChainKey getSFCKey(PortChain portChain) { + return new ServiceFunctionChainKey(new SfcName(portChain.getName())); + } + + public static ServiceFunctionPathKey getSFPKey(PortChain portChain) { + return new ServiceFunctionPathKey(new SfpName(SFP_NAME_PREFIX + portChain.getName())); + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairGroupTranslator.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairGroupTranslator.java new file mode 100644 index 0000000000..dc463c8d28 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairGroupTranslator.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.portchain; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import org.opendaylight.netvirt.sfc.translator.OvsdbMdsalHelper; +import org.opendaylight.netvirt.sfc.translator.OvsdbPortMetadata; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffDataPlaneLocatorName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SnName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.SffOvsBridgeAugmentation; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.SffOvsBridgeAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.SffOvsLocatorOptionsAugmentation; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.SffOvsLocatorOptionsAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.SffOvsNodeAugmentation; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.SffOvsNodeAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.bridge.OvsBridgeBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.node.OvsNodeBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.ovs.rev140701.options.OvsOptionsBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.Open; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocatorBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocatorKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocatorBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.service.function.forwarder.ServiceFunctionDictionary; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.service.function.forwarder.ServiceFunctionDictionaryBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.service.function.forwarder.ServiceFunctionDictionaryKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.service.function.forwarder.service.function.dictionary.SffSfDataPlaneLocatorBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.VxlanGpe; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.IpBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pair.groups.PortPairGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pairs.PortPair; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class will convert OpenStack Port Pair API yang models present in + * neutron northbound project to OpenDaylight SFC yang models. + */ +public class PortPairGroupTranslator { + private static final Logger LOG = LoggerFactory.getLogger(PortPairGroupTranslator.class); + private static final String OPT_FLOW_STR = "flow"; + private static final String OPT_GPE_STR = "gpe"; + private static final String OPT_DST_PORT = "6633"; + + private static final AtomicInteger COUNTER = new AtomicInteger(0); + private static final String SFF_DEFAULT_NAME = "sff"; + private static final String SFF_DPL_SUFFIX = "-dpl"; + private static final String SFF_DPL_FIX_NAME = "vxgpe"; + + public static ServiceFunctionForwarderBuilder buildServiceFunctionForwarder( + PortPairGroup portPairGroup, + List portPairs, + Map ovsdbPortsMetadata) { + Preconditions.checkNotNull(portPairGroup, "Port pair group must not be null"); + + ServiceFunctionForwarderBuilder sffBuilder = new ServiceFunctionForwarderBuilder(); + SffOvsNodeAugmentationBuilder sffOvsNodeAugBuilder = new SffOvsNodeAugmentationBuilder(); + + DataPlaneLocatorBuilder dplBuilder = new DataPlaneLocatorBuilder(); + + //Currently we only support one SF per type. Mean, one port-pair per port-pair-group. + //Get port pair from neutron data store. + PortPair portPair = portPairs.get(0); + if (portPair == null) { + LOG.error("Port pair {} does not exist in the neutron data store. Port Pair Group {} request can't be " + + "processed.", portPairGroup.getPortPairs().get(0), portPairGroup); + return null; + } + //Get metadata of neutron port related to port pair ingress port from ovsdb data store. + OvsdbPortMetadata ovsdbPortMetadata = ovsdbPortsMetadata.get(portPair.getIngress()); + + //Convert the port pair to service function + + //Set SFF DPL transport type + dplBuilder.setTransport(VxlanGpe.class); + + //Set SFF Locator Type + OvsdbNodeAugmentation ovsdbNodeAug = ovsdbPortMetadata.getOvsdbNode(); + IpBuilder sffLocator = new IpBuilder(); + if (ovsdbNodeAug != null) { + sffLocator.setIp(ovsdbNodeAug.getConnectionInfo().getRemoteIp()); + sffLocator.setPort(ovsdbNodeAug.getConnectionInfo().getRemotePort()); + } + dplBuilder.setLocatorType(sffLocator.build()); + //set data-path-locator for sff-data-path-locator + SffDataPlaneLocatorBuilder sffDplBuilder = new SffDataPlaneLocatorBuilder(); + sffDplBuilder.setDataPlaneLocator(dplBuilder.build()); + + //Set location options for sff-dp-locator + sffDplBuilder.addAugmentation(SffOvsLocatorOptionsAugmentation.class, buildOvsOptions().build()); + + //Set ovsdb bridge name for sff + OvsBridgeBuilder ovsBridgeBuilder = new OvsBridgeBuilder(); + OvsdbBridgeAugmentation ovsdbBridgeAugmentation = ovsdbPortMetadata.getOvsdbBridgeNode(); + if (ovsdbBridgeAugmentation != null) { + ovsBridgeBuilder.setBridgeName(ovsdbBridgeAugmentation.getBridgeName().getValue()); + + //Set SFF name + String serviceNode = OvsdbMdsalHelper.getNodeKey(ovsdbBridgeAugmentation.getManagedBy().getValue()); + if (serviceNode.isEmpty()) { + serviceNode += SFF_DEFAULT_NAME + COUNTER.incrementAndGet(); + sffBuilder.setName(new SffName(serviceNode)); + sffBuilder.setServiceNode(new SnName(serviceNode)); + } else { + //Set service node to ovsdbNode + sffBuilder.setServiceNode(new SnName(serviceNode)); + + //Set SFF name to ovsdbBridgeNode + serviceNode += "/" + ovsdbBridgeAugmentation.getBridgeName().getValue(); + sffBuilder.setName(new SffName(serviceNode)); + } + + //Set ovsdb-node iid reference for SFF + OvsNodeBuilder ovsNodeBuilder = new OvsNodeBuilder(); + ovsNodeBuilder.setNodeId(ovsdbBridgeAugmentation.getManagedBy()); + sffOvsNodeAugBuilder.setOvsNode(ovsNodeBuilder.build()); + sffBuilder.addAugmentation(SffOvsNodeAugmentation.class, sffOvsNodeAugBuilder.build()); + } + SffOvsBridgeAugmentationBuilder sffOvsBridgeAugBuilder = new SffOvsBridgeAugmentationBuilder(); + sffOvsBridgeAugBuilder.setOvsBridge(ovsBridgeBuilder.build()); + sffBuilder.addAugmentation(SffOvsBridgeAugmentation.class, sffOvsBridgeAugBuilder.build()); + + //Set management ip, same to the ovsdb node ip + sffBuilder.setIpMgmtAddress(sffLocator.getIp()); + + //TODO: DPL name should not be hardcoded. Require net-virt classifier to remove dependency on it. + sffDplBuilder.setName(new SffDataPlaneLocatorName(SFF_DPL_FIX_NAME)); + sffDplBuilder.setKey(new SffDataPlaneLocatorKey(sffDplBuilder.getName())); + List sffDataPlaneLocator = new ArrayList<>(); + sffDataPlaneLocator.add(sffDplBuilder.build()); + //set SFF key + sffBuilder.setKey(new ServiceFunctionForwarderKey(sffBuilder.getName())); + sffBuilder.setSffDataPlaneLocator(sffDataPlaneLocator); + + return sffBuilder; + } + + public static void buildServiceFunctionDictonary(ServiceFunctionForwarderBuilder sffBuilder, + ServiceFunction sf) { + ServiceFunctionDictionaryBuilder sfdBuilder = new ServiceFunctionDictionaryBuilder(); + + //Build Sff-sf-data-plane-locator + SffSfDataPlaneLocatorBuilder sffSfDplBuilder = new SffSfDataPlaneLocatorBuilder(); + sffSfDplBuilder.setSfDplName(sf.getSfDataPlaneLocator().get(0).getName()); + sffSfDplBuilder.setSffDplName(sffBuilder.getSffDataPlaneLocator().get(0).getName()); + sfdBuilder.setSffSfDataPlaneLocator(sffSfDplBuilder.build()); + + sfdBuilder.setName(sf.getName()); + sfdBuilder.setKey(new ServiceFunctionDictionaryKey(sfdBuilder.getName())); + + //NOTE: fail mode is set to Open by default + sfdBuilder.setFailmode(Open.class); + List sfdList = new ArrayList<>(); + sfdList.add(sfdBuilder.build()); + + //TODO: set interface name list + + if (sffBuilder.getServiceFunctionDictionary() != null) { + for (Iterator sfdItr = sffBuilder.getServiceFunctionDictionary().iterator(); + sfdItr.hasNext();) { + ServiceFunctionDictionary sfd = sfdItr.next(); + if (sfd.getName().equals(sfdBuilder.getName())) { + LOG.info("Existing SF dictionary {} found in SFF {}, removing the SF dictionary", sfd.getName(), + sffBuilder.getName()); + sfdItr.remove(); + break; + } + } + sffBuilder.getServiceFunctionDictionary().addAll(sfdList); + } else { + sffBuilder.setServiceFunctionDictionary(sfdList); + } + LOG.info("Final Service Function Dictionary {}", sffBuilder.getServiceFunctionDictionary()); + } + + private static SffOvsLocatorOptionsAugmentationBuilder buildOvsOptions() { + OvsOptionsBuilder ovsOptionsBuilder = new OvsOptionsBuilder(); + ovsOptionsBuilder.setRemoteIp(OPT_FLOW_STR); + ovsOptionsBuilder.setDstPort(OPT_DST_PORT); + ovsOptionsBuilder.setKey(OPT_FLOW_STR); + ovsOptionsBuilder.setExts(OPT_GPE_STR); + SffOvsLocatorOptionsAugmentationBuilder ovsOptions = new SffOvsLocatorOptionsAugmentationBuilder(); + ovsOptions.setOvsOptions(ovsOptionsBuilder.build()); + return ovsOptions; + } +} diff --git a/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairTranslator.java b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairTranslator.java new file mode 100644 index 0000000000..53b67d5321 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/java/org/opendaylight/netvirt/sfc/translator/portchain/PortPairTranslator.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2017 Red Hat, 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.netvirt.sfc.translator.portchain; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableBiMap; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.opendaylight.netvirt.sfc.translator.OvsdbMdsalHelper; +import org.opendaylight.netvirt.sfc.translator.OvsdbPortMetadata; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfDataPlaneLocatorName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SftTypeName; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.TenantId; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocator; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionBuilder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionKey; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.SlTransportType; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.VxlanGpe; +import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.IpBuilder; +import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sf.ovs.rev160107.SfDplOvsAugmentation; +import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sf.ovs.rev160107.SfDplOvsAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sf.ovs.rev160107.connected.port.OvsPortBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.AllowedAddressPairs; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.port.pair.attributes.ServiceFunctionParameters; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pair.groups.PortPairGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.sfc.rev160511.sfc.attributes.port.pairs.PortPair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class will convert OpenStack Port Pair API yang models present in + * neutron northbound project to OpenDaylight SFC Service Function yang models. + */ +public class PortPairTranslator { + private static final Logger LOG = LoggerFactory.getLogger(PortPairTranslator.class); + private static final String SF_TYPE_PARAM = "type"; + private static final String DPL_SUFFIX_PARAM = "-dpl"; + private static final String DPL_TRANSPORT_PARAM = "dpl-transport"; + private static final String DPL_IP_PARAM = "dpl-ip"; + private static final String DPL_PORT_PARAM = "dpl-port"; + private static final String SFF_STR = "sff"; + + public static final ImmutableBiMap> DPL_TRANSPORT_TYPE + = new ImmutableBiMap.Builder>() + .put("vxlan-gpe", VxlanGpe.class).build(); + private static final PortNumber SF_LOCATOR_PORT = new PortNumber(6633); + private static final AtomicInteger COUNTER = new AtomicInteger(0); + private static final String SFF_NAME_PARAM = "sff-name"; + + public static ServiceFunctionBuilder buildServiceFunction( + PortPair portPair, + PortPairGroup portPairGroup, + Port neutronPort, + OvsdbPortMetadata ovsdbPortMetadata, + ServiceFunctionForwarder sff) { + Preconditions.checkNotNull(portPair, "Port pair must not be null"); + Preconditions.checkNotNull(portPairGroup, "Port pair group must not be null"); + Preconditions.checkNotNull(neutronPort, "Neutron Port related to port pair must not be null"); + LOG.info("Port pair received : {}", portPair); + + ServiceFunctionBuilder serviceFunctionBuilder = new ServiceFunctionBuilder(); + IpBuilder sfLocator = new IpBuilder(); + + //Set SF name, key and tenant-id + serviceFunctionBuilder.setName(new SfName(portPair.getName())); + serviceFunctionBuilder.setKey(new ServiceFunctionKey(serviceFunctionBuilder.getName())); + serviceFunctionBuilder.setTenantId(new TenantId(portPair.getTenantId().getValue())); + + //Set SF Type. Setting it to PortPairGroup Type, this will be overridden if user pass + //it through service_function_params + serviceFunctionBuilder.setType(SftTypeName.getDefaultInstance(portPairGroup.getName())); + + //Set data path locator name and key + SfDataPlaneLocatorBuilder sfDataPlaneLocatorBuilder = new SfDataPlaneLocatorBuilder(); + sfDataPlaneLocatorBuilder.setName(new SfDataPlaneLocatorName(portPair.getName() + DPL_SUFFIX_PARAM)); + sfDataPlaneLocatorBuilder.setKey(new SfDataPlaneLocatorKey(sfDataPlaneLocatorBuilder.getName())); + + //Set vxlan-gpe as a default transport type, unless user pass specific transport in + //service_function_parameters + sfDataPlaneLocatorBuilder.setTransport(VxlanGpe.class); + + //Set locator type + if (neutronPort != null) { + List fixedIps = neutronPort.getFixedIps(); + List attachedIpAddresses = neutronPort.getAllowedAddressPairs(); + IpAddress ipAddress; + if (fixedIps != null && !fixedIps.isEmpty()) { + ipAddress = fixedIps.get(0).getIpAddress(); + sfLocator.setIp(ipAddress); + + } else if (attachedIpAddresses != null && !attachedIpAddresses.isEmpty()) { + //Pick up the first ip address + ipAddress = attachedIpAddresses.get(0).getIpAddress().getIpAddress(); + sfLocator.setIp(ipAddress); + } else { + LOG.warn("No ip address attached to Neutron Port {} related to Port Pair {}", neutronPort, portPair); + //Ideally we should exit here, because without IP address OpenDaylight SFC won't be able to find the + //respective overlay. But if user passes additional parameter through service_function_param + //that can be leveraged here. Parameter passed through service_function_param will take precedence. + } + sfLocator.setPort(new PortNumber(SF_LOCATOR_PORT)); + + } else { + LOG.warn("Neutron port mapped to Port pair ingress/egress port is not found : {}", portPair); + } + + if (ovsdbPortMetadata.getOvsdbPort() != null) { + String ovsdbPortName = OvsdbMdsalHelper.getOvsdbPortName(ovsdbPortMetadata.getOvsdbPort()); + SfDplOvsAugmentationBuilder sfDplOvsAugmentationBuilder = new SfDplOvsAugmentationBuilder(); + OvsPortBuilder ovsPortBuilder = new OvsPortBuilder(); + ovsPortBuilder.setPortId(ovsdbPortName); + sfDplOvsAugmentationBuilder.setOvsPort(ovsPortBuilder.build()); + sfDataPlaneLocatorBuilder.addAugmentation(SfDplOvsAugmentation.class, sfDplOvsAugmentationBuilder.build()); + } else { + LOG.warn("No OVSDB Port found for the port pair {}. Lack of ovs-port information might not configure SF " + + "properly", portPair); + } + + //But if user pass specific param using service_function_parameters, set/override it accordingly + List sfParams = portPair.getServiceFunctionParameters(); + if (sfParams != null) { + for (ServiceFunctionParameters sfParam : sfParams) { + //There is by default type set to port pair group name, override it if user pass it specific type + if (sfParam.getServiceFunctionParameter().equals(SF_TYPE_PARAM)) { + serviceFunctionBuilder.setType(new SftTypeName(sfParam.getServiceFunctionParameterValue())); + } + if (sfParam.getServiceFunctionParameter().equals(DPL_TRANSPORT_PARAM)) { + Class transportTypeClass + = DPL_TRANSPORT_TYPE.get(sfParam.getServiceFunctionParameterValue()); + sfDataPlaneLocatorBuilder.setTransport(transportTypeClass); + } + if (sfParam.getServiceFunctionParameter().equals(DPL_IP_PARAM)) { + IpAddress ipAddress = new IpAddress(new Ipv4Address(sfParam.getServiceFunctionParameterValue())); + sfLocator.setIp(ipAddress); + } + if (sfParam.getServiceFunctionParameter().equals(DPL_PORT_PARAM)) { + sfLocator.setPort(new PortNumber(new Integer(sfParam.getServiceFunctionParameterValue()))); + } + if (sfParam.getServiceFunctionParameter().equals(SFF_NAME_PARAM)) { + sfDataPlaneLocatorBuilder.setServiceFunctionForwarder( + new SffName(sfParam.getServiceFunctionParameterValue())); + } + } + } + + //Set service_function_forwarder + if (sfDataPlaneLocatorBuilder.getServiceFunctionForwarder() == null + && ovsdbPortMetadata.getOvsdbNode() != null) { + SffName sffName = sff.getName(); + if (sffName != null) { + sfDataPlaneLocatorBuilder.setServiceFunctionForwarder(sffName); + } else { + sfDataPlaneLocatorBuilder.setServiceFunctionForwarder(new SffName(SFF_STR + COUNTER.incrementAndGet())); + } + } + + sfDataPlaneLocatorBuilder.setLocatorType(sfLocator.build()); + + List sfDataPlaneLocatorList = new ArrayList<>(); + sfDataPlaneLocatorList.add(sfDataPlaneLocatorBuilder.build()); + //Set management IP to same as DPL IP. + serviceFunctionBuilder.setIpMgmtAddress(sfLocator.getIp()); + + //Set all data plane locator + serviceFunctionBuilder.setSfDataPlaneLocator(sfDataPlaneLocatorList); + LOG.info("Port Pair translated to Service Function: {}", serviceFunctionBuilder); + return serviceFunctionBuilder; + } + + public static ServiceFunctionKey getSFKey(PortPair portPair) { + return new ServiceFunctionKey(new SfName(portPair.getName())); + } +} diff --git a/vpnservice/sfc/translator/src/main/resources/org/opendaylight/blueprint/sfc-translator.xml b/vpnservice/sfc/translator/src/main/resources/org/opendaylight/blueprint/sfc-translator.xml new file mode 100644 index 0000000000..a8e2eacac8 --- /dev/null +++ b/vpnservice/sfc/translator/src/main/resources/org/opendaylight/blueprint/sfc-translator.xml @@ -0,0 +1,18 @@ + + + + + + + + -- 2.36.6