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.
21 gi
.require_version('RwSdn', '1.0')
23 import rift
.rwcal
.openstack
as openstack_drv
24 from rift
.rwcal
.openstack
import session
as sess_drv
25 from rift
.rwcal
.openstack
import keystone
as ks_drv
26 from rift
.rwcal
.openstack
import neutron
as nt_drv
27 from rift
.rwcal
.openstack
import portchain
as port_drv
32 import rift
.cal
.rwcal_status
as rwcal_status
34 import neutronclient
.common
.exceptions
as NeutronException
35 import keystoneclient
.exceptions
as KeystoneExceptions
38 from gi
.repository
import (
44 rwstatus_exception_map
= {IndexError: RwTypes
.RwStatus
.NOTFOUND
,
45 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
46 NotImplementedError: RwTypes
.RwStatus
.NOT_IMPLEMENTED
, }
48 rwstatus
= rw_status
.rwstatus_from_exc_map(rwstatus_exception_map
)
49 rwcalstatus
= rwcal_status
.rwcalstatus_from_exc_map(rwstatus_exception_map
)
52 class OpenstackSdnOperationFailure(Exception):
55 class UninitializedPluginError(Exception):
58 class OpenstackL2PortChainingDriver(object):
60 Driver for openstack keystone and neutron
62 def __init__(self
, logger
= None, **kwargs
):
64 OpenstackDriver Driver constructor
66 logger: (instance of logging.Logger)
67 kwargs: A dictionary of
69 username (string) : Username for project/tenant.
70 password (string) : Password
71 auth_url (string) : Keystone Authentication URL.
72 project (string) : Openstack project name
73 cert_validate (boolean, optional) : In case of SSL/TLS connection if certificate validation is required or not.
74 user_domain : Domain name for user
75 project_domain : Domain name for project
81 self
.log
= logging
.getLogger('rwsdn.openstack.driver')
82 self
.log
.setLevel(logging
.DEBUG
)
86 args
= dict(auth_url
= kwargs
['auth_url'],
87 username
= kwargs
['username'],
88 password
= kwargs
['password'],
89 project_name
= kwargs
['project'],
90 project_domain_name
= kwargs
['project_domain'] if 'project_domain' in kwargs
else None,
91 user_domain_name
= kwargs
['user_domain'] if 'user_domain' in kwargs
else None,)
93 self
.auth_url
= kwargs
['auth_url']
94 cert_validate
= kwargs
['cert_validate'] if 'cert_validate' in kwargs
else False
95 region
= kwargs
['region_name'] if 'region_name' in kwargs
else False
97 discover
= ks_drv
.KeystoneVersionDiscover(kwargs
['auth_url'], logger
= self
.log
)
98 (major
, minor
) = discover
.get_version()
100 self
.sess_drv
= sess_drv
.SessionDriver(auth_method
= 'password',
101 version
= str(major
),
102 cert_validate
= cert_validate
,
106 self
.neutron_drv
= nt_drv
.NeutronDriver(self
.sess_drv
,
107 region_name
= region
,
110 self
.portchain_drv
= port_drv
.L2PortChainDriver(self
.sess_drv
,
111 self
.neutron_drv
.neutron_endpoint
,
114 def validate_account_creds(self
):
115 status
= RwsdnalYang
.SdnConnectionStatus()
117 self
.sess_drv
.invalidate_auth_token()
118 self
.sess_drv
.auth_token
119 except KeystoneExceptions
.Unauthorized
as e
:
120 self
.log
.error("Invalid credentials given for SDN account ")
121 status
.status
= "failure"
122 status
.details
= "Invalid Credentials: %s" % str(e
)
124 except KeystoneExceptions
.AuthorizationFailure
as e
:
125 self
.log
.error("Bad authentication URL given for SDN account. Given auth url: %s",
127 status
.status
= "failure"
128 status
.details
= "Invalid auth url: %s" % str(e
)
130 except NeutronException
.NotFound
as e
:
131 status
.status
= "failure"
132 status
.details
= "Neutron exception %s" % str(e
)
134 except openstack_drv
.ValidationError
as e
:
135 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
136 status
.status
= "failure"
137 status
.details
= "Invalid Credentials: %s" % str(e
)
139 except Exception as e
:
140 msg
= "RwsdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
142 status
.status
= "failure"
146 status
.status
= "success"
147 status
.details
= "Connection was successful"
151 def delete_port_chain(self
, port_chain_id
):
154 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
155 port_chain
= result
.json()
156 self
.log
.debug("Port chain result is %s", port_chain
)
157 port_pair_groups
= port_chain
["port_chain"]["port_pair_groups"]
158 self
.portchain_drv
.delete_port_chain(port_chain_id
)
160 # Get port pairs and delete port pair groups
162 self
.log
.debug("Port pair groups during delete is %s", port_pair_groups
)
163 for port_pair_group_id
in port_pair_groups
:
164 result
= self
.portchain_drv
.get_port_pair_group(port_pair_group_id
)
165 port_pair_group
= result
.json()
166 self
.log
.debug("Port pair group result is %s", port_pair_group
)
167 port_pairs
.extend(port_pair_group
["port_pair_group"]["port_pairs"])
168 self
.portchain_drv
.delete_port_pair_group(port_pair_group_id
)
170 self
.log
.debug("Port pairs during delete is %s", port_pairs
)
172 for port_pair_id
in port_pairs
:
173 self
.portchain_drv
.delete_port_pair(port_pair_id
)
175 except Exception as e
:
176 self
.log
.error("Error while delete port chain with id %s, exception %s", port_chain_id
, str(e
))
178 def update_port_chain(self
, port_chain_id
, flow_classifier_list
):
179 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
180 result
.raise_for_status()
181 port_chain
= result
.json()['port_chain']
182 new_flow_classifier_list
= list()
183 if port_chain
and port_chain
['flow_classifiers']:
184 new_flow_classifier_list
.extend(port_chain
['flow_classifiers'])
185 new_flow_classifier_list
.extend(flow_classifier_list
)
186 port_chain_id
= self
.portchain_drv
.update_port_chain(port_chain
['id'], flow_classifiers
=new_flow_classifier_list
)
189 def create_flow_classifer(self
, classifier_name
, classifier_dict
):
190 "Create flow classifier"
191 flow_classifier_id
= self
.portchain_drv
.create_flow_classifier(classifier_name
, classifier_dict
)
192 return flow_classifier_id
194 def delete_flow_classifier(self
, classifier_id
):
195 "Create flow classifier"
197 self
.portchain_drv
.delete_flow_classifier(classifier_id
)
198 except Exception as e
:
199 self
.log
.error("Error while deleting flow classifier with id %s, exception %s", classifier_id
, str(e
))
201 def get_port_chain_list(self
):
202 result
= self
.portchain_drv
.get_port_chain_list()
203 port_chain_list
= result
.json()
204 if 'port_chains' in port_chain_list
:
205 return port_chain_list
['port_chains']
208 class RwsdnAccountDriver(object):
210 Container class per sdn account
212 def __init__(self
, logger
, **kwargs
):
215 self
._driver
= OpenstackL2PortChainingDriver(logger
= self
.log
, **kwargs
)
216 except (KeystoneExceptions
.Unauthorized
, KeystoneExceptions
.AuthorizationFailure
,
217 NeutronException
.NotFound
) as e
:
219 except Exception as e
:
220 self
.log
.error("RwsdnOpenstackPlugin: OpenstackL2PortChainingDriver init failed. Exception: %s" %(str(e
)))
228 class SdnOpenstackPlugin(GObject
.Object
, RwSdn
.Topology
):
232 GObject
.Object
.__init
__(self
)
233 self
.log
= logging
.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin
.instance_num
)
234 self
.log
.setLevel(logging
.DEBUG
)
236 self
._rwlog
_handler
= None
237 self
._account
_drivers
= dict()
238 SdnOpenstackPlugin
.instance_num
+= 1
240 def _use_driver(self
, account
):
241 if self
._rwlog
_handler
is None:
242 raise UninitializedPluginError("Must call init() in SDN plugin before use.")
244 if account
.name
not in self
._account
_drivers
:
245 self
.log
.debug("Creating SDN OpenstackDriver")
246 kwargs
= dict(username
= account
.openstack
.key
,
247 password
= account
.openstack
.secret
,
248 auth_url
= account
.openstack
.auth_url
,
249 project
= account
.openstack
.tenant
,
250 cert_validate
= account
.openstack
.cert_validate
,
251 user_domain
= account
.openstack
.user_domain
,
252 project_domain
= account
.openstack
.project_domain
,
253 region
= account
.openstack
.region
)
254 drv
= RwsdnAccountDriver(self
.log
, **kwargs
)
255 self
._account
_drivers
[account
.name
] = drv
258 return self
._account
_drivers
[account
.name
].driver
261 def do_init(self
, rwlog_ctx
):
262 self
._rwlog
_handler
= rwlogger
.RwLogger(
263 category
="rw-cal-log",
264 subcategory
="openstack",
267 self
.log
.addHandler(self
._rwlog
_handler
)
268 self
.log
.propagate
= False
270 @rwstatus(ret_on_failure
=[None])
271 def do_validate_sdn_creds(self
, account
):
273 Validates the sdn account credentials for the specified account.
274 Performs an access to the resources using Keystone API. If creds
275 are not valid, returns an error code & reason string
277 @param account - a SDN account
280 Validation Code and Details String
282 status
= RwsdnalYang
.SdnConnectionStatus()
284 drv
= self
._use
_driver
(account
)
285 drv
.validate_account_creds()
287 except openstack_drv
.ValidationError
as e
:
288 self
.log
.error("SdnOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
289 status
.status
= "failure"
290 status
.details
= "Invalid Credentials: %s" % str(e
)
292 except Exception as e
:
293 msg
= "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
295 status
.status
= "failure"
299 status
.status
= "success"
300 status
.details
= "Connection was successful"
304 @rwstatus(ret_on_failure
=[""])
305 def do_create_vnffg_chain(self
, account
, vnffg
):
307 Creates Service Function chain in ODL
309 @param account - a SDN account
312 self
.log
.debug('Received Create VNFFG chain for account {}, chain {}'.format(account
, vnffg
))
313 drv
= self
._use
_driver
(account
)
315 vnf_chain_list
= sorted(vnffg
.vnf_chain_path
, key
= lambda x
: x
.order
)
317 for path
in vnf_chain_list
:
318 if prev_vm_id
and path
.vnfr_ids
[0].vdu_list
[0].vm_id
== prev_vm_id
:
319 prev_entry
= port_list
.pop()
320 port_list
.append((prev_entry
[0], path
.vnfr_ids
[0].vdu_list
[0].port_id
))
323 prev_vm_id
= path
.vnfr_ids
[0].vdu_list
[0].vm_id
324 port_list
.append((path
.vnfr_ids
[0].vdu_list
[0].port_id
, path
.vnfr_ids
[0].vdu_list
[0].port_id
))
325 vnffg_id
= drv
.create_port_chain(vnffg
.name
, port_list
)
329 def do_terminate_vnffg_chain(self
, account
, vnffg_id
):
331 Terminate Service Function chain in ODL
333 @param account - a SDN account
335 self
.log
.debug('Received terminate VNFFG chain for id %s ', vnffg_id
)
336 drv
= self
._use
_driver
(account
)
337 drv
.delete_port_chain(vnffg_id
)
339 @rwstatus(ret_on_failure
=[None])
340 def do_create_vnffg_classifier(self
, account
, vnffg_classifier
):
344 @param account - a SDN account
346 self
.log
.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account
, vnffg_classifier
))
347 protocol_map
= {1: 'ICMP', 6: 'TCP', 17: 'UDP'}
348 flow_classifier_list
= list()
349 drv
= self
._use
_driver
(account
)
350 for rule
in vnffg_classifier
.match_attributes
:
351 classifier_name
= vnffg_classifier
.name
+ '_' + rule
.name
353 for field
, value
in rule
.as_dict().items():
354 if field
== 'ip_proto':
355 flow_dict
['protocol'] = protocol_map
.get(value
, None)
356 elif field
== 'source_ip_address':
357 flow_dict
['source_ip_prefix'] = value
358 elif field
== 'destination_ip_address':
359 flow_dict
['destination_ip_prefix'] = value
360 elif field
== 'source_port':
361 flow_dict
['source_port_range_min'] = value
362 flow_dict
['source_port_range_max'] = value
363 elif field
== 'destination_port':
364 flow_dict
['destination_port_range_min'] = value
365 flow_dict
['destination_port_range_max'] = value
366 if vnffg_classifier
.has_field('port_id'):
367 flow_dict
['logical_source_port'] = vnffg_classifier
.port_id
368 flow_classifier_id
= drv
.create_flow_classifer(classifier_name
, flow_dict
)
369 flow_classifier_list
.append(flow_classifier_id
)
370 drv
.update_port_chain(vnffg_classifier
.rsp_id
, flow_classifier_list
)
371 return flow_classifier_list
373 @rwstatus(ret_on_failure
=[None])
374 def do_terminate_vnffg_classifier(self
, account
, vnffg_classifier_list
):
378 @param account - a SDN account
380 self
.log
.debug('Received terminate VNFFG classifier for id %s ', vnffg_classifier_list
)
381 drv
= self
._use
_driver
(account
)
382 for classifier_id
in vnffg_classifier_list
:
383 drv
.delete_flow_classifier(classifier_id
)
385 @rwstatus(ret_on_failure
=[None])
386 def do_get_vnffg_rendered_paths(self
, account
):
388 Get Rendered Service Path List (SFC)
390 @param account - a SDN account
392 self
.log
.debug('Received get VNFFG rendered path for account %s ', account
)
393 vnffg_rsps
= RwsdnalYang
.VNFFGRenderedPaths()
394 drv
= self
._use
_driver
(account
)
395 port_chain_list
= drv
.get_port_chain_list()
396 for port_chain
in port_chain_list
:
397 #rsp = vnffg_rsps.vnffg_rendered_path.add()
398 #rsp.name = port_chain['name']