Revert "Remove unused methods"
[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 raise ROClientException("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 raise ROClientException("remove_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 if not isinstance(desc, list):
552 raise ROClientException(
553 "_get_item_uuid get a non dict with a list inside {}".format(type(desc))
554 )
555 uuid = None
556 for i in desc:
557 if item_id_name and i["name"] != item_id_name:
558 continue
559 if uuid: # found more than one
560 raise ROClientException(
561 "Found more than one {} with name '{}'. uuid must be used".format(
562 item, item_id_name
563 ),
564 http_code=404,
565 )
566 uuid = i["uuid"]
567 if not uuid:
568 raise ROClientException(
569 "No {} found with name '{}'".format(item[:-1], item_id_name),
570 http_code=404,
571 )
572 return uuid
573
574 async def _get_item(
575 self,
576 session,
577 item,
578 item_id_name,
579 extra_item=None,
580 extra_item_id=None,
581 all_tenants=False,
582 ):
583 if all_tenants:
584 tenant_text = "/any"
585 elif all_tenants is None:
586 tenant_text = ""
587 else:
588 if not self.tenant:
589 await self._get_tenant(session)
590 tenant_text = "/" + self.tenant
591
592 if self.check_if_uuid(item_id_name):
593 uuid = item_id_name
594 else:
595 # check that exist
596 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
597
598 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, uuid)
599 if extra_item:
600 url += "/" + extra_item
601 if extra_item_id:
602 url += "/" + extra_item_id
603 self.logger.debug("GET %s", url)
604 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
605 async with session.get(url, headers=self.headers_req) as response:
606 response_text = await response.read()
607 self.logger.debug(
608 "GET {} [{}] {}".format(url, response.status, response_text[:100])
609 )
610 if response.status >= 300:
611 raise ROClientException(
612 self._parse_error_yaml(response_text), http_code=response.status
613 )
614
615 return self._parse_yaml(response_text, response=True)
616
617 async def _get_tenant(self, session):
618 if not self.tenant:
619 self.tenant = await self._get_item_uuid(
620 session, "tenants", self.tenant_id_name, None
621 )
622 return self.tenant
623
624 async def _get_datacenter(self, session):
625 if not self.tenant:
626 await self._get_tenant(session)
627 if not self.datacenter:
628 self.datacenter = await self._get_item_uuid(
629 session, "datacenters", self.datacenter_id_name, True
630 )
631 return self.datacenter
632
633 async def _create_item(
634 self,
635 session,
636 item,
637 descriptor,
638 item_id_name=None,
639 action=None,
640 all_tenants=False,
641 ):
642 if all_tenants:
643 tenant_text = "/any"
644 elif all_tenants is None:
645 tenant_text = ""
646 else:
647 if not self.tenant:
648 await self._get_tenant(session)
649 tenant_text = "/" + self.tenant
650 payload_req = yaml.safe_dump(descriptor)
651 # print payload_req
652
653 api_version_text = ""
654 if item == "vnfs":
655 # assumes version v3 only
656 api_version_text = "/v3"
657 item = "vnfd"
658 elif item == "scenarios":
659 # assumes version v3 only
660 api_version_text = "/v3"
661 item = "nsd"
662
663 if not item_id_name:
664 uuid = ""
665 elif self.check_if_uuid(item_id_name):
666 uuid = "/{}".format(item_id_name)
667 else:
668 # check that exist
669 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
670 uuid = "/{}".format(uuid)
671 if not action:
672 action = ""
673 else:
674 action = "/{}".format(action)
675
676 url = "{}{apiver}{tenant}/{item}{id}{action}".format(
677 self.uri,
678 apiver=api_version_text,
679 tenant=tenant_text,
680 item=item,
681 id=uuid,
682 action=action,
683 )
684 self.logger.debug("RO POST %s %s", url, payload_req)
685 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
686 async with session.post(
687 url, headers=self.headers_req, data=payload_req
688 ) as response:
689 response_text = await response.read()
690 self.logger.debug(
691 "POST {} [{}] {}".format(url, response.status, response_text[:100])
692 )
693 if response.status >= 300:
694 raise ROClientException(
695 self._parse_error_yaml(response_text), http_code=response.status
696 )
697
698 return self._parse_yaml(response_text, response=True)
699
700 async def _del_item(self, session, item, item_id_name, all_tenants=False):
701 if all_tenants:
702 tenant_text = "/any"
703 elif all_tenants is None:
704 tenant_text = ""
705 else:
706 if not self.tenant:
707 await self._get_tenant(session)
708 tenant_text = "/" + self.tenant
709 if not self.check_if_uuid(item_id_name):
710 # check that exist
711 _all_tenants = all_tenants
712 if item in ("datacenters", "wims"):
713 _all_tenants = True
714 uuid = await self._get_item_uuid(
715 session, item, item_id_name, all_tenants=_all_tenants
716 )
717 else:
718 uuid = item_id_name
719
720 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, uuid)
721 self.logger.debug("DELETE %s", url)
722 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
723 async with session.delete(url, headers=self.headers_req) as response:
724 response_text = await response.read()
725 self.logger.debug(
726 "DELETE {} [{}] {}".format(url, response.status, response_text[:100])
727 )
728 if response.status >= 300:
729 raise ROClientException(
730 self._parse_error_yaml(response_text), http_code=response.status
731 )
732
733 return self._parse_yaml(response_text, response=True)
734
735 async def _list_item(self, session, item, all_tenants=False, filter_dict=None):
736 if all_tenants:
737 tenant_text = "/any"
738 elif all_tenants is None:
739 tenant_text = ""
740 else:
741 if not self.tenant:
742 await self._get_tenant(session)
743 tenant_text = "/" + self.tenant
744
745 url = "{}{}/{}".format(self.uri, tenant_text, item)
746 separator = "?"
747 if filter_dict:
748 for k in filter_dict:
749 url += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
750 separator = "&"
751 self.logger.debug("RO GET %s", url)
752 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
753 async with session.get(url, headers=self.headers_req) as response:
754 response_text = await response.read()
755 self.logger.debug(
756 "GET {} [{}] {}".format(url, response.status, response_text[:100])
757 )
758 if response.status >= 300:
759 raise ROClientException(
760 self._parse_error_yaml(response_text), http_code=response.status
761 )
762
763 return self._parse_yaml(response_text, response=True)
764
765 async def _edit_item(self, session, item, item_id, descriptor, all_tenants=False):
766 if all_tenants:
767 tenant_text = "/any"
768 elif all_tenants is None:
769 tenant_text = ""
770 else:
771 if not self.tenant:
772 await self._get_tenant(session)
773 tenant_text = "/" + self.tenant
774
775 payload_req = yaml.safe_dump(descriptor)
776
777 # print payload_req
778 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, item_id)
779 self.logger.debug("RO PUT %s %s", url, payload_req)
780 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
781 async with session.put(
782 url, headers=self.headers_req, data=payload_req
783 ) as response:
784 response_text = await response.read()
785 self.logger.debug(
786 "PUT {} [{}] {}".format(url, response.status, response_text[:100])
787 )
788 if response.status >= 300:
789 raise ROClientException(
790 self._parse_error_yaml(response_text), http_code=response.status
791 )
792
793 return self._parse_yaml(response_text, response=True)
794
795 async def get_version(self):
796 """
797 Obtain RO server version.
798 :return: a list with integers ["major", "minor", "release"]. Raises ROClientException on Error,
799 """
800 try:
801 response_text = ""
802 async with aiohttp.ClientSession() as session:
803 url = "{}/version".format(self.uri)
804 self.logger.debug("RO GET %s", url)
805 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
806 async with session.get(url, headers=self.headers_req) as response:
807 response_text = await response.read()
808 self.logger.debug(
809 "GET {} [{}] {}".format(
810 url, response.status, response_text[:100]
811 )
812 )
813 if response.status >= 300:
814 raise ROClientException(
815 self._parse_error_yaml(response_text),
816 http_code=response.status,
817 )
818
819 for word in str(response_text).split(" "):
820 if "." in word:
821 version_text, _, _ = word.partition("-")
822 return version_text
823 raise ROClientException(
824 "Got invalid version text: '{}'".format(response_text),
825 http_code=500,
826 )
827 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
828 raise ROClientException(e, http_code=504)
829 except asyncio.TimeoutError:
830 raise ROClientException("Timeout", http_code=504)
831 except Exception as e:
832 self.logger.critical(
833 "Got invalid version text: '{}'; causing exception {}".format(
834 response_text, str(e)
835 )
836 )
837 raise ROClientException(
838 "Got invalid version text: '{}'; causing exception {}".format(
839 response_text, e
840 ),
841 http_code=500,
842 )
843
844 async def get_list(self, item, all_tenants=False, filter_by=None):
845 """
846 List of items filtered by the contents in the dictionary "filter_by".
847 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
848 :param all_tenants: True if not filtering by tenant. Only allowed for admin
849 :param filter_by: dictionary with filtering
850 :return: a list of dict. It can be empty. Raises ROClientException on Error,
851 """
852 try:
853 if item not in self.client_to_RO:
854 raise ROClientException("Invalid item {}".format(item))
855 if item == "tenant":
856 all_tenants = None
857 async with aiohttp.ClientSession() as session:
858 content = await self._list_item(
859 session,
860 self.client_to_RO[item],
861 all_tenants=all_tenants,
862 filter_dict=filter_by,
863 )
864 if isinstance(content, dict):
865 if len(content) == 1:
866 for _, v in content.items():
867 return v
868 return content.values()[0]
869 else:
870 raise ROClientException(
871 "Output not a list neither dict with len equal 1", http_code=500
872 )
873 return content
874 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
875 raise ROClientException(e, http_code=504)
876 except asyncio.TimeoutError:
877 raise ROClientException("Timeout", http_code=504)
878
879 async def show(
880 self,
881 item,
882 item_id_name=None,
883 extra_item=None,
884 extra_item_id=None,
885 all_tenants=False,
886 ):
887 """
888 Obtain the information of an item from its id or name
889 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
890 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
891 :param extra_item: if supplied, it is used to add to the URL.
892 Can be 'action' if item='ns'; 'networks' or'images' if item='vim'
893 :param extra_item_id: if supplied, it is used get details of a concrete extra_item.
894 :param all_tenants: True if not filtering by tenant. Only allowed for admin
895 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
896 """
897 try:
898 if item not in self.client_to_RO:
899 raise ROClientException("Invalid item {}".format(item))
900 if item == "tenant":
901 all_tenants = None
902 elif item == "vim":
903 all_tenants = True
904 elif item == "vim_account":
905 all_tenants = False
906
907 async with aiohttp.ClientSession() as session:
908 content = await self._get_item(
909 session,
910 self.client_to_RO[item],
911 item_id_name,
912 extra_item=extra_item,
913 extra_item_id=extra_item_id,
914 all_tenants=all_tenants,
915 )
916 return remove_envelop(item, content)
917 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
918 raise ROClientException(e, http_code=504)
919 except asyncio.TimeoutError:
920 raise ROClientException("Timeout", http_code=504)
921
922 async def delete(self, item, item_id_name=None, all_tenants=False):
923 """
924 Delete the information of an item from its id or name
925 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
926 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
927 :param all_tenants: True if not filtering by tenant. Only allowed for admin
928 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
929 """
930 try:
931 if item not in self.client_to_RO:
932 raise ROClientException("Invalid item {}".format(item))
933 if item in ("tenant", "vim", "wim"):
934 all_tenants = None
935
936 async with aiohttp.ClientSession() as session:
937 result = await self._del_item(
938 session,
939 self.client_to_RO[item],
940 item_id_name,
941 all_tenants=all_tenants,
942 )
943 # in case of ns delete, get the action_id embeded in text
944 if item == "ns" and result.get("result"):
945 _, _, action_id = result["result"].partition("action_id=")
946 action_id, _, _ = action_id.partition(" ")
947 if action_id:
948 result["action_id"] = action_id
949 return result
950 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
951 raise ROClientException(e, http_code=504)
952 except asyncio.TimeoutError:
953 raise ROClientException("Timeout", http_code=504)
954
955 async def edit(
956 self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs
957 ):
958 """Edit an item
959 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
960 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
961 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
962 :param descriptor_format: Can be 'json' or 'yaml'
963 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
964 keys can be a dot separated list to specify elements inside dict
965 :return: dictionary with the information or raises ROClientException on Error
966 """
967 try:
968 if isinstance(descriptor, str):
969 descriptor = self._parse(descriptor, descriptor_format)
970 elif descriptor:
971 pass
972 else:
973 descriptor = {}
974
975 if item not in self.client_to_RO:
976 raise ROClientException("Invalid item {}".format(item))
977 desc = remove_envelop(item, descriptor)
978
979 # Override descriptor with kwargs
980 if kwargs:
981 desc = self.update_descriptor(desc, kwargs)
982 all_tenants = False
983 if item in ("tenant", "vim"):
984 all_tenants = None
985
986 create_desc = self._create_envelop(item, desc)
987
988 async with aiohttp.ClientSession() as session:
989 _all_tenants = all_tenants
990 if item == "vim":
991 _all_tenants = True
992 item_id = await self._get_item_uuid(
993 session,
994 self.client_to_RO[item],
995 item_id_name,
996 all_tenants=_all_tenants,
997 )
998 if item == "vim":
999 _all_tenants = None
1000 # await self._get_tenant(session)
1001 outdata = await self._edit_item(
1002 session,
1003 self.client_to_RO[item],
1004 item_id,
1005 create_desc,
1006 all_tenants=_all_tenants,
1007 )
1008 return remove_envelop(item, outdata)
1009 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1010 raise ROClientException(e, http_code=504)
1011 except asyncio.TimeoutError:
1012 raise ROClientException("Timeout", http_code=504)
1013
1014 async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
1015 """
1016 Creates an item from its descriptor
1017 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
1018 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
1019 :param descriptor_format: Can be 'json' or 'yaml'
1020 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
1021 keys can be a dot separated list to specify elements inside dict
1022 :return: dictionary with the information or raises ROClientException on Error
1023 """
1024 try:
1025 if isinstance(descriptor, str):
1026 descriptor = self._parse(descriptor, descriptor_format)
1027 elif descriptor:
1028 pass
1029 else:
1030 descriptor = {}
1031
1032 if item not in self.client_to_RO:
1033 raise ROClientException("Invalid item {}".format(item))
1034 desc = remove_envelop(item, descriptor)
1035
1036 # Override descriptor with kwargs
1037 if kwargs:
1038 desc = self.update_descriptor(desc, kwargs)
1039
1040 for mandatory in self.mandatory_for_create[item]:
1041 if mandatory not in desc:
1042 raise ROClientException(
1043 "'{}' is mandatory parameter for {}".format(mandatory, item)
1044 )
1045
1046 all_tenants = False
1047 if item in ("tenant", "vim", "wim"):
1048 all_tenants = None
1049
1050 create_desc = self._create_envelop(item, desc)
1051
1052 async with aiohttp.ClientSession() as session:
1053 outdata = await self._create_item(
1054 session,
1055 self.client_to_RO[item],
1056 create_desc,
1057 all_tenants=all_tenants,
1058 )
1059 return remove_envelop(item, outdata)
1060 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1061 raise ROClientException(e, http_code=504)
1062 except asyncio.TimeoutError:
1063 raise ROClientException("Timeout", http_code=504)
1064
1065 async def create_action(
1066 self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs
1067 ):
1068 """
1069 Performs an action over an item
1070 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
1071 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
1072 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
1073 :param descriptor_format: Can be 'json' or 'yaml'
1074 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
1075 keys can be a dot separated list to specify elements inside dict
1076 :return: dictionary with the information or raises ROClientException on Error
1077 """
1078 try:
1079 if isinstance(descriptor, str):
1080 descriptor = self._parse(descriptor, descriptor_format)
1081 elif descriptor:
1082 pass
1083 else:
1084 descriptor = {}
1085
1086 if item not in self.client_to_RO:
1087 raise ROClientException("Invalid item {}".format(item))
1088 desc = remove_envelop(item, descriptor)
1089
1090 # Override descriptor with kwargs
1091 if kwargs:
1092 desc = self.update_descriptor(desc, kwargs)
1093
1094 all_tenants = False
1095 if item in ("tenant", "vim"):
1096 all_tenants = None
1097
1098 action = None
1099 if item == "vims":
1100 action = "sdn_mapping"
1101 elif item in ("vim_account", "ns"):
1102 action = "action"
1103
1104 # create_desc = self._create_envelop(item, desc)
1105 create_desc = desc
1106
1107 async with aiohttp.ClientSession() as session:
1108 _all_tenants = all_tenants
1109 if item == "vim":
1110 _all_tenants = True
1111 # item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
1112 # all_tenants=_all_tenants)
1113 outdata = await self._create_item(
1114 session,
1115 self.client_to_RO[item],
1116 create_desc,
1117 item_id_name=item_id_name, # item_id_name=item_id
1118 action=action,
1119 all_tenants=_all_tenants,
1120 )
1121 return remove_envelop(item, outdata)
1122 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1123 raise ROClientException(e, http_code=504)
1124 except asyncio.TimeoutError:
1125 raise ROClientException("Timeout", http_code=504)
1126
1127 async def attach(
1128 self, item, item_id_name=None, descriptor=None, descriptor_format=None, **kwargs
1129 ):
1130 """
1131 Attach a datacenter or wim to a tenant, creating a vim_account, wim_account
1132 :param item: can be vim_account or wim_account
1133 :param item_id_name: id or name of the datacenter, wim
1134 :param descriptor:
1135 :param descriptor_format:
1136 :param kwargs:
1137 :return:
1138 """
1139 try:
1140 if isinstance(descriptor, str):
1141 descriptor = self._parse(descriptor, descriptor_format)
1142 elif descriptor:
1143 pass
1144 else:
1145 descriptor = {}
1146
1147 desc = remove_envelop(item, descriptor)
1148
1149 # # check that exist
1150 # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
1151 # tenant_text = "/" + self._get_tenant()
1152 if kwargs:
1153 desc = self.update_descriptor(desc, kwargs)
1154
1155 if item == "vim_account":
1156 if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
1157 raise ROClientException(
1158 "Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
1159 "provided"
1160 )
1161 elif item != "wim_account":
1162 raise ROClientException(
1163 "Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".format(
1164 item
1165 )
1166 )
1167 create_desc = self._create_envelop(item, desc)
1168 payload_req = yaml.safe_dump(create_desc)
1169 async with aiohttp.ClientSession() as session:
1170 # check that exist
1171 item_id = await self._get_item_uuid(
1172 session, self.client_to_RO[item], item_id_name, all_tenants=True
1173 )
1174 await self._get_tenant(session)
1175
1176 url = "{}/{tenant}/{item}/{item_id}".format(
1177 self.uri,
1178 tenant=self.tenant,
1179 item=self.client_to_RO[item],
1180 item_id=item_id,
1181 )
1182 self.logger.debug("RO POST %s %s", url, payload_req)
1183 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1184 async with session.post(
1185 url, headers=self.headers_req, data=payload_req
1186 ) as response:
1187 response_text = await response.read()
1188 self.logger.debug(
1189 "POST {} [{}] {}".format(
1190 url, response.status, response_text[:100]
1191 )
1192 )
1193 if response.status >= 300:
1194 raise ROClientException(
1195 self._parse_error_yaml(response_text),
1196 http_code=response.status,
1197 )
1198
1199 response_desc = self._parse_yaml(response_text, response=True)
1200 desc = remove_envelop(item, response_desc)
1201 return desc
1202 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1203 raise ROClientException(e, http_code=504)
1204 except asyncio.TimeoutError:
1205 raise ROClientException("Timeout", http_code=504)
1206
1207 async def detach(self, item, item_id_name=None):
1208 # TODO replace the code with delete_item(vim_account,...)
1209 try:
1210 async with aiohttp.ClientSession() as session:
1211 # check that exist
1212 item_id = await self._get_item_uuid(
1213 session, self.client_to_RO[item], item_id_name, all_tenants=False
1214 )
1215 tenant = await self._get_tenant(session)
1216
1217 url = "{}/{tenant}/{item}/{datacenter}".format(
1218 self.uri,
1219 tenant=tenant,
1220 item=self.client_to_RO[item],
1221 datacenter=item_id,
1222 )
1223 self.logger.debug("RO DELETE %s", url)
1224
1225 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1226 async with session.delete(url, headers=self.headers_req) as response:
1227 response_text = await response.read()
1228 self.logger.debug(
1229 "DELETE {} [{}] {}".format(
1230 url, response.status, response_text[:100]
1231 )
1232 )
1233 if response.status >= 300:
1234 raise ROClientException(
1235 self._parse_error_yaml(response_text),
1236 http_code=response.status,
1237 )
1238
1239 response_desc = self._parse_yaml(response_text, response=True)
1240 desc = remove_envelop(item, response_desc)
1241 return desc
1242 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1243 raise ROClientException(e, http_code=504)
1244 except asyncio.TimeoutError:
1245 raise ROClientException("Timeout", http_code=504)
1246
1247 # TODO convert to asyncio
1248 # DATACENTERS
1249
1250 def edit_datacenter(
1251 self,
1252 uuid=None,
1253 name=None,
1254 descriptor=None,
1255 descriptor_format=None,
1256 all_tenants=False,
1257 **kwargs
1258 ):
1259 """Edit the parameters of a datacenter
1260 Params: must supply a descriptor or/and a parameter to change
1261 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1262 descriptor: with format {'datacenter':{params to change info}}
1263 must be a dictionary or a json/yaml text.
1264 parameters to change can be supplyied by the descriptor or as parameters:
1265 new_name: the datacenter name
1266 vim_url: the datacenter URL
1267 vim_url_admin: the datacenter URL for administrative issues
1268 vim_type: the datacenter type, can be openstack or openvim.
1269 public: boolean, available to other tenants
1270 description: datacenter description
1271 Return: Raises an exception on error, not found or found several
1272 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
1273 """
1274
1275 if isinstance(descriptor, str):
1276 descriptor = self._parse(descriptor, descriptor_format)
1277 elif descriptor:
1278 pass
1279 elif kwargs:
1280 descriptor = {"datacenter": {}}
1281 else:
1282 raise ROClientException("Missing descriptor")
1283
1284 if "datacenter" not in descriptor or len(descriptor) != 1:
1285 raise ROClientException(
1286 "Descriptor must contain only one 'datacenter' field"
1287 )
1288 for param in kwargs:
1289 if param == "new_name":
1290 descriptor["datacenter"]["name"] = kwargs[param]
1291 else:
1292 descriptor["datacenter"][param] = kwargs[param]
1293 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
1294
1295 def edit_scenario(
1296 self,
1297 uuid=None,
1298 name=None,
1299 descriptor=None,
1300 descriptor_format=None,
1301 all_tenants=False,
1302 **kwargs
1303 ):
1304 """Edit the parameters of a scenario
1305 Params: must supply a descriptor or/and a parameters to change
1306 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1307 descriptor: with format {'scenario':{params to change info}}
1308 must be a dictionary or a json/yaml text.
1309 parameters to change can be supplyied by the descriptor or as parameters:
1310 new_name: the scenario name
1311 public: boolean, available to other tenants
1312 description: scenario description
1313 tenant_id. Propietary tenant
1314 Return: Raises an exception on error, not found or found several
1315 Obtain a dictionary with format {'scenario':{new_scenario_info}}
1316 """
1317
1318 if isinstance(descriptor, str):
1319 descriptor = self._parse(descriptor, descriptor_format)
1320 elif descriptor:
1321 pass
1322 elif kwargs:
1323 descriptor = {"scenario": {}}
1324 else:
1325 raise ROClientException("Missing descriptor")
1326
1327 if "scenario" not in descriptor or len(descriptor) > 2:
1328 raise ROClientException("Descriptor must contain only one 'scenario' field")
1329 for param in kwargs:
1330 if param == "new_name":
1331 descriptor["scenario"]["name"] = kwargs[param]
1332 else:
1333 descriptor["scenario"][param] = kwargs[param]
1334 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
1335
1336 # VIM ACTIONS
1337 def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
1338 """Perform an action over a vim
1339 Params:
1340 action: can be 'list', 'get'/'show', 'delete' or 'create'
1341 item: can be 'tenants' or 'networks'
1342 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
1343 other parameters:
1344 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
1345 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
1346 must be a dictionary or a json/yaml text.
1347 name: for created tenant/net Overwrite descriptor name if any
1348 description: tenant descriptor. Overwrite descriptor description if any
1349
1350 Return: Raises an exception on error
1351 Obtain a dictionary with format {'tenant':{new_tenant_info}}
1352 """
1353 session = None # TODO remove when changed to asyncio
1354 if item not in ("tenants", "networks", "images"):
1355 raise ROClientException(
1356 "Unknown value for item '{}', must be 'tenants', 'nets' or "
1357 "images".format(str(item))
1358 )
1359
1360 image_actions = ["list", "get", "show", "delete"]
1361 if item == "images" and action not in image_actions:
1362 raise ROClientException(
1363 "Only available actions for item '{}' are {}\n"
1364 "Requested action was '{}'".format(
1365 item, ", ".join(image_actions), action
1366 )
1367 )
1368 if all_tenants:
1369 tenant_text = "/any"
1370 else:
1371 tenant_text = "/" + self._get_tenant(session)
1372
1373 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
1374 datacenter = self._get_item_uuid(
1375 session,
1376 "datacenters",
1377 kwargs.get("datacenter"),
1378 all_tenants=all_tenants,
1379 )
1380 else:
1381 datacenter = self._get_datacenter(session)
1382
1383 if action == "list":
1384 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
1385 self.logger.debug("GET %s", url)
1386 mano_response = requests.get(url, headers=self.headers_req)
1387 self.logger.debug("RO response: %s", mano_response.text)
1388 content = self._parse_yaml(mano_response.text, response=True)
1389 if mano_response.status_code == 200:
1390 return content
1391 else:
1392 raise ROClientException(str(content), http_code=mano_response.status)
1393 elif action == "get" or action == "show":
1394 url = "{}{}/vim/{}/{}/{}".format(
1395 self.uri, tenant_text, datacenter, item, uuid
1396 )
1397 self.logger.debug("GET %s", url)
1398 mano_response = requests.get(url, headers=self.headers_req)
1399 self.logger.debug("RO response: %s", mano_response.text)
1400 content = self._parse_yaml(mano_response.text, response=True)
1401 if mano_response.status_code == 200:
1402 return content
1403 else:
1404 raise ROClientException(str(content), http_code=mano_response.status)
1405 elif action == "delete":
1406 url = "{}{}/vim/{}/{}/{}".format(
1407 self.uri, tenant_text, datacenter, item, uuid
1408 )
1409 self.logger.debug("DELETE %s", url)
1410 mano_response = requests.delete(url, headers=self.headers_req)
1411 self.logger.debug("RO response: %s", mano_response.text)
1412 content = self._parse_yaml(mano_response.text, response=True)
1413 if mano_response.status_code == 200:
1414 return content
1415 else:
1416 raise ROClientException(str(content), http_code=mano_response.status)
1417 elif action == "create":
1418 if "descriptor" in kwargs:
1419 if isinstance(kwargs["descriptor"], str):
1420 descriptor = self._parse(
1421 kwargs["descriptor"], kwargs.get("descriptor_format")
1422 )
1423 else:
1424 descriptor = kwargs["descriptor"]
1425 elif "name" in kwargs:
1426 descriptor = {item[:-1]: {"name": kwargs["name"]}}
1427 else:
1428 raise ROClientException("Missing descriptor")
1429
1430 if item[:-1] not in descriptor or len(descriptor) != 1:
1431 raise ROClientException(
1432 "Descriptor must contain only one 'tenant' field"
1433 )
1434 if "name" in kwargs:
1435 descriptor[item[:-1]]["name"] = kwargs["name"]
1436 if "description" in kwargs:
1437 descriptor[item[:-1]]["description"] = kwargs["description"]
1438 payload_req = yaml.safe_dump(descriptor)
1439 # print payload_req
1440 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
1441 self.logger.debug("RO POST %s %s", url, payload_req)
1442 mano_response = requests.post(
1443 url, headers=self.headers_req, data=payload_req
1444 )
1445 self.logger.debug("RO response: %s", mano_response.text)
1446 content = self._parse_yaml(mano_response.text, response=True)
1447 if mano_response.status_code == 200:
1448 return content
1449 else:
1450 raise ROClientException(str(content), http_code=mano_response.status)
1451 else:
1452 raise ROClientException("Unknown value for action '{}".format(str(action)))