Reformat LCM to standardized format
[osm/LCM.git] / osm_lcm / ROclient.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
17 # under the License.
18 #
19 ##
20
21 """
22 asyncio RO python client to interact with RO-server
23 """
24
25 import asyncio
26 import aiohttp
27 import json
28 import yaml
29 import logging
30 from urllib.parse import quote
31 from uuid import UUID
32 from copy import deepcopy
33
34 __author__ = "Alfonso Tierno"
35 __date__ = "$09-Jan-2018 09:09:48$"
36 __version__ = "0.1.2"
37 version_date = "2018-05-16"
38 requests = None
39
40
41 class ROClientException(Exception):
42 def __init__(self, message, http_code=400):
43 """Common Exception for all RO client exceptions"""
44 self.http_code = http_code
45 Exception.__init__(self, message)
46
47
48 def remove_envelop(item, indata=None):
49 """
50 Obtain the useful data removing the envelop. It goes through the vnfd or nsd catalog and returns the
51 vnfd or nsd content
52 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
53 :param indata: Content to be inspected
54 :return: the useful part of indata (a reference, not a new dictionay)
55 """
56 clean_indata = indata
57 if not indata:
58 return {}
59 if item == "vnfd":
60 if clean_indata.get("vnfd:vnfd-catalog"):
61 clean_indata = clean_indata["vnfd:vnfd-catalog"]
62 elif clean_indata.get("vnfd-catalog"):
63 clean_indata = clean_indata["vnfd-catalog"]
64 if clean_indata.get("vnfd"):
65 if (
66 not isinstance(clean_indata["vnfd"], list)
67 or len(clean_indata["vnfd"]) != 1
68 ):
69 raise ROClientException("'vnfd' must be a list only one element")
70 clean_indata = clean_indata["vnfd"][0]
71 elif item == "nsd":
72 if clean_indata.get("nsd:nsd-catalog"):
73 clean_indata = clean_indata["nsd:nsd-catalog"]
74 elif clean_indata.get("nsd-catalog"):
75 clean_indata = clean_indata["nsd-catalog"]
76 if clean_indata.get("nsd"):
77 if (
78 not isinstance(clean_indata["nsd"], list)
79 or len(clean_indata["nsd"]) != 1
80 ):
81 raise ROClientException("'nsd' must be a list only one element")
82 clean_indata = clean_indata["nsd"][0]
83 elif item == "sdn":
84 if len(indata) == 1 and "sdn_controller" in indata:
85 clean_indata = indata["sdn_controller"]
86 elif item == "tenant":
87 if len(indata) == 1 and "tenant" in indata:
88 clean_indata = indata["tenant"]
89 elif item in ("vim", "vim_account", "datacenters"):
90 if len(indata) == 1 and "datacenter" in indata:
91 clean_indata = indata["datacenter"]
92 elif item == "wim":
93 if len(indata) == 1 and "wim" in indata:
94 clean_indata = indata["wim"]
95 elif item == "wim_account":
96 if len(indata) == 1 and "wim_account" in indata:
97 clean_indata = indata["wim_account"]
98 elif item == "ns" or item == "instances":
99 if len(indata) == 1 and "instance" in indata:
100 clean_indata = indata["instance"]
101 else:
102 assert False, "remove_envelop with unknown item {}".format(item)
103
104 return clean_indata
105
106
107 class ROClient:
108 headers_req = {"Accept": "application/yaml", "content-type": "application/yaml"}
109 client_to_RO = {
110 "tenant": "tenants",
111 "vim": "datacenters",
112 "vim_account": "datacenters",
113 "sdn": "sdn_controllers",
114 "vnfd": "vnfs",
115 "nsd": "scenarios",
116 "wim": "wims",
117 "wim_account": "wims",
118 "ns": "instances",
119 }
120 mandatory_for_create = {
121 "tenant": ("name",),
122 "vnfd": ("name", "id"),
123 "nsd": ("name", "id"),
124 "ns": ("name", "scenario", "datacenter"),
125 "vim": ("name", "vim_url"),
126 "wim": ("name", "wim_url"),
127 "vim_account": (),
128 "wim_account": (),
129 "sdn": ("name", "type"),
130 }
131 timeout_large = 120
132 timeout_short = 30
133
134 def __init__(self, loop, uri, **kwargs):
135 self.loop = loop
136 self.uri = uri
137
138 self.username = kwargs.get("username")
139 self.password = kwargs.get("password")
140 self.tenant_id_name = kwargs.get("tenant")
141 self.tenant = None
142 self.datacenter_id_name = kwargs.get("datacenter")
143 self.datacenter = None
144 logger_name = kwargs.get("logger_name", "lcm.ro")
145 self.logger = logging.getLogger(logger_name)
146 if kwargs.get("loglevel"):
147 self.logger.setLevel(kwargs["loglevel"])
148 global requests
149 requests = kwargs.get("TODO remove")
150
151 def __getitem__(self, index):
152 if index == "tenant":
153 return self.tenant_id_name
154 elif index == "datacenter":
155 return self.datacenter_id_name
156 elif index == "username":
157 return self.username
158 elif index == "password":
159 return self.password
160 elif index == "uri":
161 return self.uri
162 else:
163 raise KeyError("Invalid key '{}'".format(index))
164
165 def __setitem__(self, index, value):
166 if index == "tenant":
167 self.tenant_id_name = value
168 elif index == "datacenter" or index == "vim":
169 self.datacenter_id_name = value
170 elif index == "username":
171 self.username = value
172 elif index == "password":
173 self.password = value
174 elif index == "uri":
175 self.uri = value
176 else:
177 raise KeyError("Invalid key '{}'".format(index))
178 self.tenant = None # force to reload tenant with different credentials
179 self.datacenter = None # force to reload datacenter with different credentials
180
181 @staticmethod
182 def _parse(descriptor, descriptor_format, response=False):
183 if (
184 descriptor_format
185 and descriptor_format != "json"
186 and descriptor_format != "yaml"
187 ):
188 raise ROClientException(
189 "'descriptor_format' must be a 'json' or 'yaml' text"
190 )
191 if descriptor_format != "json":
192 try:
193 return yaml.load(descriptor)
194 except yaml.YAMLError as exc:
195 error_pos = ""
196 if hasattr(exc, "problem_mark"):
197 mark = exc.problem_mark
198 error_pos = " at line:{} column:{}s".format(
199 mark.line + 1, mark.column + 1
200 )
201 error_text = "yaml format error" + error_pos
202 elif descriptor_format != "yaml":
203 try:
204 return json.loads(descriptor)
205 except Exception as e:
206 if response:
207 error_text = "json format error" + str(e)
208
209 if response:
210 raise ROClientException(error_text)
211 raise ROClientException(error_text)
212
213 @staticmethod
214 def _parse_error_yaml(descriptor):
215 json_error = None
216 try:
217 json_error = yaml.load(descriptor, Loader=yaml.Loader)
218 return json_error["error"]["description"]
219 except Exception:
220 return str(json_error or descriptor)
221
222 @staticmethod
223 def _parse_yaml(descriptor, response=False):
224 try:
225 return yaml.load(descriptor, Loader=yaml.Loader)
226 except yaml.YAMLError as exc:
227 error_pos = ""
228 if hasattr(exc, "problem_mark"):
229 mark = exc.problem_mark
230 error_pos = " at line:{} column:{}s".format(
231 mark.line + 1, mark.column + 1
232 )
233 error_text = "yaml format error" + error_pos
234 if response:
235 raise ROClientException(error_text)
236 raise ROClientException(error_text)
237
238 @staticmethod
239 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 try:
246 UUID(uuid_text)
247 return True
248 except Exception:
249 return False
250
251 @staticmethod
252 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 if item == "vnfd":
260 return {"vnfd-catalog": {"vnfd": [indata]}}
261 elif item == "nsd":
262 return {"nsd-catalog": {"nsd": [indata]}}
263 elif item == "tenant":
264 return {"tenant": indata}
265 elif item in ("vim", "vim_account", "datacenter"):
266 return {"datacenter": indata}
267 elif item == "wim":
268 return {"wim": indata}
269 elif item == "wim_account":
270 return {"wim_account": indata}
271 elif item == "ns" or item == "instances":
272 return {"instance": indata}
273 elif item == "sdn":
274 return {"sdn_controller": indata}
275 else:
276 assert False, "_create_envelop with unknown item {}".format(item)
277
278 @staticmethod
279 def update_descriptor(desc, kwargs):
280 desc = deepcopy(desc) # do not modify original descriptor
281 try:
282 for k, v in kwargs.items():
283 update_content = desc
284 kitem_old = None
285 klist = k.split(".")
286 for kitem in klist:
287 if kitem_old is not None:
288 update_content = update_content[kitem_old]
289 if isinstance(update_content, dict):
290 kitem_old = kitem
291 elif isinstance(update_content, list):
292 kitem_old = int(kitem)
293 else:
294 raise ROClientException(
295 "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(
296 k, kitem
297 )
298 )
299 if v == "__DELETE__":
300 del update_content[kitem_old]
301 else:
302 update_content[kitem_old] = v
303 return desc
304 except KeyError:
305 raise ROClientException(
306 "Invalid query string '{}'. Descriptor does not contain '{}'".format(
307 k, kitem_old
308 )
309 )
310 except ValueError:
311 raise ROClientException(
312 "Invalid query string '{}'. Expected integer index list instead of '{}'".format(
313 k, kitem
314 )
315 )
316 except IndexError:
317 raise ROClientException(
318 "Invalid query string '{}'. Index '{}' out of range".format(
319 k, kitem_old
320 )
321 )
322
323 @staticmethod
324 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 error_list = []
331 total = {"VMs": 0, "networks": 0, "SDN_networks": 0}
332 done = {"VMs": 0, "networks": 0, "SDN_networks": 0}
333
334 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 if desc.get("vim_net_id"):
337 return "'vim-net-id={}'".format(desc["vim_net_id"])
338 elif desc.get("ns_net_osm_id"):
339 return "'nsd-vld-id={}'".format(desc["ns_net_osm_id"])
340 elif desc.get("vnf_net_osm_id"):
341 return "'vnfd-vld-id={}'".format(desc["vnf_net_osm_id"])
342 # for VM
343 elif desc.get("vim_vm_id"):
344 return "'vim-vm-id={}'".format(desc["vim_vm_id"])
345 elif desc.get("vdu_osm_id"):
346 return "'vnfd-vdu-id={}'".format(desc["vdu_osm_id"])
347 else:
348 return ""
349
350 def _get_sdn_ref(sce_net_id):
351 # look for the network associated to the SDN network and obtain the identification
352 net = next(
353 (x for x in ns_descriptor["nets"] if x.get("sce_net_id") == sce_net_id),
354 None,
355 )
356 if not sce_net_id or not net:
357 return ""
358 return _get_ref(net)
359
360 try:
361 total["networks"] = len(ns_descriptor["nets"])
362 for net in ns_descriptor["nets"]:
363 if net["status"] in ("ERROR", "VIM_ERROR"):
364 error_list.append(
365 "Error at VIM network {}: {}".format(
366 _get_ref(net), net["error_msg"]
367 )
368 )
369 elif net["status"] == "ACTIVE":
370 done["networks"] += 1
371
372 total["SDN_networks"] = len(ns_descriptor["sdn_nets"])
373 for sdn_net in ns_descriptor["sdn_nets"]:
374 if sdn_net["status"] in ("ERROR", "VIM_ERROR", "WIM_ERROR"):
375 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 elif sdn_net["status"] == "ACTIVE":
382 done["SDN_networks"] += 1
383
384 for vnf in ns_descriptor["vnfs"]:
385 for vm in vnf["vms"]:
386 total["VMs"] += 1
387 if vm["status"] in ("ERROR", "VIM_ERROR"):
388 error_list.append(
389 "Error at VIM VM {}: {}".format(
390 _get_ref(vm), vm["error_msg"]
391 )
392 )
393 elif vm["status"] == "ACTIVE":
394 done["VMs"] += 1
395 if error_list:
396 # skip errors caused because other dependendent task is on error
397 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 if all(total[x] == done[x] for x in total): # DONE == TOTAL for all items
405 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 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 except Exception as e:
414 raise ROClientException(
415 "Unexpected RO ns descriptor. Wrong version? {}".format(e)
416 ) from e
417
418 @staticmethod
419 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 net_total = 0
426 vm_total = 0
427 net_done = 0
428 vm_done = 0
429 other_total = 0
430 other_done = 0
431
432 for vim_action_set in action_descriptor["actions"]:
433 for vim_action in vim_action_set["vim_wim_actions"]:
434 if vim_action["item"] == "instance_vms":
435 vm_total += 1
436 elif vim_action["item"] == "instance_nets":
437 net_total += 1
438 else:
439 other_total += 1
440 if vim_action["status"] == "FAILED":
441 return "ERROR", vim_action["error_msg"]
442 elif vim_action["status"] in ("DONE", "SUPERSEDED", "FINISHED"):
443 if vim_action["item"] == "instance_vms":
444 vm_done += 1
445 elif vim_action["item"] == "instance_nets":
446 net_done += 1
447 else:
448 other_done += 1
449
450 if net_total == net_done and vm_total == vm_done and other_total == other_done:
451 return "ACTIVE", "VMs {}, networks: {}, other: {} ".format(
452 vm_total, net_total, other_total
453 )
454 else:
455 return "BUILD", "VMs: {}/{}, networks: {}/{}, other: {}/{}".format(
456 vm_done, vm_total, net_done, net_total, other_done, other_total
457 )
458
459 @staticmethod
460 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 ns_info = {}
477 for vnf in ns_descriptor["vnfs"]:
478 if not vnf.get("ip_address") and vnf.get("vms"):
479 raise ROClientException(
480 "ns member_vnf_index '{}' has no IP address".format(
481 vnf["member_vnf_index"]
482 ),
483 http_code=409,
484 )
485 vnfr_info = {"ip_address": vnf.get("ip_address"), "vdur": {}}
486 for vm in vnf["vms"]:
487 vdur = {
488 "vim_id": vm.get("vim_vm_id"),
489 "ip_address": vm.get("ip_address"),
490 "interfaces": {},
491 }
492 for iface in vm["interfaces"]:
493 if iface.get("type") == "mgmt" and not iface.get("ip_address"):
494 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 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 vnfr_info["vdur"][vm["vdu_osm_id"]] = vdur
509 ns_info[str(vnf["member_vnf_index"])] = vnfr_info
510 return ns_info
511
512 async def _get_item_uuid(self, session, item, item_id_name, all_tenants=False):
513 if all_tenants:
514 tenant_text = "/any"
515 elif all_tenants is None:
516 tenant_text = ""
517 else:
518 if not self.tenant:
519 await self._get_tenant(session)
520 tenant_text = "/" + self.tenant
521
522 item_id = 0
523 url = "{}{}/{}".format(self.uri, tenant_text, item)
524 if self.check_if_uuid(item_id_name):
525 item_id = item_id_name
526 url += "/" + item_id_name
527 elif (
528 item_id_name and item_id_name.startswith("'") and item_id_name.endswith("'")
529 ):
530 item_id_name = item_id_name[1:-1]
531 self.logger.debug("RO GET %s", url)
532 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
533 async with session.get(url, headers=self.headers_req) as response:
534 response_text = await response.read()
535 self.logger.debug(
536 "GET {} [{}] {}".format(url, response.status, response_text[:100])
537 )
538 if response.status == 404: # NOT_FOUND
539 raise ROClientException(
540 "No {} found with id '{}'".format(item[:-1], item_id_name),
541 http_code=404,
542 )
543 if response.status >= 300:
544 raise ROClientException(
545 self._parse_error_yaml(response_text), http_code=response.status
546 )
547 content = self._parse_yaml(response_text, response=True)
548
549 if item_id:
550 return item_id
551 desc = content[item]
552 assert isinstance(
553 desc, list
554 ), "_get_item_uuid get a non dict with a list inside {}".format(type(desc))
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 raise ROClientException(
833 "Got invalid version text: '{}'; causing exception {}".format(
834 response_text, e
835 ),
836 http_code=500,
837 )
838
839 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 try:
848 if item not in self.client_to_RO:
849 raise ROClientException("Invalid item {}".format(item))
850 if item == "tenant":
851 all_tenants = None
852 async with aiohttp.ClientSession(loop=self.loop) as session:
853 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 if isinstance(content, dict):
860 if len(content) == 1:
861 for _, v in content.items():
862 return v
863 return content.values()[0]
864 else:
865 raise ROClientException(
866 "Output not a list neither dict with len equal 1", http_code=500
867 )
868 return content
869 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
870 raise ROClientException(e, http_code=504)
871 except asyncio.TimeoutError:
872 raise ROClientException("Timeout", http_code=504)
873
874 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 try:
893 if item not in self.client_to_RO:
894 raise ROClientException("Invalid item {}".format(item))
895 if item == "tenant":
896 all_tenants = None
897 elif item == "vim":
898 all_tenants = True
899 elif item == "vim_account":
900 all_tenants = False
901
902 async with aiohttp.ClientSession(loop=self.loop) as session:
903 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 return remove_envelop(item, content)
912 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
913 raise ROClientException(e, http_code=504)
914 except asyncio.TimeoutError:
915 raise ROClientException("Timeout", http_code=504)
916
917 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 try:
926 if item not in self.client_to_RO:
927 raise ROClientException("Invalid item {}".format(item))
928 if item in ("tenant", "vim", "wim"):
929 all_tenants = None
930
931 async with aiohttp.ClientSession(loop=self.loop) as session:
932 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 if item == "ns" and result.get("result"):
940 _, _, action_id = result["result"].partition("action_id=")
941 action_id, _, _ = action_id.partition(" ")
942 if action_id:
943 result["action_id"] = action_id
944 return result
945 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
946 raise ROClientException(e, http_code=504)
947 except asyncio.TimeoutError:
948 raise ROClientException("Timeout", http_code=504)
949
950 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 try:
963 if isinstance(descriptor, str):
964 descriptor = self._parse(descriptor, descriptor_format)
965 elif descriptor:
966 pass
967 else:
968 descriptor = {}
969
970 if item not in self.client_to_RO:
971 raise ROClientException("Invalid item {}".format(item))
972 desc = remove_envelop(item, descriptor)
973
974 # Override descriptor with kwargs
975 if kwargs:
976 desc = self.update_descriptor(desc, kwargs)
977 all_tenants = False
978 if item in ("tenant", "vim"):
979 all_tenants = None
980
981 create_desc = self._create_envelop(item, desc)
982
983 async with aiohttp.ClientSession(loop=self.loop) as session:
984 _all_tenants = all_tenants
985 if item == "vim":
986 _all_tenants = True
987 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 if item == "vim":
994 _all_tenants = None
995 # await self._get_tenant(session)
996 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 return remove_envelop(item, outdata)
1004 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1005 raise ROClientException(e, http_code=504)
1006 except asyncio.TimeoutError:
1007 raise ROClientException("Timeout", http_code=504)
1008
1009 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 try:
1020 if isinstance(descriptor, str):
1021 descriptor = self._parse(descriptor, descriptor_format)
1022 elif descriptor:
1023 pass
1024 else:
1025 descriptor = {}
1026
1027 if item not in self.client_to_RO:
1028 raise ROClientException("Invalid item {}".format(item))
1029 desc = remove_envelop(item, descriptor)
1030
1031 # Override descriptor with kwargs
1032 if kwargs:
1033 desc = self.update_descriptor(desc, kwargs)
1034
1035 for mandatory in self.mandatory_for_create[item]:
1036 if mandatory not in desc:
1037 raise ROClientException(
1038 "'{}' is mandatory parameter for {}".format(mandatory, item)
1039 )
1040
1041 all_tenants = False
1042 if item in ("tenant", "vim", "wim"):
1043 all_tenants = None
1044
1045 create_desc = self._create_envelop(item, desc)
1046
1047 async with aiohttp.ClientSession(loop=self.loop) as session:
1048 outdata = await self._create_item(
1049 session,
1050 self.client_to_RO[item],
1051 create_desc,
1052 all_tenants=all_tenants,
1053 )
1054 return remove_envelop(item, outdata)
1055 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1056 raise ROClientException(e, http_code=504)
1057 except asyncio.TimeoutError:
1058 raise ROClientException("Timeout", http_code=504)
1059
1060 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 try:
1074 if isinstance(descriptor, str):
1075 descriptor = self._parse(descriptor, descriptor_format)
1076 elif descriptor:
1077 pass
1078 else:
1079 descriptor = {}
1080
1081 if item not in self.client_to_RO:
1082 raise ROClientException("Invalid item {}".format(item))
1083 desc = remove_envelop(item, descriptor)
1084
1085 # Override descriptor with kwargs
1086 if kwargs:
1087 desc = self.update_descriptor(desc, kwargs)
1088
1089 all_tenants = False
1090 if item in ("tenant", "vim"):
1091 all_tenants = None
1092
1093 action = None
1094 if item == "vims":
1095 action = "sdn_mapping"
1096 elif item in ("vim_account", "ns"):
1097 action = "action"
1098
1099 # create_desc = self._create_envelop(item, desc)
1100 create_desc = desc
1101
1102 async with aiohttp.ClientSession(loop=self.loop) as session:
1103 _all_tenants = all_tenants
1104 if item == "vim":
1105 _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 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 return remove_envelop(item, outdata)
1117 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1118 raise ROClientException(e, http_code=504)
1119 except asyncio.TimeoutError:
1120 raise ROClientException("Timeout", http_code=504)
1121
1122 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 try:
1135 if isinstance(descriptor, str):
1136 descriptor = self._parse(descriptor, descriptor_format)
1137 elif descriptor:
1138 pass
1139 else:
1140 descriptor = {}
1141
1142 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 if kwargs:
1148 desc = self.update_descriptor(desc, kwargs)
1149
1150 if item == "vim_account":
1151 if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
1152 raise ROClientException(
1153 "Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
1154 "provided"
1155 )
1156 elif item != "wim_account":
1157 raise ROClientException(
1158 "Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".format(
1159 item
1160 )
1161 )
1162 create_desc = self._create_envelop(item, desc)
1163 payload_req = yaml.safe_dump(create_desc)
1164 async with aiohttp.ClientSession(loop=self.loop) as session:
1165 # check that exist
1166 item_id = await self._get_item_uuid(
1167 session, self.client_to_RO[item], item_id_name, all_tenants=True
1168 )
1169 await self._get_tenant(session)
1170
1171 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 self.logger.debug("RO POST %s %s", url, payload_req)
1178 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1179 async with session.post(
1180 url, headers=self.headers_req, data=payload_req
1181 ) as response:
1182 response_text = await response.read()
1183 self.logger.debug(
1184 "POST {} [{}] {}".format(
1185 url, response.status, response_text[:100]
1186 )
1187 )
1188 if response.status >= 300:
1189 raise ROClientException(
1190 self._parse_error_yaml(response_text),
1191 http_code=response.status,
1192 )
1193
1194 response_desc = self._parse_yaml(response_text, response=True)
1195 desc = remove_envelop(item, response_desc)
1196 return desc
1197 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1198 raise ROClientException(e, http_code=504)
1199 except asyncio.TimeoutError:
1200 raise ROClientException("Timeout", http_code=504)
1201
1202 async def detach(self, item, item_id_name=None):
1203 # TODO replace the code with delete_item(vim_account,...)
1204 try:
1205 async with aiohttp.ClientSession(loop=self.loop) as session:
1206 # check that exist
1207 item_id = await self._get_item_uuid(
1208 session, self.client_to_RO[item], item_id_name, all_tenants=False
1209 )
1210 tenant = await self._get_tenant(session)
1211
1212 url = "{}/{tenant}/{item}/{datacenter}".format(
1213 self.uri,
1214 tenant=tenant,
1215 item=self.client_to_RO[item],
1216 datacenter=item_id,
1217 )
1218 self.logger.debug("RO DELETE %s", url)
1219
1220 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1221 async with session.delete(url, headers=self.headers_req) as response:
1222 response_text = await response.read()
1223 self.logger.debug(
1224 "DELETE {} [{}] {}".format(
1225 url, response.status, response_text[:100]
1226 )
1227 )
1228 if response.status >= 300:
1229 raise ROClientException(
1230 self._parse_error_yaml(response_text),
1231 http_code=response.status,
1232 )
1233
1234 response_desc = self._parse_yaml(response_text, response=True)
1235 desc = remove_envelop(item, response_desc)
1236 return desc
1237 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
1238 raise ROClientException(e, http_code=504)
1239 except asyncio.TimeoutError:
1240 raise ROClientException("Timeout", http_code=504)
1241
1242 # TODO convert to asyncio
1243 # DATACENTERS
1244
1245 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 if isinstance(descriptor, str):
1271 descriptor = self.parse(descriptor, descriptor_format)
1272 elif descriptor:
1273 pass
1274 elif kwargs:
1275 descriptor = {"datacenter": {}}
1276 else:
1277 raise ROClientException("Missing descriptor")
1278
1279 if "datacenter" not in descriptor or len(descriptor) != 1:
1280 raise ROClientException(
1281 "Descriptor must contain only one 'datacenter' field"
1282 )
1283 for param in kwargs:
1284 if param == "new_name":
1285 descriptor["datacenter"]["name"] = kwargs[param]
1286 else:
1287 descriptor["datacenter"][param] = kwargs[param]
1288 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
1289
1290 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 if isinstance(descriptor, str):
1314 descriptor = self.parse(descriptor, descriptor_format)
1315 elif descriptor:
1316 pass
1317 elif kwargs:
1318 descriptor = {"scenario": {}}
1319 else:
1320 raise ROClientException("Missing descriptor")
1321
1322 if "scenario" not in descriptor or len(descriptor) > 2:
1323 raise ROClientException("Descriptor must contain only one 'scenario' field")
1324 for param in kwargs:
1325 if param == "new_name":
1326 descriptor["scenario"]["name"] = kwargs[param]
1327 else:
1328 descriptor["scenario"][param] = kwargs[param]
1329 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
1330
1331 # VIM ACTIONS
1332 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 session = None # TODO remove when changed to asyncio
1349 if item not in ("tenants", "networks", "images"):
1350 raise ROClientException(
1351 "Unknown value for item '{}', must be 'tenants', 'nets' or "
1352 "images".format(str(item))
1353 )
1354
1355 image_actions = ["list", "get", "show", "delete"]
1356 if item == "images" and action not in image_actions:
1357 raise ROClientException(
1358 "Only available actions for item '{}' are {}\n"
1359 "Requested action was '{}'".format(
1360 item, ", ".join(image_actions), action
1361 )
1362 )
1363 if all_tenants:
1364 tenant_text = "/any"
1365 else:
1366 tenant_text = "/" + self._get_tenant()
1367
1368 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
1369 datacenter = self._get_item_uuid(
1370 session,
1371 "datacenters",
1372 kwargs.get("datacenter"),
1373 all_tenants=all_tenants,
1374 )
1375 else:
1376 datacenter = self.get_datacenter(session)
1377
1378 if action == "list":
1379 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
1380 self.logger.debug("GET %s", url)
1381 mano_response = requests.get(url, headers=self.headers_req)
1382 self.logger.debug("RO response: %s", mano_response.text)
1383 content = self._parse_yaml(mano_response.text, response=True)
1384 if mano_response.status_code == 200:
1385 return content
1386 else:
1387 raise ROClientException(str(content), http_code=mano_response.status)
1388 elif action == "get" or action == "show":
1389 url = "{}{}/vim/{}/{}/{}".format(
1390 self.uri, tenant_text, datacenter, item, uuid
1391 )
1392 self.logger.debug("GET %s", url)
1393 mano_response = requests.get(url, headers=self.headers_req)
1394 self.logger.debug("RO response: %s", mano_response.text)
1395 content = self._parse_yaml(mano_response.text, response=True)
1396 if mano_response.status_code == 200:
1397 return content
1398 else:
1399 raise ROClientException(str(content), http_code=mano_response.status)
1400 elif action == "delete":
1401 url = "{}{}/vim/{}/{}/{}".format(
1402 self.uri, tenant_text, datacenter, item, uuid
1403 )
1404 self.logger.debug("DELETE %s", url)
1405 mano_response = requests.delete(url, headers=self.headers_req)
1406 self.logger.debug("RO response: %s", mano_response.text)
1407 content = self._parse_yaml(mano_response.text, response=True)
1408 if mano_response.status_code == 200:
1409 return content
1410 else:
1411 raise ROClientException(str(content), http_code=mano_response.status)
1412 elif action == "create":
1413 if "descriptor" in kwargs:
1414 if isinstance(kwargs["descriptor"], str):
1415 descriptor = self._parse(
1416 kwargs["descriptor"], kwargs.get("descriptor_format")
1417 )
1418 else:
1419 descriptor = kwargs["descriptor"]
1420 elif "name" in kwargs:
1421 descriptor = {item[:-1]: {"name": kwargs["name"]}}
1422 else:
1423 raise ROClientException("Missing descriptor")
1424
1425 if item[:-1] not in descriptor or len(descriptor) != 1:
1426 raise ROClientException(
1427 "Descriptor must contain only one 'tenant' field"
1428 )
1429 if "name" in kwargs:
1430 descriptor[item[:-1]]["name"] = kwargs["name"]
1431 if "description" in kwargs:
1432 descriptor[item[:-1]]["description"] = kwargs["description"]
1433 payload_req = yaml.safe_dump(descriptor)
1434 # print payload_req
1435 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
1436 self.logger.debug("RO POST %s %s", url, payload_req)
1437 mano_response = requests.post(
1438 url, headers=self.headers_req, data=payload_req
1439 )
1440 self.logger.debug("RO response: %s", mano_response.text)
1441 content = self._parse_yaml(mano_response.text, response=True)
1442 if mano_response.status_code == 200:
1443 return content
1444 else:
1445 raise ROClientException(str(content), http_code=mano_response.status)
1446 else:
1447 raise ROClientException("Unknown value for action '{}".format(str(action)))
1448
1449
1450 if __name__ == "__main__":
1451 RO_URL = "http://localhost:9090/openmano"
1452 TEST_TENANT = "myTenant"
1453 TEST_VIM1 = "myvim"
1454 TEST_URL1 = "https://localhost:5000/v1"
1455 TEST_TYPE1 = "openstack"
1456 TEST_CONFIG1 = {"use_floating_ip": True}
1457 TEST_VIM2 = "myvim2"
1458 TEST_URL2 = "https://localhost:5000/v2"
1459 TEST_TYPE2 = "openvim"
1460 TEST_CONFIG2 = {"config2": "config2", "config3": True}
1461
1462 streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1463 logging.basicConfig(format=streamformat)
1464 logger = logging.getLogger("ROClient")
1465
1466 tenant_id = None
1467 vim_id = False
1468 loop = asyncio.get_event_loop()
1469 myClient = ROClient(uri=RO_URL, loop=loop, loglevel="DEBUG")
1470 try:
1471 # test tenant
1472 content = loop.run_until_complete(myClient.get_list("tenant"))
1473 print("tenants", content)
1474 content = loop.run_until_complete(myClient.create("tenant", name=TEST_TENANT))
1475 tenant_id = True
1476 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1477 print("tenant", TEST_TENANT, content)
1478 content = loop.run_until_complete(
1479 myClient.edit("tenant", TEST_TENANT, description="another description")
1480 )
1481 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1482 print("tenant edited", TEST_TENANT, content)
1483 myClient["tenant"] = TEST_TENANT
1484
1485 # test VIM
1486 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 vim_id = True
1496 content = loop.run_until_complete(myClient.get_list("vim"))
1497 print("vim", content)
1498 content = loop.run_until_complete(myClient.show("vim", TEST_VIM1))
1499 print("vim", TEST_VIM1, content)
1500 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 content = loop.run_until_complete(myClient.show("vim", TEST_VIM2))
1512 print("vim edited", TEST_VIM2, content)
1513
1514 # test VIM_ACCOUNT
1515 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 vim_id = True
1525 content = loop.run_until_complete(myClient.get_list("vim_account"))
1526 print("vim_account", content)
1527 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1528 print("vim_account", TEST_VIM2, content)
1529 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 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1540 print("vim_account edited", TEST_VIM2, content)
1541
1542 myClient["vim"] = TEST_VIM2
1543
1544 except Exception as e:
1545 logger.error("Error {}".format(e), exc_info=True)
1546
1547 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 try:
1555 content = loop.run_until_complete(myClient.delete(item[0], item[1]))
1556 print("{} {} deleted; {}".format(item[0], item[1], content))
1557 except Exception as e:
1558 if e.http_code == 404:
1559 print("{} {} not present or already deleted".format(item[0], item[1]))
1560 else:
1561 logger.error("Error {}".format(e), exc_info=True)
1562
1563 loop.close()