b9249068d980c10593652d75748d64400e096d36
[osm/LCM.git] / osm_lcm / ROclient.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
17 # under the License.
18 #
19 ##
20
21 """
22 asyncio RO python client to interact with RO-server
23 """
24
25 import asyncio
26 import aiohttp
27 import json
28 import yaml
29 import logging
30 from urllib.parse import quote
31 from uuid import UUID
32 from copy import deepcopy
33
34 __author__ = "Alfonso Tierno"
35 __date__ = "$09-Jan-2018 09:09:48$"
36 __version__ = "0.1.2"
37 version_date = "2018-05-16"
38 requests = None
39
40
41 class ROClientException(Exception):
42 def __init__(self, message, http_code=400):
43 """Common Exception for all RO client exceptions"""
44 self.http_code = http_code
45 Exception.__init__(self, message)
46
47
48 def remove_envelop(item, indata=None):
49 """
50 Obtain the useful data removing the envelop. It goes through the vnfd or nsd catalog and returns the
51 vnfd or nsd content
52 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
53 :param indata: Content to be inspected
54 :return: the useful part of indata (a reference, not a new dictionay)
55 """
56 clean_indata = indata
57 if not indata:
58 return {}
59 if item == "vnfd":
60 if clean_indata.get("vnfd:vnfd-catalog"):
61 clean_indata = clean_indata["vnfd:vnfd-catalog"]
62 elif clean_indata.get("vnfd-catalog"):
63 clean_indata = clean_indata["vnfd-catalog"]
64 if clean_indata.get("vnfd"):
65 if (
66 not isinstance(clean_indata["vnfd"], list)
67 or len(clean_indata["vnfd"]) != 1
68 ):
69 raise ROClientException("'vnfd' must be a list only one element")
70 clean_indata = clean_indata["vnfd"][0]
71 elif item == "nsd":
72 if clean_indata.get("nsd:nsd-catalog"):
73 clean_indata = clean_indata["nsd:nsd-catalog"]
74 elif clean_indata.get("nsd-catalog"):
75 clean_indata = clean_indata["nsd-catalog"]
76 if clean_indata.get("nsd"):
77 if (
78 not isinstance(clean_indata["nsd"], list)
79 or len(clean_indata["nsd"]) != 1
80 ):
81 raise ROClientException("'nsd' must be a list only one element")
82 clean_indata = clean_indata["nsd"][0]
83 elif item == "sdn":
84 if len(indata) == 1 and "sdn_controller" in indata:
85 clean_indata = indata["sdn_controller"]
86 elif item == "tenant":
87 if len(indata) == 1 and "tenant" in indata:
88 clean_indata = indata["tenant"]
89 elif item in ("vim", "vim_account", "datacenters"):
90 if len(indata) == 1 and "datacenter" in indata:
91 clean_indata = indata["datacenter"]
92 elif item == "wim":
93 if len(indata) == 1 and "wim" in indata:
94 clean_indata = indata["wim"]
95 elif item == "wim_account":
96 if len(indata) == 1 and "wim_account" in indata:
97 clean_indata = indata["wim_account"]
98 elif item == "ns" or item == "instances":
99 if len(indata) == 1 and "instance" in indata:
100 clean_indata = indata["instance"]
101 else:
102 assert False, "remove_envelop with unknown item {}".format(item)
103
104 return clean_indata
105
106
107 class ROClient:
108 headers_req = {"Accept": "application/yaml", "content-type": "application/yaml"}
109 client_to_RO = {
110 "tenant": "tenants",
111 "vim": "datacenters",
112 "vim_account": "datacenters",
113 "sdn": "sdn_controllers",
114 "vnfd": "vnfs",
115 "nsd": "scenarios",
116 "wim": "wims",
117 "wim_account": "wims",
118 "ns": "instances",
119 }
120 mandatory_for_create = {
121 "tenant": ("name",),
122 "vnfd": ("name", "id"),
123 "nsd": ("name", "id"),
124 "ns": ("name", "scenario", "datacenter"),
125 "vim": ("name", "vim_url"),
126 "wim": ("name", "wim_url"),
127 "vim_account": (),
128 "wim_account": (),
129 "sdn": ("name", "type"),
130 }
131 timeout_large = 120
132 timeout_short = 30
133
134 def __init__(self, uri, **kwargs):
135 self.uri = uri
136
137 self.username = kwargs.get("username")
138 self.password = kwargs.get("password")
139 self.tenant_id_name = kwargs.get("tenant")
140 self.tenant = None
141 self.datacenter_id_name = kwargs.get("datacenter")
142 self.datacenter = None
143 logger_name = kwargs.get("logger_name", "lcm.ro")
144 self.logger = logging.getLogger(logger_name)
145 if kwargs.get("loglevel"):
146 self.logger.setLevel(kwargs["loglevel"])
147 global requests
148 requests = kwargs.get("TODO remove")
149
150 def __getitem__(self, index):
151 if index == "tenant":
152 return self.tenant_id_name
153 elif index == "datacenter":
154 return self.datacenter_id_name
155 elif index == "username":
156 return self.username
157 elif index == "password":
158 return self.password
159 elif index == "uri":
160 return self.uri
161 else:
162 raise KeyError("Invalid key '{}'".format(index))
163
164 def __setitem__(self, index, value):
165 if index == "tenant":
166 self.tenant_id_name = value
167 elif index == "datacenter" or index == "vim":
168 self.datacenter_id_name = value
169 elif index == "username":
170 self.username = value
171 elif index == "password":
172 self.password = value
173 elif index == "uri":
174 self.uri = value
175 else:
176 raise KeyError("Invalid key '{}'".format(index))
177 self.tenant = None # force to reload tenant with different credentials
178 self.datacenter = None # force to reload datacenter with different credentials
179
180 @staticmethod
181 def _parse(descriptor, descriptor_format, response=False):
182 if (
183 descriptor_format
184 and descriptor_format != "json"
185 and descriptor_format != "yaml"
186 ):
187 raise ROClientException(
188 "'descriptor_format' must be a 'json' or 'yaml' text"
189 )
190 if descriptor_format != "json":
191 try:
192 return yaml.safe_load(descriptor)
193 except yaml.YAMLError as exc:
194 error_pos = ""
195 if hasattr(exc, "problem_mark"):
196 mark = exc.problem_mark
197 error_pos = " at line:{} column:{}s".format(
198 mark.line + 1, mark.column + 1
199 )
200 error_text = "yaml format error" + error_pos
201 elif descriptor_format != "yaml":
202 try:
203 return json.loads(descriptor)
204 except Exception as e:
205 if response:
206 error_text = "json format error" + str(e)
207
208 if response:
209 raise ROClientException(error_text)
210 raise ROClientException(error_text)
211
212 @staticmethod
213 def _parse_error_yaml(descriptor):
214 json_error = None
215 try:
216 json_error = yaml.safe_load(descriptor)
217 return json_error["error"]["description"]
218 except Exception:
219 return str(json_error or descriptor)
220
221 @staticmethod
222 def _parse_yaml(descriptor, response=False):
223 try:
224 return yaml.safe_load(descriptor)
225 except yaml.YAMLError as exc:
226 error_pos = ""
227 if hasattr(exc, "problem_mark"):
228 mark = exc.problem_mark
229 error_pos = " at line:{} column:{}s".format(
230 mark.line + 1, mark.column + 1
231 )
232 error_text = "yaml format error" + error_pos
233 if response:
234 raise ROClientException(error_text)
235 raise ROClientException(error_text)
236
237 @staticmethod
238 def check_if_uuid(uuid_text):
239 """
240 Check if text correspond to an uuid foramt
241 :param uuid_text:
242 :return: True if it is an uuid False if not
243 """
244 try:
245 UUID(uuid_text)
246 return True
247 except Exception:
248 return False
249
250 @staticmethod
251 def _create_envelop(item, indata=None):
252 """
253 Returns a new dict that incledes indata with the expected envelop
254 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
255 :param indata: Content to be enveloped
256 :return: a new dic with {<envelop>: {indata} } where envelop can be e.g. tenant, datacenter, ...
257 """
258 if item == "vnfd":
259 return {"vnfd-catalog": {"vnfd": [indata]}}
260 elif item == "nsd":
261 return {"nsd-catalog": {"nsd": [indata]}}
262 elif item == "tenant":
263 return {"tenant": indata}
264 elif item in ("vim", "vim_account", "datacenter"):
265 return {"datacenter": indata}
266 elif item == "wim":
267 return {"wim": indata}
268 elif item == "wim_account":
269 return {"wim_account": indata}
270 elif item == "ns" or item == "instances":
271 return {"instance": indata}
272 elif item == "sdn":
273 return {"sdn_controller": indata}
274 else:
275 assert False, "_create_envelop with unknown item {}".format(item)
276
277 @staticmethod
278 def update_descriptor(desc, kwargs):
279 desc = deepcopy(desc) # do not modify original descriptor
280 try:
281 for k, v in kwargs.items():
282 update_content = desc
283 kitem_old = None
284 klist = k.split(".")
285 for kitem in klist:
286 if kitem_old is not None:
287 update_content = update_content[kitem_old]
288 if isinstance(update_content, dict):
289 kitem_old = kitem
290 elif isinstance(update_content, list):
291 kitem_old = int(kitem)
292 else:
293 raise ROClientException(
294 "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(
295 k, kitem
296 )
297 )
298 if v == "__DELETE__":
299 del update_content[kitem_old]
300 else:
301 update_content[kitem_old] = v
302 return desc
303 except KeyError:
304 raise ROClientException(
305 "Invalid query string '{}'. Descriptor does not contain '{}'".format(
306 k, kitem_old
307 )
308 )
309 except ValueError:
310 raise ROClientException(
311 "Invalid query string '{}'. Expected integer index list instead of '{}'".format(
312 k, kitem
313 )
314 )
315 except IndexError:
316 raise ROClientException(
317 "Invalid query string '{}'. Index '{}' out of range".format(
318 k, kitem_old
319 )
320 )
321
322 @staticmethod
323 def check_ns_status(ns_descriptor):
324 """
325 Inspect RO instance descriptor and indicates the status
326 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
327 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
328 """
329 error_list = []
330 total = {"VMs": 0, "networks": 0, "SDN_networks": 0}
331 done = {"VMs": 0, "networks": 0, "SDN_networks": 0}
332
333 def _get_ref(desc):
334 # return an identification for the network or vm. Try vim_id if exist, if not descriptor id for net
335 if desc.get("vim_net_id"):
336 return "'vim-net-id={}'".format(desc["vim_net_id"])
337 elif desc.get("ns_net_osm_id"):
338 return "'nsd-vld-id={}'".format(desc["ns_net_osm_id"])
339 elif desc.get("vnf_net_osm_id"):
340 return "'vnfd-vld-id={}'".format(desc["vnf_net_osm_id"])
341 # for VM
342 elif desc.get("vim_vm_id"):
343 return "'vim-vm-id={}'".format(desc["vim_vm_id"])
344 elif desc.get("vdu_osm_id"):
345 return "'vnfd-vdu-id={}'".format(desc["vdu_osm_id"])
346 else:
347 return ""
348
349 def _get_sdn_ref(sce_net_id):
350 # look for the network associated to the SDN network and obtain the identification
351 net = next(
352 (x for x in ns_descriptor["nets"] if x.get("sce_net_id") == sce_net_id),
353 None,
354 )
355 if not sce_net_id or not net:
356 return ""
357 return _get_ref(net)
358
359 try:
360 total["networks"] = len(ns_descriptor["nets"])
361 for net in ns_descriptor["nets"]:
362 if net["status"] in ("ERROR", "VIM_ERROR"):
363 error_list.append(
364 "Error at VIM network {}: {}".format(
365 _get_ref(net), net["error_msg"]
366 )
367 )
368 elif net["status"] == "ACTIVE":
369 done["networks"] += 1
370
371 total["SDN_networks"] = len(ns_descriptor["sdn_nets"])
372 for sdn_net in ns_descriptor["sdn_nets"]:
373 if sdn_net["status"] in ("ERROR", "VIM_ERROR", "WIM_ERROR"):
374 error_list.append(
375 "Error at SDN network {}: {}".format(
376 _get_sdn_ref(sdn_net.get("sce_net_id")),
377 sdn_net["error_msg"],
378 )
379 )
380 elif sdn_net["status"] == "ACTIVE":
381 done["SDN_networks"] += 1
382
383 for vnf in ns_descriptor["vnfs"]:
384 for vm in vnf["vms"]:
385 total["VMs"] += 1
386 if vm["status"] in ("ERROR", "VIM_ERROR"):
387 error_list.append(
388 "Error at VIM VM {}: {}".format(
389 _get_ref(vm), vm["error_msg"]
390 )
391 )
392 elif vm["status"] == "ACTIVE":
393 done["VMs"] += 1
394 if error_list:
395 # skip errors caused because other dependendent task is on error
396 return "ERROR", "; ".join(
397 [
398 el
399 for el in error_list
400 if "because depends on failed ACTION" not in el
401 ]
402 )
403 if all(total[x] == done[x] for x in total): # DONE == TOTAL for all items
404 return "ACTIVE", str(
405 {x: total[x] for x in total if total[x]}
406 ) # print only those which value is not 0
407 else:
408 return "BUILD", str(
409 {x: "{}/{}".format(done[x], total[x]) for x in total if total[x]}
410 )
411 # print done/total for each item if total is not 0
412 except Exception as e:
413 raise ROClientException(
414 "Unexpected RO ns descriptor. Wrong version? {}".format(e)
415 ) from e
416
417 @staticmethod
418 def check_action_status(action_descriptor):
419 """
420 Inspect RO instance descriptor and indicates the status
421 :param action_descriptor: action instance descriptor obtained with self.show("ns", "action")
422 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
423 """
424 net_total = 0
425 vm_total = 0
426 net_done = 0
427 vm_done = 0
428 other_total = 0
429 other_done = 0
430
431 for vim_action_set in action_descriptor["actions"]:
432 for vim_action in vim_action_set["vim_wim_actions"]:
433 if vim_action["item"] == "instance_vms":
434 vm_total += 1
435 elif vim_action["item"] == "instance_nets":
436 net_total += 1
437 else:
438 other_total += 1
439 if vim_action["status"] == "FAILED":
440 return "ERROR", vim_action["error_msg"]
441 elif vim_action["status"] in ("DONE", "SUPERSEDED", "FINISHED"):
442 if vim_action["item"] == "instance_vms":
443 vm_done += 1
444 elif vim_action["item"] == "instance_nets":
445 net_done += 1
446 else:
447 other_done += 1
448
449 if net_total == net_done and vm_total == vm_done and other_total == other_done:
450 return "ACTIVE", "VMs {}, networks: {}, other: {} ".format(
451 vm_total, net_total, other_total
452 )
453 else:
454 return "BUILD", "VMs: {}/{}, networks: {}/{}, other: {}/{}".format(
455 vm_done, vm_total, net_done, net_total, other_done, other_total
456 )
457
458 @staticmethod
459 def get_ns_vnf_info(ns_descriptor):
460 """
461 Get a dict with the VIM_id, ip_addresses, mac_addresses of every vnf and vdu
462 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
463 :return: dict with:
464 <member_vnf_index>:
465 ip_address: XXXX,
466 vdur:
467 <vdu_osm_id>:
468 ip_address: XXX
469 vim_id: XXXX
470 interfaces:
471 <name>:
472 ip_address: XXX
473 mac_address: XXX
474 """
475 ns_info = {}
476 for vnf in ns_descriptor["vnfs"]:
477 if not vnf.get("ip_address") and vnf.get("vms"):
478 raise ROClientException(
479 "ns member_vnf_index '{}' has no IP address".format(
480 vnf["member_vnf_index"]
481 ),
482 http_code=409,
483 )
484 vnfr_info = {"ip_address": vnf.get("ip_address"), "vdur": {}}
485 for vm in vnf["vms"]:
486 vdur = {
487 "vim_id": vm.get("vim_vm_id"),
488 "ip_address": vm.get("ip_address"),
489 "interfaces": {},
490 }
491 for iface in vm["interfaces"]:
492 if iface.get("type") == "mgmt" and not iface.get("ip_address"):
493 raise ROClientException(
494 "ns member_vnf_index '{}' vm '{}' management interface '{}' has no IP "
495 "address".format(
496 vnf["member_vnf_index"],
497 vm["vdu_osm_id"],
498 iface["external_name"],
499 ),
500 http_code=409,
501 )
502 vdur["interfaces"][iface["internal_name"]] = {
503 "ip_address": iface.get("ip_address"),
504 "mac_address": iface.get("mac_address"),
505 "vim_id": iface.get("vim_interface_id"),
506 }
507 vnfr_info["vdur"][vm["vdu_osm_id"]] = vdur
508 ns_info[str(vnf["member_vnf_index"])] = vnfr_info
509 return ns_info
510
511 async def _get_item_uuid(self, session, item, item_id_name, all_tenants=False):
512 if all_tenants:
513 tenant_text = "/any"
514 elif all_tenants is None:
515 tenant_text = ""
516 else:
517 if not self.tenant:
518 await self._get_tenant(session)
519 tenant_text = "/" + self.tenant
520
521 item_id = 0
522 url = "{}{}/{}".format(self.uri, tenant_text, item)
523 if self.check_if_uuid(item_id_name):
524 item_id = item_id_name
525 url += "/" + item_id_name
526 elif (
527 item_id_name and item_id_name.startswith("'") and item_id_name.endswith("'")
528 ):
529 item_id_name = item_id_name[1:-1]
530 self.logger.debug("RO GET %s", url)
531 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
532 async with session.get(url, headers=self.headers_req) as response:
533 response_text = await response.read()
534 self.logger.debug(
535 "GET {} [{}] {}".format(url, response.status, response_text[:100])
536 )
537 if response.status == 404: # NOT_FOUND
538 raise ROClientException(
539 "No {} found with id '{}'".format(item[:-1], item_id_name),
540 http_code=404,
541 )
542 if response.status >= 300:
543 raise ROClientException(
544 self._parse_error_yaml(response_text), http_code=response.status
545 )
546 content = self._parse_yaml(response_text, response=True)
547
548 if item_id:
549 return item_id
550 desc = content[item]
551 assert isinstance(
552 desc, list
553 ), "_get_item_uuid get a non dict with a list inside {}".format(type(desc))
554 uuid = None
555 for i in desc:
556 if item_id_name and i["name"] != item_id_name:
557 continue
558 if uuid: # found more than one
559 raise ROClientException(
560 "Found more than one {} with name '{}'. uuid must be used".format(
561 item, item_id_name
562 ),
563 http_code=404,
564 )
565 uuid = i["uuid"]
566 if not uuid:
567 raise ROClientException(
568 "No {} found with name '{}'".format(item[:-1], item_id_name),
569 http_code=404,
570 )
571 return uuid
572
573 async def _get_item(
574 self,
575 session,
576 item,
577 item_id_name,
578 extra_item=None,
579 extra_item_id=None,
580 all_tenants=False,
581 ):
582 if all_tenants:
583 tenant_text = "/any"
584 elif all_tenants is None:
585 tenant_text = ""
586 else:
587 if not self.tenant:
588 await self._get_tenant(session)
589 tenant_text = "/" + self.tenant
590
591 if self.check_if_uuid(item_id_name):
592 uuid = item_id_name
593 else:
594 # check that exist
595 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
596
597 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, uuid)
598 if extra_item:
599 url += "/" + extra_item
600 if extra_item_id:
601 url += "/" + extra_item_id
602 self.logger.debug("GET %s", url)
603 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
604 async with session.get(url, headers=self.headers_req) as response:
605 response_text = await response.read()
606 self.logger.debug(
607 "GET {} [{}] {}".format(url, response.status, response_text[:100])
608 )
609 if response.status >= 300:
610 raise ROClientException(
611 self._parse_error_yaml(response_text), http_code=response.status
612 )
613
614 return self._parse_yaml(response_text, response=True)
615
616 async def _get_tenant(self, session):
617 if not self.tenant:
618 self.tenant = await self._get_item_uuid(
619 session, "tenants", self.tenant_id_name, None
620 )
621 return self.tenant
622
623 async def _get_datacenter(self, session):
624 if not self.tenant:
625 await self._get_tenant(session)
626 if not self.datacenter:
627 self.datacenter = await self._get_item_uuid(
628 session, "datacenters", self.datacenter_id_name, True
629 )
630 return self.datacenter
631
632 async def _create_item(
633 self,
634 session,
635 item,
636 descriptor,
637 item_id_name=None,
638 action=None,
639 all_tenants=False,
640 ):
641 if all_tenants:
642 tenant_text = "/any"
643 elif all_tenants is None:
644 tenant_text = ""
645 else:
646 if not self.tenant:
647 await self._get_tenant(session)
648 tenant_text = "/" + self.tenant
649 payload_req = yaml.safe_dump(descriptor)
650 # print payload_req
651
652 api_version_text = ""
653 if item == "vnfs":
654 # assumes version v3 only
655 api_version_text = "/v3"
656 item = "vnfd"
657 elif item == "scenarios":
658 # assumes version v3 only
659 api_version_text = "/v3"
660 item = "nsd"
661
662 if not item_id_name:
663 uuid = ""
664 elif self.check_if_uuid(item_id_name):
665 uuid = "/{}".format(item_id_name)
666 else:
667 # check that exist
668 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
669 uuid = "/{}".format(uuid)
670 if not action:
671 action = ""
672 else:
673 action = "/{}".format(action)
674
675 url = "{}{apiver}{tenant}/{item}{id}{action}".format(
676 self.uri,
677 apiver=api_version_text,
678 tenant=tenant_text,
679 item=item,
680 id=uuid,
681 action=action,
682 )
683 self.logger.debug("RO POST %s %s", url, payload_req)
684 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
685 async with session.post(
686 url, headers=self.headers_req, data=payload_req
687 ) as response:
688 response_text = await response.read()
689 self.logger.debug(
690 "POST {} [{}] {}".format(url, response.status, response_text[:100])
691 )
692 if response.status >= 300:
693 raise ROClientException(
694 self._parse_error_yaml(response_text), http_code=response.status
695 )
696
697 return self._parse_yaml(response_text, response=True)
698
699 async def _del_item(self, session, item, item_id_name, all_tenants=False):
700 if all_tenants:
701 tenant_text = "/any"
702 elif all_tenants is None:
703 tenant_text = ""
704 else:
705 if not self.tenant:
706 await self._get_tenant(session)
707 tenant_text = "/" + self.tenant
708 if not self.check_if_uuid(item_id_name):
709 # check that exist
710 _all_tenants = all_tenants
711 if item in ("datacenters", "wims"):
712 _all_tenants = True
713 uuid = await self._get_item_uuid(
714 session, item, item_id_name, all_tenants=_all_tenants
715 )
716 else:
717 uuid = item_id_name
718
719 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, uuid)
720 self.logger.debug("DELETE %s", url)
721 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
722 async with session.delete(url, headers=self.headers_req) as response:
723 response_text = await response.read()
724 self.logger.debug(
725 "DELETE {} [{}] {}".format(url, response.status, response_text[:100])
726 )
727 if response.status >= 300:
728 raise ROClientException(
729 self._parse_error_yaml(response_text), http_code=response.status
730 )
731
732 return self._parse_yaml(response_text, response=True)
733
734 async def _list_item(self, session, item, all_tenants=False, filter_dict=None):
735 if all_tenants:
736 tenant_text = "/any"
737 elif all_tenants is None:
738 tenant_text = ""
739 else:
740 if not self.tenant:
741 await self._get_tenant(session)
742 tenant_text = "/" + self.tenant
743
744 url = "{}{}/{}".format(self.uri, tenant_text, item)
745 separator = "?"
746 if filter_dict:
747 for k in filter_dict:
748 url += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
749 separator = "&"
750 self.logger.debug("RO GET %s", url)
751 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
752 async with session.get(url, headers=self.headers_req) as response:
753 response_text = await response.read()
754 self.logger.debug(
755 "GET {} [{}] {}".format(url, response.status, response_text[:100])
756 )
757 if response.status >= 300:
758 raise ROClientException(
759 self._parse_error_yaml(response_text), http_code=response.status
760 )
761
762 return self._parse_yaml(response_text, response=True)
763
764 async def _edit_item(self, session, item, item_id, descriptor, all_tenants=False):
765 if all_tenants:
766 tenant_text = "/any"
767 elif all_tenants is None:
768 tenant_text = ""
769 else:
770 if not self.tenant:
771 await self._get_tenant(session)
772 tenant_text = "/" + self.tenant
773
774 payload_req = yaml.safe_dump(descriptor)
775
776 # print payload_req
777 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, item_id)
778 self.logger.debug("RO PUT %s %s", url, payload_req)
779 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
780 async with session.put(
781 url, headers=self.headers_req, data=payload_req
782 ) as response:
783 response_text = await response.read()
784 self.logger.debug(
785 "PUT {} [{}] {}".format(url, response.status, response_text[:100])
786 )
787 if response.status >= 300:
788 raise ROClientException(
789 self._parse_error_yaml(response_text), http_code=response.status
790 )
791
792 return self._parse_yaml(response_text, response=True)
793
794 async def get_version(self):
795 """
796 Obtain RO server version.
797 :return: a list with integers ["major", "minor", "release"]. Raises ROClientException on Error,
798 """
799 try:
800 response_text = ""
801 async with aiohttp.ClientSession() as session:
802 url = "{}/version".format(self.uri)
803 self.logger.debug("RO GET %s", url)
804 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
805 async with session.get(url, headers=self.headers_req) as response:
806 response_text = await response.read()
807 self.logger.debug(
808 "GET {} [{}] {}".format(
809 url, response.status, response_text[:100]
810 )
811 )
812 if response.status >= 300:
813 raise ROClientException(
814 self._parse_error_yaml(response_text),
815 http_code=response.status,
816 )
817
818 for word in str(response_text).split(" "):
819 if "." in word:
820 version_text, _, _ = word.partition("-")
821 return version_text
822 raise ROClientException(
823 "Got invalid version text: '{}'".format(response_text),
824 http_code=500,
825 )
826 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
827 raise ROClientException(e, http_code=504)
828 except asyncio.TimeoutError:
829 raise ROClientException("Timeout", http_code=504)
830 except Exception as e:
831 self.logger.critical(
832 "Got invalid version text: '{}'; causing exception {}".format(
833 response_text, str(e)
834 )
835 )
836 raise ROClientException(
837 "Got invalid version text: '{}'; causing exception {}".format(
838 response_text, e
839 ),
840 http_code=500,
841 )
842
843 async def get_list(self, item, all_tenants=False, filter_by=None):
844 """
845 List of items filtered by the contents in the dictionary "filter_by".
846 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
847 :param all_tenants: True if not filtering by tenant. Only allowed for admin
848 :param filter_by: dictionary with filtering
849 :return: a list of dict. It can be empty. Raises ROClientException on Error,
850 """
851 try:
852 if item not in self.client_to_RO:
853 raise ROClientException("Invalid item {}".format(item))
854 if item == "tenant":
855 all_tenants = None
856 async with aiohttp.ClientSession() as session:
857 content = await self._list_item(
858 session,
859 self.client_to_RO[item],
860 all_tenants=all_tenants,
861 filter_dict=filter_by,
862 )
863 if isinstance(content, dict):
864 if len(content) == 1:
865 for _, v in content.items():
866 return v
867 return content.values()[0]
868 else:
869 raise ROClientException(
870 "Output not a list neither dict with len equal 1", http_code=500
871 )
872 return content
873 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
874 raise ROClientException(e, http_code=504)
875 except asyncio.TimeoutError:
876 raise ROClientException("Timeout", http_code=504)
877
878 async def show(
879 self,
880 item,
881 item_id_name=None,
882 extra_item=None,
883 extra_item_id=None,
884 all_tenants=False,
885 ):
886 """
887 Obtain the information of an item from its id or name
888 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
889 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
890 :param extra_item: if supplied, it is used to add to the URL.
891 Can be 'action' if item='ns'; 'networks' or'images' if item='vim'
892 :param extra_item_id: if supplied, it is used get details of a concrete extra_item.
893 :param all_tenants: True if not filtering by tenant. Only allowed for admin
894 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
895 """
896 try:
897 if item not in self.client_to_RO:
898 raise ROClientException("Invalid item {}".format(item))
899 if item == "tenant":
900 all_tenants = None
901 elif item == "vim":
902 all_tenants = True
903 elif item == "vim_account":
904 all_tenants = False
905
906 async with aiohttp.ClientSession() as session:
907 content = await self._get_item(
908 session,
909 self.client_to_RO[item],
910 item_id_name,
911 extra_item=extra_item,
912 extra_item_id=extra_item_id,
913 all_tenants=all_tenants,
914 )
915 return remove_envelop(item, content)
916 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
917 raise ROClientException(e, http_code=504)
918 except asyncio.TimeoutError:
919 raise ROClientException("Timeout", http_code=504)
920
921 async def delete(self, item, item_id_name=None, all_tenants=False):
922 """
923 Delete the information of an item from its id or name
924 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
925 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
926 :param all_tenants: True if not filtering by tenant. Only allowed for admin
927 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
928 """
929 try:
930 if item not in self.client_to_RO:
931 raise ROClientException("Invalid item {}".format(item))
932 if item in ("tenant", "vim", "wim"):
933 all_tenants = None
934
935 async with aiohttp.ClientSession() as session:
936 result = await self._del_item(
937 session,
938 self.client_to_RO[item],
939 item_id_name,
940 all_tenants=all_tenants,
941 )
942 # in case of ns delete, get the action_id embeded in text
943 if item == "ns" and result.get("result"):
944 _, _, action_id = result["result"].partition("action_id=")
945 action_id, _, _ = action_id.partition(" ")
946 if action_id:
947 result["action_id"] = action_id
948 return result
949 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
950 raise ROClientException(e, http_code=504)
951 except asyncio.TimeoutError:
952 raise ROClientException("Timeout", http_code=504)
953
954 async def edit(
955 self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs
956 ):
957 """Edit an item
958 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
959 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
960 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
961 :param descriptor_format: Can be 'json' or 'yaml'
962 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
963 keys can be a dot separated list to specify elements inside dict
964 :return: dictionary with the information or raises ROClientException on Error
965 """
966 try:
967 if isinstance(descriptor, str):
968 descriptor = self._parse(descriptor, descriptor_format)
969 elif descriptor:
970 pass
971 else:
972 descriptor = {}
973
974 if item not in self.client_to_RO:
975 raise ROClientException("Invalid item {}".format(item))
976 desc = remove_envelop(item, descriptor)
977
978 # Override descriptor with kwargs
979 if kwargs:
980 desc = self.update_descriptor(desc, kwargs)
981 all_tenants = False
982 if item in ("tenant", "vim"):
983 all_tenants = None
984
985 create_desc = self._create_envelop(item, desc)
986
987 async with aiohttp.ClientSession() as session:
988 _all_tenants = all_tenants
989 if item == "vim":
990 _all_tenants = True
991 item_id = await self._get_item_uuid(
992 session,
993 self.client_to_RO[item],
994 item_id_name,
995 all_tenants=_all_tenants,
996 )
997 if item == "vim":
998 _all_tenants = None
999 # await self._get_tenant(session)
1000 outdata = await self._edit_item(
1001 session,
1002 self.client_to_RO[item],
1003 item_id,
1004 create_desc,
1005 all_tenants=_all_tenants,
1006 )
1007 return remove_envelop(item, outdata)
1008 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1009 raise ROClientException(e, http_code=504)
1010 except asyncio.TimeoutError:
1011 raise ROClientException("Timeout", http_code=504)
1012
1013 async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
1014 """
1015 Creates an item from its descriptor
1016 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
1017 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
1018 :param descriptor_format: Can be 'json' or 'yaml'
1019 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
1020 keys can be a dot separated list to specify elements inside dict
1021 :return: dictionary with the information or raises ROClientException on Error
1022 """
1023 try:
1024 if isinstance(descriptor, str):
1025 descriptor = self._parse(descriptor, descriptor_format)
1026 elif descriptor:
1027 pass
1028 else:
1029 descriptor = {}
1030
1031 if item not in self.client_to_RO:
1032 raise ROClientException("Invalid item {}".format(item))
1033 desc = remove_envelop(item, descriptor)
1034
1035 # Override descriptor with kwargs
1036 if kwargs:
1037 desc = self.update_descriptor(desc, kwargs)
1038
1039 for mandatory in self.mandatory_for_create[item]:
1040 if mandatory not in desc:
1041 raise ROClientException(
1042 "'{}' is mandatory parameter for {}".format(mandatory, item)
1043 )
1044
1045 all_tenants = False
1046 if item in ("tenant", "vim", "wim"):
1047 all_tenants = None
1048
1049 create_desc = self._create_envelop(item, desc)
1050
1051 async with aiohttp.ClientSession() as session:
1052 outdata = await self._create_item(
1053 session,
1054 self.client_to_RO[item],
1055 create_desc,
1056 all_tenants=all_tenants,
1057 )
1058 return remove_envelop(item, outdata)
1059 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1060 raise ROClientException(e, http_code=504)
1061 except asyncio.TimeoutError:
1062 raise ROClientException("Timeout", http_code=504)
1063
1064 async def create_action(
1065 self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs
1066 ):
1067 """
1068 Performs an action over an item
1069 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
1070 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
1071 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
1072 :param descriptor_format: Can be 'json' or 'yaml'
1073 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
1074 keys can be a dot separated list to specify elements inside dict
1075 :return: dictionary with the information or raises ROClientException on Error
1076 """
1077 try:
1078 if isinstance(descriptor, str):
1079 descriptor = self._parse(descriptor, descriptor_format)
1080 elif descriptor:
1081 pass
1082 else:
1083 descriptor = {}
1084
1085 if item not in self.client_to_RO:
1086 raise ROClientException("Invalid item {}".format(item))
1087 desc = remove_envelop(item, descriptor)
1088
1089 # Override descriptor with kwargs
1090 if kwargs:
1091 desc = self.update_descriptor(desc, kwargs)
1092
1093 all_tenants = False
1094 if item in ("tenant", "vim"):
1095 all_tenants = None
1096
1097 action = None
1098 if item == "vims":
1099 action = "sdn_mapping"
1100 elif item in ("vim_account", "ns"):
1101 action = "action"
1102
1103 # create_desc = self._create_envelop(item, desc)
1104 create_desc = desc
1105
1106 async with aiohttp.ClientSession() as session:
1107 _all_tenants = all_tenants
1108 if item == "vim":
1109 _all_tenants = True
1110 # item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
1111 # all_tenants=_all_tenants)
1112 outdata = await self._create_item(
1113 session,
1114 self.client_to_RO[item],
1115 create_desc,
1116 item_id_name=item_id_name, # item_id_name=item_id
1117 action=action,
1118 all_tenants=_all_tenants,
1119 )
1120 return remove_envelop(item, outdata)
1121 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1122 raise ROClientException(e, http_code=504)
1123 except asyncio.TimeoutError:
1124 raise ROClientException("Timeout", http_code=504)
1125
1126 async def attach(
1127 self, item, item_id_name=None, descriptor=None, descriptor_format=None, **kwargs
1128 ):
1129 """
1130 Attach a datacenter or wim to a tenant, creating a vim_account, wim_account
1131 :param item: can be vim_account or wim_account
1132 :param item_id_name: id or name of the datacenter, wim
1133 :param descriptor:
1134 :param descriptor_format:
1135 :param kwargs:
1136 :return:
1137 """
1138 try:
1139 if isinstance(descriptor, str):
1140 descriptor = self._parse(descriptor, descriptor_format)
1141 elif descriptor:
1142 pass
1143 else:
1144 descriptor = {}
1145
1146 desc = remove_envelop(item, descriptor)
1147
1148 # # check that exist
1149 # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
1150 # tenant_text = "/" + self._get_tenant()
1151 if kwargs:
1152 desc = self.update_descriptor(desc, kwargs)
1153
1154 if item == "vim_account":
1155 if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
1156 raise ROClientException(
1157 "Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
1158 "provided"
1159 )
1160 elif item != "wim_account":
1161 raise ROClientException(
1162 "Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".format(
1163 item
1164 )
1165 )
1166 create_desc = self._create_envelop(item, desc)
1167 payload_req = yaml.safe_dump(create_desc)
1168 async with aiohttp.ClientSession() as session:
1169 # check that exist
1170 item_id = await self._get_item_uuid(
1171 session, self.client_to_RO[item], item_id_name, all_tenants=True
1172 )
1173 await self._get_tenant(session)
1174
1175 url = "{}/{tenant}/{item}/{item_id}".format(
1176 self.uri,
1177 tenant=self.tenant,
1178 item=self.client_to_RO[item],
1179 item_id=item_id,
1180 )
1181 self.logger.debug("RO POST %s %s", url, payload_req)
1182 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1183 async with session.post(
1184 url, headers=self.headers_req, data=payload_req
1185 ) as response:
1186 response_text = await response.read()
1187 self.logger.debug(
1188 "POST {} [{}] {}".format(
1189 url, response.status, response_text[:100]
1190 )
1191 )
1192 if response.status >= 300:
1193 raise ROClientException(
1194 self._parse_error_yaml(response_text),
1195 http_code=response.status,
1196 )
1197
1198 response_desc = self._parse_yaml(response_text, response=True)
1199 desc = remove_envelop(item, response_desc)
1200 return desc
1201 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1202 raise ROClientException(e, http_code=504)
1203 except asyncio.TimeoutError:
1204 raise ROClientException("Timeout", http_code=504)
1205
1206 async def detach(self, item, item_id_name=None):
1207 # TODO replace the code with delete_item(vim_account,...)
1208 try:
1209 async with aiohttp.ClientSession() as session:
1210 # check that exist
1211 item_id = await self._get_item_uuid(
1212 session, self.client_to_RO[item], item_id_name, all_tenants=False
1213 )
1214 tenant = await self._get_tenant(session)
1215
1216 url = "{}/{tenant}/{item}/{datacenter}".format(
1217 self.uri,
1218 tenant=tenant,
1219 item=self.client_to_RO[item],
1220 datacenter=item_id,
1221 )
1222 self.logger.debug("RO DELETE %s", url)
1223
1224 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1225 async with session.delete(url, headers=self.headers_req) as response:
1226 response_text = await response.read()
1227 self.logger.debug(
1228 "DELETE {} [{}] {}".format(
1229 url, response.status, response_text[:100]
1230 )
1231 )
1232 if response.status >= 300:
1233 raise ROClientException(
1234 self._parse_error_yaml(response_text),
1235 http_code=response.status,
1236 )
1237
1238 response_desc = self._parse_yaml(response_text, response=True)
1239 desc = remove_envelop(item, response_desc)
1240 return desc
1241 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1242 raise ROClientException(e, http_code=504)
1243 except asyncio.TimeoutError:
1244 raise ROClientException("Timeout", http_code=504)
1245
1246 # TODO convert to asyncio
1247 # DATACENTERS
1248
1249 def edit_datacenter(
1250 self,
1251 uuid=None,
1252 name=None,
1253 descriptor=None,
1254 descriptor_format=None,
1255 all_tenants=False,
1256 **kwargs
1257 ):
1258 """Edit the parameters of a datacenter
1259 Params: must supply a descriptor or/and a parameter to change
1260 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1261 descriptor: with format {'datacenter':{params to change info}}
1262 must be a dictionary or a json/yaml text.
1263 parameters to change can be supplyied by the descriptor or as parameters:
1264 new_name: the datacenter name
1265 vim_url: the datacenter URL
1266 vim_url_admin: the datacenter URL for administrative issues
1267 vim_type: the datacenter type, can be openstack or openvim.
1268 public: boolean, available to other tenants
1269 description: datacenter description
1270 Return: Raises an exception on error, not found or found several
1271 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
1272 """
1273
1274 if isinstance(descriptor, str):
1275 descriptor = self._parse(descriptor, descriptor_format)
1276 elif descriptor:
1277 pass
1278 elif kwargs:
1279 descriptor = {"datacenter": {}}
1280 else:
1281 raise ROClientException("Missing descriptor")
1282
1283 if "datacenter" not in descriptor or len(descriptor) != 1:
1284 raise ROClientException(
1285 "Descriptor must contain only one 'datacenter' field"
1286 )
1287 for param in kwargs:
1288 if param == "new_name":
1289 descriptor["datacenter"]["name"] = kwargs[param]
1290 else:
1291 descriptor["datacenter"][param] = kwargs[param]
1292 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
1293
1294 def edit_scenario(
1295 self,
1296 uuid=None,
1297 name=None,
1298 descriptor=None,
1299 descriptor_format=None,
1300 all_tenants=False,
1301 **kwargs
1302 ):
1303 """Edit the parameters of a scenario
1304 Params: must supply a descriptor or/and a parameters to change
1305 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1306 descriptor: with format {'scenario':{params to change info}}
1307 must be a dictionary or a json/yaml text.
1308 parameters to change can be supplyied by the descriptor or as parameters:
1309 new_name: the scenario name
1310 public: boolean, available to other tenants
1311 description: scenario description
1312 tenant_id. Propietary tenant
1313 Return: Raises an exception on error, not found or found several
1314 Obtain a dictionary with format {'scenario':{new_scenario_info}}
1315 """
1316
1317 if isinstance(descriptor, str):
1318 descriptor = self._parse(descriptor, descriptor_format)
1319 elif descriptor:
1320 pass
1321 elif kwargs:
1322 descriptor = {"scenario": {}}
1323 else:
1324 raise ROClientException("Missing descriptor")
1325
1326 if "scenario" not in descriptor or len(descriptor) > 2:
1327 raise ROClientException("Descriptor must contain only one 'scenario' field")
1328 for param in kwargs:
1329 if param == "new_name":
1330 descriptor["scenario"]["name"] = kwargs[param]
1331 else:
1332 descriptor["scenario"][param] = kwargs[param]
1333 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
1334
1335 # VIM ACTIONS
1336 def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
1337 """Perform an action over a vim
1338 Params:
1339 action: can be 'list', 'get'/'show', 'delete' or 'create'
1340 item: can be 'tenants' or 'networks'
1341 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
1342 other parameters:
1343 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
1344 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
1345 must be a dictionary or a json/yaml text.
1346 name: for created tenant/net Overwrite descriptor name if any
1347 description: tenant descriptor. Overwrite descriptor description if any
1348
1349 Return: Raises an exception on error
1350 Obtain a dictionary with format {'tenant':{new_tenant_info}}
1351 """
1352 session = None # TODO remove when changed to asyncio
1353 if item not in ("tenants", "networks", "images"):
1354 raise ROClientException(
1355 "Unknown value for item '{}', must be 'tenants', 'nets' or "
1356 "images".format(str(item))
1357 )
1358
1359 image_actions = ["list", "get", "show", "delete"]
1360 if item == "images" and action not in image_actions:
1361 raise ROClientException(
1362 "Only available actions for item '{}' are {}\n"
1363 "Requested action was '{}'".format(
1364 item, ", ".join(image_actions), action
1365 )
1366 )
1367 if all_tenants:
1368 tenant_text = "/any"
1369 else:
1370 tenant_text = "/" + self._get_tenant(session)
1371
1372 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
1373 datacenter = self._get_item_uuid(
1374 session,
1375 "datacenters",
1376 kwargs.get("datacenter"),
1377 all_tenants=all_tenants,
1378 )
1379 else:
1380 datacenter = self._get_datacenter(session)
1381
1382 if action == "list":
1383 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
1384 self.logger.debug("GET %s", url)
1385 mano_response = requests.get(url, headers=self.headers_req)
1386 self.logger.debug("RO response: %s", mano_response.text)
1387 content = self._parse_yaml(mano_response.text, response=True)
1388 if mano_response.status_code == 200:
1389 return content
1390 else:
1391 raise ROClientException(str(content), http_code=mano_response.status)
1392 elif action == "get" or action == "show":
1393 url = "{}{}/vim/{}/{}/{}".format(
1394 self.uri, tenant_text, datacenter, item, uuid
1395 )
1396 self.logger.debug("GET %s", url)
1397 mano_response = requests.get(url, headers=self.headers_req)
1398 self.logger.debug("RO response: %s", mano_response.text)
1399 content = self._parse_yaml(mano_response.text, response=True)
1400 if mano_response.status_code == 200:
1401 return content
1402 else:
1403 raise ROClientException(str(content), http_code=mano_response.status)
1404 elif action == "delete":
1405 url = "{}{}/vim/{}/{}/{}".format(
1406 self.uri, tenant_text, datacenter, item, uuid
1407 )
1408 self.logger.debug("DELETE %s", url)
1409 mano_response = requests.delete(url, headers=self.headers_req)
1410 self.logger.debug("RO response: %s", mano_response.text)
1411 content = self._parse_yaml(mano_response.text, response=True)
1412 if mano_response.status_code == 200:
1413 return content
1414 else:
1415 raise ROClientException(str(content), http_code=mano_response.status)
1416 elif action == "create":
1417 if "descriptor" in kwargs:
1418 if isinstance(kwargs["descriptor"], str):
1419 descriptor = self._parse(
1420 kwargs["descriptor"], kwargs.get("descriptor_format")
1421 )
1422 else:
1423 descriptor = kwargs["descriptor"]
1424 elif "name" in kwargs:
1425 descriptor = {item[:-1]: {"name": kwargs["name"]}}
1426 else:
1427 raise ROClientException("Missing descriptor")
1428
1429 if item[:-1] not in descriptor or len(descriptor) != 1:
1430 raise ROClientException(
1431 "Descriptor must contain only one 'tenant' field"
1432 )
1433 if "name" in kwargs:
1434 descriptor[item[:-1]]["name"] = kwargs["name"]
1435 if "description" in kwargs:
1436 descriptor[item[:-1]]["description"] = kwargs["description"]
1437 payload_req = yaml.safe_dump(descriptor)
1438 # print payload_req
1439 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
1440 self.logger.debug("RO POST %s %s", url, payload_req)
1441 mano_response = requests.post(
1442 url, headers=self.headers_req, data=payload_req
1443 )
1444 self.logger.debug("RO response: %s", mano_response.text)
1445 content = self._parse_yaml(mano_response.text, response=True)
1446 if mano_response.status_code == 200:
1447 return content
1448 else:
1449 raise ROClientException(str(content), http_code=mano_response.status)
1450 else:
1451 raise ROClientException("Unknown value for action '{}".format(str(action)))