public OpendaylightSfc() {
- executor = Executors.newFixedThreadPool(6);
+ executor = Executors.newFixedThreadPool(10);
opendaylightSfcObj = this;
}
try {
method = c.getDeclaredMethod(methodName, parameterTypes);
method.invoke(this, parameters);
- } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
+ } catch (IllegalAccessException | NoSuchMethodException e) {
LOG.error("Could not find method {} in class", methodName);
return;
+ } catch (InvocationTargetException e) {
+ LOG.error("Invocation target exception: {}", e.getMessage());
+ return;
} catch (UniformInterfaceException e) {
LOG.error("REST Server error. Message: {}",
e.getMessage());
*/
String sffJSON = getRESTObj(getServiceFunctionForwarderURI(serviceFunctionForwarder));
- String restURI = serviceFunctionForwarder.getRestUri().toString();
+ String restURI = serviceFunctionForwarder.getRestUri().getValue();
//restURI = "http://127.0.0.1:5000";
sffURI = restURI + "/config/service" +
ServiceFunctionForwarder serviceFunctionForwarder =
(ServiceFunctionForwarder) future
.get();
- restURI = serviceFunctionForwarder.getRestUri().toString();
+ restURI = serviceFunctionForwarder.getRestUri().getValue();
// Testing
//restURI = "http://127.0.0.1:5000";
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.provider.api;
+
+import com.google.common.util.concurrent.FutureCallback;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements the general Datastore checked future
+ * callback
+ * <p/>
+ *
+ * @version 0.1
+ * <p/>
+ * @since 2014-06-30
+ */
+public class SfcDataStoreCallback implements FutureCallback<Void>
+{
+ private static final Logger LOG = LoggerFactory.getLogger(SfcDataStoreCallback.class);
+ private boolean transaction_successful;
+
+ public boolean getTransactioSuccessful() {return transaction_successful; }
+
+ @Override
+ public void onSuccess(final Void result)
+ {
+ // Commited successfully
+ this.transaction_successful = true;
+ }
+
+ @Override
+ public void onFailure(final Throwable t)
+ {
+ // Transaction failed
+ this.transaction_successful = false;
+
+ if (t instanceof OptimisticLockFailedException)
+ {
+ // Failed because of concurrent transaction modifying same data
+ } else
+ {
+ // Some other type of TransactionCommitFailedException
+ }
+ }
+}
package org.opendaylight.sfc.provider.api;
import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
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.TransactionCommitFailedException;
import org.opendaylight.sfc.provider.SfcProviderRestAPI;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf
- .rev140701.ServiceFunctionsState;
-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.state.ServiceFunctionState;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf
- .rev140701.service.functions.state.ServiceFunctionStateKey;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc
- .rev140701.ServiceFunctionChainsState;
-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.service.function.chain
- .SfcServiceFunction;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc
- .rev140701.service.function.chains.state.ServiceFunctionChainState;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc
- .rev140701.service.function.chains.state
- .ServiceFunctionChainStateBuilder;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc
- .rev140701.service.function.chains.state.ServiceFunctionChainStateKey;
-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.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.cisco.params.xml.ns.yang.sfc.sfp
- .rev140701.service.function.paths.service.function.path.ServicePathHop;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp
- .rev140701.service.function.paths.service.function.path
- .ServicePathHopBuilder;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft
- .rev140701.service.function.types.ServiceFunctionType;
-import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft
- .rev140701.service.function.types.service.function.type
- .SftServiceFunctionName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.ServiceFunctionsState;
+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.state.ServiceFunctionState;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.state.ServiceFunctionStateKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.ServiceFunctionChainsState;
+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.service.function.chain.SfcServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chains.state.ServiceFunctionChainState;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chains.state.ServiceFunctionChainStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfc.rev140701.service.function.chains.state.ServiceFunctionChainStateKey;
+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.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.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.service.function.path.ServicePathHop;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.service.function.path.ServicePathHopBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.service.function.types.ServiceFunctionType;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.service.function.types.service.function.type.SftServiceFunctionName;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
.build();
ReadOnlyTransaction readTx = odlSfc.getDataProvider().newReadOnlyTransaction();
- Optional<ServiceFunctionState> serviceFunctionStateObject = null;
+ Optional<ServiceFunctionState> serviceFunctionStateObject;
try {
serviceFunctionStateObject = readTx.read(LogicalDatastoreType.OPERATIONAL, sfStateIID).get();
} catch (InterruptedException | ExecutionException e) {
return;
}
- if ((serviceFunctionStateObject != null) &&
+ if ((serviceFunctionStateObject.isPresent()) &&
(serviceFunctionStateObject.get() instanceof ServiceFunctionState)) {
serviceFunctionState = serviceFunctionStateObject.get();
List<String> sfServiceFunctionPathList =
WriteTransaction writeTx = odlSfc.getDataProvider().newWriteOnlyTransaction();
writeTx.delete(LogicalDatastoreType.CONFIGURATION,
sfpIID);
- writeTx.commit();
+ //writeTx.commit();
+ CheckedFuture<Void,TransactionCommitFailedException> submitFuture = writeTx.submit();
+ Futures.addCallback(submitFuture, new SfcDataStoreCallback());
+
// TODO: Need to consider failure of transaction
removedPaths.add(pathName);
}
package org.opendaylight.sfc.provider.api;
import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
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.TransactionCommitFailedException;
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.sft.rev140701.ServiceFunctionTypes;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.ServiceFunctionTypesBuilder;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.service.function.types.ServiceFunctionType;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.service.function.types.ServiceFunctionTypeKey;
import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sft.rev140701.service.function.types.service.function.type.SftServiceFunctionName;
printTraceStart(LOG);
if (dataBroker != null) {
- InstanceIdentifier<ServiceFunctionType> sftEntryIID = InstanceIdentifier.builder(ServiceFunctionTypes.class)
+ InstanceIdentifier<ServiceFunctionType> sftEntryIID =
+ InstanceIdentifier.builder(ServiceFunctionTypes.class)
.child(ServiceFunctionType.class, sft.getKey()).toInstance();
WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
return ret;
}
+ /**
+ * This method is used to retrieve a Service Function Type from the DataStore
+ * <p>
+ * @param serviceFunctionTypeName Service Function Type Name
+ * @return Service Function Type Object
+ */
protected ServiceFunctionType readServiceFunctionType(String serviceFunctionTypeName) {
printTraceStart(LOG);
ServiceFunctionType sft = null;
sftIID = InstanceIdentifier.builder(ServiceFunctionTypes.class)
.child(ServiceFunctionType.class, serviceFunctionTypeKey).build();
- if (dataBroker != null) {
- ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
- Optional<ServiceFunctionType> serviceFunctionChainDataObject = null;
- try {
- serviceFunctionChainDataObject = readTx
- .read(LogicalDatastoreType.CONFIGURATION, sftIID).get();
- } catch (InterruptedException | ExecutionException e) {
- LOG.error("Could not read Service Function list for Type {} " +
- "", serviceFunctionTypeName);
- }
- if (serviceFunctionChainDataObject != null
- && serviceFunctionChainDataObject.isPresent()) {
- sft = serviceFunctionChainDataObject.get();
- }
+ ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
+ Optional<ServiceFunctionType> serviceFunctionChainDataObject = null;
+ try {
+ serviceFunctionChainDataObject = readTx
+ .read(LogicalDatastoreType.CONFIGURATION, sftIID).get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Could not read Service Function list for Type {} " +
+ "", serviceFunctionTypeName);
+ }
+ if (serviceFunctionChainDataObject != null
+ && serviceFunctionChainDataObject.isPresent()) {
+ sft = serviceFunctionChainDataObject.get();
}
+
printTraceStop(LOG);
return sft;
}
+ /**
+ * This method is used to delete a Service Function Type from the DataStore
+ * <p>
+ * @param serviceFunctionTypeName Service Function Type Name
+ * @return Service Function Type Object
+ */
protected boolean deleteServiceFunctionType(String serviceFunctionTypeName) {
- boolean ret = false;
+ boolean ret;
printTraceStart(LOG);
ServiceFunctionTypeKey serviceFunctionTypeKey = new
ServiceFunctionTypeKey(serviceFunctionTypeName);
InstanceIdentifier.builder(ServiceFunctionTypes.class)
.child(ServiceFunctionType.class, serviceFunctionTypeKey).toInstance();
- if (dataBroker != null) {
- WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
- writeTx.delete(LogicalDatastoreType.CONFIGURATION, sftEntryIID);
- writeTx.commit();
+ SfcDataStoreCallback sfcDataStoreCallback = new SfcDataStoreCallback();
+ WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+ writeTx.delete(LogicalDatastoreType.CONFIGURATION, sftEntryIID);
+
+ CheckedFuture<Void,TransactionCommitFailedException> submitFuture = writeTx.submit();
+ Futures.addCallback(submitFuture, sfcDataStoreCallback);
+ if (sfcDataStoreCallback.getTransactioSuccessful()) {
ret = true;
+ } else {
+ ret = false;
+ LOG.error("Failed to create Service Function Type entry for name: {}",
+ serviceFunctionTypeName);
}
+
printTraceStop(LOG);
return ret;
}
return ret;
}
+ /**
+ * This method reads and returns an object with all Service Function Types
+ * present in the Data Store
+ * <p>
+ * @param
+ * @return Nothing.
+ */
protected ServiceFunctionTypes readAllServiceFunctionTypes() {
ServiceFunctionTypes sfts = null;
printTraceStart(LOG);
InstanceIdentifier<ServiceFunctionTypes> sftsIID =
InstanceIdentifier.builder(ServiceFunctionTypes.class).toInstance();
- if (odlSfc.getDataProvider() != null) {
- ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
- Optional<ServiceFunctionTypes> serviceFunctionTypesDataObject;
- try {
- serviceFunctionTypesDataObject = readTx
- .read(LogicalDatastoreType.CONFIGURATION, sftsIID).get();
- if (serviceFunctionTypesDataObject != null
- && serviceFunctionTypesDataObject.isPresent()) {
- sfts = serviceFunctionTypesDataObject.get();
- }
- } catch (InterruptedException | ExecutionException e) {
- LOG.error("Could not read Service Function Types");
+
+ ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
+ Optional<ServiceFunctionTypes> serviceFunctionTypesDataObject;
+ try {
+ serviceFunctionTypesDataObject = readTx
+ .read(LogicalDatastoreType.CONFIGURATION, sftsIID).get();
+ if (serviceFunctionTypesDataObject != null
+ && serviceFunctionTypesDataObject.isPresent()) {
+ sfts = serviceFunctionTypesDataObject.get();
}
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Could not read All Service Function Types");
}
+
printTraceStop(LOG);
return sfts;
}
+ /**
+ * Delete all Service Function Types from data store
+ * <p>
+ * @param
+ * @return Nothing.
+ */
protected boolean deleteAllServiceFunctionTypes() {
boolean ret = false;
printTraceStart(LOG);
- if (odlSfc.getDataProvider() != null) {
-
- InstanceIdentifier<ServiceFunctionTypes> sftsIID =
- InstanceIdentifier.builder(ServiceFunctionTypes.class).toInstance();
- WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
- writeTx.delete(LogicalDatastoreType.CONFIGURATION, sftsIID);
- writeTx.commit();
+ InstanceIdentifier<ServiceFunctionTypes> sftsIID =
+ InstanceIdentifier.builder(ServiceFunctionTypes.class).toInstance();
+ WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+ writeTx.delete(LogicalDatastoreType.CONFIGURATION,
+ sftsIID);
+ SfcDataStoreCallback sfcDataStoreCallback = new SfcDataStoreCallback();
+ CheckedFuture<Void,TransactionCommitFailedException> submitFuture = writeTx.submit();
+ Futures.addCallback(submitFuture, sfcDataStoreCallback);
+ if (sfcDataStoreCallback.getTransactioSuccessful()) {
ret = true;
+ } else {
+ LOG.error("Could not delete all Service Function Types");
}
printTraceStop(LOG);
return ret;
}
- /*
- public static ServiceFunctionType getServiceFunctionTypeList(String typeName) {
-
- LOG.debug("\n########## Start: {}", Thread.currentThread().getStackTrace()[1]);
- InstanceIdentifier<ServiceFunctionType> sftListIID;
- ServiceFunctionTypeKey serviceFunctionTypeKey;
- serviceFunctionTypeKey = new ServiceFunctionTypeKey(typeName);
-
- sftListIID = InstanceIdentifier.builder(ServiceFunctionTypes.class)
- .child(ServiceFunctionType.class, serviceFunctionTypeKey).build();
-
- ReadOnlyTransaction readTx = odlSfc.dataProvider.newReadOnlyTransaction();
- Optional<ServiceFunctionType> serviceFunctionTypeObject = null;
- try {
- serviceFunctionTypeObject = readTx.read(LogicalDatastoreType.CONFIGURATION, sftListIID).get();
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- }
-
- if (serviceFunctionTypeObject != null &&
- (serviceFunctionTypeObject.get() instanceof ServiceFunctionType)) {
- ServiceFunctionType serviceFunctionType = serviceFunctionTypeObject.get();
- printTraceStop(LOG);
- return serviceFunctionType;
- } else {
- printTraceStop(LOG);
- return null;
- }
- }
- */
+ /**
+ * This method creates a Service function Type entry from a Service
+ * Function.
+ * <p>
+ * @param serviceFunction Service Function Object
+ * @return Nothing.
+ */
public void createServiceFunctionTypeEntry(ServiceFunction serviceFunction) {
printTraceStart(LOG);
String sfkey = serviceFunction.getType();
ServiceFunctionTypeKey serviceFunctionTypeKey = new ServiceFunctionTypeKey(sfkey);
+ if (readAllServiceFunctionTypes() == null) {
+ InstanceIdentifier<ServiceFunctionTypes> sftIID;
+ sftIID = InstanceIdentifier.builder(ServiceFunctionTypes.class).build();
+ ServiceFunctionTypesBuilder serviceFunctionTypesBuilder = new ServiceFunctionTypesBuilder();
+
+ WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION,
+ sftIID, serviceFunctionTypesBuilder.build());
+ //writeTx.commit();
+ SfcDataStoreCallback sfcDataStoreCallback = new SfcDataStoreCallback();
+ CheckedFuture<Void,TransactionCommitFailedException> submitFuture = writeTx.submit();
+ Futures.addCallback(submitFuture, sfcDataStoreCallback);
+ if (!sfcDataStoreCallback.getTransactioSuccessful()) {
+ LOG.error("Failed to create top level Service Function Type object");
+
+ }
+
+ }
+
//Build the instance identifier all the way down to the bottom child
SftServiceFunctionNameKey sftServiceFunctionNameKey =
WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
writeTx.merge(LogicalDatastoreType.CONFIGURATION,
sftentryIID, sftServiceFunctionName, true);
- writeTx.commit();
+ //writeTx.commit();
+ SfcDataStoreCallback sfcDataStoreCallback = new SfcDataStoreCallback();
+ CheckedFuture<Void,TransactionCommitFailedException> submitFuture = writeTx.submit();
+ Futures.addCallback(submitFuture, sfcDataStoreCallback);
+ if (!sfcDataStoreCallback.getTransactioSuccessful()) {
+ LOG.error("Failed to create Service Function Type entry for SF: {}",
+ serviceFunction.getName());
+
+ }
printTraceStop(LOG);
}
+ /**
+ * This method is used to delete a Service Function Type from the Data Store
+ * that was instantiated from a Service Function passed as a parameter
+ * <p>
+ * @param serviceFunction Service Function object
+ * @return Service Function Type Object
+ */
public void deleteServiceFunctionTypeEntry(ServiceFunction serviceFunction) {
LOG.debug("\n########## Start: {}", Thread.currentThread().getStackTrace()[1]);
WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
writeTx.delete(LogicalDatastoreType.CONFIGURATION,
sftentryIID);
- writeTx.commit();
+ SfcDataStoreCallback sfcDataStoreCallback = new SfcDataStoreCallback();
+ CheckedFuture<Void,TransactionCommitFailedException> submitFuture = writeTx.submit();
+ Futures.addCallback(submitFuture, sfcDataStoreCallback);
printTraceStop(LOG);
}
--- /dev/null
+*.class
+*.pyc
+**/target
+bin/
+dist
+**/logs
+products
+repository
+workspace
+*~
+1target
+target-ide
+.classpath
+.project
+.settings
+MANIFEST.MF
+opendaylight/northbound/integrationtest/logs/*
+*.ipr
+*.iml
+*.iws
+.idea
+xtend-gen
+yang-gen-config
+yang-gen-sal
+classes
+out/
+.externalToolBuilders
+maven-eclipse.xml
+.DS_STORE
+.metadata
+.hg
+/target
+node_modules/
+coverage/
--- /dev/null
+__author__ = "Paul Quinn, Reinaldo Penno"
+__copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
+__version__ = "0.2"
+__status__ = "alpha"
+
+#
+# Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+
+""" SFF REST Server. This Server should be co-located with a OVS switch """
+
+
+import logging
+import socket
+
+from flask import *
+from random import randint
+import sys
+import getopt
+import json
+import requests
+from odl2ovs_cli import *
+
+app = Flask(__name__)
+my_topo = {}
+
+sff_topo = {}
+path = {}
+
+# ODL IP:port
+ODLIP = "127.0.0.1:8181"
+# Static URLs for testing
+SF_URL = "http://" + ODLIP + "/restconf/config/service-function:service-functions/"
+SFC_URL = "http://" + ODLIP + "/restconf/config/service-function-chain:service-function-chains/"
+SFF_URL = "http://" + ODLIP + "/restconf/config/service-function-forwarder:service-function-forwarders/"
+SFT_URL = "http://" + ODLIP + "/restconf/config/service-function-type:service-function-types/"
+SFP_URL = "http://" + ODLIP + "/restconf/config/service-function-path:service-function-paths/"
+
+SFF_PARAMETER_URL = "http://{}/restconf/config/service-function-forwarder:service-function-forwarders/"
+
+USERNAME = "admin"
+PASSWORD = "admin"
+
+logger = logging.getLogger(__name__)
+
+
+def sffinit():
+ """
+ This function is used when testing without actual OVS switch
+ :return:
+ """
+
+ sff_topo_init = {
+ "service-function-forwarders": {
+ "service-function-forwarder": [
+ {
+ "name": "SFF1",
+ "service-node": "OVSDB1",
+ "sff-data-plane-locator": [
+ {
+ "name": "eth0",
+ "service-function-forwarder-ovs:ovs-bridge": {
+ "bridge-name": "br-tun",
+ "uuid": "4c3778e4-840d-47f4-b45e-0988e514d26c"
+ },
+ "data-plane-locator": {
+ "port": 4789,
+ "ip": "10.100.100.1",
+ "transport": "service-locator:vxlan-gpe"
+ }
+ }
+ ],
+ "rest-uri": "http://198.18.134.23",
+ "service-function-dictionary": [
+ {
+ "name": "SF1",
+ "type": "dp1",
+ "sff-sf-data-plane-locator": {
+ "port": 4789,
+ "ip": "10.1.1.4",
+ "transport": "service-locator:vxlan-gpe",
+ "service-function-forwarder-ovs:ovs-bridge": {
+ "bridge-name": "br-int"
+ }
+ }
+ },
+ {
+ "name": "SF2",
+ "type": "napt44",
+ "sff-sf-data-plane-locator": {
+ "port": 4789,
+ "ip": "10.1.1.5",
+ "transport": "service-locator:vxlan-gpe",
+ "service-function-forwarder-ovs:ovs-bridge": {
+ "bridge-name": "br-int"
+ }
+ }
+ }
+ ],
+ "classifier": "acl-sfp-1",
+ "ip-mgmt-address": "198.18.134.23"
+ },
+ {
+ "name": "SFF2",
+ "service-node": "OVSDB2",
+ "sff-data-plane-locator": [
+ {
+ "name": "eth0",
+ "service-function-forwarder-ovs:ovs-bridge": {
+ "bridge-name": "br-tun",
+ "uuid": "fd4d849f-5140-48cd-bc60-6ad1f5fc0a0"
+ },
+ "data-plane-locator": {
+ "port": 4789,
+ "ip": "10.100.100.2",
+ "transport": "service-locator:vxlan-gpe"
+ }
+ }
+ ],
+ "rest-uri": "http://198.18.134.23",
+ "service-function-dictionary": [
+ {
+ "name": "SF3",
+ "type": "firewall",
+ "sff-sf-data-plane-locator": {
+ "port": 4789,
+ "ip": "10.1.2.6",
+ "transport": "service-locator:vxlan-gpe",
+ "service-function-forwarder-ovs:ovs-bridge": {
+ "bridge-name": "br-int"
+ }
+ }
+ }
+ ],
+ "ip-mgmt-address": "198.18.134.24"
+ }
+ ]
+ }
+ }
+
+ return sff_topo_init
+
+
+def pathinit():
+ """
+ This function is used when testing without actual OVS switch
+ :return:
+ """
+ path_init = {
+ "service-function-paths": {
+ "service-function-path": [
+ {
+ "name": "Path-1-SFC1",
+ "path-id": 1,
+ "starting-index": 3,
+ "service-chain-name": "SFC1",
+ "service-path-hop": [
+ {
+ "hop-number": 0,
+ "service-function-name": "SF1",
+ "service_index": 3,
+ "service-function-forwarder": "SFF1"
+ },
+ {
+ "hop-number": 1,
+ "service-function-name": "SF2",
+ "service_index": 2,
+ "service-function-forwarder": "SFF1"
+ },
+ {
+ "hop-number": 2,
+ "service-function-name": "SF3",
+ "service_index": 1,
+ "service-function-forwarder": "SFF2"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+ return path_init
+
+
+# the following dictionaries are for testing only. Remove when running on OVS.
+def get_bridge_info():
+ b1 = {
+ 'status': '{}',
+ 'fail_mode': '[]',
+ 'datapath_id': '"0000e21a84dd0c4c"',
+ 'datapath_type': '""',
+ 'sflow': '[]',
+ 'mirrors': '[]',
+ 'ipfix': '[]',
+ '_uuid': 'dd841ae1-0a6e-4c0c-b24c-059e7b0b87f8',
+ 'other_config': '{}',
+ 'flood_vlans': '[]',
+ 'stp_enable': 'false',
+ 'controller': '[]',
+ 'mcast_snooping_enable': 'false',
+ 'flow_tables': '{}',
+ 'ports': '[60ce3635-70d2-4c48-98f6-cefd65ab0e58]',
+ 'external_ids': '{bridge-id="SFF1"}',
+ 'netflow': '[]',
+ 'protocols': '[]',
+ 'name': '"br-int"'
+ }
+
+ b2 = {
+ 'status': '{}',
+ 'fail_mode': '[]',
+ 'datapath_id': '"000052f810c06148"',
+ 'datapath_type': '""',
+ 'sflow': '[]',
+ 'mirrors': '[]',
+ 'ipfix': '[]',
+ '_uuid': 'c010f853-5c8a-4861-9e53-050981fbc121',
+ 'other_config': '{}',
+ 'flood_vlans': '[]',
+ 'stp_enable': 'false',
+ 'controller': '[]',
+ 'mcast_snooping_enable': 'false',
+ 'flow_tables': '{}',
+ 'ports': '[4a194fdd-ed59-47cf-998b-7c996c46e3e6]',
+ 'external_ids': '{}',
+ 'netflow': '[]',
+ 'protocols': '[]',
+ 'name': '"br-tun"'
+ }
+
+ # br_list = []
+ br_dict_list = []
+ # bc = 0
+ # br_dict={}
+
+ # bridges = subprocess.check_output(['ovs-vsctl', 'list-br'])
+
+ # for line in bridges.split('\n'):
+ # br_list.append(line)
+
+ # while bc < (len(br_list) - 1):
+ # b = subprocess.check_output(['ovs-vsctl', 'list', 'bridge', br_list[b]])
+ # for row in b.split('\n'):
+ # if ': ' in row:
+ # key, value = row.split(': ')
+ # br_dict[key.strip()] = value.strip()
+ # br_dict_list.append(br_dict)
+ # b = b+1
+
+ # test code
+
+ br_dict_list.append(b1)
+ br_dict_list.append(b2)
+ return br_dict_list
+
+
+# This function does not work if machine has more than one IP/interface
+def get_my_ip():
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(('8.8.8.8', 80))
+ myip = (s.getsockname()[0])
+ s.close()
+ myip = "http://" + myip + ":/paths"
+ return myip
+
+
+def parse_bridges(bridge_list):
+ #num_of_bridges = len(bridge_list)
+ all_bridges = []
+ br_dict = {}
+
+ for bridge in bridge_list:
+ if bridge['name'] == '"br-tun"' or '"br-int"':
+ br_dict = {
+ 'name': bridge['name'][1:-1],
+ 'external_ids': bridge['external_ids'], 'uuid': bridge['_uuid']
+ }
+ all_bridges.append(br_dict)
+ return all_bridges
+
+
+# Not used anymore
+def who_am_i(path, bridges):
+ for path in path['service-function-paths']['service-function-path']:
+ for sff in path['service-path-hop']:
+ for bridge in bridges:
+ if sff['service-function-forwarder'] == bridge['external_ids'][12:-2]:
+ return sff['service-function-forwarder']
+
+
+def who_am_i_sfp(service_path):
+ """
+ Determines the name of the local attached SFF
+ :param service_path: A single Service Function Path
+ :return: The name of the local attached SFF
+ """
+ ovsbridges = get_bridge_info()
+ bridges = parse_bridges(ovsbridges)
+ for sff in service_path['service-path-hop']:
+ for bridge in bridges:
+ if sff['service-function-forwarder'] == bridge['external_ids'][12:-2]:
+ return sff['service-function-forwarder']
+ return None
+
+
+def who_am_i_sff():
+ """
+ Determines the name of the local attached SFF by checking
+ against the collections of all known SFFs
+ :return: The name of the local attached SFF
+ """
+ ovsbridges = get_bridge_info()
+ bridges = parse_bridges(ovsbridges)
+ for bridge in bridges:
+ if bridge['external_ids'][12:-2] in sff_topo.keys():
+ return bridge['external_ids'][12:-2]
+ return None
+
+
+# Not used anymore
+def build_a_path(path, my_sff):
+ # me = 'SFF-bootstrap'
+ sflist = []
+ nextsff = {}
+ sfdict = {}
+ count = 0
+ pid = 0
+
+ for path in path['service-function-paths']['service-function-path']:
+ pid = path['path-id']
+ for sf in path['service-path-hop']:
+ if sf['service-function-forwarder'] == my_sff:
+ sfdict['sff'] = sf['service-function-forwarder']
+ sfdict['pid'] = path['path-id']
+ sfdict['name'] = sf['service-function-name']
+ sfdict['index'] = sf['service_index']
+ find_sf_loc(sfdict)
+ sflist.append(sfdict)
+ sfdict = {}
+ count += 1
+ nextsff['sff-name'] = path['service-path-hop'][count]['service-function-forwarder']
+ nextsff['sff-index'] = path['service-path-hop'][count]['service_index']
+ nextsffloc = find_sff_loc(nextsff)
+ return sflist, nextsffloc, nextsff, my_sff, pid
+
+
+def build_service_path(service_path, my_sff_name):
+ """
+ Builds a dictionary of the local attached Service Functions
+ :param path: A single Service Function Path
+ :param my_sff_name: The name of the local attached SFF
+ :return:
+ """
+ # my_sff = 'SFF-bootstrap'
+ sflist = []
+ nextsff = {}
+ sfdict = {}
+ count = 0
+
+ for service_hop in service_path['service-path-hop']:
+ if service_hop['service-function-forwarder'] == my_sff_name:
+ sfdict['sff'] = service_hop['service-function-forwarder']
+ sfdict['pid'] = service_path['path-id']
+ sfdict['name'] = service_hop['service-function-name']
+ sfdict['index'] = service_hop['service_index']
+ sfdict['locator'] = find_sf_locator(sfdict['name'], sfdict['sff'])
+ if sfdict['locator'] is None:
+ logger.error("Could not find data plane locator for SF: %s", sfdict['name'])
+ sflist.append(sfdict)
+ sfdict = {}
+ count += 1
+ nextsff['sff-name'] = service_path['service-path-hop'][count]['service-function-forwarder']
+ nextsff['sff-index'] = service_path['service-path-hop'][count]['service_index']
+ nextsffloc = find_sff_locator(nextsff['sff-name'])
+ if nextsffloc is None:
+ logger.error("Could not find data plane locator for SFF: %s", nextsff['sff-name'])
+ return sflist, nextsffloc, nextsff
+
+
+def find_sf_locator(sf_name, sff_name):
+ """
+ Looks for the SF name within the service function
+ dictionary of sff_name. If found, return the
+ corresponding data plane locator
+
+ :param sfdict: A dictionary with a single SF attributes
+ :return: SF data plane locator
+ """
+ service_dictionary = sff_topo[sff_name]['service-function-dictionary']
+ for service_function in service_dictionary:
+ if sf_name == service_function['name']:
+ return service_function['sff-sf-data-plane-locator']['ip']
+ return None
+
+
+def find_sff_locator(sff_name):
+ """
+ For a given SFF name, look into local SFF topology for a match
+ and returns the corresponding data plane locator
+ :param sff_name:
+ :return: SFF data plane locator
+ """
+ try:
+ return sff_topo[sff_name]['sff-data-plane-locator'][0]['data-plane-locator']['ip']
+ except KeyError, e:
+ msg = "SFF {} locator not found".format(sff_name)
+ logger.warning(msg)
+ return None
+ except:
+ logger.warning("Unexpected exception, re-raising it")
+ raise
+
+
+# Not used anymore
+def find_sff_loc(sff):
+ for sffi in sff_topo['service-function-forwarders']['service-function-forwarder']:
+ if sffi['name'] == sff['sff-name']:
+ return sffi['sff-data-plane-locator'][0]['data-plane-locator']['ip']
+
+
+# Not used anymore
+def find_sf_loc(sfdict):
+ count = 0
+ while count < len(sff_topo['service-function-forwarders']['service-function-forwarder']):
+ if sff_topo['service-function-forwarders']['service-function-forwarder'] \
+ [count]['name'] == sfdict['sff']:
+ for sfi in (sff_topo['service-function-forwarders']
+ ['service-function-forwarder']
+ [count]['service-function-dictionary']):
+ if sfdict['name'] == sfi['name']:
+ sfdict['locator'] = sfi['sff-sf-data-plane-locator']['ip']
+ count += 1
+ return
+
+
+def mytopo(nextsffloc, vxlanid):
+ global my_topo
+ if nextsffloc in my_topo.keys():
+ return my_topo[nextsffloc]
+ else:
+ vxlan = 'vxlan' + str(vxlanid)
+ my_topo[nextsffloc] = vxlan
+ vxlanid += 1
+ return vxlanid
+
+
+def cli():
+ global path
+ global sff_topo
+ path = pathinit()
+ sff_topo = sffinit()
+ ovsbridges = get_bridge_info()
+ bridge_info = parse_bridges(ovsbridges)
+ my_sff = who_am_i(path, bridge_info)
+ vxlanid = 0
+ key = hex(randint(1, 16777216))
+ build_a_path(path, my_sff)
+ mysflist, nextsffloc, nextsff, my_sff, pid = build_a_path(path, my_sff)
+ vxlanid = mytopo(nextsffloc, vxlanid)
+ vxlanid = cli_local(mysflist, vxlanid, key)
+ cli_nextsff(nextsffloc, nextsff, key, vxlanid, pid)
+ return
+
+
+# Not used anymore
+def ovsbuildit(path):
+ print "BUILDING CHAIN..."
+ ovsbridges = get_bridge_info()
+ bridge_info = parse_bridges(ovsbridges)
+ my_sff = who_am_i(path, bridge_info)
+ # my_topo = {}
+ vxlanid = 0
+ key = hex(randint(1, 16777216))
+
+ mysflist, nextsffloc, nextsff, me, pid = build_a_path(path, my_sff)
+ my_topo, vxlanid = mytopo(nextsffloc, vxlanid)
+ vxlanid = ovs_cli_local(mysflist, vxlanid, key)
+ ovs_cli_nextsff(nextsffloc, nextsff, key, vxlanid, pid)
+ return
+
+
+def ovsbuild_one_path(service_path):
+ """
+ :param path: A single Service Function Path
+ :return: Nothing
+ """
+ logger.info("BUILDING CHAIN...")
+ my_sff_name = who_am_i_sfp(service_path)
+ if my_sff_name is None:
+ logger.info("Service path does not contain local SFF")
+ return
+ # Is this correct?
+ vxlanid = 0
+ key = hex(randint(1, 16777216))
+
+ mysflist, nextsffloc, nextsff = build_service_path(service_path, my_sff_name)
+ vxlanid = mytopo(nextsffloc, vxlanid)
+ vxlanid = ovs_cli_local(mysflist, vxlanid, key)
+ pid = mysflist[0]['pid']
+ ovs_cli_nextsff(nextsffloc, nextsff, key, vxlanid, pid)
+ return
+
+
[email protected]('/config/service-function-path:service-function-paths/', methods=['GET'])
+def get_paths():
+ return jsonify({'Service paths': path})
+
+
[email protected]('/config/service-function-forwarder:service-function-forwarders/', methods=['GET'])
+def get_sffs():
+ return jsonify({'SFFs': sff_topo})
+
+
[email protected]('/config/service-function-path:service-function-paths/', methods=['PUT'])
+def create_paths():
+ global path
+ if not request.json:
+ abort(400)
+ else:
+ path = {
+ 'service-function-paths': request.json['service-function-paths']
+ }
+ if any(sff_topo):
+ ovsbuildit(path)
+ return jsonify({'path': path}), 201
+
+
[email protected]('/config/service-function-path:service-function-paths/service-function-path/<sfpname>', methods=['PUT'])
+def create_path(sfpname):
+ global path
+ if not request.json:
+ abort(400)
+ else:
+ # print json.dumps(sfpjson)
+ # sfpj_name = sfpjson["service-function-path"][0]['name']
+ path[sfpname] = request.get_json()["service-function-path"][0]
+
+ if any(sff_topo):
+ ovsbuild_one_path(path[sfpname])
+ return jsonify({'path': path}), 201
+
+
[email protected]('/config/service-function-path:service-function-paths/service-function-path/<sfpname>', methods=['DELETE'])
+def delete_path(sfpname):
+ global path
+ try:
+ del path[sfpname]
+ except KeyError, e:
+ msg = "SFP name {} not found, message".format(sfpname)
+ logger.warning(msg)
+ return msg, 404
+ except:
+ logger.warning("Unexpected exception, re-raising it")
+ raise
+ return '', 204
+
+
[email protected]('/config/service-function-forwarder:service-function-forwarders/service-function-forwarder/<sffname>',
+ methods=['PUT'])
+def create_sff(sffname):
+ global sff_topo
+ if not request.json:
+ abort(400)
+ else:
+ sff_topo[sffname] = request.get_json()['service-function-forwarder'][0]
+ if any(path):
+ ovsbuild_one_path(path)
+ return jsonify({'sff': sff_topo}), 201
+
+
[email protected]('/config/service-function-forwarder:service-function-forwarders/service-function-forwarder/<sffname>',
+ methods=['DELETE'])
+def delete_sff(sffname):
+ global sff_topo
+ try:
+ del sff_topo[sffname]
+ except KeyError, e:
+ msg = "SFF name {} not found, message".format(sffname)
+ logger.warning(msg)
+ return msg, 404
+ except:
+ logger.warning("Unexpected exception, re-raising it")
+ raise
+ return '', 204
+
+
[email protected]('/config/service-function-forwarder:service-function-forwarders/', methods=['PUT'])
+def create_sffs():
+ global sff_topo
+ if not request.json:
+ abort(400)
+ else:
+ me = ""
+ sff_topo = {
+ 'service-function-forwarders': request.json['service-function-forwarders']
+ }
+ if any(path):
+ ovsbuildit(path)
+ return jsonify({'sff': sff_topo}), 201
+
+
[email protected]('/config/service-function-forwarder:service-function-forwarders/', methods=['DELETE'])
+def delete_sffs():
+ global sff_topo
+ sff_topo = {}
+ return jsonify({'sff': sff_topo}), 201
+
+
+def page_not_found(e):
+ return render_template('404.html'), 404
+
+
+def get_sff_from_odl(odl_ip_port):
+ """
+ Retrieves the list fo configured SFFs from ODL
+ :return: Nothing
+ """
+ s = requests.Session()
+ print ("Getting SFF information from ODL... \n")
+ r = s.get(SFF_PARAMETER_URL.format(odl_ip_port), stream=False, auth=(USERNAME, PASSWORD))
+ if r.status_code == 200:
+ sff_json = json.loads(r.text)['service-function-forwarders']['service-function-forwarder']
+ for sff in sff_json:
+ sff_topo[sff['name']] = sff
+ else:
+ print ("=>Failed to GET SFF from ODL \n")
+
+
+def main(argv):
+ global ODLIP
+ try:
+ logging.basicConfig(level=logging.INFO)
+ opt, args = getopt.getopt(argv, "hrc", ["help", "rest", "cli", "odl-get-sff", "odl-ip-port="])
+ except getopt.GetoptError:
+ print 'rest2ovs --help | --rest | --cli | --odl-get-sff | --odl-ip-port'
+ sys.exit(2)
+
+ odl_get_sff = False
+ rest = False
+ for opt, arg in opt:
+ if opt == "--odl-get-sff":
+ odl_get_sff = True
+ continue
+
+ if opt == "--odl-ip-port":
+ ODLIP = arg
+ continue
+
+ if opt in ('-h', '--help'):
+ print 'rest2ovs -m cli | rest --odl-get-sff --odl-ip-port'
+ sys.exit()
+
+ if opt in ('-c', '--cli'):
+ cli()
+ sys.exit()
+
+ if opt in ('-r', '--rest'):
+ rest = True
+
+ if odl_get_sff:
+ get_sff_from_odl(ODLIP)
+
+ if rest:
+ app.debug = True
+ app.run(host='0.0.0.0')
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
--- /dev/null
+__author__ = "Paul Quinn, Reinaldo Penno"
+__copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
+__version__ = "0.2"
+__status__ = "alpha"
+
+
+#
+# Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+
+import subprocess
+
+def cli_local(sfdpinfo, vxlan, key):
+ sfi = 0
+ if len(sfdpinfo) > 1:
+ for sf in sfdpinfo:
+ part1 = 'ovs-vsctl add-port br-int ' + 'vxlan' + str(vxlan) + ' -- set interface vxlan' + str(vxlan) + ' options:dst_port=6633 type=vxlan options:remote_ip=' + sfdpinfo[sfi]['locator']
+ part2 = ' options:key=' + str(key) + ' options:nsp=' + str(sfdpinfo[sfi]['pid']) + ' options:nsi=' + str(sfdpinfo[sfi]['index'])
+ cli = part1 + part2
+ print cli
+ #subprocess.call([cli], shell=True)
+ vxlan += 1
+ sfi += 1
+ else:
+ print "No locally attached services on SFF"
+ return vxlan
+
+
+def ovs_cli_local(sfdpinfo, vxlan, key):
+ sfi = 0
+ if len(sfdpinfo) > 1:
+ for sf in sfdpinfo:
+ part1 = 'ovs-vsctl add-port br-int ' + 'vxlan' + str(vxlan) + '-- set interface vxlan' + str(vxlan) + ' options:dst_port=6633 type=vxlan options:remote_ip=' + sfdpinfo[sfi]['locator']
+ part2 = ' options:key=' + str(key) + ' options:nsp=' + str(sfdpinfo[sfi]['pid']) + ' options:nsi=' + str(sfdpinfo[sfi]['index'])
+ cli = part1 + part2
+ subprocess.call([cli], shell=True)
+ vxlan += 1
+ sfi += 1
+ print cli
+ else:
+ print "No locally attached services on SFF"
+ return vxlan
+
+
+def cli_nextsff(nextsffloc, nextsff, key, vxlan, pid):
+ part1 = 'ovs-vsctl add-port br-tun ' + 'vxlan' + str(vxlan) + ' -- set interface vxlan' + str(vxlan) + ' options:dst_port=6633 type=vxlan options:remote_ip=' + nextsffloc
+ part2 = ' options:key=' + str(key) + ' options:nsp=' + str(pid) + ' options:nsi=' + str(nextsff['sff-index'])
+ cli = part1 + part2
+ print cli
+ #subprocess.call([cli], shell=True)
+ vxlan += 1
+ return
+
+
+def ovs_cli_nextsff(nextsffloc, nextsff, key, vxlan, path):
+ part1 = 'ovs-vsctl add-port br-tun ' + 'vxlan' + str(vxlan) + ' -- set interface vxlan' + str(vxlan) + ' options:dst_port=6633 type=vxlan options:remote_ip=' + nextsffloc
+ part2 = ' options:key=' + str(key) + ' options:nsp=' + str(path) + ' options:nsi=' + str(nextsff['sff-index'])
+ cli = part1 + part2
+
+ print cli
+ subprocess.call([cli], shell=True)
+
+ vxlan += 1
+ return
--- /dev/null
+__author__ = "Jim Guichard"
+__copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
+__version__ = "0.1"
+__status__ = "alpha"
+
+#
+# Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+
+"""Network Service Header (NSH) Enabled Service Function"""
+
+import argparse
+import asyncio
+import sys, struct, socket
+import binascii
+from ctypes import *
+
+try:
+ import signal
+except ImportError:
+ signal = None
+
+class BASEHEADER(Structure):
+ _fields_ = [("version", c_ushort, 2),
+ ("flags", c_ushort, 8),
+ ("length", c_ushort, 6),
+ ("next_protocol", c_uint, 16),
+ ("service_path", c_uint, 24),
+ ("service_index", c_uint, 8)]
+
+class CONTEXTHEADER(Structure):
+ _fields_ = [("network_platform", c_uint),
+ ("network_shared", c_uint),
+ ("service_platform",c_uint),
+ ("service_shared", c_uint)]
+
+# Decode base NSH header and context headers
+
+base_values = BASEHEADER()
+ctx_values = CONTEXTHEADER()
+
+class MyFwService:
+
+ def connection_made(self, transport):
+ self.transport = transport
+
+ def datagram_received(self, data, addr):
+ print('\nfw service received packet from SFF:\n', addr, binascii.hexlify(data))
+ rw_data = process_incoming_packet(data)
+ self.transport.sendto(rw_data, addr)
+ loop.stop()
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('closing transport', exc)
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+class MyNatService:
+
+ def connection_made(self, transport):
+ self.transport = transport
+
+ def datagram_received(self, data, addr):
+ print('\nnat service received packet from SFF:\n', addr, binascii.hexlify(data))
+ print('\n')
+ rw_data = process_incoming_packet(data)
+ self.transport.sendto(rw_data, addr)
+ loop.stop()
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('closing transport', exc)
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+class MyDpiService:
+
+ def connection_made(self, transport):
+ self.transport = transport
+
+ def datagram_received(self, data, addr):
+ print('\ndpi service received packet from SFF:\n', addr, binascii.hexlify(data))
+ print('\n')
+ rw_data = process_incoming_packet(data)
+ self.transport.sendto(rw_data, addr)
+ loop.stop()
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('closing transport', exc)
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+def process_incoming_packet(data):
+ print('Processing recieved packet')
+ rw_data = bytearray(data)
+ decode_baseheader(data)
+ decode_contextheader(data)
+ base_values.service_index -= 1
+ set_service_index(rw_data, base_values.service_index)
+ return(rw_data)
+
+def decode_baseheader(payload):
+ # Base Service header
+ #base_header = payload[8:17] #starts at offset 8 of payload
+ base_header = payload[7:16]
+
+ start_idx, base_values.md_type, base_values.next_protocol, path_idx = struct.unpack('!H B H I', base_header)
+
+ base_values.version = start_idx >> 14
+ base_values.flags = start_idx >> 6
+ base_values.length = start_idx >> 0
+ base_values.service_path = path_idx >> 8;
+ base_values.service_index = path_idx & 0x000000FF
+
+ if (__debug__ == False):
+ print ("\nBase NSH Header Decode:")
+ print (binascii.hexlify(base_header))
+ #print ('NSH Version:', base_values.version)
+ #print ('NSH base header flags:', base_values.flags)
+ #print ('NSH base header length:', base_values.length)
+ #print ('NSH MD-type:', base_values.md_type)
+ #print ('NSH base header next protocol:', base_values.next_protocol)
+ print ('Service Path Identifier:', base_values.service_path)
+ print ('Service Index:', base_values.service_index)
+
+# Decode the NSH context headers for a received packet at this SFF.
+
+def decode_contextheader(payload):
+ # Context header
+ context_header = payload[16:32]
+
+ ctx_values.network_platform, ctx_values.network_shared, ctx_values.service_platform, \
+ ctx_values.service_shared = struct.unpack('!I I I I', context_header)
+
+ if (__debug__ == False):
+ print ("\nNSH Context Header Decode:")
+ print (binascii.hexlify(context_header))
+ print ('Network Platform Context:', ctx_values.network_platform)
+ print ('Network Shared Context:', ctx_values.network_shared)
+ print ('Service Platform Context:', ctx_values.service_platform)
+ print ('Service Shared Context:', ctx_values.service_shared)
+
+def set_service_index(rw_data, service_index):
+ rw_data[16] = service_index
+
+def start_server(loop, addr, service, myip):
+ t = asyncio.Task(loop.create_datagram_endpoint(
+ service, local_addr=(myip, 6633)))
+ loop.run_until_complete(t)
+ print('Connection made with SFF:', addr)
+ print('Listening for packets on port:', myip)
+
+def find_service(service):
+ if service == 'fw':
+ return(MyFwService)
+ elif service == 'nat':
+ return(MyNatService)
+ elif service == 'dpi':
+ return(MyDpiService)
+
+def get_service_ip():
+ # Let's find a local IP address to use as the source IP of client generated packets
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s.connect(('8.8.8.8', 80))
+ client = (s.getsockname()[0])
+ except socket.error:
+ client = "Unknown IP"
+ finally:
+ s.close()
+ return client
+
+ARGS = argparse.ArgumentParser(description="NSH Service Function")
+ARGS.add_argument(
+ '--type', action="store", dest='type',
+ default=False, help='Run service function. Options: fw, nat, dpi')
+ARGS.add_argument(
+ '--host', action="store", dest='host',
+ default='127.0.0.1', help='SFF host name')
+ARGS.add_argument(
+ '--port', action="store", dest='port',
+ default=4789, type=int, help='SFF port number')
+
+if __name__ == '__main__':
+ args = ARGS.parse_args()
+ if ':' in args.host:
+ args.host, port = args.host.split(':', 1)
+ args.port = int(port)
+
+ if (not (args.type)):
+ print('Please specify --type\n')
+ ARGS.print_help()
+ else:
+ loop = asyncio.get_event_loop()
+ if signal is not None:
+ loop.add_signal_handler(signal.SIGINT, loop.stop)
+
+ if '--type' in sys.argv:
+ #local_ip = get_service_ip()
+ local_ip = "10.1.1.4"
+ service = find_service(args.type)
+ print('Starting', args.type, 'service...')
+ start_server(loop, (args.host, args.port), service, local_ip)
+ else:
+ print('something went wrong')
+
+ loop.run_forever()
--- /dev/null
+__author__ = "Jim Guichard"
+__copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
+__version__ = "0.1"
+__status__ = "alpha"
+
+#
+# Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+
+"""Network Service Header (NSH) Enabled Service Function"""
+
+import argparse
+import asyncio
+import sys
+import struct
+import socket
+import binascii
+from ctypes import *
+
+try:
+ import signal
+except ImportError:
+ signal = None
+
+
+class BASEHEADER(Structure):
+ _fields_ = [("version", c_ushort, 2),
+ ("flags", c_ushort, 8),
+ ("length", c_ushort, 6),
+ ("md_type", c_ubyte),
+ ("next_protocol", c_ubyte),
+ ("service_path", c_uint, 24),
+ ("service_index", c_uint, 8)]
+
+# Decode base NSH header
+
+base_values = BASEHEADER()
+
+
+class MyFwService:
+ def connection_made(self, transport):
+ self.transport = transport
+
+ def datagram_received(self, data, addr):
+ print('\nfw service received packet from SFF:\n', addr, binascii.hexlify(data))
+ rw_data = process_incoming_packet(data)
+ self.transport.sendto(rw_data, addr)
+ loop.stop()
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('closing transport', exc)
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+
+class MyNatService:
+ def connection_made(self, transport):
+ self.transport = transport
+
+ def datagram_received(self, data, addr):
+ print('\nnat service received packet from SFF:\n', addr, binascii.hexlify(data))
+ print('\n')
+ rw_data = process_incoming_packet(data)
+ self.transport.sendto(rw_data, addr)
+ loop.stop()
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('closing transport', exc)
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+
+class MyDpiService:
+ def connection_made(self, transport):
+ self.transport = transport
+
+ def datagram_received(self, data, addr):
+ print('\ndpi service received packet from SFF:\n', addr, binascii.hexlify(data))
+ print('\n')
+ rw_data = process_incoming_packet(data)
+ self.transport.sendto(rw_data, addr)
+ loop.stop()
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('closing transport', exc)
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+
+def process_incoming_packet(data):
+ print('Processing recieved packet')
+ rw_data = bytearray(data)
+ decode_baseheader(data)
+ base_values.service_index -= 1
+ set_service_index(rw_data, base_values.service_index)
+ return rw_data
+
+
+def decode_baseheader(payload):
+ # Base Service header
+ base_header = payload[8:17] # starts at offset 8 of payload
+
+ start_idx, base_values.md_type, base_values.next_protocol, path_idx = struct.unpack('!H B H I', base_header)
+
+ base_values.version = start_idx >> 14
+ base_values.flags = start_idx >> 6
+ base_values.length = start_idx >> 0
+ base_values.service_path = path_idx >> 8
+ base_values.service_index = path_idx & 0x000000FF
+
+ if __debug__ is False:
+ print ("\nBase NSH Header Decode:")
+ print (binascii.hexlify(base_header))
+ print ('NSH Version:', base_values.version)
+ print ('NSH base header flags:', base_values.flags)
+ print ('NSH base header length:', base_values.length)
+ print ('NSH MD-type:', base_values.md_type)
+ print ('NSH base header next protocol:', base_values.next_protocol)
+ print ('Service Path Identifier:', base_values.service_path)
+ print ('Service Index:', base_values.service_index)
+
+
+def set_service_index(rw_data, service_index):
+ rw_data[16] = service_index
+
+
+def start_server(loop, addr, service, myip):
+ t = asyncio.Task(loop.create_datagram_endpoint(
+ service, local_addr=(myip, 57444), remote_addr=addr))
+ loop.run_until_complete(t)
+ print('Connection made with SFF:', addr)
+
+
+def find_service(service):
+ if service == 'fw':
+ return MyFwService
+ elif service == 'nat':
+ return MyNatService
+ elif service == 'dpi':
+ return MyDpiService
+
+
+def get_service_ip():
+ # Let's find a local IP address to use as the source IP of client generated packets
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s.connect(('8.8.8.8', 80))
+ client = (s.getsockname()[0])
+ except socket.error:
+ client = "Unknown IP"
+ finally:
+ s.close()
+ return client
+
+
+ARGS = argparse.ArgumentParser(description="NSH Service Function")
+ARGS.add_argument(
+ '--type', action="store", dest='type',
+ default=False, help='Run service function. Options: fw, nat, dpi')
+ARGS.add_argument(
+ '--host', action="store", dest='host',
+ default='127.0.0.1', help='SFF host name')
+ARGS.add_argument(
+ '--port', action="store", dest='port',
+ default=4789, type=int, help='SFF port number')
+
+if __name__ == '__main__':
+ args = ARGS.parse_args()
+ if ':' in args.host:
+ args.host, port = args.host.split(':', 1)
+ args.port = int(port)
+
+ if not args.type:
+ print('Please specify --type\n')
+ ARGS.print_help()
+ else:
+ loop = asyncio.get_event_loop()
+ if signal is not None:
+ loop.add_signal_handler(signal.SIGINT, loop.stop)
+
+ if '--type' in sys.argv:
+ # local_ip = get_service_ip()
+ local_ip = '127.0.0.1'
+ service = find_service(args.type)
+ print('Starting', args.type, 'service...')
+ start_server(loop, (args.host, args.port), service, local_ip)
+ else:
+ print('something went wrong')
+
+ loop.run_forever()
\ No newline at end of file
--- /dev/null
+__author__ = "Reinaldo Penno, Jim Guichard, Paul Quinn"
+__copyright__ = "Copyright(c) 2014, Cisco Systems, Inc."
+__version__ = "0.2"
+__status__ = "alpha"
+
+#
+# Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+
+"""Service Function Forwarder (SFF) server & client"""
+
+import argparse
+import asyncio
+import sys
+import struct
+import socket
+import binascii
+from ctypes import *
+
+try:
+ import signal
+except ImportError:
+ signal = None
+
+# Service Function Forwarder (SFF) table structure. Table referenced by Service Path Identifier as key. This table
+# contains all the service chains known to this SFF and lists an ordered set of service function types.
+
+sf_config_map = {'1': ['fw1', 'dpi1', 'nat1'], '2': ['fw2', 'dpi2', 'nat2'], '3': ['fw1', 'dpi1']}
+
+# Service Function Instance Registry.
+# This serves as a Service Function Registry referenced by service function type. A Service Function registers a
+# callback or an IP address/port combination.
+
+sf_map = {"fw1": {"function": "fw1_process_packet", "ip_address": "127.0.0.1", "port": "57444"},
+ "fw2": {"function": "", "ip_address": "", "port": ""},
+ "dpi1": {"function": "dpi1_process_packet", "ip_address": "", "port": "10000"},
+ "dpi2": {"function": "", "ip_address": "", "port": ""},
+ "nat1": {"function": "nat1_process_packet", "ip_address": "", "port": "10001"},
+ "nat2": {"function": "", "ip_address": "", "port": ""}}
+
+# VXLAN, NSH Base Header, and NSH Context Header data structures.
+
+class VXLANGPE(Structure):
+ _fields_ = [("flags", c_ubyte),
+ ("reserved", c_ubyte),
+ ("protocol_type", c_ushort),
+ ("vni", c_uint, 24),
+ ("reserved2", c_uint, 8)]
+
+
+class BASEHEADER(Structure):
+ _fields_ = [("version", c_ushort, 2),
+ ("flags", c_ushort, 8),
+ ("length", c_ushort, 6),
+ ("md_type", c_ubyte),
+ ("next_protocol", c_ubyte),
+ ("service_path", c_uint, 24),
+ ("service_index", c_uint, 8)]
+
+
+class CONTEXTHEADER(Structure):
+ _fields_ = [("network_platform", c_uint),
+ ("network_shared", c_uint),
+ ("service_platform", c_uint),
+ ("service_shared", c_uint)]
+
+# Global flags used for indication of current packet processing status.
+
+PACKET_CHAIN = 0b00000000 # Packet needs more processing within this SFF
+PACKET_CONSUMED = 0b00000001 # Packet was sent to another SFF or service function
+PACKET_ERROR = 0b00000010 # Packet will be dropped
+SERVICEFUNCTION_INVALID = 0xDEADBEEF # Referenced service function is invalid
+
+# Client side code: Choose values for VXLAN, base NSH and context headers as part of packet generation
+
+vxlan_values = VXLANGPE(int('00000100', 2), 0, 0x894F, int('111111111111111111111111', 2), 64)
+ctx_values = CONTEXTHEADER(0xffffffff, 0, 0xffffffff, 0)
+base_values = BASEHEADER(0x1, int('01000000', 2), 0x6, 0x1, 0x1, 0x000001, 0x3)
+
+# Service side code: Store received values for VXLAN, base NSH and context headers data structures
+
+server_vxlan_values = VXLANGPE()
+server_ctx_values = CONTEXTHEADER()
+server_base_values = BASEHEADER()
+
+# Local service function callbacks. These are dummy services to track progress of packets through the service chain
+# at this SFF.
+
+
+def fw1_process_packet(data, addr):
+ print('fw1 processed packet from:', addr)
+ return PACKET_CHAIN
+
+
+def dpi1_process_packet(data, addr):
+ print('dpi1 processed packet from:', addr)
+ return PACKET_CHAIN
+
+
+def nat1_process_packet(data, addr):
+ print('nat1 processed packet from:', addr)
+ return PACKET_CHAIN
+
+
+# Client side code: Build NSH packet encapsulated in VXLAN & NSH.
+
+def build_packet():
+ # Build VXLAN header
+ vxlan_header = struct.pack('!B B H I', vxlan_values.flags, vxlan_values.reserved, vxlan_values.protocol_type,
+ (vxlan_values.vni << 8) + vxlan_values.reserved2)
+ # Build base NSH header
+ base_header = struct.pack('!H B H I', (base_values.version << 14) + (base_values.flags << 6) + base_values.length,
+ base_values.md_type,
+ base_values.next_protocol, (base_values.service_path << 8) + base_values.service_index)
+ #Build NSH context headers
+ context_header = struct.pack('!I I I I', ctx_values.network_platform, ctx_values.network_shared,
+ ctx_values.service_platform, ctx_values.service_shared)
+ return vxlan_header + base_header + context_header
+
+
+# Decode the VXLAN header for a received packet at this SFF.
+
+def decode_vxlan(payload):
+ # VXLAN header
+ vxlan_header = payload[0:8]
+ server_vxlan_values.flags, server_vxlan_values.reserved, server_vxlan_values.protocol_type, \
+ vni_rsvd2 = struct.unpack('!B B H I', vxlan_header)
+
+ server_vxlan_values.vni = vni_rsvd2 >> 8;
+ server_vxlan_values.reserved2 = vni_rsvd2 & 0x000000FF
+
+ # Yes, it is weird but the comparison is against False. Display debug if started with -O option.
+ if __debug__ is False:
+ print("\nVXLAN Header Decode:")
+ print(binascii.hexlify(vxlan_header))
+ print('Flags:', server_vxlan_values.flags)
+ print('Reserved:', server_vxlan_values.reserved)
+ print('Protocol Type:', hex(int(server_vxlan_values.protocol_type)))
+ print('VNI:', server_vxlan_values.vni)
+ print('Reserved:', server_vxlan_values.reserved2)
+
+
+# Decode the NSH base header for a received packet at this SFF.
+
+def decode_baseheader(payload):
+ # Base Service header
+ base_header = payload[8:17] # starts at offset 8 of payload
+
+ start_idx, server_base_values.md_type, server_base_values.next_protocol, path_idx = struct.unpack('!H B H I',
+ base_header)
+
+ server_base_values.version = start_idx >> 14
+ server_base_values.flags = start_idx >> 6
+ server_base_values.length = start_idx >> 0
+ server_base_values.service_path = path_idx >> 8;
+ server_base_values.service_index = path_idx & 0x000000FF
+
+ if __debug__ is False:
+ print ("\nBase NSH Header Decode:")
+ print (binascii.hexlify(base_header))
+ print ('NSH Version:', server_base_values.version)
+ print ('NSH base header flags:', server_base_values.flags)
+ print ('NSH base header length:', server_base_values.length)
+ print ('NSH MD-type:', server_base_values.md_type)
+ print ('NSH base header next protocol:', server_base_values.next_protocol)
+ print ('Service Path Identifier:', server_base_values.service_path)
+ print ('Service Index:', server_base_values.service_index)
+
+
+# Decode the NSH context headers for a received packet at this SFF.
+
+def decode_contextheader(payload):
+ # Context header
+ context_header = payload[17:33]
+
+ server_ctx_values.network_platform, server_ctx_values.network_shared, server_ctx_values.service_platform, server_ctx_values.service_shared = struct.unpack(
+ '!I I I I', context_header)
+
+ if __debug__ is False:
+ print ("\nNSH Context Header Decode:")
+ print (binascii.hexlify(context_header))
+ print ('First context header:', server_ctx_values.network_platform)
+ print ('Second context header:', server_ctx_values.network_shared)
+ print ('Third context header:', server_ctx_values.service_platform)
+ print ('Fourth context header:', server_ctx_values.service_shared)
+
+
+def lookup_next_sf(service_path, service_index):
+ next_sfi = SERVICEFUNCTION_INVALID
+ # First we determine the list of SFs in the received packet based on SPI value extracted from packet
+ try:
+ sf_list = sf_config_map[str(service_path)]
+ except KeyError as detail:
+ print('Apparently no valid SPI entry', detail)
+ # return SERVICEFUNCTION_INVALID to indicate no valid SPI entry
+ return next_sfi
+
+ if __debug__ is False:
+ print("\nSFI list for received SFP: ", sf_list)
+
+ # Now we determine the next SFI name
+ try:
+ next_sfi = sf_list[-service_index]
+ if __debug__ is False:
+ print("Next SF: ", next_sfi)
+ except IndexError as detail:
+ print('guess something went wrong - Error:', detail)
+ except:
+ print("Unexpected error:", sys.exc_info()[0])
+ finally:
+ return next_sfi
+
+
+def send_next_service(next_sfi, rw_data, addr):
+ # First we need to find if this SFI is internal to this Service Node
+ if next_sfi in sf_map:
+
+ if sf_map[next_sfi]['function'] != '' and sf_map[next_sfi]['ip_address'] == '':
+ functionp = globals()[sf_map[next_sfi]['function']]
+ address = sf_map[next_sfi]['ip_address']
+ ret = functionp(rw_data, addr)
+ if ret != PACKET_CONSUMED:
+ print('decrementing service index by 1 as packet processed by:', next_sfi)
+ server_base_values.service_index -= 1
+ set_service_index(rw_data, server_base_values.service_index)
+ print('current service index value:', server_base_values.service_index)
+ return ret, address
+ elif sf_map[next_sfi]['ip_address'] != '':
+ address = sf_map[next_sfi]['ip_address'], int(sf_map[next_sfi]['port'])
+ packet_status = PACKET_CONSUMED
+ print('were done - service', next_sfi, 'at address', address, 'consumed the packet')
+ return packet_status, address
+
+
+def set_service_index(rw_data, service_index):
+ rw_data[16] = service_index
+
+
+def process_incoming_packet(data, addr):
+ print("Processing packet from:", addr)
+ # Copy payload into bytearray so it can be changed
+ rw_data = bytearray(data)
+ # Decode the incoming packet for debug purposes and to strip out various header values
+ decode_vxlan(data)
+ decode_baseheader(data)
+ decode_contextheader(data)
+ # Lookup what to do with the packet based on Service Path Identifier (SPI)
+ print("\nLooking up received Service Path Identifier...")
+ packet_status = PACKET_CHAIN
+ address = ()
+ while packet_status == PACKET_CHAIN and server_base_values.service_index != 0:
+ next_sfi = lookup_next_sf(server_base_values.service_path, server_base_values.service_index)
+ if next_sfi == SERVICEFUNCTION_INVALID:
+ # bye, bye packet
+ print('we reached end of chain')
+ print('ended up with:', binascii.hexlify(rw_data))
+ print('service index end up as:', server_base_values.service_index)
+ rw_data.__init__()
+ data = ""
+ addr = ""
+ break
+ packet_status, address = send_next_service(next_sfi, rw_data, addr)
+
+ print('\nfinished processing packet from:', addr)
+ print('\nlistening for NSH packets ...')
+ return rw_data, address
+
+
+class MyUdpServer:
+ def connection_made(self, transport):
+ self.transport = transport
+
+ def datagram_received(self, data, addr):
+ print('Received packet from:', addr)
+ # Process the incoming packet
+ rw_data, address = process_incoming_packet(data, addr)
+ if address != '':
+ # send the packet to the next SFF based on address
+ self.transport.sendto(rw_data, address)
+ else:
+ # if want to echo packet back to client use following uncommented
+ self.transport.sendto(rw_data, addr)
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('stop', exc)
+
+ def __init__(self, loop):
+ self.transport = None
+ self.loop = loop
+
+
+class MyUdpClient:
+ def connection_made(self, transport):
+ self.transport = transport
+ # Building client packet to send to SFF
+ packet = build_packet()
+ print('\nsending packet to SFF:\n', binascii.hexlify(packet))
+ # Send the packet
+ self.transport.sendto(packet)
+
+ def datagram_received(self, data, addr):
+ print('\nreceived packet from SFF:\n', binascii.hexlify(data))
+ print('\n')
+ # Decode all the headers
+ decode_vxlan(data)
+ decode_baseheader(data)
+ decode_contextheader(data)
+ self.loop.stop()
+
+ def connection_refused(self, exc):
+ print('Connection refused:', exc)
+
+ def connection_lost(self, exc):
+ print('closing transport', exc)
+ self.loop = asyncio.get_event_loop()
+ self.loop.stop()
+
+ def __init__(self, loop):
+ self.transport = None
+ self.loop = loop
+
+
+def start_server(loop, addr, udpserver):
+ t = asyncio.Task(loop.create_datagram_endpoint(
+ lambda: udpserver, local_addr=addr))
+ loop.run_until_complete(t)
+ print('\nStarting Service Function Forwarder (SFF)')
+ print('listening for NSH packets on port: ', addr[1])
+
+
+# note using port 57444 but could be any port, just remove port syntax and
+# update get_client_ip() to remove [0] from getsockname()
+def start_client(loop, addr, myip, udpclient):
+ t = asyncio.Task(loop.create_datagram_endpoint(
+ lambda: udpclient, local_addr=(myip, 57444), remote_addr=addr))
+ loop.run_until_complete(t)
+
+
+def get_client_ip():
+ # Let's find a local IP address to use as the source IP of client generated packets
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s.connect(('8.8.8.8', 80))
+ client = (s.getsockname()[0])
+ except socket.error:
+ client = "Unknown IP"
+ finally:
+ s.close()
+ return client
+
+
+ARGS = argparse.ArgumentParser(description="UDP Echo example.")
+ARGS.add_argument(
+ '--server', action="store_true", dest='server',
+ default=False, help='Run udp server')
+ARGS.add_argument(
+ '--client', action="store_true", dest='client',
+ default=False, help='Run udp client')
+ARGS.add_argument(
+ '--host', action="store", dest='host',
+ default='127.0.0.1', help='Host name')
+ARGS.add_argument(
+ '--port', action="store", dest='port',
+ default=4789, type=int, help='Port number')
+
+
+def main():
+ args = ARGS.parse_args()
+ if ':' in args.host:
+ args.host, port = args.host.split(':', 1)
+ args.port = int(port)
+
+ if (not (args.server or args.client)) or (args.server and args.client):
+ print('Please specify --server or --client\n')
+ ARGS.print_help()
+ else:
+ loop = asyncio.get_event_loop()
+ # if signal is not None:
+ # loop.add_signal_handler(signal.SIGINT, loop.stop)
+
+ if '--server' in sys.argv:
+ udpserver = MyUdpServer(loop)
+ start_server(loop, (args.host, args.port), udpserver)
+ else:
+ # Figure out a source IP address / source UDP port for the client connection
+ udpclient = MyUdpClient(loop)
+ local_ip = get_client_ip()
+ start_client(loop, (args.host, args.port), local_ip, udpclient)
+
+ loop.run_forever()
+
+
+if __name__ == '__main__':
+ main()
}
"""
+
+
+
SERVICE_FUNCTION_FORWARDERS_JSON = """
{
"service-function-forwarders": {
print ("=>Deleted all Service Function Forwarders \n")
else:
print ("=>Failure to delete SFFs, response code = {} \n". format(r.status_code))
- r = s.delete(SFT_URL, stream=False, auth=(USERNAME, PASSWORD))
- if r.status_code == 200:
- print ("=>Deleted all Service Function Types \n")
- else:
- print ("=>Failure to delete SFTs, response code = {} \n". format(r.status_code))
+ # r = s.delete(SFT_URL, stream=False, auth=(USERNAME, PASSWORD))
+ # if r.status_code == 200:
+ # print ("=>Deleted all Service Function Types \n")
+ # else:
+ # print ("=>Failure to delete SFTs, response code = {} \n". format(r.status_code))
r = s.delete(SFP_URL, stream=False, auth=(USERNAME, PASSWORD))
if r.status_code == 200:
print ("=>Deleted all Service Function Paths \n")
if __name__ == "__main__":
delete_configuration()
put_and_check(SF_URL, SERVICE_FUNCTIONS_JSON, SERVICE_FUNCTIONS_JSON)
- put_and_check(SFF_URL, SERVICE_FUNCTION_FORWARDERS_JSON, SERVICE_FUNCTION_FORWARDERS_JSON)
- put_and_check(SFC_URL, SERVICE_CHAINS_JSON, SERVICE_CHAINS_JSON)
+ #put_and_check(SFF_URL, SERVICE_FUNCTION_FORWARDERS_JSON, SERVICE_FUNCTION_FORWARDERS_JSON)
+ #put_and_check(SFC_URL, SERVICE_CHAINS_JSON, SERVICE_CHAINS_JSON)
#put_and_check(SFP_URL, SERVICE_PATH_JSON, SERVICE_PATH_RESP_JSON)