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'],
100 (major
, minor
) = discover
.get_version()
102 self
.sess_drv
= sess_drv
.SessionDriver(auth_method
= 'password',
103 version
= str(major
),
104 cert_validate
= cert_validate
,
108 self
.neutron_drv
= nt_drv
.NeutronDriver(self
.sess_drv
,
109 region_name
= region
,
112 self
.portchain_drv
= port_drv
.L2PortChainDriver(self
.sess_drv
,
113 self
.neutron_drv
.neutron_endpoint
,
116 def validate_account_creds(self
):
117 status
= RwsdnalYang
.YangData_RwProject_Project_SdnAccounts_SdnAccountList_ConnectionStatus()
119 self
.sess_drv
.invalidate_auth_token()
120 self
.sess_drv
.auth_token
121 except KeystoneExceptions
.Unauthorized
as e
:
122 self
.log
.error("Invalid credentials given for SDN account ")
123 status
.status
= "failure"
124 status
.details
= "Invalid Credentials: %s" % str(e
)
126 except KeystoneExceptions
.AuthorizationFailure
as e
:
127 self
.log
.error("Bad authentication URL given for SDN account. Given auth url: %s",
129 status
.status
= "failure"
130 status
.details
= "Invalid auth url: %s" % str(e
)
132 except NeutronException
.NotFound
as e
:
133 status
.status
= "failure"
134 status
.details
= "Neutron exception %s" % str(e
)
136 except openstack_drv
.ValidationError
as e
:
137 self
.log
.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
138 status
.status
= "failure"
139 status
.details
= "Invalid Credentials: %s" % str(e
)
141 except Exception as e
:
142 msg
= "RwsdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
144 status
.status
= "failure"
148 status
.status
= "success"
149 status
.details
= "Connection was successful"
153 def delete_port_chain(self
, port_chain_id
):
156 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
157 port_chain
= result
.json()
158 self
.log
.debug("Port chain result is %s", port_chain
)
159 port_pair_groups
= port_chain
["port_chain"]["port_pair_groups"]
160 self
.portchain_drv
.delete_port_chain(port_chain_id
)
162 # Get port pairs and delete port pair groups
164 self
.log
.debug("Port pair groups during delete is %s", port_pair_groups
)
165 for port_pair_group_id
in port_pair_groups
:
166 result
= self
.portchain_drv
.get_port_pair_group(port_pair_group_id
)
167 port_pair_group
= result
.json()
168 self
.log
.debug("Port pair group result is %s", port_pair_group
)
169 port_pairs
.extend(port_pair_group
["port_pair_group"]["port_pairs"])
170 self
.portchain_drv
.delete_port_pair_group(port_pair_group_id
)
172 self
.log
.debug("Port pairs during delete is %s", port_pairs
)
174 for port_pair_id
in port_pairs
:
175 self
.portchain_drv
.delete_port_pair(port_pair_id
)
177 except Exception as e
:
178 self
.log
.error("Error while delete port chain with id %s, exception %s", port_chain_id
, str(e
))
180 def update_port_chain(self
, port_chain_id
, flow_classifier_list
):
181 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
182 result
.raise_for_status()
183 port_chain
= result
.json()['port_chain']
184 new_flow_classifier_list
= list()
185 if port_chain
and port_chain
['flow_classifiers']:
186 new_flow_classifier_list
.extend(port_chain
['flow_classifiers'])
187 new_flow_classifier_list
.extend(flow_classifier_list
)
188 port_chain_id
= self
.portchain_drv
.update_port_chain(port_chain
['id'], flow_classifiers
=new_flow_classifier_list
)
191 def create_flow_classifer(self
, classifier_name
, classifier_dict
):
192 "Create flow classifier"
193 flow_classifier_id
= self
.portchain_drv
.create_flow_classifier(classifier_name
, classifier_dict
)
194 return flow_classifier_id
196 def delete_flow_classifier(self
, classifier_id
):
197 "Create flow classifier"
199 self
.portchain_drv
.delete_flow_classifier(classifier_id
)
200 except Exception as e
:
201 self
.log
.error("Error while deleting flow classifier with id %s, exception %s", classifier_id
, str(e
))
203 def get_port_chain_list(self
):
204 result
= self
.portchain_drv
.get_port_chain_list()
205 port_chain_list
= result
.json()
206 if 'port_chains' in port_chain_list
:
207 return port_chain_list
['port_chains']
210 class RwsdnAccountDriver(object):
212 Container class per sdn account
214 def __init__(self
, logger
, **kwargs
):
217 self
._driver
= OpenstackL2PortChainingDriver(logger
= self
.log
, **kwargs
)
218 except (KeystoneExceptions
.Unauthorized
, KeystoneExceptions
.AuthorizationFailure
,
219 NeutronException
.NotFound
) as e
:
221 except Exception as e
:
222 self
.log
.error("RwsdnOpenstackPlugin: OpenstackL2PortChainingDriver init failed. Exception: %s" %(str(e
)))
230 class SdnOpenstackPlugin(GObject
.Object
, RwSdn
.Topology
):
234 GObject
.Object
.__init
__(self
)
235 self
.log
= logging
.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin
.instance_num
)
236 self
.log
.setLevel(logging
.DEBUG
)
238 self
._rwlog
_handler
= None
239 self
._account
_drivers
= dict()
240 SdnOpenstackPlugin
.instance_num
+= 1
242 def _use_driver(self
, account
):
243 if self
._rwlog
_handler
is None:
244 raise UninitializedPluginError("Must call init() in SDN plugin before use.")
246 if account
.name
not in self
._account
_drivers
:
247 self
.log
.debug("Creating SDN OpenstackDriver")
248 kwargs
= dict(username
= account
.openstack
.key
,
249 password
= account
.openstack
.secret
,
250 auth_url
= account
.openstack
.auth_url
,
251 project
= account
.openstack
.tenant
,
252 cert_validate
= account
.openstack
.cert_validate
,
253 user_domain
= account
.openstack
.user_domain
,
254 project_domain
= account
.openstack
.project_domain
,
255 region
= account
.openstack
.region
)
256 drv
= RwsdnAccountDriver(self
.log
, **kwargs
)
257 self
._account
_drivers
[account
.name
] = drv
260 return self
._account
_drivers
[account
.name
].driver
263 def do_init(self
, rwlog_ctx
):
264 self
._rwlog
_handler
= rwlogger
.RwLogger(
265 category
="rw-cal-log",
266 subcategory
="openstack",
269 self
.log
.addHandler(self
._rwlog
_handler
)
270 self
.log
.propagate
= False
272 @rwstatus(ret_on_failure
=[None])
273 def do_validate_sdn_creds(self
, account
):
275 Validates the sdn account credentials for the specified account.
276 Performs an access to the resources using Keystone API. If creds
277 are not valid, returns an error code & reason string
279 @param account - a SDN account
282 Validation Code and Details String
284 status
= RwsdnalYang
.YangData_RwProject_Project_SdnAccounts_SdnAccountList_ConnectionStatus()
286 drv
= self
._use
_driver
(account
)
287 drv
.validate_account_creds()
289 except openstack_drv
.ValidationError
as e
:
290 self
.log
.error("SdnOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
291 status
.status
= "failure"
292 status
.details
= "Invalid Credentials: %s" % str(e
)
294 except Exception as e
:
295 msg
= "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
297 status
.status
= "failure"
301 status
.status
= "success"
302 status
.details
= "Connection was successful"
306 @rwstatus(ret_on_failure
=[""])
307 def do_create_vnffg_chain(self
, account
, vnffg
):
309 Creates Service Function chain in ODL
311 @param account - a SDN account
314 self
.log
.debug('Received Create VNFFG chain for account {}, chain {}'.format(account
, vnffg
))
315 drv
= self
._use
_driver
(account
)
317 vnf_chain_list
= sorted(vnffg
.vnf_chain_path
, key
= lambda x
: x
.order
)
319 for path
in vnf_chain_list
:
320 if prev_vm_id
and path
.vnfr_ids
[0].vdu_list
[0].vm_id
== prev_vm_id
:
321 prev_entry
= port_list
.pop()
322 port_list
.append((prev_entry
[0], path
.vnfr_ids
[0].vdu_list
[0].port_id
))
325 prev_vm_id
= path
.vnfr_ids
[0].vdu_list
[0].vm_id
326 port_list
.append((path
.vnfr_ids
[0].vdu_list
[0].port_id
, path
.vnfr_ids
[0].vdu_list
[0].port_id
))
327 vnffg_id
= drv
.portchain_drv
.create_port_chain(vnffg
.name
, port_list
)
331 def do_terminate_vnffg_chain(self
, account
, vnffg_id
):
333 Terminate Service Function chain in ODL
335 @param account - a SDN account
337 self
.log
.debug('Received terminate VNFFG chain for id %s ', vnffg_id
)
338 drv
= self
._use
_driver
(account
)
339 drv
.delete_port_chain(vnffg_id
)
341 @rwstatus(ret_on_failure
=[None])
342 def do_create_vnffg_classifier(self
, account
, vnffg_classifier
):
346 @param account - a SDN account
348 self
.log
.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account
, vnffg_classifier
))
349 protocol_map
= {1: 'ICMP', 6: 'TCP', 17: 'UDP'}
350 flow_classifier_list
= list()
351 drv
= self
._use
_driver
(account
)
352 for rule
in vnffg_classifier
.match_attributes
:
353 classifier_name
= vnffg_classifier
.name
+ '_' + rule
.name
355 for field
, value
in rule
.as_dict().items():
356 if field
== 'ip_proto':
357 flow_dict
['protocol'] = protocol_map
.get(value
, None)
358 elif field
== 'source_ip_address':
359 flow_dict
['source_ip_prefix'] = value
360 elif field
== 'destination_ip_address':
361 flow_dict
['destination_ip_prefix'] = value
362 elif field
== 'source_port':
363 flow_dict
['source_port_range_min'] = value
364 flow_dict
['source_port_range_max'] = value
365 elif field
== 'destination_port':
366 flow_dict
['destination_port_range_min'] = value
367 flow_dict
['destination_port_range_max'] = value
368 if vnffg_classifier
.has_field('port_id'):
369 flow_dict
['logical_source_port'] = vnffg_classifier
.port_id
370 flow_classifier_id
= drv
.create_flow_classifer(classifier_name
, flow_dict
)
371 flow_classifier_list
.append(flow_classifier_id
)
372 drv
.update_port_chain(vnffg_classifier
.rsp_id
, flow_classifier_list
)
373 return flow_classifier_list
375 @rwstatus(ret_on_failure
=[None])
376 def do_terminate_vnffg_classifier(self
, account
, vnffg_classifier_list
):
380 @param account - a SDN account
382 self
.log
.debug('Received terminate VNFFG classifier for id %s ', vnffg_classifier_list
)
383 drv
= self
._use
_driver
(account
)
384 for classifier_id
in vnffg_classifier_list
:
385 drv
.delete_flow_classifier(classifier_id
)
387 @rwstatus(ret_on_failure
=[None])
388 def do_get_vnffg_rendered_paths(self
, account
):
390 Get Rendered Service Path List (SFC)
392 @param account - a SDN account
394 self
.log
.debug('Received get VNFFG rendered path for account %s ', account
)
395 vnffg_rsps
= RwsdnalYang
.YangData_RwProject_Project_VnffgRenderedPaths()
396 drv
= self
._use
_driver
(account
)
397 port_chain_list
= drv
.get_port_chain_list()
398 for port_chain
in port_chain_list
:
399 #rsp = vnffg_rsps.vnffg_rendered_path.add()
400 #rsp.name = port_chain['name']