RIFT OSM R1 Initial Submission
[osm/SO.git] / rwlaunchpad / plugins / rwvns / vala / rwsdn_odl / rwsdn_odl.py
diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py b/rwlaunchpad/plugins/rwvns/vala/rwsdn_odl/rwsdn_odl.py
new file mode 100644 (file)
index 0000000..3eb39fc
--- /dev/null
@@ -0,0 +1,1082 @@
+
+# 
+#   Copyright 2016 RIFT.IO Inc
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+
+import logging
+
+import requests
+
+import json
+import re
+import socket
+import time
+
+import gi
+gi.require_version('RwTypes', '1.0')
+gi.require_version('RwsdnYang', '1.0')
+gi.require_version('RwSdn', '1.0')
+gi.require_version('RwTopologyYang','1.0')
+
+from gi.repository import (
+    GObject,
+    RwSdn, # Vala package
+    RwTypes,
+    RwsdnYang, 
+    RwTopologyYang as RwTl,
+    )
+
+import rw_status
+import rwlogger
+
+
+logger = logging.getLogger('rwsdn.sdnodl')
+logger.setLevel(logging.DEBUG)
+
+
+sff_rest_based = True
+
+class UnknownAccountError(Exception):
+    pass
+
+
+class MissingFileError(Exception):
+    pass
+
+
+rwstatus = rw_status.rwstatus_from_exc_map({
+    IndexError: RwTypes.RwStatus.NOTFOUND,
+    KeyError: RwTypes.RwStatus.NOTFOUND,
+    UnknownAccountError: RwTypes.RwStatus.NOTFOUND,
+    MissingFileError: RwTypes.RwStatus.NOTFOUND,
+    })
+
+
+class SdnOdlPlugin(GObject.Object, RwSdn.Topology):
+
+    def __init__(self):
+        GObject.Object.__init__(self)
+        self.sdnodl = SdnOdl()
+        
+
+    @rwstatus
+    def do_init(self, rwlog_ctx):
+        if not any(isinstance(h, rwlogger.RwLogger) for h in logger.handlers):
+            logger.addHandler(
+                rwlogger.RwLogger(
+                    category="rw-cal-log",
+                    subcategory="odl", 
+                    log_hdl=rwlog_ctx,
+                )
+            )
+
+    @rwstatus(ret_on_failure=[None])
+    def do_validate_sdn_creds(self, account):
+        """
+        Validates the sdn account credentials for the specified account.
+        Performs an access to the resources using Keystone API. If creds
+        are not valid, returns an error code & reason string
+
+        @param account - a SDN account
+
+        Returns:
+            Validation Code and Details String
+        """
+        #logger.debug('Received validate SDN creds')
+        status = self.sdnodl.validate_account_creds(account)
+        #logger.debug('Done with validate SDN creds: %s', type(status))
+        return status
+
+    @rwstatus(ret_on_failure=[None])
+    def do_get_network_list(self, account):
+        """
+        Returns the list of discovered networks
+
+        @param account - a SDN account
+
+        """
+        logger.debug('Received Get network list: ')
+        nwtop = self.sdnodl.get_network_list( account)
+        logger.debug('Done with get network list: %s', type(nwtop))
+        return nwtop
+
+    @rwstatus(ret_on_failure=[""])
+    def do_create_vnffg_chain(self, account,vnffg_chain):
+        """
+        Creates Service Function chain in ODL
+
+        @param account - a SDN account
+
+        """
+        logger.debug('Received Create VNFFG chain ')
+        vnffg_id = self.sdnodl.create_sfc( account,vnffg_chain)
+        logger.debug('Done with create VNFFG chain with name : %s', vnffg_id)
+        return vnffg_id
+
+    @rwstatus
+    def do_terminate_vnffg_chain(self, account,vnffg_id):
+        """
+        Terminate Service Function chain in ODL
+
+        @param account - a SDN account
+
+        """
+        logger.debug('Received terminate VNFFG chain for id %s ', vnffg_id)
+        # TODO: Currently all the RSP, SFPs , SFFs and SFs are deleted
+        # Need to handle deletion of specific RSP, SFFs, SFs etc
+        self.sdnodl.terminate_all_sfc(account)
+        logger.debug('Done with terminate VNFFG chain with name : %s', vnffg_id)
+
+    @rwstatus(ret_on_failure=[None])
+    def do_get_vnffg_rendered_paths(self, account):
+        """
+           Get ODL Rendered Service Path List (SFC)
+
+           @param account - a SDN account
+        """
+        vnffg_list = self.sdnodl.get_rsp_list(account)
+        return vnffg_list 
+
+    @rwstatus(ret_on_failure=[None])
+    def do_create_vnffg_classifier(self, account, vnffg_classifier):
+        """
+           Add VNFFG Classifier 
+
+           @param account - a SDN account
+        """
+        classifier_name = self.sdnodl.create_sfc_classifier(account,vnffg_classifier)
+        return classifier_name 
+
+    @rwstatus(ret_on_failure=[None])
+    def do_terminate_vnffg_classifier(self, account, vnffg_classifier_name):
+        """
+           Add VNFFG Classifier 
+
+           @param account - a SDN account
+        """
+        self.sdnodl.terminate_sfc_classifier(account,vnffg_classifier_name)
+
+
+class Sff(object):
+    """
+    Create SFF object to hold SFF related details
+    """
+
+    def __init__(self,sff_name, mgmt_address, mgmt_port, dp_address, dp_port,sff_dp_name, sff_br_name=''):
+        self.name = sff_name
+        self.ip = mgmt_address
+        self.sff_rest_port = mgmt_port
+        self.sff_port = dp_port
+        self.dp_name = sff_dp_name
+        self.dp_ip = dp_address
+        self.br_name = sff_br_name
+        self.sf_dp_list = list()
+    
+    def add_sf_dp_to_sff(self,sf_dp):
+        self.sf_dp_list.append(sf_dp)
+
+    def __repr__(self):
+        return 'Name:{},Bridge Name:{}, IP: {}, SF List: {}'.format(self.dp_name,self.br_name, self.ip, self.sf_dp_list) 
+
+class SfDpLocator(object):
+    """
+    Create Service Function Data Plane Locator related Object to hold details related to each DP Locator endpoint
+    """
+    def __init__(self,name,sfdp_id,vnfr_name,vm_id):
+        self.name = name
+        self.port_id = sfdp_id
+        self.vnfr_name = vnfr_name
+        self.vm_id = vm_id
+        self.sff_name = None 
+        self.ovsdb_tp_name = None
+
+    def _update_sff_name(self,sff_name):
+        self.sff_name = sff_name
+
+    def _update_vnf_params(self,service_function_type,address, port,transport_type):
+        self.service_function_type = service_function_type
+        self.address = address
+        self.port = port
+        self.transport_type = "service-locator:{}".format(transport_type)
+
+    def __repr__(self):
+        return 'Name:{},Port id:{}, VNFR ID: {}, VM ID: {}, SFF Name: {}'.format(self.name,self.port_id, self.vnfr_name, self.vm_id,self.sff_name) 
+
+class SdnOdl(object):
+    """
+    SDN ODL Class to support REST based API calls
+    """
+
+    @property
+    def _network_topology_path(self):
+        return 'restconf/operational/network-topology:network-topology'  
+
+    @property
+    def _node_inventory_path(self):
+        return 'restconf/operational/opendaylight-inventory:nodes'  
+     
+    def _network_topology_rest_url(self,account):
+        return '{}/{}'.format(account.odl.url,self._network_topology_path)
+
+    def _node_inventory_rest_url(self,account):
+        return '{}/{}'.format(account.odl.url,self._node_inventory_path)
+
+    def _get_rest_url(self,account, rest_path):
+        return '{}/{}'.format(account.odl.url,rest_path)
+
+
+    def _get_peer_termination_point(self,node_inv,tp_id):
+        for node in node_inv['nodes']['node']:
+            if "node-connector" in node and len(node['node-connector']) > 0:
+                for nodec in node['node-connector']:
+                    if ("flow-node-inventory:name" in nodec and nodec["flow-node-inventory:name"] == tp_id):
+                        return(node['id'], nodec['id'])
+        return (None,None)
+
+    def _get_termination_point_mac_address(self,node_inv,tp_id):
+        for node in node_inv['nodes']['node']:
+            if "node-connector" in node and len(node['node-connector']) > 0:
+                for nodec in node['node-connector']:
+                    if ("flow-node-inventory:name" in nodec and nodec["flow-node-inventory:name"] == tp_id):
+                        return nodec.get("flow-node-inventory:hardware-address")
+
+    def _add_host(self,ntwk,node,term_point,vmid,node_inv):
+        for ntwk_node in ntwk.node:
+            if ntwk_node.node_id ==  vmid:
+                break
+        else:
+            ntwk_node = ntwk.node.add()
+            if "ovsdb:bridge-name" in node:
+                ntwk_node.rw_node_attributes.ovs_bridge_name = node["ovsdb:bridge-name"]
+            ntwk_node.node_id = vmid
+            intf_id = [res for res in term_point['ovsdb:interface-external-ids'] if res['external-id-key'] == 'iface-id']
+            if intf_id:
+                ntwk_node_tp = ntwk_node.termination_point.add()
+                ntwk_node_tp.tp_id = intf_id[0]['external-id-value']
+                att_mac = [res for res in term_point['ovsdb:interface-external-ids'] if res['external-id-key'] == 'attached-mac']
+                if att_mac:
+                    ntwk_node_tp.l2_termination_point_attributes.mac_address = att_mac[0]['external-id-value']
+                peer_node,peer_node_tp = self._get_peer_termination_point(node_inv,term_point['tp-id'])
+                if peer_node and peer_node_tp:
+                    nw_lnk = ntwk.link.add()
+                    nw_lnk.source.source_tp = ntwk_node_tp.tp_id
+                    nw_lnk.source.source_node = ntwk_node.node_id
+                    nw_lnk.destination.dest_tp = term_point['tp-id']
+                    nw_lnk.destination.dest_node = node['node-id']
+                    nw_lnk.link_id = peer_node_tp + '-' + 'source'
+
+                    nw_lnk = ntwk.link.add()
+                    nw_lnk.source.source_tp = term_point['tp-id']
+                    nw_lnk.source.source_node = node['node-id']
+                    nw_lnk.destination.dest_tp = ntwk_node_tp.tp_id
+                    nw_lnk.destination.dest_node = ntwk_node.node_id
+                    nw_lnk.link_id = peer_node_tp + '-' + 'dest'
+
+    def _get_address_from_node_inventory(self,node_inv,node_id):
+        for node in node_inv['nodes']['node']:
+            if node['id'] == node_id:
+                return node["flow-node-inventory:ip-address"]
+        return None
+
+    def _fill_network_list(self,nw_topo,node_inventory):
+        """
+        Fill Topology related information
+        """
+        nwtop = RwTl.YangData_IetfNetwork()
+
+        for topo in nw_topo['network-topology']['topology']:
+            if ('node' in topo and len(topo['node']) > 0):
+                ntwk = nwtop.network.add()
+                ntwk.network_id = topo['topology-id']
+                ntwk.server_provided = True
+                for node in topo['node']:
+                    if ('termination-point' in node and len(node['termination-point']) > 0):
+                        ntwk_node = ntwk.node.add()
+                        ntwk_node.node_id = node['node-id']
+                        addr = self._get_address_from_node_inventory(node_inventory,ntwk_node.node_id)
+                        if addr:
+                            ntwk_node.l2_node_attributes.management_address.append(addr)
+                        for term_point in node['termination-point']:
+                            ntwk_node_tp = ntwk_node.termination_point.add()
+                            ntwk_node_tp.tp_id = term_point['tp-id']
+                            mac_address = self._get_termination_point_mac_address(node_inventory,term_point['tp-id'])
+                            if mac_address:
+                                ntwk_node_tp.l2_termination_point_attributes.mac_address = mac_address
+                            if 'ovsdb:interface-external-ids' in term_point:
+                                vm_id = [res for res in term_point['ovsdb:interface-external-ids'] if res['external-id-key'] == 'vm-id']
+                                if vm_id:
+                                    vmid = vm_id[0]['external-id-value']
+                                    self._add_host(ntwk,node,term_point,vmid,node_inventory)
+                if ('link' in topo and len(topo['link']) > 0):
+                    for link in topo['link']:
+                        nw_link = ntwk.link.add()
+                        if 'destination' in link:  
+                            nw_link.destination.dest_tp = link['destination'].get('dest-tp')
+                            nw_link.destination.dest_node = link['destination'].get('dest-node')
+                        if 'source' in link:
+                            nw_link.source.source_node = link['source'].get('source-node')
+                            nw_link.source.source_tp = link['source'].get('source-tp')
+                        nw_link.link_id = link.get('link-id')
+        return nwtop
+
+
+    def validate_account_creds(self, account):
+        """
+            Validate the SDN account credentials by accessing the rest API using the provided credentials
+        """
+        status = RwsdnYang.SdnConnectionStatus()
+        url = '{}/{}'.format(account.odl.url,"restconf")
+        try:
+            r=requests.get(url,auth=(account.odl.username,account.odl.password))
+            r.raise_for_status()
+        except requests.exceptions.HTTPError as e:
+            msg = "SdnOdlPlugin: SDN account credential validation failed. Exception: %s", str(e)
+            #logger.error(msg)
+            print(msg)
+            status.status = "failure"
+            status.details = "Invalid Credentials: %s" % str(e)
+        except Exception as e:
+            msg = "SdnPdlPlugin: SDN connection failed. Exception: %s", str(e)
+            #logger.error(msg)
+            print(msg)
+            status.status = "failure"
+            status.details = "Connection Failed (Invlaid URL): %s" % str(e)
+        else:
+            print("SDN Successfully connected")
+            status.status = "success"
+            status.details = "Connection was successful"
+
+        return status
+
+    def get_network_list(self, account):
+        """
+           Get the networks details from ODL
+        """
+        url = self._network_topology_rest_url(account)
+        r=requests.get(url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+        nw_topo = r.json()
+
+        url = self._node_inventory_rest_url(account)
+        r = requests.get(url,auth=(account.odl.username,account.odl.password)) 
+        r.raise_for_status()
+        node_inventory = r.json()
+        return self._fill_network_list(nw_topo,node_inventory)
+
+    @property
+    def _service_functions_path(self):
+        return 'restconf/config/service-function:service-functions'
+
+    @property
+    def _service_function_path(self):
+        return 'restconf/config/service-function:service-functions/service-function/{}'
+
+    @property
+    def _service_function_forwarders_path(self):
+        return 'restconf/config/service-function-forwarder:service-function-forwarders'
+
+    @property
+    def _service_function_forwarder_path(self):
+        return 'restconf/config/service-function-forwarder:service-function-forwarders/service-function-forwarder/{}'
+
+    @property
+    def _service_function_chains_path(self):
+        return 'restconf/config/service-function-chain:service-function-chains'
+
+    @property
+    def _service_function_chain_path(self):
+        return 'restconf/config/service-function-chain:service-function-chains/service-function-chain/{}'
+   
+    @property
+    def _sfp_metadata_path(self):
+        return 'restconf/config/service-function-path-metadata:service-function-metadata/context-metadata/{}'
+   
+    @property
+    def _sfps_metadata_path(self):
+        return 'restconf/config/service-function-path-metadata:service-function-metadata'
+   
+    @property
+    def _sfps_path(self):
+        return 'restconf/config/service-function-path:service-function-paths'
+
+    @property
+    def _sfp_path(self):
+        return 'restconf/config/service-function-path:service-function-paths/service-function-path/{}'
+
+
+    @property
+    def _create_rsp_path(self):
+        return 'restconf/operations/rendered-service-path:create-rendered-path'
+
+    @property
+    def _delete_rsp_path(self):
+        return 'restconf/operations/rendered-service-path:delete-rendered-path'
+
+
+    @property
+    def _get_rsp_paths(self):
+        return 'restconf/operational/rendered-service-path:rendered-service-paths'
+
+    @property
+    def _get_rsp_path(self):
+        return 'restconf/operational/rendered-service-path:rendered-service-paths/rendered-service-path/{}'
+
+    @property
+    def _access_list_path(self):
+        return 'restconf/config/ietf-access-control-list:access-lists/acl/{}'
+
+    @property
+    def _service_function_classifier_path(self):
+        return 'restconf/config/service-function-classifier:service-function-classifiers/service-function-classifier/{}'
+
+    @property
+    def _access_lists_path(self):
+        return 'restconf/config/ietf-access-control-list:access-lists'
+
+    @property
+    def _service_function_classifiers_path(self):
+        return 'restconf/config/service-function-classifier:service-function-classifiers'
+
+
+    def _create_sf(self,account,vnffg_chain,sf_dp_list):
+        "Create SF"
+        sf_json = {}
+
+        for vnf in vnffg_chain.vnf_chain_path:
+            for vnfr in vnf.vnfr_ids:
+                sf_url = self._get_rest_url(account,self._service_function_path.format(vnfr.vnfr_name))
+                print(sf_url)
+                r=requests.get(sf_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'})
+                # If the SF is not found; create new SF
+                if r.status_code == 200:
+                    logger.info("SF with name %s is already present in ODL. Skipping update", vnfr.vnfr_name)
+                    continue
+                elif r.status_code != 404:
+                    r.raise_for_status()
+
+                sf_dict = {}
+                sf_dict['name'] = vnfr.vnfr_name
+                sf_dict['nsh-aware'] = vnf.nsh_aware
+                sf_dict['type'] = vnf.service_function_type
+                sf_dict['ip-mgmt-address'] = vnfr.mgmt_address
+                sf_dict['rest-uri'] = 'http://{}:{}'.format(vnfr.mgmt_address, vnfr.mgmt_port)
+
+                sf_dict['sf-data-plane-locator'] = list()
+                for vdu in vnfr.vdu_list:
+                    sf_dp = {}
+                    if vdu.port_id in sf_dp_list.keys():
+                        sf_dp_entry = sf_dp_list[vdu.port_id]
+                        sf_dp['name'] = sf_dp_entry.name
+                        sf_dp['ip'] = vdu.address
+                        sf_dp['port'] = vdu.port
+                        sf_dp['transport'] = "service-locator:{}".format(vnf.transport_type)
+                        if vnfr.sff_name:
+                            sf_dp['service-function-forwarder'] = vnfr.sff_name
+                        else:
+                            sff_name = sf_dp_entry.sff_name
+                            if sff_name is None:
+                                logger.error("SFF not found for port %s in SF %s", vdu.port_id, vnfr.vnfr_name)
+                            sf_dp['service-function-forwarder'] = sff_name
+                            sf_dp['service-function-ovs:ovs-port'] = dict()
+                            if sf_dp_entry.ovsdb_tp_name is not None:
+                                sf_dp['service-function-ovs:ovs-port']['port-id'] =  sf_dp_entry.ovsdb_tp_name
+                        sf_dict['sf-data-plane-locator'].append(sf_dp)
+                    else:
+                        logger.error("Port %s not found in SF DP list",vdu.port_id)
+
+                sf_json['service-function'] = sf_dict
+                sf_data = json.dumps(sf_json)
+                sf_url = self._get_rest_url(account,self._service_function_path.format(vnfr.vnfr_name))
+                print(sf_url)
+                print(sf_data)
+                r=requests.put(sf_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sf_data)
+                r.raise_for_status()
+
+
+    def _create_sff(self,account,vnffg_chain,sff):
+        "Create SFF"
+        sff_json = {}
+        sff_dict = {}
+        #sff_dp_name = "SFF1" + '-' + 'DP1'
+        sff_dp_name = sff.dp_name 
+                
+        sff_url = self._get_rest_url(account,self._service_function_forwarder_path.format(sff.name))
+        print(sff_url)
+        r=requests.get(sff_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'})
+        # If the SFF is not found; create new SF
+        if r.status_code == 200:
+            logger.info("SFF with name %s is already present in ODL. Skipping full update", sff.name)
+            sff_dict = r.json()
+            sff_updated = False
+            for sf_dp in sff.sf_dp_list:
+                for sff_sf in sff_dict['service-function-forwarder'][0]['service-function-dictionary']:
+                    if sf_dp.vnfr_name == sff_sf['name']:
+                        logger.info("SF with name %s is already found in SFF %s SF Dictionay. Skipping update",sf_dp.vnfr_name,sff.name) 
+                        break
+                else:
+                    logger.info("SF with name %s is not found in SFF %s SF Dictionay",sf_dp.vnfr_name, sff.name)
+                    sff_updated = True
+                    sff_sf_dict = {}
+                    sff_sf_dp_loc = {}
+                    sff_sf_dict['name'] = sf_dp.vnfr_name
+
+                    # Below two lines are enabled only for ODL Beryillium
+                    sff_sf_dp_loc['sff-dpl-name'] = sff_dp_name
+                    sff_sf_dp_loc['sf-dpl-name'] = sf_dp.name
+
+                    sff_sf_dict['sff-sf-data-plane-locator'] = sff_sf_dp_loc
+                    sff_dict['service-function-forwarder'][0]['service-function-dictionary'].append(sff_sf_dict)
+            if sff_updated is True:
+                sff_data = json.dumps(sff_dict)
+                print(sff_data)
+                r=requests.put(sff_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sff_data)
+                r.raise_for_status()
+            return        
+        elif r.status_code != 404:
+            r.raise_for_status()
+        
+        sff_name = sff.name
+        sff_ip = sff.ip
+        sff_dp_ip = sff.dp_ip
+        sff_port = sff.sff_port
+        sff_bridge_name = ''
+        sff_rest_port = sff.sff_rest_port
+        sff_ovs_op = {}
+        if sff_rest_based is False:
+            sff_bridge_name = sff.br_name
+            sff_ovs_op  = {"key": "flow",
+                       "nshc1": "flow",
+                       "nsp": "flow",
+                       "remote-ip": "flow",
+                       "dst-port": sff_port,
+                       "nshc3": "flow",
+                       "nshc2": "flow",
+                       "nshc4": "flow",
+                       "nsi": "flow"}
+
+
+        sff_dict['name'] = sff_name
+        sff_dict['service-node'] = ''
+        sff_dict['ip-mgmt-address'] = sff_ip
+        if sff_rest_based:
+            sff_dict['rest-uri'] = 'http://{}:{}'.format(sff_ip, sff_rest_port)
+        else:
+            sff_dict['service-function-forwarder-ovs:ovs-bridge'] = {"bridge-name": sff_bridge_name}
+        sff_dict['service-function-dictionary'] = list()
+        for sf_dp in sff.sf_dp_list:
+            sff_sf_dict = {}
+            sff_sf_dp_loc = {}
+            sff_sf_dict['name'] = sf_dp.vnfr_name
+
+            # Below set of lines are reqd for Lithium
+            #sff_sf_dict['type'] = sf_dp.service_function_type
+            #sff_sf_dp_loc['ip'] = sf_dp.address
+            #sff_sf_dp_loc['port'] = sf_dp.port
+            #sff_sf_dp_loc['transport'] = sf_dp.transport_type
+            #sff_sf_dp_loc['service-function-forwarder-ovs:ovs-bridge'] = {}
+
+            # Below two lines are enabled only for ODL Beryillium
+            sff_sf_dp_loc['sff-dpl-name'] = sff_dp_name
+            sff_sf_dp_loc['sf-dpl-name'] = sf_dp.name
+
+            sff_sf_dict['sff-sf-data-plane-locator'] = sff_sf_dp_loc
+            sff_dict['service-function-dictionary'].append(sff_sf_dict)
+
+        sff_dict['sff-data-plane-locator'] = list()
+        sff_dp = {}
+        dp_loc = {} 
+        sff_dp['name'] = sff_dp_name 
+        dp_loc['ip'] = sff_dp_ip
+        dp_loc['port'] = sff_port
+        dp_loc['transport'] = 'service-locator:vxlan-gpe'
+        sff_dp['data-plane-locator'] = dp_loc
+        if sff_rest_based is False:
+            sff_dp['service-function-forwarder-ovs:ovs-options'] = sff_ovs_op
+            #sff_dp["service-function-forwarder-ovs:ovs-bridge"] = {'bridge-name':sff_bridge_name}
+            sff_dp["service-function-forwarder-ovs:ovs-bridge"] = {}
+        sff_dict['sff-data-plane-locator'].append(sff_dp)
+
+        sff_json['service-function-forwarder'] = sff_dict
+        sff_data = json.dumps(sff_json)
+        print(sff_data)
+        r=requests.put(sff_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sff_data)
+        r.raise_for_status()
+
+    def _create_sfc(self,account,vnffg_chain):
+        "Create SFC"
+        sfc_json = {}
+        sfc_dict = {}
+        sfc_dict['name'] = vnffg_chain.name
+        sfc_dict['sfc-service-function'] = list()
+        vnf_chain_list = sorted(vnffg_chain.vnf_chain_path, key = lambda x: x.order)
+        for vnf in vnf_chain_list:
+            sfc_sf_dict = {}
+            sfc_sf_dict['name'] = vnf.service_function_type
+            sfc_sf_dict['type'] = vnf.service_function_type
+            sfc_sf_dict['order'] = vnf.order 
+            sfc_dict['sfc-service-function'].append(sfc_sf_dict)
+        sfc_json['service-function-chain'] = sfc_dict
+        sfc_data = json.dumps(sfc_json)
+        sfc_url = self._get_rest_url(account,self._service_function_chain_path.format(vnffg_chain.name))
+        print(sfc_url)
+        print(sfc_data)
+        r=requests.put(sfc_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sfc_data)
+        r.raise_for_status()
+       
+    def _create_sfp_metadata(self,account,sfc_classifier):
+        " Create SFP metadata"
+        sfp_meta_json = {}
+        sfp_meta_dict = {}
+        sfp_meta_dict['name'] = sfc_classifier.name
+        if sfc_classifier.vnffg_metadata.ctx1:
+            sfp_meta_dict['context-header1'] = sfc_classifier.vnffg_metadata.ctx1
+        if sfc_classifier.vnffg_metadata.ctx2:
+            sfp_meta_dict['context-header2'] = sfc_classifier.vnffg_metadata.ctx2
+        if sfc_classifier.vnffg_metadata.ctx3:
+            sfp_meta_dict['context-header3'] = sfc_classifier.vnffg_metadata.ctx3
+        if sfc_classifier.vnffg_metadata.ctx4:
+            sfp_meta_dict['context-header4'] = sfc_classifier.vnffg_metadata.ctx4
+
+        sfp_meta_json['context-metadata'] = sfp_meta_dict
+        sfp_meta_data = json.dumps(sfp_meta_json)
+        sfp_meta_url = self._get_rest_url(account,self._sfp_metadata_path.format(sfc_classifier.name))
+        print(sfp_meta_url)
+        print(sfp_meta_data)
+        r=requests.put(sfp_meta_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sfp_meta_data)
+        r.raise_for_status()
+
+    def _create_sfp(self,account,vnffg_chain, sym_chain=False,classifier_name=None,vnffg_metadata_name=None):
+        "Create SFP"
+        sfp_json = {}
+        sfp_dict = {}
+        sfp_dict['name'] = vnffg_chain.name
+        sfp_dict['service-chain-name'] = vnffg_chain.name
+        sfp_dict['symmetric'] = sym_chain
+        sfp_dict['transport-type'] = 'service-locator:vxlan-gpe'
+        if vnffg_metadata_name:
+            sfp_dict['context-metadata'] = vnffg_metadata_name 
+        if classifier_name: 
+            sfp_dict['classifier'] = classifier_name 
+
+        sfp_json['service-function-path'] = sfp_dict
+        sfp_data = json.dumps(sfp_json)
+        sfp_url = self._get_rest_url(account,self._sfp_path.format(vnffg_chain.name))
+        print(sfp_url)
+        print(sfp_data)
+        r=requests.put(sfp_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sfp_data)
+        r.raise_for_status()
+
+    def _create_rsp(self,account,vnffg_chain_name, sym_chain=True):
+        "Create RSP"
+        rsp_json = {}
+        rsp_input = {}
+        rsp_json['input'] = {}
+        rsp_input['name'] = vnffg_chain_name
+        rsp_input['parent-service-function-path'] = vnffg_chain_name
+        rsp_input['symmetric'] = sym_chain
+
+        rsp_json['input'] = rsp_input
+        rsp_data = json.dumps(rsp_json)
+        self._rsp_data = rsp_json
+        rsp_url = self._get_rest_url(account,self._create_rsp_path)
+        print(rsp_url)
+        print(rsp_data)
+        r=requests.post(rsp_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=rsp_data)
+        r.raise_for_status()
+        print(r.json())
+        output_json = r.json()
+        return output_json['output']['name']
+        
+    def _get_sff_list_for_chain(self, account,sf_dp_list):
+        """
+        Get List of all SFF that needs to be created based on VNFs included in VNFFG chain.
+        """
+
+        sff_list = {}
+        if sf_dp_list is None:
+            logger.error("VM List for vnffg chain is empty while trying to get SFF list")
+        url = self._network_topology_rest_url(account)
+        r=requests.get(url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+        nw_topo = r.json()
+
+        for topo in nw_topo['network-topology']['topology']:
+            if ('node' in topo and len(topo['node']) > 0):
+                for node in topo['node']:
+                    if ('termination-point' in node and len(node['termination-point']) > 0):
+                        for term_point in node['termination-point']:
+                            if 'ovsdb:interface-external-ids' in term_point:
+                                vm_id = [res for res in term_point['ovsdb:interface-external-ids'] if res['external-id-key'] == 'vm-id']
+                                if len(vm_id) == 0:
+                                    continue
+                                vmid = vm_id[0]['external-id-value']
+                                intf_id = [res for res in term_point['ovsdb:interface-external-ids'] if res['external-id-key'] == 'iface-id']
+                                if len(intf_id) == 0:
+                                    continue 
+                                intfid = intf_id[0]['external-id-value'] 
+                                if intfid not in sf_dp_list.keys():
+                                    continue
+                                if sf_dp_list[intfid].vm_id != vmid:
+                                    logger.error("Intf ID %s is not present in VM %s", intfid, vmid)  
+                                    continue 
+                                sf_dp_list[intfid].ovsdb_tp_name = term_point['ovsdb:name']
+                           
+                                if 'ovsdb:managed-by' in node:
+                                    rr=re.search('network-topology:node-id=\'([-\w\:\/]*)\'',node['ovsdb:managed-by'])
+                                    node_id = rr.group(1)
+                                    ovsdb_node = [node  for node in topo['node'] if node['node-id'] == node_id]
+                                    if ovsdb_node:
+                                        if 'ovsdb:connection-info' in ovsdb_node[0]:
+                                            sff_ip = ovsdb_node[0]['ovsdb:connection-info']['local-ip']
+                                            sff_br_name = node['ovsdb:bridge-name']
+                                            sff_br_uuid = node['ovsdb:bridge-uuid']
+                                            sff_dp_ip = sff_ip
+
+                                            if 'ovsdb:openvswitch-other-configs' in  ovsdb_node[0]: 
+                                                for other_key in ovsdb_node[0]['ovsdb:openvswitch-other-configs']:
+                                                    if other_key['other-config-key'] == 'local_ip':
+                                                        local_ip_str = other_key['other-config-value']
+                                                        sff_dp_ip = local_ip_str.split(',')[0]
+                                                        break
+
+                                            sff_name = socket.getfqdn(sff_ip)
+                                            if sff_br_uuid in sff_list:
+                                                sff_list[sff_name].add_sf_dp_to_sff(sf_dp_list[intfid])
+                                                sf_dp_list[intfid]._update_sff_name(sff_name)
+                                            else:
+                                                sff_dp_ip = sff_ip   #overwrite sff_dp_ip to SFF ip for now
+                                                sff_list[sff_name] = Sff(sff_name,sff_ip,6000, sff_dp_ip, 4790,sff_br_uuid,sff_br_name)
+                                                sf_dp_list[intfid]._update_sff_name(sff_name)
+                                                sff_list[sff_name].add_sf_dp_to_sff(sf_dp_list[intfid])
+        return sff_list
+                                         
+
+    def _get_sf_dp_list_for_chain(self,account,vnffg_chain):
+        """
+        Get list of all Service Function Data Plane Locators present in VNFFG 
+        useful for easy reference while creating SF and SFF
+        """
+        sfdp_list = {}
+        for vnf in vnffg_chain.vnf_chain_path:
+            for vnfr in vnf.vnfr_ids:
+                for vdu in vnfr.vdu_list:
+                    sfdp = SfDpLocator(vdu.name,vdu.port_id,vnfr.vnfr_name, vdu.vm_id)
+                    sfdp._update_vnf_params(vnf.service_function_type, vdu.address, vdu.port, vnf.transport_type)
+                    if vnfr.sff_name:
+                        sfdp._update_sff_name(vnfr.sff_name)
+                    sfdp_list[vdu.port_id] = sfdp 
+        return sfdp_list
+
+    def create_sfc(self, account, vnffg_chain):
+        "Create SFC chain"
+
+        sff_list = {}
+        sf_dp_list = {}
+
+        sf_dp_list = self._get_sf_dp_list_for_chain(account,vnffg_chain)
+
+        if sff_rest_based is False and len(vnffg_chain.sff) == 0:
+            # Get the list of all SFFs required for vnffg chain
+            sff_list = self._get_sff_list_for_chain(account,sf_dp_list)
+
+        for sff in vnffg_chain.sff:
+          sff_list[sff.name] = Sff(sff.name, sff.mgmt_address,sff.mgmt_port,sff.dp_endpoints[0].address, sff.dp_endpoints[0].port, sff.name)
+          for _,sf_dp in sf_dp_list.items():
+              if sf_dp.sff_name and sf_dp.sff_name == sff.name:
+                  sff_list[sff.name].add_sf_dp_to_sff(sf_dp) 
+
+        #Create all the SF in VNFFG chain
+        self._create_sf(account,vnffg_chain,sf_dp_list)
+
+        for _,sff in sff_list.items():
+            self._create_sff(account,vnffg_chain,sff)
+
+
+        self._create_sfc(account,vnffg_chain)
+
+        self._create_sfp(account,vnffg_chain,classifier_name=vnffg_chain.classifier_name,
+                                   vnffg_metadata_name=vnffg_chain.classifier_name)
+
+        ## Update to SFF could have deleted some RSP; so get list of SFP and 
+        ## check RSP exists for same and create any as necessary
+        #rsp_name = self._create_rsp(account,vnffg_chain)
+        #return rsp_name
+        self._create_all_rsps(account)
+        self._recreate_all_sf_classifiers(account)
+        return vnffg_chain.name
+
+    def _recreate_all_sf_classifiers(self,account):
+        """
+        Re create all SF classifiers
+        """
+        sfcl_url = self._get_rest_url(account,self._service_function_classifiers_path)
+        print(sfcl_url)
+        #Get the classifier
+        r=requests.get(sfcl_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'})
+        if r.status_code == 200:
+            print(r)
+            sfcl_json = r.json()
+        elif r.status_code == 404:
+            return         
+        else: 
+            r.raise_for_status()
+
+        #Delete the classifiers and re-add same back
+        r=requests.delete(sfcl_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+        #Readd it back
+        time.sleep(3)
+        print(sfcl_json)
+        sfcl_data = json.dumps(sfcl_json)
+        r=requests.put(sfcl_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sfcl_data)
+        r.raise_for_status()
+
+    def _create_all_rsps(self,account):
+        """
+        Create all the RSPs for SFP found
+        """
+        sfps_url = self._get_rest_url(account,self._sfps_path)
+        r=requests.get(sfps_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'})
+        r.raise_for_status()
+        sfps_json = r.json()
+        if 'service-function-path' in sfps_json['service-function-paths']:
+            for sfp in sfps_json['service-function-paths']['service-function-path']:
+                rsp_url = self._get_rest_url(account,self._get_rsp_path.format(sfp['name']))
+                r = requests.get(rsp_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'})
+                if r.status_code == 404:
+                    # Create the RSP
+                    logger.info("Creating RSP for Service Path with name %s",sfp['name'])
+                    self._create_rsp(account,sfp['name'])
+
+    def delete_all_sf(self, account):
+        "Delete all the SFs"
+        sf_url = self._get_rest_url(account,self._service_functions_path)
+        print(sf_url)
+        r=requests.delete(sf_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+
+
+    def delete_all_sff(self, account):
+        "Delete all the SFFs"
+        sff_url = self._get_rest_url(account,self._service_function_forwarders_path)
+        print(sff_url)
+        r=requests.delete(sff_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+
+    def delete_all_sfc(self, account):
+        "Delete all the SFCs"
+        sfc_url = self._get_rest_url(account,self._service_function_chains_path)
+        print(sfc_url)
+        r=requests.delete(sfc_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+
+    def delete_all_sfp_metadata(self, account):
+        "Delete all the SFPs metadata"
+        sfp_metadata_url = self._get_rest_url(account,self._sfps_metadata_path)
+        print(sfp_metadata_url)
+        r=requests.delete(sfp_metadata_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+
+    def delete_all_sfp(self, account):
+        "Delete all the SFPs"
+        sfp_url = self._get_rest_url(account,self._sfps_path)
+        print(sfp_url)
+        r=requests.delete(sfp_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+
+    def delete_all_rsp(self, account):
+        "Delete all the RSP"
+        #rsp_list = self.get_rsp_list(account)
+        url = self._get_rest_url(account,self._get_rsp_paths)
+        print(url)
+        r = requests.get(url,auth=(account.odl.username,account.odl.password)) 
+        r.raise_for_status()
+        print(r.json())
+        rsp_list = r.json()
+
+        #for vnffg in rsp_list.vnffg_rendered_path: 
+        for sfc_rsp in rsp_list['rendered-service-paths']['rendered-service-path']:
+            rsp_json = {}
+            rsp_input = {}
+            rsp_json['input'] = {}
+            rsp_input['name'] = sfc_rsp['name']
+
+            rsp_json['input'] = rsp_input
+            rsp_data = json.dumps(rsp_json)
+            self._rsp_data = rsp_json
+            rsp_url = self._get_rest_url(account,self._delete_rsp_path)
+            print(rsp_url)
+            print(rsp_data)
+
+            r=requests.post(rsp_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=rsp_data)
+            r.raise_for_status()
+            print(r.json())
+            #output_json = r.json()
+            #return output_json['output']['name']
+            
+    def terminate_all_sfc(self, account):
+        "Terminate SFC chain"
+        self.delete_all_rsp(account)
+        self.delete_all_sfp(account)
+        self.delete_all_sfc(account)
+        self.delete_all_sff(account)
+        self.delete_all_sf(account)
+
+    def _fill_rsp_list(self,sfc_rsp_list,sff_list):
+        vnffg_rsps = RwsdnYang.VNFFGRenderedPaths()
+        for sfc_rsp in sfc_rsp_list['rendered-service-paths']['rendered-service-path']:
+            rsp = vnffg_rsps.vnffg_rendered_path.add()
+            rsp.name = sfc_rsp['name']
+            rsp.path_id = sfc_rsp['path-id']
+            for sfc_rsp_hop in sfc_rsp['rendered-service-path-hop']:
+                rsp_hop = rsp.rendered_path_hop.add()
+                rsp_hop.hop_number =  sfc_rsp_hop['hop-number']
+                rsp_hop.service_index = sfc_rsp_hop['service-index']
+                rsp_hop.vnfr_name =  sfc_rsp_hop['service-function-name']
+                rsp_hop.service_function_forwarder.name = sfc_rsp_hop['service-function-forwarder']
+                for sff in sff_list['service-function-forwarders']['service-function-forwarder']:
+                    if sff['name'] == rsp_hop.service_function_forwarder.name:
+                        rsp_hop.service_function_forwarder.ip_address = sff['sff-data-plane-locator'][0]['data-plane-locator']['ip']
+                        rsp_hop.service_function_forwarder.port = sff['sff-data-plane-locator'][0]['data-plane-locator']['port']
+                        break
+        return vnffg_rsps
+             
+
+    def get_rsp_list(self,account):
+        "Get RSP list"
+
+        sff_url = self._get_rest_url(account,self._service_function_forwarders_path)
+        print(sff_url)
+        r=requests.get(sff_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'})
+        r.raise_for_status()
+        sff_list = r.json()
+
+        url = self._get_rest_url(account,self._get_rsp_paths)
+        print(url)
+        r = requests.get(url,auth=(account.odl.username,account.odl.password)) 
+        r.raise_for_status()
+        print(r.json())
+        return self._fill_rsp_list(r.json(),sff_list)
+
+    def create_sfc_classifier(self, account, sfc_classifiers):
+        "Create SFC Classifiers"
+        self._create_sfp_metadata(account,sfc_classifiers)
+        self._add_acl_rules(account, sfc_classifiers)
+        self._create_sf_classifier(account, sfc_classifiers)
+        return sfc_classifiers.name
+
+    def terminate_sfc_classifier(self, account, sfc_classifier_name):
+        "Create SFC Classifiers"
+        self.delete_all_sfp_metadata(account)
+        self._terminate_sf_classifier(account, sfc_classifier_name)
+        self._del_acl_rules(account, sfc_classifier_name)
+
+    def _del_acl_rules(self,account,sfc_classifier_name):
+        " Terminate SF classifiers"
+        acl_url = self._get_rest_url(account,self._access_lists_path)
+        print(acl_url)
+        r=requests.delete(acl_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+
+    def _terminate_sf_classifier(self,account,sfc_classifier_name):
+        " Terminate SF classifiers"
+        sfcl_url = self._get_rest_url(account,self._service_function_classifiers_path)
+        print(sfcl_url)
+        r=requests.delete(sfcl_url,auth=(account.odl.username,account.odl.password))
+        r.raise_for_status()
+
+    def _create_sf_classifier(self,account,sfc_classifiers):
+        " Create SF classifiers"
+        sf_classifier_json = {}
+        sf_classifier_dict = {}
+        sf_classifier_dict['name'] = sfc_classifiers.name
+        sf_classifier_dict['access-list'] = sfc_classifiers.name
+        sf_classifier_dict['scl-service-function-forwarder'] = list()
+        scl_sff = {}
+        scl_sff_name = ''
+
+        if sfc_classifiers.has_field('sff_name') and sfc_classifiers.sff_name is not None:
+            scl_sff_name = sfc_classifiers.sff_name
+        elif  sfc_classifiers.has_field('port_id') and sfc_classifiers.has_field('vm_id'):
+            sf_dp = SfDpLocator(sfc_classifiers.port_id, sfc_classifiers.port_id,'', sfc_classifiers.vm_id)
+            sf_dp_list= {}
+            sf_dp_list[sfc_classifiers.port_id] = sf_dp
+            self._get_sff_list_for_chain(account,sf_dp_list)
+
+            if sf_dp.sff_name is None:
+                logger.error("SFF not found for port %s, VM: %s",sfc_classifiers.port_id,sfc_classifiers.vm_id) 
+            else:
+                logger.info("SFF with name %s  found for port %s, VM: %s",sf_dp.sff_name, sfc_classifiers.port_id,sfc_classifiers.vm_id) 
+                scl_sff_name = sf_dp.sff_name
+        else:
+            rsp_url = self._get_rest_url(account,self._get_rsp_path.format(sfc_classifiers.rsp_name))
+            r = requests.get(rsp_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'})
+            if r.status_code == 200:
+                rsp_data = r.json()
+                if 'rendered-service-path' in rsp_data and len(rsp_data['rendered-service-path'][0]['rendered-service-path-hop']) > 0:
+                    scl_sff_name = rsp_data['rendered-service-path'][0]['rendered-service-path-hop'][0]['service-function-forwarder']
+        
+        logger.debug("SFF for classifer %s found is %s",sfc_classifiers.name, scl_sff_name)        
+        scl_sff['name'] = scl_sff_name
+        #scl_sff['interface'] = sff_intf_name
+        sf_classifier_dict['scl-service-function-forwarder'].append(scl_sff)
+
+        sf_classifier_json['service-function-classifier'] = sf_classifier_dict
+
+        sfcl_data = json.dumps(sf_classifier_json)
+        sfcl_url = self._get_rest_url(account,self._service_function_classifier_path.format(sfc_classifiers.name))
+        print(sfcl_url)
+        print(sfcl_data)
+        r=requests.put(sfcl_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=sfcl_data)
+        r.raise_for_status()
+
+    def _add_acl_rules(self, account,sfc_classifiers):
+        "Create ACL rules"
+        access_list_json = {}
+        access_list_dict = {}
+        acl_entry_list = list()
+        acl_list_dict = {}
+        for acl_rule in sfc_classifiers.match_attributes:
+            acl_entry = {} 
+            acl_entry['rule-name']  = acl_rule.name
+            acl_entry['actions'] = {}
+            #acl_entry['actions']['netvirt-sfc-acl:rsp-name'] = sfc_classifiers.rsp_name
+            acl_entry['actions']['service-function-acl:rendered-service-path'] = sfc_classifiers.rsp_name
+
+            matches = {}
+            for field, value in acl_rule.as_dict().items():
+                if field == 'ip_proto':
+                    matches['protocol'] = value
+                elif field == 'source_ip_address':
+                    matches['source-ipv4-network'] = value
+                elif field == 'destination_ip_address':
+                    matches['destination-ipv4-network'] = value
+                elif field == 'source_port':
+                    matches['source-port-range'] = {'lower-port':value, 'upper-port':value}
+                elif field == 'destination_port':
+                    matches['destination-port-range'] = {'lower-port':value, 'upper-port':value}
+            acl_entry['matches'] = matches
+            acl_entry_list.append(acl_entry)    
+        acl_list_dict['ace'] = acl_entry_list 
+        access_list_dict['acl-name'] = sfc_classifiers.name
+        access_list_dict['access-list-entries'] = acl_list_dict
+        access_list_json['acl'] = access_list_dict
+
+        acl_data = json.dumps(access_list_json)
+        acl_url = self._get_rest_url(account,self._access_list_path.format(sfc_classifiers.name))
+        print(acl_url)
+        print(acl_data)
+        r=requests.put(acl_url,auth=(account.odl.username,account.odl.password),headers={'content-type': 'application/json'}, data=acl_data)
+        r.raise_for_status()