Allow instance of only networks without scenario
[osm/RO.git] / osm_ro / openmanoclient.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U.
6 # This file is part of openmano
7 # All Rights Reserved.
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License"); you may
10 # not use this file except in compliance with the License. You may obtain
11 # a copy of the License at
12 #
13 # http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 # License for the specific language governing permissions and limitations
19 # under the License.
20 #
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: nfvlabs@tid.es
23 ##
24
25 """
26 openmano python client used to interact with openmano-server
27 """
28
29 import requests
30 import json
31 import yaml
32 import logging
33 import sys
34
35 __author__ = "Alfonso Tierno, Pablo Montes"
36 __date__ = "$09-Mar-2016 09:09:48$"
37 __version__ = "0.1.0-r470"
38 version_date = "Oct 2017"
39
40 if sys.version_info.major == 3:
41 from urllib.parse import quote
42 elif sys.version_info.major == 2:
43 from urllib import quote
44
45 class OpenmanoException(Exception):
46 '''Common Exception for all openmano client exceptions'''
47
48 class OpenmanoBadParamsException(OpenmanoException):
49 '''Bad or missing input parameters'''
50
51 class OpenmanoResponseException(OpenmanoException):
52 '''Unexpected response from openmano server'''
53
54 class OpenmanoNotFoundException(OpenmanoException):
55 '''Not found at server'''
56
57 # class vnf():
58 # def __init__(self, message):
59 # print "Error: %s" %message
60 # print
61 # self.print_usage()
62 # #self.print_help()
63 # print
64 # print "Type 'openmano -h' for help"
65
66 class openmanoclient():
67 headers_req = {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
68
69 def __init__(self, **kwargs):
70 self.username = kwargs.get("username")
71 self.password = kwargs.get("password")
72 self.endpoint_url = kwargs.get("endpoint_url")
73 self.tenant_id = kwargs.get("tenant_id")
74 self.tenant_name = kwargs.get("tenant_name")
75 self.tenant = None
76 self.datacenter_id = kwargs.get("datacenter_id")
77 self.datacenter_name = kwargs.get("datacenter_name")
78 self.datacenter = None
79 self.logger = logging.getLogger(kwargs.get('logger','manoclient'))
80 if kwargs.get("debug"):
81 self.logger.setLevel(logging.DEBUG)
82
83 def __getitem__(self, index):
84 if index=='tenant_name':
85 return self.tenant_name
86 elif index=='tenant_id':
87 return self.tenant_id
88 elif index=='datacenter_name':
89 return self.datacenter_name
90 elif index=='datacenter_id':
91 return self.datacenter_id
92 elif index=='username':
93 return self.username
94 elif index=='password':
95 return self.password
96 elif index=='endpoint_url':
97 return self.endpoint_url
98 else:
99 raise KeyError("Invalid key '%s'" %str(index))
100
101 def __setitem__(self,index, value):
102 if index=='tenant_name':
103 self.tenant_name = value
104 elif index=='tenant_id':
105 self.tenant_id = value
106 elif index=='datacenter_name':
107 self.datacenter_name = value
108 elif index=='datacenter_id':
109 self.datacenter_id = value
110 elif index=='username':
111 self.username = value
112 elif index=='password':
113 self.password = value
114 elif index=='endpoint_url':
115 self.endpoint_url = value
116 else:
117 raise KeyError("Invalid key '%s'" %str(index))
118 self.tenant = None # force to reload tenant with different credentials
119 self.datacenter = None # force to reload datacenter with different credentials
120
121 def _parse(self, descriptor, descriptor_format, response=False):
122 #try yaml
123 if descriptor_format and descriptor_format != "json" and descriptor_format != "yaml":
124 raise OpenmanoBadParamsException("'descriptor_format' must be a 'json' or 'yaml' text")
125 if descriptor_format != "json":
126 try:
127 return yaml.load(descriptor)
128 except yaml.YAMLError as exc:
129 error_pos = ""
130 if hasattr(exc, 'problem_mark'):
131 mark = exc.problem_mark
132 error_pos = " at line:{} column:{}s".format(mark.line+1, mark.column+1)
133 error_text = "yaml format error" + error_pos
134 elif descriptor_format != "yaml":
135 try:
136 return json.loads(descriptor)
137 except Exception as e:
138 if response:
139 error_text = "json format error" + str(e)
140
141 if response:
142 raise OpenmanoResponseException(error_text)
143 raise OpenmanoBadParamsException(error_text)
144
145 def _parse_yaml(self, descriptor, response=False):
146 try:
147 return yaml.load(descriptor)
148 except yaml.YAMLError as exc:
149 error_pos = ""
150 if hasattr(exc, 'problem_mark'):
151 mark = exc.problem_mark
152 error_pos = " at line:{} column:{}s".format(mark.line+1, mark.column+1)
153 error_text = "yaml format error" + error_pos
154 if response:
155 raise OpenmanoResponseException(error_text)
156 raise OpenmanoBadParamsException(error_text)
157
158
159 def _get_item_uuid(self, item, item_id=None, item_name=None, all_tenants=False):
160 if all_tenants == None:
161 tenant_text = ""
162 elif all_tenants == False:
163 tenant_text = "/" + self.tenant
164 else:
165 tenant_text = "/any"
166 URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
167 self.logger.debug("GET %s", URLrequest )
168 mano_response = requests.get(URLrequest, headers=self.headers_req)
169 self.logger.debug("openmano response: %s", mano_response.text )
170 content = self._parse_yaml(mano_response.text, response=True)
171 #print content
172 found = 0
173 if not item_id and not item_name:
174 raise OpenmanoResponseException("Missing either {0}_name or {0}_id".format(item[:-1]))
175 for i in content[item]:
176 if item_id and i["uuid"] == item_id:
177 return item_id
178 elif item_name and i["name"] == item_name:
179 uuid = i["uuid"]
180 found += 1
181
182 if found == 0:
183 if item_id:
184 raise OpenmanoNotFoundException("No {} found with id '{}'".format(item[:-1], item_id))
185 else:
186 #print(item, item_name)
187 raise OpenmanoNotFoundException("No {} found with name '{}'".format(item[:-1], item_name) )
188 elif found > 1:
189 raise OpenmanoNotFoundException("{} {} found with name '{}'. uuid must be used".format(found, item, item_name))
190 return uuid
191
192 def _get_item(self, item, uuid=None, name=None, all_tenants=False):
193 if all_tenants:
194 tenant_text = "/any"
195 elif all_tenants==None:
196 tenant_text = ""
197 else:
198 tenant_text = "/"+self._get_tenant()
199 if not uuid:
200 #check that exist
201 uuid = self._get_item_uuid(item, uuid, name, all_tenants)
202
203 URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
204 self.logger.debug("GET %s", URLrequest )
205 mano_response = requests.get(URLrequest, headers=self.headers_req)
206 self.logger.debug("openmano response: %s", mano_response.text )
207
208 content = self._parse_yaml(mano_response.text, response=True)
209 if mano_response.status_code==200:
210 return content
211 else:
212 raise OpenmanoResponseException(str(content))
213
214 def _get_tenant(self):
215 if not self.tenant:
216 self.tenant = self._get_item_uuid("tenants", self.tenant_id, self.tenant_name, None)
217 return self.tenant
218
219 def _get_datacenter(self):
220 if not self.tenant:
221 self._get_tenant()
222 if not self.datacenter:
223 self.datacenter = self._get_item_uuid("datacenters", self.datacenter_id, self.datacenter_name, False)
224 return self.datacenter
225
226 def _create_item(self, item, descriptor, all_tenants=False, api_version=None):
227 if all_tenants:
228 tenant_text = "/any"
229 elif all_tenants is None:
230 tenant_text = ""
231 else:
232 tenant_text = "/"+self._get_tenant()
233 payload_req = yaml.safe_dump(descriptor)
234
235 api_version_text = ""
236 if api_version:
237 api_version_text = "/v3"
238
239 #print payload_req
240
241 URLrequest = "{}{apiver}{tenant}/{item}".format(self.endpoint_url, apiver=api_version_text, tenant=tenant_text,
242 item=item)
243 self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
244 mano_response = requests.post(URLrequest, headers=self.headers_req, data=payload_req)
245 self.logger.debug("openmano response: %s", mano_response.text)
246
247 content = self._parse_yaml(mano_response.text, response=True)
248 if mano_response.status_code == 200:
249 return content
250 else:
251 raise OpenmanoResponseException(str(content))
252
253 def _del_item(self, item, uuid=None, name=None, all_tenants=False):
254 if all_tenants:
255 tenant_text = "/any"
256 elif all_tenants==None:
257 tenant_text = ""
258 else:
259 tenant_text = "/"+self._get_tenant()
260 if not uuid:
261 #check that exist
262 uuid = self._get_item_uuid(item, uuid, name, all_tenants)
263
264 URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
265 self.logger.debug("DELETE %s", URLrequest )
266 mano_response = requests.delete(URLrequest, headers = self.headers_req)
267 self.logger.debug("openmano response: %s", mano_response.text )
268
269 content = self._parse_yaml(mano_response.text, response=True)
270 if mano_response.status_code==200:
271 return content
272 else:
273 raise OpenmanoResponseException(str(content))
274
275 def _list_item(self, item, all_tenants=False, filter_dict=None):
276 if all_tenants:
277 tenant_text = "/any"
278 elif all_tenants==None:
279 tenant_text = ""
280 else:
281 tenant_text = "/"+self._get_tenant()
282
283 URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
284 separator="?"
285 if filter_dict:
286 for k in filter_dict:
287 URLrequest += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
288 separator = "&"
289 self.logger.debug("openmano GET %s", URLrequest)
290 mano_response = requests.get(URLrequest, headers=self.headers_req)
291 self.logger.debug("openmano response: %s", mano_response.text )
292
293 content = self._parse_yaml(mano_response.text, response=True)
294 if mano_response.status_code==200:
295 return content
296 else:
297 raise OpenmanoResponseException(str(content))
298
299 def _edit_item(self, item, descriptor, uuid=None, name=None, all_tenants=False):
300 if all_tenants:
301 tenant_text = "/any"
302 elif all_tenants==None:
303 tenant_text = ""
304 else:
305 tenant_text = "/"+self._get_tenant()
306
307 if not uuid:
308 #check that exist
309 uuid = self._get_item_uuid("tenants", uuid, name, all_tenants)
310
311 payload_req = yaml.safe_dump(descriptor)
312
313 #print payload_req
314
315 URLrequest = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
316 self.logger.debug("openmano PUT %s %s", URLrequest, payload_req)
317 mano_response = requests.put(URLrequest, headers = self.headers_req, data=payload_req)
318 self.logger.debug("openmano response: %s", mano_response.text )
319
320 content = self._parse_yaml(mano_response.text, response=True)
321 if mano_response.status_code==200:
322 return content
323 else:
324 raise OpenmanoResponseException(str(content))
325
326 #TENANTS
327 def list_tenants(self, **kwargs):
328 '''Obtain a list of tenants
329 Params: can be filtered by 'uuid','name','description'
330 Return: Raises an exception on error
331 Obtain a dictionary with format {'tenants':[{tenant1_info},{tenant2_info},...]}}
332 '''
333 return self._list_item("tenants", all_tenants=None, filter_dict=kwargs)
334
335 def get_tenant(self, uuid=None, name=None):
336 '''Obtain the information of a tenant
337 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
338 Return: Raises an exception on error, not found, found several
339 Obtain a dictionary with format {'tenant':{tenant_info}}
340 '''
341 return self._get_item("tenants", uuid, name, all_tenants=None)
342
343 def delete_tenant(self, uuid=None, name=None):
344 '''Delete a tenant
345 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
346 Return: Raises an exception on error, not found, found several
347 Obtain a dictionary with format {'result': text indicating deleted}
348 '''
349 return self._del_item("tenants", uuid, name, all_tenants=None)
350
351 def create_tenant(self, descriptor=None, descriptor_format=None, name=None, description=None):
352 '''Creates a tenant
353 Params: must supply a descriptor or/and just a name
354 descriptor: with format {'tenant':{new_tenant_info}}
355 newtenant_info must contain 'name', and optionally 'description'
356 must be a dictionary or a json/yaml text.
357 name: the tenant name. Overwrite descriptor name if any
358 description: tenant descriptor.. Overwrite descriptor description if any
359 Return: Raises an exception on error
360 Obtain a dictionary with format {'tenant':{new_tenant_info}}
361 '''
362 if isinstance(descriptor, str):
363 descriptor = self._parse(descriptor, descriptor_format)
364 elif descriptor:
365 pass
366 elif name:
367 descriptor={"tenant": {"name": name}}
368 else:
369 raise OpenmanoBadParamsException("Missing descriptor")
370
371 if 'tenant' not in descriptor or len(descriptor)!=1:
372 raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
373 if name:
374 descriptor['tenant']['name'] = name
375 if description:
376 descriptor['tenant']['description'] = description
377
378 return self._create_item("tenants", descriptor, all_tenants=None)
379
380 def edit_tenant(self, uuid=None, name=None, descriptor=None, descriptor_format=None, new_name=None, new_description=None):
381 '''Edit the parameters of a tenant
382 Params: must supply a descriptor or/and a new_name or new_description
383 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
384 descriptor: with format {'tenant':{params to change info}}
385 must be a dictionary or a json/yaml text.
386 name: the tenant name. Overwrite descriptor name if any
387 description: tenant descriptor.. Overwrite descriptor description if any
388 Return: Raises an exception on error, not found or found several
389 Obtain a dictionary with format {'tenant':{newtenant_info}}
390 '''
391
392 if isinstance(descriptor, str):
393 descriptor = self.parse(descriptor, descriptor_format)
394 elif descriptor:
395 pass
396 elif new_name or new_description:
397 descriptor={"tenant": {}}
398 else:
399 raise OpenmanoBadParamsException("Missing descriptor")
400
401 if 'tenant' not in descriptor or len(descriptor)!=1:
402 raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
403 if new_name:
404 descriptor['tenant']['name'] = new_name
405 if new_description:
406 descriptor['tenant']['description'] = new_description
407
408 return self._edit_item("tenants", descriptor, uuid, name, all_tenants=None)
409
410 #DATACENTERS
411
412 def list_datacenters(self, all_tenants=False, **kwargs):
413 '''Obtain a list of datacenters, that are the VIM information at openmano
414 Params: can be filtered by 'uuid','name','vim_url','type'
415 Return: Raises an exception on error
416 Obtain a dictionary with format {'datacenters':[{datacenter1_info},{datacenter2_info},...]}}
417 '''
418 return self._list_item("datacenters", all_tenants, filter_dict=kwargs)
419
420 def get_datacenter(self, uuid=None, name=None, all_tenants=False):
421 '''Obtain the information of a datacenter
422 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
423 Return: Raises an exception on error, not found, found several
424 Obtain a dictionary with format {'datacenter':{datacenter_info}}
425 '''
426 return self._get_item("datacenters", uuid, name, all_tenants)
427
428 def delete_datacenter(self, uuid=None, name=None):
429 '''Delete a datacenter
430 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
431 Return: Raises an exception on error, not found, found several, not free
432 Obtain a dictionary with format {'result': text indicating deleted}
433 '''
434 if not uuid:
435 # check that exist
436 uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=True)
437 return self._del_item("datacenters", uuid, name, all_tenants=None)
438
439 def create_datacenter(self, descriptor=None, descriptor_format=None, name=None, vim_url=None, **kwargs):
440 #, type="openvim", public=False, description=None):
441 '''Creates a datacenter
442 Params: must supply a descriptor or/and just a name and vim_url
443 descriptor: with format {'datacenter':{new_datacenter_info}}
444 newdatacenter_info must contain 'name', 'vim_url', and optionally 'description'
445 must be a dictionary or a json/yaml text.
446 name: the datacenter name. Overwrite descriptor name if any
447 vim_url: the datacenter URL. Overwrite descriptor vim_url if any
448 vim_url_admin: the datacenter URL for administrative issues. Overwrite descriptor vim_url if any
449 vim_type: the datacenter type, can be openstack or openvim. Overwrite descriptor type if any
450 public: boolean, by default not public
451 description: datacenter description. Overwrite descriptor description if any
452 config: dictionary with extra configuration for the concrete datacenter
453 Return: Raises an exception on error
454 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
455 '''
456 if isinstance(descriptor, str):
457 descriptor = self.parse(descriptor, descriptor_format)
458 elif descriptor:
459 pass
460 elif name and vim_url:
461 descriptor={"datacenter": {"name": name, "vim_url": vim_url}}
462 else:
463 raise OpenmanoBadParamsException("Missing descriptor, or name and vim_url")
464
465 if 'datacenter' not in descriptor or len(descriptor)!=1:
466 raise OpenmanoBadParamsException("Descriptor must contain only one 'datacenter' field")
467 if name:
468 descriptor['datacenter']['name'] = name
469 if vim_url:
470 descriptor['datacenter']['vim_url'] = vim_url
471 for param in kwargs:
472 descriptor['datacenter'][param] = kwargs[param]
473
474 return self._create_item("datacenters", descriptor, all_tenants=None)
475
476 def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
477 '''Edit the parameters of a datacenter
478 Params: must supply a descriptor or/and a parameter to change
479 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
480 descriptor: with format {'datacenter':{params to change info}}
481 must be a dictionary or a json/yaml text.
482 parameters to change can be supplyied by the descriptor or as parameters:
483 new_name: the datacenter name
484 vim_url: the datacenter URL
485 vim_url_admin: the datacenter URL for administrative issues
486 vim_type: the datacenter type, can be openstack or openvim.
487 public: boolean, available to other tenants
488 description: datacenter description
489 Return: Raises an exception on error, not found or found several
490 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
491 '''
492
493 if isinstance(descriptor, str):
494 descriptor = self.parse(descriptor, descriptor_format)
495 elif descriptor:
496 pass
497 elif kwargs:
498 descriptor={"datacenter": {}}
499 else:
500 raise OpenmanoBadParamsException("Missing descriptor")
501
502 if 'datacenter' not in descriptor or len(descriptor)!=1:
503 raise OpenmanoBadParamsException("Descriptor must contain only one 'datacenter' field")
504 for param in kwargs:
505 if param=='new_name':
506 descriptor['datacenter']['name'] = kwargs[param]
507 else:
508 descriptor['datacenter'][param] = kwargs[param]
509 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
510
511 def attach_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, vim_user=None, vim_password=None, vim_tenant_name=None, vim_tenant_id=None):
512 #check that exist
513 uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=True)
514 tenant_text = "/"+self._get_tenant()
515
516 if isinstance(descriptor, str):
517 descriptor = self.parse(descriptor, descriptor_format)
518 elif descriptor:
519 pass
520 elif vim_user or vim_password or vim_tenant_name or vim_tenant_id:
521 descriptor={"datacenter": {}}
522 else:
523 raise OpenmanoBadParamsException("Missing descriptor or params")
524
525 if vim_user or vim_password or vim_tenant_name or vim_tenant_id:
526 #print args.name
527 try:
528 if vim_user:
529 descriptor['datacenter']['vim_user'] = vim_user
530 if vim_password:
531 descriptor['datacenter']['vim_password'] = vim_password
532 if vim_tenant_name:
533 descriptor['datacenter']['vim_tenant_name'] = vim_tenant_name
534 if vim_tenant_id:
535 descriptor['datacenter']['vim_tenant'] = vim_tenant_id
536 except (KeyError, TypeError) as e:
537 if str(e)=='datacenter': error_pos= "missing field 'datacenter'"
538 else: error_pos="wrong format"
539 raise OpenmanoBadParamsException("Wrong datacenter descriptor: " + error_pos)
540
541 payload_req = yaml.safe_dump(descriptor)
542 #print payload_req
543 URLrequest = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
544 self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
545 mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
546 self.logger.debug("openmano response: %s", mano_response.text )
547
548 content = self._parse_yaml(mano_response.text, response=True)
549 if mano_response.status_code==200:
550 return content
551 else:
552 raise OpenmanoResponseException(str(content))
553
554 def detach_datacenter(self, uuid=None, name=None):
555 if not uuid:
556 #check that exist
557 uuid = self._get_item_uuid("datacenters", uuid, name, all_tenants=False)
558 tenant_text = "/"+self._get_tenant()
559 URLrequest = "{}{}/datacenters/{}".format(self.endpoint_url, tenant_text, uuid)
560 self.logger.debug("openmano DELETE %s", URLrequest)
561 mano_response = requests.delete(URLrequest, headers = self.headers_req)
562 self.logger.debug("openmano response: %s", mano_response.text )
563
564 content = self._parse_yaml(mano_response.text, response=True)
565 if mano_response.status_code==200:
566 return content
567 else:
568 raise OpenmanoResponseException(str(content))
569
570 #VNFS
571 def list_vnfs(self, all_tenants=False, **kwargs):
572 '''Obtain a list of vnfs
573 Params: can be filtered by 'uuid','name','description','public', "tenant_id"
574 Return: Raises an exception on error
575 Obtain a dictionary with format {'vnfs':[{vnf1_info},{vnf2_info},...]}}
576 '''
577 return self._list_item("vnfs", all_tenants, kwargs)
578
579 def get_vnf(self, uuid=None, name=None, all_tenants=False):
580 '''Obtain the information of a vnf
581 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
582 Return: Raises an exception on error, not found, found several
583 Obtain a dictionary with format {'vnf':{vnf_info}}
584 '''
585 return self._get_item("vnfs", uuid, name, all_tenants)
586
587 def delete_vnf(self, uuid=None, name=None, all_tenants=False):
588 '''Delete a vnf
589 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
590 Return: Raises an exception on error, not found, found several, not free
591 Obtain a dictionary with format {'result': text indicating deleted}
592 '''
593 return self._del_item("vnfs", uuid, name, all_tenants)
594
595 def create_vnf(self, descriptor=None, descriptor_format=None, **kwargs):
596 '''Creates a vnf
597 Params: must supply a descriptor
598 descriptor: with format {'vnf':{new_vnf_info}}
599 must be a dictionary or a json/yaml text.
600 must be a dictionary or a json/yaml text.
601 Other parameters can be:
602 #TODO, revise
603 name: the vnf name. Overwrite descriptor name if any
604 image_path: Can be a string or a string list. Overwrite the image_path at descriptor
605 description: vnf descriptor.. Overwrite descriptor description if any
606 public: boolean, available to other tenants
607 class: user text for vnf classification
608 tenant_id: Propietary tenant
609 ...
610 Return: Raises an exception on error
611 Obtain a dictionary with format {'vnf':{new_vnf_info}}
612 '''
613 if isinstance(descriptor, str):
614 descriptor = self.parse(descriptor, descriptor_format)
615 elif descriptor:
616 pass
617 else:
618 raise OpenmanoBadParamsException("Missing descriptor")
619
620 try:
621 if "vnfd:vnfd-catalog" in descriptor or "vnfd-catalog" in descriptor:
622 api_version = "v3"
623 token = "vnfd"
624 vnfd_catalog = descriptor.get("vnfd:vnfd-catalog")
625 if not vnfd_catalog:
626 vnfd_catalog = descriptor.get("vnfd-catalog")
627 vnfds = vnfd_catalog.get("vnfd:vnfd")
628 if not vnfds:
629 vnfds = vnfd_catalog.get("vnfd")
630 vnfd = vnfds[0]
631 vdu_list = vnfd["vdu"]
632 elif "vnf" in descriptor: # old API
633 api_version = None
634 token = "vnfs"
635 vnfd = descriptor['vnf']
636 vdu_list = vnfd["VNFC"]
637 else:
638 raise OpenmanoBadParamsException("Invalid VNF Descriptor must contain only one 'vnf' field or vnd-catalog")
639 except (KeyError, TypeError) as e:
640 raise OpenmanoBadParamsException("Invalid VNF Descriptor. Missing field {}".format(e))
641
642 if kwargs:
643 try:
644 if kwargs.get('name'):
645 vnfd['name'] = kwargs['name']
646 if kwargs.get('description'):
647 vnfd['description'] = kwargs['description']
648 if kwargs.get('image_path'):
649 error_param = 'image_path'
650 image_list = kwargs['image_path'].split(",")
651 image_item = image_list.pop(0)
652 # print "image-path", image_path_
653 for vdu in vdu_list:
654 if api_version == "v3":
655 if vdu.get("image"):
656 if image_item:
657 vdu['image'] = image_item
658 if "image-checksum" in vdu:
659 del vdu["image-checksum"]
660 if image_list:
661 image_item = image_list.pop(0)
662 for vol in vdu.get("volumes", ()): # image name in volumes
663 if image_item:
664 vol["image"] = image_item
665 if "image-checksum" in vol:
666 del vol["image-checksum"]
667 if image_list:
668 image_item = image_list.pop(0)
669 else:
670 if image_item:
671 vdu['VNFC image'] = image_item
672 if "image name" in vdu:
673 del vdu["image name"]
674 if "image checksum" in vdu:
675 del vdu["image checksum"]
676 if image_list:
677 image_item = image_list.pop(0)
678 for vol in vdu.get('devices', ()):
679 if vol['type'] != 'disk':
680 continue
681 if image_item:
682 vol['image'] = image_item
683 if "image name" in vol:
684 del vol["image name"]
685 if "image checksum" in vol:
686 del vol["image checksum"]
687 if image_list:
688 image_item = image_list.pop(0)
689 if kwargs.get('image_name'): # image name precedes if both are supplied
690 error_param = 'image_name'
691 image_list = kwargs['image_name'].split(",")
692 image_item = image_list.pop(0)
693 for vdu in vdu_list:
694 if api_version == "v3":
695 if vdu.get("image"):
696 if image_item:
697 vdu['image'] = image_item
698 if "image-checksum" in vdu:
699 del vdu["image-checksum"]
700 if image_list:
701 image_item = image_list.pop(0)
702 for vol in vdu.get("volumes", ()): # image name in volumes
703 if image_item:
704 vol["image"] = image_item
705 if "image-checksum" in vol:
706 del vol["image-checksum"]
707 if image_list:
708 image_item = image_list.pop(0)
709 else:
710 if image_item:
711 vdu['image name'] = image_item
712 if "VNFC image" in vdu:
713 del vdu["VNFC image"]
714 if image_list:
715 image_item = image_list.pop(0)
716 for vol in vdu.get('devices', ()):
717 if vol['type'] != 'disk':
718 continue
719 if image_item:
720 vol['image name'] = image_item
721 if "image" in vol:
722 del vol["image"]
723 if "image checksum" in vol:
724 del vol["image checksum"]
725 if image_list:
726 image_item = image_list.pop(0)
727
728 if kwargs.get('image_checksum'):
729 error_param = 'image_checksum'
730 image_list = kwargs['image_checksum'].split(",")
731 image_item = image_list.pop(0)
732 for vdu in vdu_list:
733 if api_version == "v3":
734 if vdu.get("image"):
735 if image_item:
736 vdu['image-checksum'] = image_item
737 if image_list:
738 image_item = image_list.pop(0)
739 for vol in vdu.get("volumes", ()): # image name in volumes
740 if image_item:
741 vol["mage-checksum"] = image_item
742 if image_list:
743 image_item = image_list.pop(0)
744 else:
745 if image_item:
746 vdu['image checksum'] = image_item
747 if "VNFC image" in vdu:
748 del vdu["VNFC image"]
749 if image_list:
750 image_item = image_list.pop(0)
751 for vol in vdu.get('devices', ()):
752 if vol['type'] != 'disk':
753 continue
754 if image_item:
755 vol['image checksum'] = image_item
756 if "image" in vol:
757 del vol["image"]
758 if image_list:
759 image_item = image_list.pop(0)
760 except IndexError:
761 raise OpenmanoBadParamsException("{} contains more items than {} at descriptor".format(
762 error_param, "vnfd-catalog:vnfd:vdu" if api_version else "vnf:VNFC"))
763 except (KeyError, TypeError) as e:
764 raise OpenmanoBadParamsException("Invalid VNF Descriptor. Missing field {}".format(e))
765 return self._create_item(token, descriptor, api_version=api_version)
766
767 # def edit_vnf(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
768 # '''Edit the parameters of a vnf
769 # Params: must supply a descriptor or/and a parameters to change
770 # uuid or/and name. If only name is supplied, there must be only one or an exception is raised
771 # descriptor: with format {'vnf':{params to change info}}
772 # parameters to change can be supplyied by the descriptor or as parameters:
773 # new_name: the vnf name
774 # vim_url: the vnf URL
775 # vim_url_admin: the vnf URL for administrative issues
776 # vim_type: the vnf type, can be openstack or openvim.
777 # public: boolean, available to other tenants
778 # description: vnf description
779 # Return: Raises an exception on error, not found or found several
780 # Obtain a dictionary with format {'vnf':{new_vnf_info}}
781 # '''
782 #
783 # if isinstance(descriptor, str):
784 # descriptor = self.parse(descriptor, descriptor_format)
785 # elif descriptor:
786 # pass
787 # elif kwargs:
788 # descriptor={"vnf": {}}
789 # else:
790 # raise OpenmanoBadParamsException("Missing descriptor")
791 #
792 # if 'vnf' not in descriptor or len(descriptor)>2:
793 # raise OpenmanoBadParamsException("Descriptor must contain only one 'vnf' field")
794 # for param in kwargs:
795 # if param=='new_name':
796 # descriptor['vnf']['name'] = kwargs[param]
797 # else:
798 # descriptor['vnf'][param] = kwargs[param]
799 # return self._edit_item("vnfs", descriptor, uuid, name, all_tenants=None)
800
801 #SCENARIOS
802 def list_scenarios(self, all_tenants=False, **kwargs):
803 '''Obtain a list of scenarios
804 Params: can be filtered by 'uuid','name','description','public', "tenant_id"
805 Return: Raises an exception on error
806 Obtain a dictionary with format {'scenarios':[{scenario1_info},{scenario2_info},...]}}
807 '''
808 return self._list_item("scenarios", all_tenants, kwargs)
809
810 def get_scenario(self, uuid=None, name=None, all_tenants=False):
811 '''Obtain the information of a scenario
812 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
813 Return: Raises an exception on error, not found, found several
814 Obtain a dictionary with format {'scenario':{scenario_info}}
815 '''
816 return self._get_item("scenarios", uuid, name, all_tenants)
817
818 def delete_scenario(self, uuid=None, name=None, all_tenants=False):
819 '''Delete a scenario
820 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
821 Return: Raises an exception on error, not found, found several, not free
822 Obtain a dictionary with format {'result': text indicating deleted}
823 '''
824 return self._del_item("scenarios", uuid, name, all_tenants)
825
826 def create_scenario(self, descriptor=None, descriptor_format=None, **kwargs):
827 """Creates a scenario
828 Params: must supply a descriptor
829 descriptor: with format {'scenario':{new_scenario_info}}
830 must be a dictionary or a json/yaml text.
831 Other parameters can be:
832 name: the scenario name. Overwrite descriptor name if any
833 description: scenario descriptor.. Overwrite descriptor description if any
834 public: boolean, available to other tenants
835 tenant_id. Propietary tenant
836 Return: Raises an exception on error
837 Obtain a dictionary with format {'scenario':{new_scenario_info}}
838 """
839 if isinstance(descriptor, str):
840 descriptor = self.parse(descriptor, descriptor_format)
841 elif descriptor:
842 pass
843 else:
844 raise OpenmanoBadParamsException("Missing descriptor")
845
846 try:
847 if "nsd:nsd-catalog" in descriptor or "nsd-catalog" in descriptor:
848 api_version = "v3"
849 token = "nsd"
850 nsd_catalog = descriptor.get("nsd:nsd-catalog")
851 if not nsd_catalog:
852 nsd_catalog = descriptor.get("nsd-catalog")
853 nsds = nsd_catalog.get("nsd:nsd")
854 if not nsds:
855 nsds = nsd_catalog.get("nsd")
856 nsd = nsds[0]
857 elif "scenario" in descriptor: # old API
858 api_version = None
859 token = "scenarios"
860 nsd = descriptor['scenario']
861 else:
862 raise OpenmanoBadParamsException("Invalid NS Descriptor must contain only one 'scenario' field or nsd-catalog")
863 except (KeyError, TypeError) as e:
864 raise OpenmanoBadParamsException("Invalid NS Descriptor. Missing field {}".format(e))
865
866 for param in kwargs:
867 nsd[param] = kwargs[param]
868 return self._create_item(token, descriptor, api_version=api_version)
869
870 def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
871 '''Edit the parameters of a scenario
872 Params: must supply a descriptor or/and a parameters to change
873 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
874 descriptor: with format {'scenario':{params to change info}}
875 must be a dictionary or a json/yaml text.
876 parameters to change can be supplyied by the descriptor or as parameters:
877 new_name: the scenario name
878 public: boolean, available to other tenants
879 description: scenario description
880 tenant_id. Propietary tenant
881 Return: Raises an exception on error, not found or found several
882 Obtain a dictionary with format {'scenario':{new_scenario_info}}
883 '''
884
885 if isinstance(descriptor, str):
886 descriptor = self.parse(descriptor, descriptor_format)
887 elif descriptor:
888 pass
889 elif kwargs:
890 descriptor={"scenario": {}}
891 else:
892 raise OpenmanoBadParamsException("Missing descriptor")
893
894 if 'scenario' not in descriptor or len(descriptor)>2:
895 raise OpenmanoBadParamsException("Descriptor must contain only one 'scenario' field")
896 for param in kwargs:
897 if param=='new_name':
898 descriptor['scenario']['name'] = kwargs[param]
899 else:
900 descriptor['scenario'][param] = kwargs[param]
901 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
902
903
904 #INSTANCE-SCENARIOS
905 def list_instances(self, all_tenants=False, **kwargs):
906 '''Obtain a list of instances
907 Params: can be filtered by 'uuid','name','description','scenario_id', "tenant_id"
908 Return: Raises an exception on error
909 Obtain a dictionary with format {'instances':[{instance1_info},{instance2_info},...]}}
910 '''
911 return self._list_item("instances", all_tenants, kwargs)
912
913 def get_instance(self, uuid=None, name=None, all_tenants=False):
914 '''Obtain the information of a instance
915 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
916 Return: Raises an exception on error, not found, found several
917 Obtain a dictionary with format {'instance':{instance_info}}
918 '''
919 return self._get_item("instances", uuid, name, all_tenants)
920
921 def delete_instance(self, uuid=None, name=None, all_tenants=False):
922 '''Delete a instance
923 Params: uuid or/and name. If only name is supplied, there must be only one or an exception is raised
924 Return: Raises an exception on error, not found, found several, not free
925 Obtain a dictionary with format {'result': text indicating deleted}
926 '''
927 return self._del_item("instances", uuid, name, all_tenants)
928
929 def create_instance(self, descriptor=None, descriptor_format=None, name=None, **kwargs):
930 '''Creates a instance
931 Params: must supply a descriptor or/and a name and scenario
932 descriptor: with format {'instance':{new_instance_info}}
933 must be a dictionary or a json/yaml text.
934 name: the instance name. Overwrite descriptor name if any
935 Other parameters can be:
936 description: instance descriptor.. Overwrite descriptor description if any
937 datacenter_name, datacenter_id: datacenter where to be deployed
938 scenario_name, scenario_id: Scenario this instance is based on
939 Return: Raises an exception on error
940 Obtain a dictionary with format {'instance':{new_instance_info}}
941 '''
942 if isinstance(descriptor, str):
943 descriptor = self.parse(descriptor, descriptor_format)
944 elif descriptor:
945 pass
946 elif name and ("scenario_name" in kwargs or "scenario_id" in kwargs):
947 descriptor = {"instance": {"name": name}}
948 else:
949 raise OpenmanoBadParamsException("Missing descriptor")
950
951 if 'instance' not in descriptor or len(descriptor)>2:
952 raise OpenmanoBadParamsException("Descriptor must contain only one 'instance' field, and an optional version")
953 if name:
954 descriptor['instance']["name"] = name
955 if "scenario_name" in kwargs or "scenario_id" in kwargs:
956 descriptor['instance']["scenario"] = self._get_item_uuid("scenarios", kwargs.get("scenario_id"), kwargs.get("scenario_name"))
957 if "datacenter_name" in kwargs or "datacenter_id" in kwargs:
958 descriptor['instance']["datacenter"] = self._get_item_uuid("datacenters", kwargs.get("datacenter_id"), kwargs.get("datacenter_name"))
959 if "description" in kwargs:
960 descriptor['instance']["description"] = kwargs.get("description")
961 #for param in kwargs:
962 # descriptor['instance'][param] = kwargs[param]
963 if "datacenter" not in descriptor['instance']:
964 descriptor['instance']["datacenter"] = self._get_datacenter()
965 return self._create_item("instances", descriptor)
966
967 #VIM ACTIONS
968 def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
969 '''Perform an action over a vim
970 Params:
971 action: can be 'list', 'get'/'show', 'delete' or 'create'
972 item: can be 'tenants' or 'networks'
973 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
974 other parameters:
975 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
976 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
977 must be a dictionary or a json/yaml text.
978 name: for created tenant/net Overwrite descriptor name if any
979 description: tenant descriptor. Overwrite descriptor description if any
980
981 Return: Raises an exception on error
982 Obtain a dictionary with format {'tenant':{new_tenant_info}}
983 '''
984 if item not in ("tenants", "networks", "images"):
985 raise OpenmanoBadParamsException("Unknown value for item '{}', must be 'tenants', 'nets' or "
986 "images".format(str(item)))
987
988 image_actions = ['list','get','show','delete']
989 if item == "images" and action not in image_actions:
990 raise OpenmanoBadParamsException("Only available actions for item '{}' are {}\n"
991 "Requested action was '{}'".format(item, ', '.join(image_actions), action))
992 if all_tenants:
993 tenant_text = "/any"
994 else:
995 tenant_text = "/"+self._get_tenant()
996
997 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
998 datacenter = self._get_item_uuid("datacenters", kwargs.get("datacenter_id"), kwargs.get("datacenter_name"), all_tenants=all_tenants)
999 else:
1000 datacenter = self._get_datacenter()
1001
1002 if action=="list":
1003 URLrequest = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1004 self.logger.debug("GET %s", URLrequest )
1005 mano_response = requests.get(URLrequest, headers=self.headers_req)
1006 self.logger.debug("openmano response: %s", mano_response.text )
1007 content = self._parse_yaml(mano_response.text, response=True)
1008 if mano_response.status_code==200:
1009 return content
1010 else:
1011 raise OpenmanoResponseException(str(content))
1012 elif action=="get" or action=="show":
1013 URLrequest = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1014 self.logger.debug("GET %s", URLrequest )
1015 mano_response = requests.get(URLrequest, headers=self.headers_req)
1016 self.logger.debug("openmano response: %s", mano_response.text )
1017 content = self._parse_yaml(mano_response.text, response=True)
1018 if mano_response.status_code==200:
1019 return content
1020 else:
1021 raise OpenmanoResponseException(str(content))
1022 elif action=="delete":
1023 URLrequest = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1024 self.logger.debug("DELETE %s", URLrequest )
1025 mano_response = requests.delete(URLrequest, headers=self.headers_req)
1026 self.logger.debug("openmano response: %s", mano_response.text )
1027 content = self._parse_yaml(mano_response.text, response=True)
1028 if mano_response.status_code==200:
1029 return content
1030 else:
1031 raise OpenmanoResponseException(str(content))
1032 elif action=="create":
1033 if "descriptor" in kwargs:
1034 if isinstance(kwargs["descriptor"], str):
1035 descriptor = self._parse(kwargs["descriptor"], kwargs.get("descriptor_format") )
1036 else:
1037 descriptor = kwargs["descriptor"]
1038 elif "name" in kwargs:
1039 descriptor={item[:-1]: {"name": kwargs["name"]}}
1040 else:
1041 raise OpenmanoResponseException("Missing descriptor")
1042
1043 if item[:-1] not in descriptor or len(descriptor)!=1:
1044 raise OpenmanoBadParamsException("Descriptor must contain only one 'tenant' field")
1045 if "name" in kwargs:
1046 descriptor[ item[:-1] ]['name'] = kwargs["name"]
1047 if "description" in kwargs:
1048 descriptor[ item[:-1] ]['description'] = kwargs["description"]
1049 payload_req = yaml.safe_dump(descriptor)
1050 #print payload_req
1051 URLrequest = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1052 self.logger.debug("openmano POST %s %s", URLrequest, payload_req)
1053 mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req)
1054 self.logger.debug("openmano response: %s", mano_response.text )
1055 content = self._parse_yaml(mano_response.text, response=True)
1056 if mano_response.status_code==200:
1057 return content
1058 else:
1059 raise OpenmanoResponseException(str(content))
1060 else:
1061 raise OpenmanoBadParamsException("Unknown value for action '{}".format(str(action)))
1062