Revert "Full Juju Charm support"
[osm/SO.git] / rwlaunchpad / plugins / rwvns / vala / rwsdn_openstack / rwsdn_openstack.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16 #
17
18 import logging
19
20 import gi
21 gi.require_version('RwSdn', '1.0')
22
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
28
29
30
31 import rw_status
32 import rift.cal.rwcal_status as rwcal_status
33 import rwlogger
34 import neutronclient.common.exceptions as NeutronException
35 import keystoneclient.exceptions as KeystoneExceptions
36
37
38 from gi.repository import (
39 GObject,
40 RwSdn, # Vala package
41 RwsdnalYang,
42 RwTypes)
43
44 rwstatus_exception_map = {IndexError: RwTypes.RwStatus.NOTFOUND,
45 KeyError: RwTypes.RwStatus.NOTFOUND,
46 NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED, }
47
48 rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
49 rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
50
51
52 class OpenstackSdnOperationFailure(Exception):
53 pass
54
55 class UninitializedPluginError(Exception):
56 pass
57
58 class OpenstackL2PortChainingDriver(object):
59 """
60 Driver for openstack keystone and neutron
61 """
62 def __init__(self, logger = None, **kwargs):
63 """
64 OpenstackDriver Driver constructor
65 Arguments:
66 logger: (instance of logging.Logger)
67 kwargs: A dictionary of
68 {
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
76 region : Region name
77 }
78 """
79
80 if logger is None:
81 self.log = logging.getLogger('rwsdn.openstack.driver')
82 self.log.setLevel(logging.DEBUG)
83 else:
84 self.log = logger
85
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,)
92
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
96
97 discover = ks_drv.KeystoneVersionDiscover(kwargs['auth_url'],
98 cert_validate,
99 logger = self.log)
100 (major, minor) = discover.get_version()
101
102 self.sess_drv = sess_drv.SessionDriver(auth_method = 'password',
103 version = str(major),
104 cert_validate = cert_validate,
105 logger = self.log,
106 **args)
107
108 self.neutron_drv = nt_drv.NeutronDriver(self.sess_drv,
109 region_name = region,
110 logger = self.log)
111
112 self.portchain_drv = port_drv.L2PortChainDriver(self.sess_drv,
113 self.neutron_drv.neutron_endpoint,
114 logger = self.log)
115
116 def validate_account_creds(self):
117 status = RwsdnalYang.YangData_RwProject_Project_SdnAccounts_SdnAccountList_ConnectionStatus()
118 try:
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)
125
126 except KeystoneExceptions.AuthorizationFailure as e:
127 self.log.error("Bad authentication URL given for SDN account. Given auth url: %s",
128 self.auth_url)
129 status.status = "failure"
130 status.details = "Invalid auth url: %s" % str(e)
131
132 except NeutronException.NotFound as e:
133 status.status = "failure"
134 status.details = "Neutron exception %s" % str(e)
135
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)
140
141 except Exception as e:
142 msg = "RwsdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
143 self.log.error(msg)
144 status.status = "failure"
145 status.details = msg
146
147 else:
148 status.status = "success"
149 status.details = "Connection was successful"
150
151 return status
152
153 def delete_port_chain(self, port_chain_id):
154 "Delete port chain"
155 try:
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)
161
162 # Get port pairs and delete port pair groups
163 port_pairs = list()
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)
171
172 self.log.debug("Port pairs during delete is %s", port_pairs)
173
174 for port_pair_id in port_pairs:
175 self.portchain_drv.delete_port_pair(port_pair_id)
176 pass
177 except Exception as e:
178 self.log.error("Error while delete port chain with id %s, exception %s", port_chain_id, str(e))
179
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)
189 return port_chain_id
190
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
195
196 def delete_flow_classifier(self, classifier_id):
197 "Create flow classifier"
198 try:
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))
202
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']
208
209
210 class RwsdnAccountDriver(object):
211 """
212 Container class per sdn account
213 """
214 def __init__(self, logger, **kwargs):
215 self.log = logger
216 try:
217 self._driver = OpenstackL2PortChainingDriver(logger = self.log, **kwargs)
218 except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure,
219 NeutronException.NotFound) as e:
220 raise
221 except Exception as e:
222 self.log.error("RwsdnOpenstackPlugin: OpenstackL2PortChainingDriver init failed. Exception: %s" %(str(e)))
223 raise
224
225 @property
226 def driver(self):
227 return self._driver
228
229
230 class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
231 instance_num = 1
232
233 def __init__(self):
234 GObject.Object.__init__(self)
235 self.log = logging.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin.instance_num)
236 self.log.setLevel(logging.DEBUG)
237
238 self._rwlog_handler = None
239 self._account_drivers = dict()
240 SdnOpenstackPlugin.instance_num += 1
241
242 def _use_driver(self, account):
243 if self._rwlog_handler is None:
244 raise UninitializedPluginError("Must call init() in SDN plugin before use.")
245
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
258 return drv.driver
259 else:
260 return self._account_drivers[account.name].driver
261
262 @rwstatus
263 def do_init(self, rwlog_ctx):
264 self._rwlog_handler = rwlogger.RwLogger(
265 category="rw-cal-log",
266 subcategory="openstack",
267 log_hdl=rwlog_ctx,
268 )
269 self.log.addHandler(self._rwlog_handler)
270 self.log.propagate = False
271
272 @rwstatus(ret_on_failure=[None])
273 def do_validate_sdn_creds(self, account):
274 """
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
278
279 @param account - a SDN account
280
281 Returns:
282 Validation Code and Details String
283 """
284 status = RwsdnalYang.YangData_RwProject_Project_SdnAccounts_SdnAccountList_ConnectionStatus()
285 try:
286 drv = self._use_driver(account)
287 drv.validate_account_creds()
288
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)
293
294 except Exception as e:
295 msg = "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
296 self.log.error(msg)
297 status.status = "failure"
298 status.details = msg
299
300 else:
301 status.status = "success"
302 status.details = "Connection was successful"
303
304 return status
305
306 @rwstatus(ret_on_failure=[""])
307 def do_create_vnffg_chain(self, account, vnffg):
308 """
309 Creates Service Function chain in ODL
310
311 @param account - a SDN account
312
313 """
314 self.log.debug('Received Create VNFFG chain for account {}, chain {}'.format(account, vnffg))
315 drv = self._use_driver(account)
316 port_list = list()
317 vnf_chain_list = sorted(vnffg.vnf_chain_path, key = lambda x: x.order)
318 prev_vm_id = None
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))
323 prev_vm_id = None
324 else:
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)
328 return vnffg_id
329
330 @rwstatus
331 def do_terminate_vnffg_chain(self, account, vnffg_id):
332 """
333 Terminate Service Function chain in ODL
334
335 @param account - a SDN account
336 """
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)
340
341 @rwstatus(ret_on_failure=[None])
342 def do_create_vnffg_classifier(self, account, vnffg_classifier):
343 """
344 Add VNFFG Classifier
345
346 @param account - a SDN account
347 """
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
354 flow_dict = {}
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
374
375 @rwstatus(ret_on_failure=[None])
376 def do_terminate_vnffg_classifier(self, account, vnffg_classifier_list):
377 """
378 Add VNFFG Classifier
379
380 @param account - a SDN account
381 """
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)
386
387 @rwstatus(ret_on_failure=[None])
388 def do_get_vnffg_rendered_paths(self, account):
389 """
390 Get Rendered Service Path List (SFC)
391
392 @param account - a SDN account
393 """
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']
401 pass
402 return vnffg_rsps
403
404