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.
22 gi
.require_version('RwSdn', '1.0')
23 gi
.require_version('RwCal', '1.0')
24 gi
.require_version('RwcalYang', '1.0')
26 from rift
.rwcal
.openstack
import session
as sess_drv
27 from rift
.rwcal
.openstack
import keystone
as ks_drv
28 from rift
.rwcal
.openstack
import neutron
as nt_drv
29 from rift
.rwcal
.openstack
import portchain
as port_drv
34 import rift
.cal
.rwcal_status
as rwcal_status
36 import neutronclient
.common
.exceptions
as NeutronException
37 import keystoneclient
.exceptions
as KeystoneExceptions
40 from gi
.repository
import (
48 rwstatus_exception_map
= { IndexError: RwTypes
.RwStatus
.NOTFOUND
,
49 KeyError: RwTypes
.RwStatus
.NOTFOUND
,
50 NotImplementedError: RwTypes
.RwStatus
.NOT_IMPLEMENTED
,}
52 rwstatus
= rw_status
.rwstatus_from_exc_map(rwstatus_exception_map
)
53 rwcalstatus
= rwcal_status
.rwcalstatus_from_exc_map(rwstatus_exception_map
)
56 class OpenstackSdnOperationFailure(Exception):
59 class UninitializedPluginError(Exception):
62 class OpenstackL2PortChainingDriver(object):
64 Driver for openstack keystone and neutron
66 def __init__(self
, logger
= None, **kwargs
):
68 OpenstackDriver Driver constructor
70 logger: (instance of logging.Logger)
71 kwargs: A dictionary of
73 username (string) : Username for project/tenant.
74 password (string) : Password
75 auth_url (string) : Keystone Authentication URL.
76 project (string) : Openstack project name
77 cert_validate (boolean, optional) : In case of SSL/TLS connection if certificate validation is required or not.
78 user_domain : Domain name for user
79 project_domain : Domain name for project
85 self
.log
= logging
.getLogger('rwsdn.openstack.driver')
86 self
.log
.setLevel(logging
.DEBUG
)
90 args
= dict(auth_url
= kwargs
['auth_url'],
91 username
= kwargs
['username'],
92 password
= kwargs
['password'],
93 project_name
= kwargs
['project'],
94 project_domain_name
= kwargs
['project_domain'] if 'project_domain' in kwargs
else None,
95 user_domain_name
= kwargs
['user_domain'] if 'user_domain' in kwargs
else None,)
97 cert_validate
= kwargs
['cert_validate'] if 'cert_validate' in kwargs
else False
98 region
= kwargs
['region_name'] if 'region_name' in kwargs
else False
100 discover
= ks_drv
.KeystoneVersionDiscover(kwargs
['auth_url'], logger
= self
.log
)
101 (major
, minor
) = discover
.get_version()
103 self
.sess_drv
= sess_drv
.SessionDriver(auth_method
= 'password',
104 version
= str(major
),
105 cert_validate
= cert_validate
,
109 self
.neutron_drv
= nt_drv
.NeutronDriver(self
.sess_drv
,
110 region_name
= region
,
113 self
.portchain_drv
= port_drv
.L2PortChainDriver(self
.sess_drv
,
114 self
.neutron_drv
.neutron_endpoint
,
117 def validate_account_creds(self
):
119 self
.sess_drv
.invalidate_auth_token()
120 self
.sess_drv
.auth_token
121 except KeystoneExceptions
.AuthorizationFailure
as e
:
122 self
.log
.error("Unable to authenticate or validate the existing credentials. Exception: %s", str(e
))
123 raise ValidationError("Invalid Credentials: "+ str(e
))
124 except Exception as e
:
125 self
.log
.error("Could not connect to Openstack. Exception: %s", str(e
))
126 raise ValidationError("Connection Error: "+ str(e
))
128 def delete_port_chain(self
,port_chain_id
):
131 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
132 port_chain
= result
.json()
133 self
.log
.debug("Port chain result is %s", port_chain
)
134 port_pair_groups
= port_chain
["port_chain"]["port_pair_groups"]
135 self
.portchain_drv
.delete_port_chain(port_chain_id
)
137 # Get port pairs and delete port pair groups
139 self
.log
.debug("Port pair groups during delete is %s", port_pair_groups
)
140 for port_pair_group_id
in port_pair_groups
:
141 result
= self
.portchain_drv
.get_port_pair_group(port_pair_group_id
)
142 port_pair_group
= result
.json()
143 self
.log
.debug("Port pair group result is %s", port_pair_group
)
144 port_pairs
.extend(port_pair_group
["port_pair_group"]["port_pairs"])
145 self
.portchain_drv
.delete_port_pair_group(port_pair_group_id
)
147 self
.log
.debug("Port pairs during delete is %s",port_pairs
)
149 for port_pair_id
in port_pairs
:
150 self
.portchain_drv
.delete_port_pair(port_pair_id
)
152 except Exception as e
:
153 self
.log
.error("Error while delete port chain with id %s, exception %s", port_chain_id
,str(e
))
155 def update_port_chain(self
,port_chain_id
,flow_classifier_list
):
156 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
157 result
.raise_for_status()
158 port_chain
= result
.json()['port_chain']
159 new_flow_classifier_list
= list()
160 if port_chain
and port_chain
['flow_classifiers']:
161 new_flow_classifier_list
.extend(port_chain
['flow_classifiers'])
162 new_flow_classifier_list
.extend(flow_classifier_list
)
163 port_chain_id
= self
.portchain_drv
.update_port_chain(port_chain
['id'],flow_classifiers
=new_flow_classifier_list
)
166 def create_flow_classifer(self
,classifier_name
,classifier_dict
):
167 "Create flow classifier"
168 flow_classifier_id
= self
.portchain_drv
.create_flow_classifier(classifier_name
,classifier_dict
)
169 return flow_classifier_id
171 def delete_flow_classifier(self
,classifier_id
):
172 "Create flow classifier"
174 self
.portchain_drv
.delete_flow_classifier(classifier_id
)
175 except Exception as e
:
176 self
.log
.error("Error while deleting flow classifier with id %s, exception %s", classifier_id
,str(e
))
178 def get_port_chain_list(self
):
179 result
= self
.portchain_drv
.get_port_chain_list()
180 port_chain_list
= result
.json()
181 if 'port_chains' in port_chain_list
:
182 return port_chain_list
['port_chains']
185 class RwsdnAccountDriver(object):
187 Container class per sdn account
189 def __init__(self
, logger
, **kwargs
):
192 self
._driver
= OpenstackL2PortChainingDriver(logger
= self
.log
, **kwargs
)
193 except (KeystoneExceptions
.Unauthorized
, KeystoneExceptions
.AuthorizationFailure
,
194 NeutronException
.NotFound
) as e
:
196 except Exception as e
:
197 self
.log
.error("RwsdnOpenstackPlugin: OpenstackL2PortChainingDriver init failed. Exception: %s" %(str(e
)))
205 class SdnOpenstackPlugin(GObject
.Object
, RwSdn
.Topology
):
208 GObject
.Object
.__init
__(self
)
209 self
.log
= logging
.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin
.instance_num
)
210 self
.log
.setLevel(logging
.DEBUG
)
212 self
._rwlog
_handler
= None
213 self
._account
_drivers
= dict()
214 SdnOpenstackPlugin
.instance_num
+= 1
216 def _use_driver(self
, account
):
217 if self
._rwlog
_handler
is None:
218 raise UninitializedPluginError("Must call init() in SDN plugin before use.")
220 if account
.name
not in self
._account
_drivers
:
221 self
.log
.debug("Creating SDN OpenstackDriver")
222 kwargs
= dict(username
= account
.openstack
.key
,
223 password
= account
.openstack
.secret
,
224 auth_url
= account
.openstack
.auth_url
,
225 project
= account
.openstack
.tenant
,
226 cert_validate
= account
.openstack
.cert_validate
,
227 user_domain
= account
.openstack
.user_domain
,
228 project_domain
= account
.openstack
.project_domain
,
229 region
= account
.openstack
.region
)
230 drv
= RwsdnAccountDriver(self
.log
, **kwargs
)
231 self
._account
_drivers
[account
.name
] = drv
234 return self
._account
_drivers
[account
.name
].driver
237 def do_init(self
, rwlog_ctx
):
238 self
._rwlog
_handler
= rwlogger
.RwLogger(
239 category
="rw-cal-log",
240 subcategory
="openstack",
243 self
.log
.addHandler(self
._rwlog
_handler
)
244 self
.log
.propagate
= False
246 @rwstatus(ret_on_failure
=[None])
247 def do_validate_sdn_creds(self
, account
):
249 Validates the sdn account credentials for the specified account.
250 Performs an access to the resources using Keystone API. If creds
251 are not valid, returns an error code & reason string
253 @param account - a SDN account
256 Validation Code and Details String
258 status
= RwsdnYang
.SdnConnectionStatus()
259 drv
= self
._use
_driver
(account
)
261 drv
.validate_account_creds()
263 except openstack_drv
.ValidationError
as e
:
264 self
.log
.error("SdnOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e
))
265 status
.status
= "failure"
266 status
.details
= "Invalid Credentials: %s" % str(e
)
268 except Exception as e
:
269 msg
= "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e
))
271 status
.status
= "failure"
275 status
.status
= "success"
276 status
.details
= "Connection was successful"
280 @rwstatus(ret_on_failure
=[""])
281 def do_create_vnffg_chain(self
, account
,vnffg
):
283 Creates Service Function chain in ODL
285 @param account - a SDN account
288 self
.log
.debug('Received Create VNFFG chain for account {}, chain {}'.format(account
,vnffg
))
289 drv
= self
._use
_driver
(account
)
291 vnf_chain_list
= sorted(vnffg
.vnf_chain_path
, key
= lambda x
: x
.order
)
293 for path
in vnf_chain_list
:
294 if prev_vm_id
and path
.vnfr_ids
[0].vdu_list
[0].vm_id
== prev_vm_id
:
295 prev_entry
= port_list
.pop()
296 port_list
.append((prev_entry
[0],path
.vnfr_ids
[0].vdu_list
[0].port_id
))
299 prev_vm_id
= path
.vnfr_ids
[0].vdu_list
[0].vm_id
300 port_list
.append((path
.vnfr_ids
[0].vdu_list
[0].port_id
,path
.vnfr_ids
[0].vdu_list
[0].port_id
))
301 vnffg_id
= drv
.create_port_chain(vnffg
.name
,port_list
)
305 def do_terminate_vnffg_chain(self
, account
,vnffg_id
):
307 Terminate Service Function chain in ODL
309 @param account - a SDN account
311 self
.log
.debug('Received terminate VNFFG chain for id %s ', vnffg_id
)
312 drv
= self
._use
_driver
(account
)
313 drv
.delete_port_chain(vnffg_id
)
315 @rwstatus(ret_on_failure
=[None])
316 def do_create_vnffg_classifier(self
, account
, vnffg_classifier
):
320 @param account - a SDN account
322 self
.log
.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account
,vnffg_classifier
))
323 protocol_map
= {1:'ICMP',6:'TCP',17:'UDP'}
324 flow_classifier_list
= list()
325 drv
= self
._use
_driver
(account
)
326 for rule
in vnffg_classifier
.match_attributes
:
327 classifier_name
= vnffg_classifier
.name
+ '_' + rule
.name
329 for field
, value
in rule
.as_dict().items():
330 if field
== 'ip_proto':
331 flow_dict
['protocol'] = protocol_map
.get(value
,None)
332 elif field
== 'source_ip_address':
333 flow_dict
['source_ip_prefix'] = value
334 elif field
== 'destination_ip_address':
335 flow_dict
['destination_ip_prefix'] = value
336 elif field
== 'source_port':
337 flow_dict
['source_port_range_min'] = value
338 flow_dict
['source_port_range_max'] = value
339 elif field
== 'destination_port':
340 flow_dict
['destination_port_range_min'] = value
341 flow_dict
['destination_port_range_max'] = value
342 if vnffg_classifier
.has_field('port_id'):
343 flow_dict
['logical_source_port'] = vnffg_classifier
.port_id
344 flow_classifier_id
= drv
.create_flow_classifer(classifier_name
, flow_dict
)
345 flow_classifier_list
.append(flow_classifier_id
)
346 drv
.update_port_chain(vnffg_classifier
.rsp_id
,flow_classifier_list
)
347 return flow_classifier_list
349 @rwstatus(ret_on_failure
=[None])
350 def do_terminate_vnffg_classifier(self
, account
, vnffg_classifier_list
):
354 @param account - a SDN account
356 self
.log
.debug('Received terminate VNFFG classifier for id %s ', vnffg_classifier_list
)
357 drv
= self
._use
_driver
(account
)
358 for classifier_id
in vnffg_classifier_list
:
359 drv
.delete_flow_classifier(classifier_id
)
361 @rwstatus(ret_on_failure
=[None])
362 def do_get_vnffg_rendered_paths(self
, account
):
364 Get Rendered Service Path List (SFC)
366 @param account - a SDN account
368 self
.log
.debug('Received get VNFFG rendered path for account %s ', account
)
369 vnffg_rsps
= RwsdnYang
.VNFFGRenderedPaths()
370 drv
= self
._use
_driver
(account
)
371 port_chain_list
= drv
.get_port_chain_list()
372 for port_chain
in port_chain_list
:
373 #rsp = vnffg_rsps.vnffg_rendered_path.add()
374 #rsp.name = port_chain['name']