3 # Copyright 2016 RIFT.IO Inc
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
28 gi
.require_version('RwTypes', '1.0')
29 gi
.require_version('RwsdnalYang', '1.0')
30 gi
.require_version('RwSdn', '1.0')
31 gi
.require_version('RwTopologyYang','1.0')
33 from gi
.repository
import (
38 RwTopologyYang
as RwTl
,
45 logger
= logging
.getLogger('rwsdn.sdnodl')
46 logger
.setLevel(logging
.DEBUG
)
51 class UnknownAccountError(Exception):
55 class MissingFileError(Exception):
59 rwstatus
= rw_status
.rwstatus_from_exc_map({
60 IndexError: RwTypes
.RwStatus
.NOTFOUND
,
61 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
62 UnknownAccountError
: RwTypes
.RwStatus
.NOTFOUND
,
63 MissingFileError
: RwTypes
.RwStatus
.NOTFOUND
,
67 class SdnOdlPlugin(GObject
.Object
, RwSdn
.Topology
):
70 GObject
.Object
.__init
__(self
)
71 self
.sdnodl
= SdnOdl()
75 def do_init(self
, rwlog_ctx
):
76 if not any(isinstance(h
, rwlogger
.RwLogger
) for h
in logger
.handlers
):
79 category
="rw-cal-log",
85 @rwstatus(ret_on_failure
=[None])
86 def do_validate_sdn_creds(self
, account
):
88 Validates the sdn account credentials for the specified account.
89 Performs an access to the resources using Keystone API. If creds
90 are not valid, returns an error code & reason string
92 @param account - a SDN account
95 Validation Code and Details String
97 #logger.debug('Received validate SDN creds')
98 status
= self
.sdnodl
.validate_account_creds(account
)
99 #logger.debug('Done with validate SDN creds: %s', type(status))
102 @rwstatus(ret_on_failure
=[None])
103 def do_get_network_list(self
, account
):
105 Returns the list of discovered networks
107 @param account - a SDN account
110 logger
.debug('Received Get network list: ')
111 nwtop
= self
.sdnodl
.get_network_list( account
)
112 logger
.debug('Done with get network list: %s', type(nwtop
))
115 @rwstatus(ret_on_failure
=[""])
116 def do_create_vnffg_chain(self
, account
,vnffg_chain
):
118 Creates Service Function chain in ODL
120 @param account - a SDN account
123 logger
.debug('Received Create VNFFG chain ')
124 vnffg_id
= self
.sdnodl
.create_sfc( account
,vnffg_chain
)
125 logger
.debug('Done with create VNFFG chain with name : %s', vnffg_id
)
129 def do_terminate_vnffg_chain(self
, account
,vnffg_id
):
131 Terminate Service Function chain in ODL
133 @param account - a SDN account
136 logger
.debug('Received terminate VNFFG chain for id %s ', vnffg_id
)
137 # TODO: Currently all the RSP, SFPs , SFFs and SFs are deleted
138 # Need to handle deletion of specific RSP, SFFs, SFs etc
139 self
.sdnodl
.terminate_all_sfc(account
)
140 logger
.debug('Done with terminate VNFFG chain with name : %s', vnffg_id
)
142 @rwstatus(ret_on_failure
=[None])
143 def do_get_vnffg_rendered_paths(self
, account
):
145 Get ODL Rendered Service Path List (SFC)
147 @param account - a SDN account
149 vnffg_list
= self
.sdnodl
.get_rsp_list(account
)
152 @rwstatus(ret_on_failure
=[None])
153 def do_create_vnffg_classifier(self
, account
, vnffg_classifier
):
157 @param account - a SDN account
159 classifier_list
= list()
160 classifier_name
= self
.sdnodl
.create_sfc_classifier(account
,vnffg_classifier
)
161 classifier_list
.append(classifier_name
)
162 return classifier_list
164 @rwstatus(ret_on_failure
=[None])
165 def do_terminate_vnffg_classifier(self
, account
, vnffg_classifier_name
):
169 @param account - a SDN account
171 self
.sdnodl
.terminate_sfc_classifier(account
,vnffg_classifier_name
)
176 Create SFF object to hold SFF related details
179 def __init__(self
,sff_name
, mgmt_address
, mgmt_port
, dp_address
, dp_port
,sff_dp_name
, sff_br_name
=''):
181 self
.ip
= mgmt_address
182 self
.sff_rest_port
= mgmt_port
183 self
.sff_port
= dp_port
184 self
.dp_name
= sff_dp_name
185 self
.dp_ip
= dp_address
186 self
.br_name
= sff_br_name
187 self
.sf_dp_list
= list()
189 def add_sf_dp_to_sff(self
,sf_dp
):
190 self
.sf_dp_list
.append(sf_dp
)
193 return 'Name:{},Bridge Name:{}, IP: {}, SF List: {}'.format(self
.dp_name
,self
.br_name
, self
.ip
, self
.sf_dp_list
)
195 class SfDpLocator(object):
197 Create Service Function Data Plane Locator related Object to hold details related to each DP Locator endpoint
199 def __init__(self
,name
,sfdp_id
,vnfr_name
,vm_id
):
201 self
.port_id
= sfdp_id
202 self
.vnfr_name
= vnfr_name
205 self
.ovsdb_tp_name
= None
207 def _update_sff_name(self
,sff_name
):
208 self
.sff_name
= sff_name
210 def _update_vnf_params(self
,service_function_type
,address
, port
,transport_type
):
211 self
.service_function_type
= service_function_type
212 self
.address
= address
214 self
.transport_type
= "service-locator:{}".format(transport_type
)
217 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
)
219 class SdnOdl(object):
221 SDN ODL Class to support REST based API calls
225 def _network_topology_path(self
):
226 return 'restconf/operational/network-topology:network-topology'
229 def _node_inventory_path(self
):
230 return 'restconf/operational/opendaylight-inventory:nodes'
232 def _network_topology_rest_url(self
,account
):
233 return '{}/{}'.format(account
.odl
.url
,self
._network
_topology
_path
)
235 def _node_inventory_rest_url(self
,account
):
236 return '{}/{}'.format(account
.odl
.url
,self
._node
_inventory
_path
)
238 def _get_rest_url(self
,account
, rest_path
):
239 return '{}/{}'.format(account
.odl
.url
,rest_path
)
242 def _get_peer_termination_point(self
,node_inv
,tp_id
):
243 for node
in node_inv
['nodes']['node']:
244 if "node-connector" in node
and len(node
['node-connector']) > 0:
245 for nodec
in node
['node-connector']:
246 if ("flow-node-inventory:name" in nodec
and nodec
["flow-node-inventory:name"] == tp_id
):
247 return(node
['id'], nodec
['id'])
250 def _get_termination_point_mac_address(self
,node_inv
,tp_id
):
251 for node
in node_inv
['nodes']['node']:
252 if "node-connector" in node
and len(node
['node-connector']) > 0:
253 for nodec
in node
['node-connector']:
254 if ("flow-node-inventory:name" in nodec
and nodec
["flow-node-inventory:name"] == tp_id
):
255 return nodec
.get("flow-node-inventory:hardware-address")
257 def _add_host(self
,ntwk
,node
,term_point
,vmid
,node_inv
):
258 for ntwk_node
in ntwk
.node
:
259 if ntwk_node
.node_id
== vmid
:
262 ntwk_node
= ntwk
.node
.add()
263 if "ovsdb:bridge-name" in node
:
264 ntwk_node
.rw_node_attributes
.ovs_bridge_name
= node
["ovsdb:bridge-name"]
265 ntwk_node
.node_id
= vmid
266 intf_id
= [res
for res
in term_point
['ovsdb:interface-external-ids'] if res
['external-id-key'] == 'iface-id']
268 ntwk_node_tp
= ntwk_node
.termination_point
.add()
269 ntwk_node_tp
.tp_id
= intf_id
[0]['external-id-value']
270 att_mac
= [res
for res
in term_point
['ovsdb:interface-external-ids'] if res
['external-id-key'] == 'attached-mac']
272 ntwk_node_tp
.l2_termination_point_attributes
.mac_address
= att_mac
[0]['external-id-value']
273 peer_node
,peer_node_tp
= self
._get
_peer
_termination
_point
(node_inv
,term_point
['tp-id'])
274 if peer_node
and peer_node_tp
:
275 nw_lnk
= ntwk
.link
.add()
276 nw_lnk
.source
.source_tp
= ntwk_node_tp
.tp_id
277 nw_lnk
.source
.source_node
= ntwk_node
.node_id
278 nw_lnk
.destination
.dest_tp
= term_point
['tp-id']
279 nw_lnk
.destination
.dest_node
= node
['node-id']
280 nw_lnk
.link_id
= peer_node_tp
+ '-' + 'source'
282 nw_lnk
= ntwk
.link
.add()
283 nw_lnk
.source
.source_tp
= term_point
['tp-id']
284 nw_lnk
.source
.source_node
= node
['node-id']
285 nw_lnk
.destination
.dest_tp
= ntwk_node_tp
.tp_id
286 nw_lnk
.destination
.dest_node
= ntwk_node
.node_id
287 nw_lnk
.link_id
= peer_node_tp
+ '-' + 'dest'
289 def _get_address_from_node_inventory(self
,node_inv
,node_id
):
290 for node
in node_inv
['nodes']['node']:
291 if node
['id'] == node_id
:
292 return node
["flow-node-inventory:ip-address"]
295 def _fill_network_list(self
,nw_topo
,node_inventory
):
297 Fill Topology related information
299 nwtop
= RwTl
.YangData_IetfNetwork()
301 for topo
in nw_topo
['network-topology']['topology']:
302 if ('node' in topo
and len(topo
['node']) > 0):
303 ntwk
= nwtop
.network
.add()
304 ntwk
.network_id
= topo
['topology-id']
305 ntwk
.server_provided
= True
306 for node
in topo
['node']:
307 if ('termination-point' in node
and len(node
['termination-point']) > 0):
308 ntwk_node
= ntwk
.node
.add()
309 ntwk_node
.node_id
= node
['node-id']
310 addr
= self
._get
_address
_from
_node
_inventory
(node_inventory
,ntwk_node
.node_id
)
312 ntwk_node
.l2_node_attributes
.management_address
.append(addr
)
313 for term_point
in node
['termination-point']:
314 ntwk_node_tp
= ntwk_node
.termination_point
.add()
315 ntwk_node_tp
.tp_id
= term_point
['tp-id']
316 mac_address
= self
._get
_termination
_point
_mac
_address
(node_inventory
,term_point
['tp-id'])
318 ntwk_node_tp
.l2_termination_point_attributes
.mac_address
= mac_address
319 if 'ovsdb:interface-external-ids' in term_point
:
320 vm_id
= [res
for res
in term_point
['ovsdb:interface-external-ids'] if res
['external-id-key'] == 'vm-id']
322 vmid
= vm_id
[0]['external-id-value']
323 self
._add
_host
(ntwk
,node
,term_point
,vmid
,node_inventory
)
324 if ('link' in topo
and len(topo
['link']) > 0):
325 for link
in topo
['link']:
326 nw_link
= ntwk
.link
.add()
327 if 'destination' in link
:
328 nw_link
.destination
.dest_tp
= link
['destination'].get('dest-tp')
329 nw_link
.destination
.dest_node
= link
['destination'].get('dest-node')
331 nw_link
.source
.source_node
= link
['source'].get('source-node')
332 nw_link
.source
.source_tp
= link
['source'].get('source-tp')
333 nw_link
.link_id
= link
.get('link-id')
337 def validate_account_creds(self
, account
):
339 Validate the SDN account credentials by accessing the rest API using the provided credentials
341 status
= RwsdnalYang
.YangData_RwProject_Project_SdnAccounts_SdnAccountList_ConnectionStatus()
342 url
= '{}/{}'.format(account
.odl
.url
,"restconf")
344 r
=requests
.get(url
,auth
=(account
.odl
.username
,account
.odl
.password
))
346 except requests
.exceptions
.HTTPError
as e
:
347 msg
= "SdnOdlPlugin: SDN account credential validation failed. Exception: %s", str(e
)
350 status
.status
= "failure"
351 status
.details
= "Invalid Credentials: %s" % str(e
)
352 except Exception as e
:
353 msg
= "SdnPdlPlugin: SDN connection failed. Exception: %s", str(e
)
356 status
.status
= "failure"
357 status
.details
= "Connection Failed (Invlaid URL): %s" % str(e
)
359 status
.status
= "success"
360 status
.details
= "Connection was successful"
364 def get_network_list(self
, account
):
366 Get the networks details from ODL
368 url
= self
._network
_topology
_rest
_url
(account
)
369 r
=requests
.get(url
,auth
=(account
.odl
.username
,account
.odl
.password
))
373 url
= self
._node
_inventory
_rest
_url
(account
)
374 r
= requests
.get(url
,auth
=(account
.odl
.username
,account
.odl
.password
))
376 node_inventory
= r
.json()
377 return self
._fill
_network
_list
(nw_topo
,node_inventory
)
380 def _service_functions_path(self
):
381 return 'restconf/config/service-function:service-functions'
384 def _service_function_path(self
):
385 return 'restconf/config/service-function:service-functions/service-function/{}'
388 def _service_function_forwarders_path(self
):
389 return 'restconf/config/service-function-forwarder:service-function-forwarders'
392 def _service_function_forwarder_path(self
):
393 return 'restconf/config/service-function-forwarder:service-function-forwarders/service-function-forwarder/{}'
396 def _service_function_chains_path(self
):
397 return 'restconf/config/service-function-chain:service-function-chains'
400 def _service_function_chain_path(self
):
401 return 'restconf/config/service-function-chain:service-function-chains/service-function-chain/{}'
404 def _sfp_metadata_path(self
):
405 return 'restconf/config/service-function-path-metadata:service-function-metadata/context-metadata/{}'
408 def _sfps_metadata_path(self
):
409 return 'restconf/config/service-function-path-metadata:service-function-metadata'
412 def _sfps_path(self
):
413 return 'restconf/config/service-function-path:service-function-paths'
417 return 'restconf/config/service-function-path:service-function-paths/service-function-path/{}'
421 def _create_rsp_path(self
):
422 return 'restconf/operations/rendered-service-path:create-rendered-path'
425 def _delete_rsp_path(self
):
426 return 'restconf/operations/rendered-service-path:delete-rendered-path'
430 def _get_rsp_paths(self
):
431 return 'restconf/operational/rendered-service-path:rendered-service-paths'
434 def _get_rsp_path(self
):
435 return 'restconf/operational/rendered-service-path:rendered-service-paths/rendered-service-path/{}'
438 def _access_list_path(self
):
439 return 'restconf/config/ietf-access-control-list:access-lists/acl/{}'
442 def _service_function_classifier_path(self
):
443 return 'restconf/config/service-function-classifier:service-function-classifiers/service-function-classifier/{}'
446 def _access_lists_path(self
):
447 return 'restconf/config/ietf-access-control-list:access-lists'
450 def _service_function_classifiers_path(self
):
451 return 'restconf/config/service-function-classifier:service-function-classifiers'
454 def _create_sf(self
,account
,vnffg_chain
,sf_dp_list
):
458 for vnf
in vnffg_chain
.vnf_chain_path
:
459 for vnfr
in vnf
.vnfr_ids
:
460 sf_url
= self
._get
_rest
_url
(account
,self
._service
_function
_path
.format(vnfr
.vnfr_name
))
462 r
=requests
.get(sf_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'})
463 # If the SF is not found; create new SF
464 if r
.status_code
== 200:
465 logger
.info("SF with name %s is already present in ODL. Skipping update", vnfr
.vnfr_name
)
467 elif r
.status_code
!= 404:
471 sf_dict
['name'] = vnfr
.vnfr_name
472 sf_dict
['nsh-aware'] = vnf
.nsh_aware
473 sf_dict
['type'] = vnf
.service_function_type
474 sf_dict
['ip-mgmt-address'] = vnfr
.mgmt_address
475 sf_dict
['rest-uri'] = 'http://{}:{}'.format(vnfr
.mgmt_address
, vnfr
.mgmt_port
)
477 sf_dict
['sf-data-plane-locator'] = list()
478 for vdu
in vnfr
.vdu_list
:
480 if vdu
.port_id
in sf_dp_list
.keys():
481 sf_dp_entry
= sf_dp_list
[vdu
.port_id
]
482 sf_dp
['name'] = sf_dp_entry
.name
483 sf_dp
['ip'] = vdu
.address
484 sf_dp
['port'] = vdu
.port
485 sf_dp
['transport'] = "service-locator:{}".format(vnf
.transport_type
)
487 sf_dp
['service-function-forwarder'] = vnfr
.sff_name
489 sff_name
= sf_dp_entry
.sff_name
491 logger
.error("SFF not found for port %s in SF %s", vdu
.port_id
, vnfr
.vnfr_name
)
492 sf_dp
['service-function-forwarder'] = sff_name
493 sf_dp
['service-function-ovs:ovs-port'] = dict()
494 if sf_dp_entry
.ovsdb_tp_name
is not None:
495 sf_dp
['service-function-ovs:ovs-port']['port-id'] = sf_dp_entry
.ovsdb_tp_name
496 sf_dict
['sf-data-plane-locator'].append(sf_dp
)
498 logger
.error("Port %s not found in SF DP list",vdu
.port_id
)
500 sf_json
['service-function'] = sf_dict
501 sf_data
= json
.dumps(sf_json
)
502 sf_url
= self
._get
_rest
_url
(account
,self
._service
_function
_path
.format(vnfr
.vnfr_name
))
505 r
=requests
.put(sf_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sf_data
)
509 def _create_sff(self
,account
,vnffg_chain
,sff
):
513 #sff_dp_name = "SFF1" + '-' + 'DP1'
514 sff_dp_name
= sff
.dp_name
516 sff_url
= self
._get
_rest
_url
(account
,self
._service
_function
_forwarder
_path
.format(sff
.name
))
518 r
=requests
.get(sff_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'})
519 # If the SFF is not found; create new SF
520 if r
.status_code
== 200:
521 logger
.info("SFF with name %s is already present in ODL. Skipping full update", sff
.name
)
524 for sf_dp
in sff
.sf_dp_list
:
525 for sff_sf
in sff_dict
['service-function-forwarder'][0]['service-function-dictionary']:
526 if sf_dp
.vnfr_name
== sff_sf
['name']:
527 logger
.info("SF with name %s is already found in SFF %s SF Dictionay. Skipping update",sf_dp
.vnfr_name
,sff
.name
)
530 logger
.info("SF with name %s is not found in SFF %s SF Dictionay",sf_dp
.vnfr_name
, sff
.name
)
534 sff_sf_dict
['name'] = sf_dp
.vnfr_name
536 # Below two lines are enabled only for ODL Beryillium
537 sff_sf_dp_loc
['sff-dpl-name'] = sff_dp_name
538 sff_sf_dp_loc
['sf-dpl-name'] = sf_dp
.name
540 sff_sf_dict
['sff-sf-data-plane-locator'] = sff_sf_dp_loc
541 sff_dict
['service-function-forwarder'][0]['service-function-dictionary'].append(sff_sf_dict
)
542 if sff_updated
is True:
543 sff_data
= json
.dumps(sff_dict
)
545 r
=requests
.put(sff_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sff_data
)
548 elif r
.status_code
!= 404:
553 sff_dp_ip
= sff
.dp_ip
554 sff_port
= sff
.sff_port
556 sff_rest_port
= sff
.sff_rest_port
558 if sff_rest_based
is False:
559 sff_bridge_name
= sff
.br_name
560 sff_ovs_op
= {"key": "flow",
564 "dst-port": sff_port
,
571 sff_dict
['name'] = sff_name
572 sff_dict
['service-node'] = ''
573 sff_dict
['ip-mgmt-address'] = sff_ip
575 sff_dict
['rest-uri'] = 'http://{}:{}'.format(sff_ip
, sff_rest_port
)
577 sff_dict
['service-function-forwarder-ovs:ovs-bridge'] = {"bridge-name": sff_bridge_name
}
578 sff_dict
['service-function-dictionary'] = list()
579 for sf_dp
in sff
.sf_dp_list
:
582 sff_sf_dict
['name'] = sf_dp
.vnfr_name
584 # Below set of lines are reqd for Lithium
585 #sff_sf_dict['type'] = sf_dp.service_function_type
586 #sff_sf_dp_loc['ip'] = sf_dp.address
587 #sff_sf_dp_loc['port'] = sf_dp.port
588 #sff_sf_dp_loc['transport'] = sf_dp.transport_type
589 #sff_sf_dp_loc['service-function-forwarder-ovs:ovs-bridge'] = {}
591 # Below two lines are enabled only for ODL Beryillium
592 sff_sf_dp_loc
['sff-dpl-name'] = sff_dp_name
593 sff_sf_dp_loc
['sf-dpl-name'] = sf_dp
.name
595 sff_sf_dict
['sff-sf-data-plane-locator'] = sff_sf_dp_loc
596 sff_dict
['service-function-dictionary'].append(sff_sf_dict
)
598 sff_dict
['sff-data-plane-locator'] = list()
601 sff_dp
['name'] = sff_dp_name
602 dp_loc
['ip'] = sff_dp_ip
603 dp_loc
['port'] = sff_port
604 dp_loc
['transport'] = 'service-locator:vxlan-gpe'
605 sff_dp
['data-plane-locator'] = dp_loc
606 if sff_rest_based
is False:
607 sff_dp
['service-function-forwarder-ovs:ovs-options'] = sff_ovs_op
608 #sff_dp["service-function-forwarder-ovs:ovs-bridge"] = {'bridge-name':sff_bridge_name}
609 sff_dp
["service-function-forwarder-ovs:ovs-bridge"] = {}
610 sff_dict
['sff-data-plane-locator'].append(sff_dp
)
612 sff_json
['service-function-forwarder'] = sff_dict
613 sff_data
= json
.dumps(sff_json
)
615 r
=requests
.put(sff_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sff_data
)
618 def _create_sfc(self
,account
,vnffg_chain
):
622 sfc_dict
['name'] = vnffg_chain
.name
623 sfc_dict
['sfc-service-function'] = list()
624 vnf_chain_list
= sorted(vnffg_chain
.vnf_chain_path
, key
= lambda x
: x
.order
)
625 for vnf
in vnf_chain_list
:
627 sfc_sf_dict
['name'] = vnf
.service_function_type
628 sfc_sf_dict
['type'] = vnf
.service_function_type
629 sfc_sf_dict
['order'] = vnf
.order
630 sfc_dict
['sfc-service-function'].append(sfc_sf_dict
)
631 sfc_json
['service-function-chain'] = sfc_dict
632 sfc_data
= json
.dumps(sfc_json
)
633 sfc_url
= self
._get
_rest
_url
(account
,self
._service
_function
_chain
_path
.format(vnffg_chain
.name
))
636 r
=requests
.put(sfc_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sfc_data
)
639 def _create_sfp_metadata(self
,account
,sfc_classifier
):
640 " Create SFP metadata"
643 sfp_meta_dict
['name'] = sfc_classifier
.name
644 if sfc_classifier
.vnffg_metadata
.ctx1
:
645 sfp_meta_dict
['context-header1'] = sfc_classifier
.vnffg_metadata
.ctx1
646 if sfc_classifier
.vnffg_metadata
.ctx2
:
647 sfp_meta_dict
['context-header2'] = sfc_classifier
.vnffg_metadata
.ctx2
648 if sfc_classifier
.vnffg_metadata
.ctx3
:
649 sfp_meta_dict
['context-header3'] = sfc_classifier
.vnffg_metadata
.ctx3
650 if sfc_classifier
.vnffg_metadata
.ctx4
:
651 sfp_meta_dict
['context-header4'] = sfc_classifier
.vnffg_metadata
.ctx4
653 sfp_meta_json
['context-metadata'] = sfp_meta_dict
654 sfp_meta_data
= json
.dumps(sfp_meta_json
)
655 sfp_meta_url
= self
._get
_rest
_url
(account
,self
._sfp
_metadata
_path
.format(sfc_classifier
.name
))
658 r
=requests
.put(sfp_meta_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sfp_meta_data
)
661 def _create_sfp(self
,account
,vnffg_chain
, sym_chain
=False,classifier_name
=None,vnffg_metadata_name
=None):
665 sfp_dict
['name'] = vnffg_chain
.name
666 sfp_dict
['service-chain-name'] = vnffg_chain
.name
667 sfp_dict
['symmetric'] = sym_chain
668 sfp_dict
['transport-type'] = 'service-locator:vxlan-gpe'
669 if vnffg_metadata_name
:
670 sfp_dict
['context-metadata'] = vnffg_metadata_name
672 sfp_dict
['classifier'] = classifier_name
674 sfp_json
['service-function-path'] = sfp_dict
675 sfp_data
= json
.dumps(sfp_json
)
676 sfp_url
= self
._get
_rest
_url
(account
,self
._sfp
_path
.format(vnffg_chain
.name
))
679 r
=requests
.put(sfp_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sfp_data
)
682 def _create_rsp(self
,account
,vnffg_chain_name
, sym_chain
=True):
686 rsp_json
['input'] = {}
687 rsp_input
['name'] = vnffg_chain_name
688 rsp_input
['parent-service-function-path'] = vnffg_chain_name
689 rsp_input
['symmetric'] = sym_chain
691 rsp_json
['input'] = rsp_input
692 rsp_data
= json
.dumps(rsp_json
)
693 self
._rsp
_data
= rsp_json
694 rsp_url
= self
._get
_rest
_url
(account
,self
._create
_rsp
_path
)
697 r
=requests
.post(rsp_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=rsp_data
)
700 output_json
= r
.json()
701 return output_json
['output']['name']
703 def _get_sff_list_for_chain(self
, account
,sf_dp_list
):
705 Get List of all SFF that needs to be created based on VNFs included in VNFFG chain.
709 if sf_dp_list
is None:
710 logger
.error("VM List for vnffg chain is empty while trying to get SFF list")
711 url
= self
._network
_topology
_rest
_url
(account
)
712 r
=requests
.get(url
,auth
=(account
.odl
.username
,account
.odl
.password
))
716 for topo
in nw_topo
['network-topology']['topology']:
717 if ('node' in topo
and len(topo
['node']) > 0):
718 for node
in topo
['node']:
719 if ('termination-point' in node
and len(node
['termination-point']) > 0):
720 for term_point
in node
['termination-point']:
721 if 'ovsdb:interface-external-ids' in term_point
:
722 vm_id
= [res
for res
in term_point
['ovsdb:interface-external-ids'] if res
['external-id-key'] == 'vm-id']
725 vmid
= vm_id
[0]['external-id-value']
726 intf_id
= [res
for res
in term_point
['ovsdb:interface-external-ids'] if res
['external-id-key'] == 'iface-id']
727 if len(intf_id
) == 0:
729 intfid
= intf_id
[0]['external-id-value']
730 if intfid
not in sf_dp_list
.keys():
732 if sf_dp_list
[intfid
].vm_id
!= vmid
:
733 logger
.error("Intf ID %s is not present in VM %s", intfid
, vmid
)
735 sf_dp_list
[intfid
].ovsdb_tp_name
= term_point
['ovsdb:name']
737 if 'ovsdb:managed-by' in node
:
738 rr
=re
.search('network-topology:node-id=\'([-\w\:\/]*)\'',node
['ovsdb:managed-by'])
739 node_id
= rr
.group(1)
740 ovsdb_node
= [node
for node
in topo
['node'] if node
['node-id'] == node_id
]
742 if 'ovsdb:connection-info' in ovsdb_node
[0]:
743 sff_ip
= ovsdb_node
[0]['ovsdb:connection-info']['local-ip']
744 sff_br_name
= node
['ovsdb:bridge-name']
745 sff_br_uuid
= node
['ovsdb:bridge-uuid']
748 if 'ovsdb:openvswitch-other-configs' in ovsdb_node
[0]:
749 for other_key
in ovsdb_node
[0]['ovsdb:openvswitch-other-configs']:
750 if other_key
['other-config-key'] == 'local_ip':
751 local_ip_str
= other_key
['other-config-value']
752 sff_dp_ip
= local_ip_str
.split(',')[0]
755 sff_name
= socket
.getfqdn(sff_ip
)
756 if sff_br_uuid
in sff_list
:
757 sff_list
[sff_name
].add_sf_dp_to_sff(sf_dp_list
[intfid
])
758 sf_dp_list
[intfid
]._update
_sff
_name
(sff_name
)
760 sff_dp_ip
= sff_ip
#overwrite sff_dp_ip to SFF ip for now
761 sff_list
[sff_name
] = Sff(sff_name
,sff_ip
,6000, sff_dp_ip
, 4790,sff_br_uuid
,sff_br_name
)
762 sf_dp_list
[intfid
]._update
_sff
_name
(sff_name
)
763 sff_list
[sff_name
].add_sf_dp_to_sff(sf_dp_list
[intfid
])
767 def _get_sf_dp_list_for_chain(self
,account
,vnffg_chain
):
769 Get list of all Service Function Data Plane Locators present in VNFFG
770 useful for easy reference while creating SF and SFF
773 for vnf
in vnffg_chain
.vnf_chain_path
:
774 for vnfr
in vnf
.vnfr_ids
:
775 for vdu
in vnfr
.vdu_list
:
776 sfdp
= SfDpLocator(vdu
.name
,vdu
.port_id
,vnfr
.vnfr_name
, vdu
.vm_id
)
777 sfdp
._update
_vnf
_params
(vnf
.service_function_type
, vdu
.address
, vdu
.port
, vnf
.transport_type
)
779 sfdp
._update
_sff
_name
(vnfr
.sff_name
)
780 sfdp_list
[vdu
.port_id
] = sfdp
783 def create_sfc(self
, account
, vnffg_chain
):
789 sf_dp_list
= self
._get
_sf
_dp
_list
_for
_chain
(account
,vnffg_chain
)
791 if sff_rest_based
is False and len(vnffg_chain
.sff
) == 0:
792 # Get the list of all SFFs required for vnffg chain
793 sff_list
= self
._get
_sff
_list
_for
_chain
(account
,sf_dp_list
)
795 for sff
in vnffg_chain
.sff
:
796 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
)
797 for _
,sf_dp
in sf_dp_list
.items():
798 if sf_dp
.sff_name
and sf_dp
.sff_name
== sff
.name
:
799 sff_list
[sff
.name
].add_sf_dp_to_sff(sf_dp
)
801 #Create all the SF in VNFFG chain
802 self
._create
_sf
(account
,vnffg_chain
,sf_dp_list
)
804 for _
,sff
in sff_list
.items():
805 self
._create
_sff
(account
,vnffg_chain
,sff
)
808 self
._create
_sfc
(account
,vnffg_chain
)
810 self
._create
_sfp
(account
,vnffg_chain
,classifier_name
=vnffg_chain
.classifier_name
,
811 vnffg_metadata_name
=vnffg_chain
.classifier_name
)
813 ## Update to SFF could have deleted some RSP; so get list of SFP and
814 ## check RSP exists for same and create any as necessary
815 #rsp_name = self._create_rsp(account,vnffg_chain)
817 self
._create
_all
_rsps
(account
)
818 self
._recreate
_all
_sf
_classifiers
(account
)
819 return vnffg_chain
.name
821 def _recreate_all_sf_classifiers(self
,account
):
823 Re create all SF classifiers
825 sfcl_url
= self
._get
_rest
_url
(account
,self
._service
_function
_classifiers
_path
)
828 r
=requests
.get(sfcl_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'})
829 if r
.status_code
== 200:
832 elif r
.status_code
== 404:
837 #Delete the classifiers and re-add same back
838 r
=requests
.delete(sfcl_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
843 sfcl_data
= json
.dumps(sfcl_json
)
844 r
=requests
.put(sfcl_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sfcl_data
)
847 def _create_all_rsps(self
,account
):
849 Create all the RSPs for SFP found
851 sfps_url
= self
._get
_rest
_url
(account
,self
._sfps
_path
)
852 r
=requests
.get(sfps_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'})
855 if 'service-function-path' in sfps_json
['service-function-paths']:
856 for sfp
in sfps_json
['service-function-paths']['service-function-path']:
857 rsp_url
= self
._get
_rest
_url
(account
,self
._get
_rsp
_path
.format(sfp
['name']))
858 r
= requests
.get(rsp_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'})
859 if r
.status_code
== 404:
861 logger
.info("Creating RSP for Service Path with name %s",sfp
['name'])
862 self
._create
_rsp
(account
,sfp
['name'])
864 def delete_all_sf(self
, account
):
866 sf_url
= self
._get
_rest
_url
(account
,self
._service
_functions
_path
)
868 r
=requests
.delete(sf_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
872 def delete_all_sff(self
, account
):
873 "Delete all the SFFs"
874 sff_url
= self
._get
_rest
_url
(account
,self
._service
_function
_forwarders
_path
)
876 r
=requests
.delete(sff_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
879 def delete_all_sfc(self
, account
):
880 "Delete all the SFCs"
881 sfc_url
= self
._get
_rest
_url
(account
,self
._service
_function
_chains
_path
)
883 r
=requests
.delete(sfc_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
886 def delete_all_sfp_metadata(self
, account
):
887 "Delete all the SFPs metadata"
888 sfp_metadata_url
= self
._get
_rest
_url
(account
,self
._sfps
_metadata
_path
)
889 print(sfp_metadata_url
)
890 r
=requests
.delete(sfp_metadata_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
893 def delete_all_sfp(self
, account
):
894 "Delete all the SFPs"
895 sfp_url
= self
._get
_rest
_url
(account
,self
._sfps
_path
)
897 r
=requests
.delete(sfp_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
900 def delete_all_rsp(self
, account
):
902 #rsp_list = self.get_rsp_list(account)
903 url
= self
._get
_rest
_url
(account
,self
._get
_rsp
_paths
)
905 r
= requests
.get(url
,auth
=(account
.odl
.username
,account
.odl
.password
))
910 #for vnffg in rsp_list.vnffg_rendered_path:
911 for sfc_rsp
in rsp_list
['rendered-service-paths']['rendered-service-path']:
914 rsp_json
['input'] = {}
915 rsp_input
['name'] = sfc_rsp
['name']
917 rsp_json
['input'] = rsp_input
918 rsp_data
= json
.dumps(rsp_json
)
919 self
._rsp
_data
= rsp_json
920 rsp_url
= self
._get
_rest
_url
(account
,self
._delete
_rsp
_path
)
924 r
=requests
.post(rsp_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=rsp_data
)
927 #output_json = r.json()
928 #return output_json['output']['name']
930 def terminate_all_sfc(self
, account
):
931 "Terminate SFC chain"
932 self
.delete_all_rsp(account
)
933 self
.delete_all_sfp(account
)
934 self
.delete_all_sfc(account
)
935 self
.delete_all_sff(account
)
936 self
.delete_all_sf(account
)
938 def _fill_rsp_list(self
,sfc_rsp_list
,sff_list
):
939 vnffg_rsps
= RwsdnalYang
.YangData_RwProject_Project_VnffgRenderedPaths()
940 for sfc_rsp
in sfc_rsp_list
['rendered-service-paths']['rendered-service-path']:
941 rsp
= vnffg_rsps
.vnffg_rendered_path
.add()
942 rsp
.name
= sfc_rsp
['name']
943 rsp
.path_id
= sfc_rsp
['path-id']
944 for sfc_rsp_hop
in sfc_rsp
['rendered-service-path-hop']:
945 rsp_hop
= rsp
.rendered_path_hop
.add()
946 rsp_hop
.hop_number
= sfc_rsp_hop
['hop-number']
947 rsp_hop
.service_index
= sfc_rsp_hop
['service-index']
948 rsp_hop
.vnfr_name
= sfc_rsp_hop
['service-function-name']
949 rsp_hop
.service_function_forwarder
.name
= sfc_rsp_hop
['service-function-forwarder']
950 for sff
in sff_list
['service-function-forwarders']['service-function-forwarder']:
951 if sff
['name'] == rsp_hop
.service_function_forwarder
.name
:
952 rsp_hop
.service_function_forwarder
.ip_address
= sff
['sff-data-plane-locator'][0]['data-plane-locator']['ip']
953 rsp_hop
.service_function_forwarder
.port
= sff
['sff-data-plane-locator'][0]['data-plane-locator']['port']
958 def get_rsp_list(self
,account
):
961 sff_url
= self
._get
_rest
_url
(account
,self
._service
_function
_forwarders
_path
)
963 r
=requests
.get(sff_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'})
967 url
= self
._get
_rest
_url
(account
,self
._get
_rsp
_paths
)
969 r
= requests
.get(url
,auth
=(account
.odl
.username
,account
.odl
.password
))
972 return self
._fill
_rsp
_list
(r
.json(),sff_list
)
974 def create_sfc_classifier(self
, account
, sfc_classifiers
):
975 "Create SFC Classifiers"
976 self
._create
_sfp
_metadata
(account
,sfc_classifiers
)
977 self
._add
_acl
_rules
(account
, sfc_classifiers
)
978 self
._create
_sf
_classifier
(account
, sfc_classifiers
)
979 return sfc_classifiers
.name
981 def terminate_sfc_classifier(self
, account
, sfc_classifier_name
):
982 "Create SFC Classifiers"
983 self
.delete_all_sfp_metadata(account
)
984 self
._terminate
_sf
_classifier
(account
, sfc_classifier_name
)
985 self
._del
_acl
_rules
(account
, sfc_classifier_name
)
987 def _del_acl_rules(self
,account
,sfc_classifier_name
):
988 " Terminate SF classifiers"
989 acl_url
= self
._get
_rest
_url
(account
,self
._access
_lists
_path
)
991 r
=requests
.delete(acl_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
994 def _terminate_sf_classifier(self
,account
,sfc_classifier_name
):
995 " Terminate SF classifiers"
996 sfcl_url
= self
._get
_rest
_url
(account
,self
._service
_function
_classifiers
_path
)
998 r
=requests
.delete(sfcl_url
,auth
=(account
.odl
.username
,account
.odl
.password
))
1001 def _create_sf_classifier(self
,account
,sfc_classifiers
):
1002 " Create SF classifiers"
1003 sf_classifier_json
= {}
1004 sf_classifier_dict
= {}
1005 sf_classifier_dict
['name'] = sfc_classifiers
.name
1006 sf_classifier_dict
['access-list'] = sfc_classifiers
.name
1007 sf_classifier_dict
['scl-service-function-forwarder'] = list()
1011 if sfc_classifiers
.has_field('sff_name') and sfc_classifiers
.sff_name
is not None:
1012 scl_sff_name
= sfc_classifiers
.sff_name
1013 elif sfc_classifiers
.has_field('port_id') and sfc_classifiers
.has_field('vm_id'):
1014 sf_dp
= SfDpLocator(sfc_classifiers
.port_id
, sfc_classifiers
.port_id
,'', sfc_classifiers
.vm_id
)
1016 sf_dp_list
[sfc_classifiers
.port_id
] = sf_dp
1017 self
._get
_sff
_list
_for
_chain
(account
,sf_dp_list
)
1019 if sf_dp
.sff_name
is None:
1020 logger
.error("SFF not found for port %s, VM: %s",sfc_classifiers
.port_id
,sfc_classifiers
.vm_id
)
1022 logger
.info("SFF with name %s found for port %s, VM: %s",sf_dp
.sff_name
, sfc_classifiers
.port_id
,sfc_classifiers
.vm_id
)
1023 scl_sff_name
= sf_dp
.sff_name
1025 rsp_url
= self
._get
_rest
_url
(account
,self
._get
_rsp
_path
.format(sfc_classifiers
.rsp_name
))
1026 r
= requests
.get(rsp_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'})
1027 if r
.status_code
== 200:
1029 if 'rendered-service-path' in rsp_data
and len(rsp_data
['rendered-service-path'][0]['rendered-service-path-hop']) > 0:
1030 scl_sff_name
= rsp_data
['rendered-service-path'][0]['rendered-service-path-hop'][0]['service-function-forwarder']
1032 logger
.debug("SFF for classifer %s found is %s",sfc_classifiers
.name
, scl_sff_name
)
1033 scl_sff
['name'] = scl_sff_name
1034 #scl_sff['interface'] = sff_intf_name
1035 sf_classifier_dict
['scl-service-function-forwarder'].append(scl_sff
)
1037 sf_classifier_json
['service-function-classifier'] = sf_classifier_dict
1039 sfcl_data
= json
.dumps(sf_classifier_json
)
1040 sfcl_url
= self
._get
_rest
_url
(account
,self
._service
_function
_classifier
_path
.format(sfc_classifiers
.name
))
1043 r
=requests
.put(sfcl_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=sfcl_data
)
1044 r
.raise_for_status()
1046 def _add_acl_rules(self
, account
,sfc_classifiers
):
1048 access_list_json
= {}
1049 access_list_dict
= {}
1050 acl_entry_list
= list()
1052 for acl_rule
in sfc_classifiers
.match_attributes
:
1054 acl_entry
['rule-name'] = acl_rule
.name
1055 acl_entry
['actions'] = {}
1056 #acl_entry['actions']['netvirt-sfc-acl:rsp-name'] = sfc_classifiers.rsp_name
1057 acl_entry
['actions']['service-function-acl:rendered-service-path'] = sfc_classifiers
.rsp_name
1060 for field
, value
in acl_rule
.as_dict().items():
1061 if field
== 'ip_proto':
1062 matches
['protocol'] = value
1063 elif field
== 'source_ip_address':
1064 matches
['source-ipv4-network'] = value
1065 elif field
== 'destination_ip_address':
1066 matches
['destination-ipv4-network'] = value
1067 elif field
== 'source_port':
1068 matches
['source-port-range'] = {'lower-port':value
, 'upper-port':value
}
1069 elif field
== 'destination_port':
1070 matches
['destination-port-range'] = {'lower-port':value
, 'upper-port':value
}
1071 acl_entry
['matches'] = matches
1072 acl_entry_list
.append(acl_entry
)
1073 acl_list_dict
['ace'] = acl_entry_list
1074 access_list_dict
['acl-name'] = sfc_classifiers
.name
1075 access_list_dict
['access-list-entries'] = acl_list_dict
1076 access_list_json
['acl'] = access_list_dict
1078 acl_data
= json
.dumps(access_list_json
)
1079 acl_url
= self
._get
_rest
_url
(account
,self
._access
_list
_path
.format(sfc_classifiers
.name
))
1082 r
=requests
.put(acl_url
,auth
=(account
.odl
.username
,account
.odl
.password
),headers
={'content-type': 'application/json'}, data
=acl_data
)
1083 r
.raise_for_status()