Code Coverage

Cobertura Coverage Report > osm_lcm >

ROclient.py

Trend

File Coverage summary

NameClassesLinesConditionals
ROclient.py
100%
1/1
8%
65/824
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
ROclient.py
8%
65/824
N/A

Source

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