ff5d019c9647057f4614c5f953721c5a5eeaaee1
[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 contextlib
19 import logging
20
21 import gi
22 gi.require_version('RwSdn', '1.0')
23 gi.require_version('RwCal', '1.0')
24 gi.require_version('RwcalYang', '1.0')
25
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
30
31
32
33 import rw_status
34 import rift.cal.rwcal_status as rwcal_status
35 import rwlogger
36 import neutronclient.common.exceptions as NeutronException
37 import keystoneclient.exceptions as KeystoneExceptions
38
39
40 from gi.repository import (
41 GObject,
42 RwCal,
43 RwSdn, # Vala package
44 RwsdnYang,
45 RwTypes,
46 RwcalYang)
47
48 rwstatus_exception_map = { IndexError: RwTypes.RwStatus.NOTFOUND,
49 KeyError: RwTypes.RwStatus.NOTFOUND,
50 NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED,}
51
52 rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
53 rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
54
55
56 class OpenstackSdnOperationFailure(Exception):
57 pass
58
59 class UninitializedPluginError(Exception):
60 pass
61
62 class OpenstackL2PortChainingDriver(object):
63 """
64 Driver for openstack keystone and neutron
65 """
66 def __init__(self, logger = None, **kwargs):
67 """
68 OpenstackDriver Driver constructor
69 Arguments:
70 logger: (instance of logging.Logger)
71 kwargs: A dictionary of
72 {
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
80 region : Region name
81 }
82 """
83
84 if logger is None:
85 self.log = logging.getLogger('rwsdn.openstack.driver')
86 self.log.setLevel(logging.DEBUG)
87 else:
88 self.log = logger
89
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,)
96
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
99
100 discover = ks_drv.KeystoneVersionDiscover(kwargs['auth_url'], logger = self.log)
101 (major, minor) = discover.get_version()
102
103 self.sess_drv = sess_drv.SessionDriver(auth_method = 'password',
104 version = str(major),
105 cert_validate = cert_validate,
106 logger = self.log,
107 **args)
108
109 self.neutron_drv = nt_drv.NeutronDriver(self.sess_drv,
110 region_name = region,
111 logger = self.log)
112
113 self.portchain_drv = port_drv.L2PortChainDriver(self.sess_drv,
114 self.neutron_drv.neutron_endpoint,
115 logger = self.log)
116
117 def validate_account_creds(self):
118 try:
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))
127
128 def delete_port_chain(self,port_chain_id):
129 "Delete port chain"
130 try:
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)
136
137 # Get port pairs and delete port pair groups
138 port_pairs = list()
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)
146
147 self.log.debug("Port pairs during delete is %s",port_pairs)
148
149 for port_pair_id in port_pairs:
150 self.portchain_drv.delete_port_pair(port_pair_id)
151 pass
152 except Exception as e:
153 self.log.error("Error while delete port chain with id %s, exception %s", port_chain_id,str(e))
154
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)
164 return port_chain_id
165
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
170
171 def delete_flow_classifier(self,classifier_id):
172 "Create flow classifier"
173 try:
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))
177
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']
183
184
185 class RwsdnAccountDriver(object):
186 """
187 Container class per sdn account
188 """
189 def __init__(self, logger, **kwargs):
190 self.log = logger
191 try:
192 self._driver = OpenstackL2PortChainingDriver(logger = self.log, **kwargs)
193 except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure,
194 NeutronException.NotFound) as e:
195 raise
196 except Exception as e:
197 self.log.error("RwsdnOpenstackPlugin: OpenstackL2PortChainingDriver init failed. Exception: %s" %(str(e)))
198 raise
199
200 @property
201 def driver(self):
202 return self._driver
203
204
205 class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
206 instance_num = 1
207 def __init__(self):
208 GObject.Object.__init__(self)
209 self.log = logging.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin.instance_num)
210 self.log.setLevel(logging.DEBUG)
211
212 self._rwlog_handler = None
213 self._account_drivers = dict()
214 SdnOpenstackPlugin.instance_num += 1
215
216 def _use_driver(self, account):
217 if self._rwlog_handler is None:
218 raise UninitializedPluginError("Must call init() in SDN plugin before use.")
219
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
232 return drv.driver
233 else:
234 return self._account_drivers[account.name].driver
235
236 @rwstatus
237 def do_init(self, rwlog_ctx):
238 self._rwlog_handler = rwlogger.RwLogger(
239 category="rw-cal-log",
240 subcategory="openstack",
241 log_hdl=rwlog_ctx,
242 )
243 self.log.addHandler(self._rwlog_handler)
244 self.log.propagate = False
245
246 @rwstatus(ret_on_failure=[None])
247 def do_validate_sdn_creds(self, account):
248 """
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
252
253 @param account - a SDN account
254
255 Returns:
256 Validation Code and Details String
257 """
258 status = RwsdnYang.SdnConnectionStatus()
259 drv = self._use_driver(account)
260 try:
261 drv.validate_account_creds()
262
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)
267
268 except Exception as e:
269 msg = "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
270 self.log.error(msg)
271 status.status = "failure"
272 status.details = msg
273
274 else:
275 status.status = "success"
276 status.details = "Connection was successful"
277
278 return status
279
280 @rwstatus(ret_on_failure=[""])
281 def do_create_vnffg_chain(self, account,vnffg):
282 """
283 Creates Service Function chain in ODL
284
285 @param account - a SDN account
286
287 """
288 self.log.debug('Received Create VNFFG chain for account {}, chain {}'.format(account,vnffg))
289 drv = self._use_driver(account)
290 port_list = list()
291 vnf_chain_list = sorted(vnffg.vnf_chain_path, key = lambda x: x.order)
292 prev_vm_id = None
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))
297 prev_vm_id = None
298 else:
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)
302 return vnffg_id
303
304 @rwstatus
305 def do_terminate_vnffg_chain(self, account,vnffg_id):
306 """
307 Terminate Service Function chain in ODL
308
309 @param account - a SDN account
310 """
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)
314
315 @rwstatus(ret_on_failure=[None])
316 def do_create_vnffg_classifier(self, account, vnffg_classifier):
317 """
318 Add VNFFG Classifier
319
320 @param account - a SDN account
321 """
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
328 flow_dict = {}
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
348
349 @rwstatus(ret_on_failure=[None])
350 def do_terminate_vnffg_classifier(self, account, vnffg_classifier_list):
351 """
352 Add VNFFG Classifier
353
354 @param account - a SDN account
355 """
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)
360
361 @rwstatus(ret_on_failure=[None])
362 def do_get_vnffg_rendered_paths(self, account):
363 """
364 Get Rendered Service Path List (SFC)
365
366 @param account - a SDN account
367 """
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']
375 pass
376 return vnffg_rsps
377
378