2 # -*- coding: utf-8 -*-
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
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
11 # http://www.apache.org/licenses/LICENSE-2.0
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
22 asyncio RO python client to interact with RO-server
30 from urllib
.parse
import quote
32 from copy
import deepcopy
34 __author__
= "Alfonso Tierno"
35 __date__
= "$09-Jan-2018 09:09:48$"
37 version_date
= "2018-05-16"
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
)
48 def remove_envelop(item
, indata
=None):
50 Obtain the useful data removing the envelop. It goes through the vnfd or nsd catalog and returns the
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)
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"):
66 not isinstance(clean_indata
["vnfd"], list)
67 or len(clean_indata
["vnfd"]) != 1
69 raise ROClientException("'vnfd' must be a list only one element")
70 clean_indata
= clean_indata
["vnfd"][0]
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"):
78 not isinstance(clean_indata
["nsd"], list)
79 or len(clean_indata
["nsd"]) != 1
81 raise ROClientException("'nsd' must be a list only one element")
82 clean_indata
= clean_indata
["nsd"][0]
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"]
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"]
102 raise ROClientException("remove_envelop with unknown item {}".format(item
))
108 headers_req
= {"Accept": "application/yaml", "content-type": "application/yaml"}
111 "vim": "datacenters",
112 "vim_account": "datacenters",
113 "sdn": "sdn_controllers",
117 "wim_account": "wims",
120 mandatory_for_create
= {
122 "vnfd": ("name", "id"),
123 "nsd": ("name", "id"),
124 "ns": ("name", "scenario", "datacenter"),
125 "vim": ("name", "vim_url"),
126 "wim": ("name", "wim_url"),
129 "sdn": ("name", "type"),
134 def __init__(self
, uri
, **kwargs
):
137 self
.username
= kwargs
.get("username")
138 self
.password
= kwargs
.get("password")
139 self
.tenant_id_name
= kwargs
.get("tenant")
141 self
.datacenter_id_name
= kwargs
.get("datacenter")
142 self
.datacenter
= None
143 logger_name
= kwargs
.get("logger_name", "lcm.ro")
144 self
.logger
= logging
.getLogger(logger_name
)
145 if kwargs
.get("loglevel"):
146 self
.logger
.setLevel(kwargs
["loglevel"])
148 requests
= kwargs
.get("TODO remove")
150 def __getitem__(self
, index
):
151 if index
== "tenant":
152 return self
.tenant_id_name
153 elif index
== "datacenter":
154 return self
.datacenter_id_name
155 elif index
== "username":
157 elif index
== "password":
162 raise KeyError("Invalid key '{}'".format(index
))
164 def __setitem__(self
, index
, value
):
165 if index
== "tenant":
166 self
.tenant_id_name
= value
167 elif index
== "datacenter" or index
== "vim":
168 self
.datacenter_id_name
= value
169 elif index
== "username":
170 self
.username
= value
171 elif index
== "password":
172 self
.password
= value
176 raise KeyError("Invalid key '{}'".format(index
))
177 self
.tenant
= None # force to reload tenant with different credentials
178 self
.datacenter
= None # force to reload datacenter with different credentials
181 def _parse(descriptor
, descriptor_format
, response
=False):
184 and descriptor_format
!= "json"
185 and descriptor_format
!= "yaml"
187 raise ROClientException(
188 "'descriptor_format' must be a 'json' or 'yaml' text"
190 if descriptor_format
!= "json":
192 return yaml
.safe_load(descriptor
)
193 except yaml
.YAMLError
as exc
:
195 if hasattr(exc
, "problem_mark"):
196 mark
= exc
.problem_mark
197 error_pos
= " at line:{} column:{}s".format(
198 mark
.line
+ 1, mark
.column
+ 1
200 error_text
= "yaml format error" + error_pos
201 elif descriptor_format
!= "yaml":
203 return json
.loads(descriptor
)
204 except Exception as e
:
206 error_text
= "json format error" + str(e
)
209 raise ROClientException(error_text
)
210 raise ROClientException(error_text
)
213 def _parse_error_yaml(descriptor
):
216 json_error
= yaml
.safe_load(descriptor
)
217 return json_error
["error"]["description"]
219 return str(json_error
or descriptor
)
222 def _parse_yaml(descriptor
, response
=False):
224 return yaml
.safe_load(descriptor
)
225 except yaml
.YAMLError
as exc
:
227 if hasattr(exc
, "problem_mark"):
228 mark
= exc
.problem_mark
229 error_pos
= " at line:{} column:{}s".format(
230 mark
.line
+ 1, mark
.column
+ 1
232 error_text
= "yaml format error" + error_pos
234 raise ROClientException(error_text
)
235 raise ROClientException(error_text
)
238 def check_if_uuid(uuid_text
):
240 Check if text correspond to an uuid foramt
242 :return: True if it is an uuid False if not
251 def _create_envelop(item
, indata
=None):
253 Returns a new dict that incledes indata with the expected envelop
254 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
255 :param indata: Content to be enveloped
256 :return: a new dic with {<envelop>: {indata} } where envelop can be e.g. tenant, datacenter, ...
259 return {"vnfd-catalog": {"vnfd": [indata
]}}
261 return {"nsd-catalog": {"nsd": [indata
]}}
262 elif item
== "tenant":
263 return {"tenant": indata
}
264 elif item
in ("vim", "vim_account", "datacenter"):
265 return {"datacenter": indata
}
267 return {"wim": indata
}
268 elif item
== "wim_account":
269 return {"wim_account": indata
}
270 elif item
== "ns" or item
== "instances":
271 return {"instance": indata
}
273 return {"sdn_controller": indata
}
275 raise ROClientException("remove_envelop with unknown item {}".format(item
))
278 def update_descriptor(desc
, kwargs
):
279 desc
= deepcopy(desc
) # do not modify original descriptor
281 for k
, v
in kwargs
.items():
282 update_content
= desc
286 if kitem_old
is not None:
287 update_content
= update_content
[kitem_old
]
288 if isinstance(update_content
, dict):
290 elif isinstance(update_content
, list):
291 kitem_old
= int(kitem
)
293 raise ROClientException(
294 "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(
298 if v
== "__DELETE__":
299 del update_content
[kitem_old
]
301 update_content
[kitem_old
] = v
304 raise ROClientException(
305 "Invalid query string '{}'. Descriptor does not contain '{}'".format(
310 raise ROClientException(
311 "Invalid query string '{}'. Expected integer index list instead of '{}'".format(
316 raise ROClientException(
317 "Invalid query string '{}'. Index '{}' out of range".format(
323 def check_ns_status(ns_descriptor
):
325 Inspect RO instance descriptor and indicates the status
326 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
327 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
330 total
= {"VMs": 0, "networks": 0, "SDN_networks": 0}
331 done
= {"VMs": 0, "networks": 0, "SDN_networks": 0}
334 # return an identification for the network or vm. Try vim_id if exist, if not descriptor id for net
335 if desc
.get("vim_net_id"):
336 return "'vim-net-id={}'".format(desc
["vim_net_id"])
337 elif desc
.get("ns_net_osm_id"):
338 return "'nsd-vld-id={}'".format(desc
["ns_net_osm_id"])
339 elif desc
.get("vnf_net_osm_id"):
340 return "'vnfd-vld-id={}'".format(desc
["vnf_net_osm_id"])
342 elif desc
.get("vim_vm_id"):
343 return "'vim-vm-id={}'".format(desc
["vim_vm_id"])
344 elif desc
.get("vdu_osm_id"):
345 return "'vnfd-vdu-id={}'".format(desc
["vdu_osm_id"])
349 def _get_sdn_ref(sce_net_id
):
350 # look for the network associated to the SDN network and obtain the identification
352 (x
for x
in ns_descriptor
["nets"] if x
.get("sce_net_id") == sce_net_id
),
355 if not sce_net_id
or not net
:
360 total
["networks"] = len(ns_descriptor
["nets"])
361 for net
in ns_descriptor
["nets"]:
362 if net
["status"] in ("ERROR", "VIM_ERROR"):
364 "Error at VIM network {}: {}".format(
365 _get_ref(net
), net
["error_msg"]
368 elif net
["status"] == "ACTIVE":
369 done
["networks"] += 1
371 total
["SDN_networks"] = len(ns_descriptor
["sdn_nets"])
372 for sdn_net
in ns_descriptor
["sdn_nets"]:
373 if sdn_net
["status"] in ("ERROR", "VIM_ERROR", "WIM_ERROR"):
375 "Error at SDN network {}: {}".format(
376 _get_sdn_ref(sdn_net
.get("sce_net_id")),
377 sdn_net
["error_msg"],
380 elif sdn_net
["status"] == "ACTIVE":
381 done
["SDN_networks"] += 1
383 for vnf
in ns_descriptor
["vnfs"]:
384 for vm
in vnf
["vms"]:
386 if vm
["status"] in ("ERROR", "VIM_ERROR"):
388 "Error at VIM VM {}: {}".format(
389 _get_ref(vm
), vm
["error_msg"]
392 elif vm
["status"] == "ACTIVE":
395 # skip errors caused because other dependendent task is on error
396 return "ERROR", "; ".join(
400 if "because depends on failed ACTION" not in el
403 if all(total
[x
] == done
[x
] for x
in total
): # DONE == TOTAL for all items
404 return "ACTIVE", str(
405 {x
: total
[x
] for x
in total
if total
[x
]}
406 ) # print only those which value is not 0
409 {x
: "{}/{}".format(done
[x
], total
[x
]) for x
in total
if total
[x
]}
411 # print done/total for each item if total is not 0
412 except Exception as e
:
413 raise ROClientException(
414 "Unexpected RO ns descriptor. Wrong version? {}".format(e
)
418 def check_action_status(action_descriptor
):
420 Inspect RO instance descriptor and indicates the status
421 :param action_descriptor: action instance descriptor obtained with self.show("ns", "action")
422 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
431 for vim_action_set
in action_descriptor
["actions"]:
432 for vim_action
in vim_action_set
["vim_wim_actions"]:
433 if vim_action
["item"] == "instance_vms":
435 elif vim_action
["item"] == "instance_nets":
439 if vim_action
["status"] == "FAILED":
440 return "ERROR", vim_action
["error_msg"]
441 elif vim_action
["status"] in ("DONE", "SUPERSEDED", "FINISHED"):
442 if vim_action
["item"] == "instance_vms":
444 elif vim_action
["item"] == "instance_nets":
449 if net_total
== net_done
and vm_total
== vm_done
and other_total
== other_done
:
450 return "ACTIVE", "VMs {}, networks: {}, other: {} ".format(
451 vm_total
, net_total
, other_total
454 return "BUILD", "VMs: {}/{}, networks: {}/{}, other: {}/{}".format(
455 vm_done
, vm_total
, net_done
, net_total
, other_done
, other_total
459 def get_ns_vnf_info(ns_descriptor
):
461 Get a dict with the VIM_id, ip_addresses, mac_addresses of every vnf and vdu
462 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
476 for vnf
in ns_descriptor
["vnfs"]:
477 if not vnf
.get("ip_address") and vnf
.get("vms"):
478 raise ROClientException(
479 "ns member_vnf_index '{}' has no IP address".format(
480 vnf
["member_vnf_index"]
484 vnfr_info
= {"ip_address": vnf
.get("ip_address"), "vdur": {}}
485 for vm
in vnf
["vms"]:
487 "vim_id": vm
.get("vim_vm_id"),
488 "ip_address": vm
.get("ip_address"),
491 for iface
in vm
["interfaces"]:
492 if iface
.get("type") == "mgmt" and not iface
.get("ip_address"):
493 raise ROClientException(
494 "ns member_vnf_index '{}' vm '{}' management interface '{}' has no IP "
496 vnf
["member_vnf_index"],
498 iface
["external_name"],
502 vdur
["interfaces"][iface
["internal_name"]] = {
503 "ip_address": iface
.get("ip_address"),
504 "mac_address": iface
.get("mac_address"),
505 "vim_id": iface
.get("vim_interface_id"),
507 vnfr_info
["vdur"][vm
["vdu_osm_id"]] = vdur
508 ns_info
[str(vnf
["member_vnf_index"])] = vnfr_info
511 async def _get_item_uuid(self
, session
, item
, item_id_name
, all_tenants
=False):
514 elif all_tenants
is None:
518 await self
._get
_tenant
(session
)
519 tenant_text
= "/" + self
.tenant
522 url
= "{}{}/{}".format(self
.uri
, tenant_text
, item
)
523 if self
.check_if_uuid(item_id_name
):
524 item_id
= item_id_name
525 url
+= "/" + item_id_name
527 item_id_name
and item_id_name
.startswith("'") and item_id_name
.endswith("'")
529 item_id_name
= item_id_name
[1:-1]
530 self
.logger
.debug("RO GET %s", url
)
531 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
532 async with session
.get(url
, headers
=self
.headers_req
) as response
:
533 response_text
= await response
.read()
535 "GET {} [{}] {}".format(url
, response
.status
, response_text
[:100])
537 if response
.status
== 404: # NOT_FOUND
538 raise ROClientException(
539 "No {} found with id '{}'".format(item
[:-1], item_id_name
),
542 if response
.status
>= 300:
543 raise ROClientException(
544 self
._parse
_error
_yaml
(response_text
), http_code
=response
.status
546 content
= self
._parse
_yaml
(response_text
, response
=True)
551 if not isinstance(desc
, list):
552 raise ROClientException(
553 "_get_item_uuid get a non dict with a list inside {}".format(type(desc
))
557 if item_id_name
and i
["name"] != item_id_name
:
559 if uuid
: # found more than one
560 raise ROClientException(
561 "Found more than one {} with name '{}'. uuid must be used".format(
568 raise ROClientException(
569 "No {} found with name '{}'".format(item
[:-1], item_id_name
),
585 elif all_tenants
is None:
589 await self
._get
_tenant
(session
)
590 tenant_text
= "/" + self
.tenant
592 if self
.check_if_uuid(item_id_name
):
596 uuid
= await self
._get
_item
_uuid
(session
, item
, item_id_name
, all_tenants
)
598 url
= "{}{}/{}/{}".format(self
.uri
, tenant_text
, item
, uuid
)
600 url
+= "/" + extra_item
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()
608 "GET {} [{}] {}".format(url
, response
.status
, response_text
[:100])
610 if response
.status
>= 300:
611 raise ROClientException(
612 self
._parse
_error
_yaml
(response_text
), http_code
=response
.status
615 return self
._parse
_yaml
(response_text
, response
=True)
617 async def _get_tenant(self
, session
):
619 self
.tenant
= await self
._get
_item
_uuid
(
620 session
, "tenants", self
.tenant_id_name
, None
624 async def _get_datacenter(self
, session
):
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
631 return self
.datacenter
633 async def _create_item(
644 elif all_tenants
is None:
648 await self
._get
_tenant
(session
)
649 tenant_text
= "/" + self
.tenant
650 payload_req
= yaml
.safe_dump(descriptor
)
653 api_version_text
= ""
655 # assumes version v3 only
656 api_version_text
= "/v3"
658 elif item
== "scenarios":
659 # assumes version v3 only
660 api_version_text
= "/v3"
665 elif self
.check_if_uuid(item_id_name
):
666 uuid
= "/{}".format(item_id_name
)
669 uuid
= await self
._get
_item
_uuid
(session
, item
, item_id_name
, all_tenants
)
670 uuid
= "/{}".format(uuid
)
674 action
= "/{}".format(action
)
676 url
= "{}{apiver}{tenant}/{item}{id}{action}".format(
678 apiver
=api_version_text
,
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
689 response_text
= await response
.read()
691 "POST {} [{}] {}".format(url
, response
.status
, response_text
[:100])
693 if response
.status
>= 300:
694 raise ROClientException(
695 self
._parse
_error
_yaml
(response_text
), http_code
=response
.status
698 return self
._parse
_yaml
(response_text
, response
=True)
700 async def _del_item(self
, session
, item
, item_id_name
, all_tenants
=False):
703 elif all_tenants
is None:
707 await self
._get
_tenant
(session
)
708 tenant_text
= "/" + self
.tenant
709 if not self
.check_if_uuid(item_id_name
):
711 _all_tenants
= all_tenants
712 if item
in ("datacenters", "wims"):
714 uuid
= await self
._get
_item
_uuid
(
715 session
, item
, item_id_name
, all_tenants
=_all_tenants
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()
726 "DELETE {} [{}] {}".format(url
, response
.status
, response_text
[:100])
728 if response
.status
>= 300:
729 raise ROClientException(
730 self
._parse
_error
_yaml
(response_text
), http_code
=response
.status
733 return self
._parse
_yaml
(response_text
, response
=True)
735 async def _list_item(self
, session
, item
, all_tenants
=False, filter_dict
=None):
738 elif all_tenants
is None:
742 await self
._get
_tenant
(session
)
743 tenant_text
= "/" + self
.tenant
745 url
= "{}{}/{}".format(self
.uri
, tenant_text
, item
)
748 for k
in filter_dict
:
749 url
+= separator
+ quote(str(k
)) + "=" + quote(str(filter_dict
[k
]))
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()
756 "GET {} [{}] {}".format(url
, response
.status
, response_text
[:100])
758 if response
.status
>= 300:
759 raise ROClientException(
760 self
._parse
_error
_yaml
(response_text
), http_code
=response
.status
763 return self
._parse
_yaml
(response_text
, response
=True)
765 async def _edit_item(self
, session
, item
, item_id
, descriptor
, all_tenants
=False):
768 elif all_tenants
is None:
772 await self
._get
_tenant
(session
)
773 tenant_text
= "/" + self
.tenant
775 payload_req
= yaml
.safe_dump(descriptor
)
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
784 response_text
= await response
.read()
786 "PUT {} [{}] {}".format(url
, response
.status
, response_text
[:100])
788 if response
.status
>= 300:
789 raise ROClientException(
790 self
._parse
_error
_yaml
(response_text
), http_code
=response
.status
793 return self
._parse
_yaml
(response_text
, response
=True)
795 async def get_version(self
):
797 Obtain RO server version.
798 :return: a list with integers ["major", "minor", "release"]. Raises ROClientException on Error,
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()
809 "GET {} [{}] {}".format(
810 url
, response
.status
, response_text
[:100]
813 if response
.status
>= 300:
814 raise ROClientException(
815 self
._parse
_error
_yaml
(response_text
),
816 http_code
=response
.status
,
819 for word
in str(response_text
).split(" "):
821 version_text
, _
, _
= word
.partition("-")
823 raise ROClientException(
824 "Got invalid version text: '{}'".format(response_text
),
827 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
828 raise ROClientException(e
, http_code
=504)
829 except asyncio
.TimeoutError
:
830 raise ROClientException("Timeout", http_code
=504)
831 except Exception as e
:
832 self
.logger
.critical(
833 "Got invalid version text: '{}'; causing exception {}".format(
834 response_text
, str(e
)
837 raise ROClientException(
838 "Got invalid version text: '{}'; causing exception {}".format(
844 async def get_list(self
, item
, all_tenants
=False, filter_by
=None):
846 List of items filtered by the contents in the dictionary "filter_by".
847 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
848 :param all_tenants: True if not filtering by tenant. Only allowed for admin
849 :param filter_by: dictionary with filtering
850 :return: a list of dict. It can be empty. Raises ROClientException on Error,
853 if item
not in self
.client_to_RO
:
854 raise ROClientException("Invalid item {}".format(item
))
857 async with aiohttp
.ClientSession() as session
:
858 content
= await self
._list
_item
(
860 self
.client_to_RO
[item
],
861 all_tenants
=all_tenants
,
862 filter_dict
=filter_by
,
864 if isinstance(content
, dict):
865 if len(content
) == 1:
866 for _
, v
in content
.items():
868 return content
.values()[0]
870 raise ROClientException(
871 "Output not a list neither dict with len equal 1", http_code
=500
874 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
875 raise ROClientException(e
, http_code
=504)
876 except asyncio
.TimeoutError
:
877 raise ROClientException("Timeout", http_code
=504)
888 Obtain the information of an item from its id or name
889 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
890 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
891 :param extra_item: if supplied, it is used to add to the URL.
892 Can be 'action' if item='ns'; 'networks' or'images' if item='vim'
893 :param extra_item_id: if supplied, it is used get details of a concrete extra_item.
894 :param all_tenants: True if not filtering by tenant. Only allowed for admin
895 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
898 if item
not in self
.client_to_RO
:
899 raise ROClientException("Invalid item {}".format(item
))
904 elif item
== "vim_account":
907 async with aiohttp
.ClientSession() as session
:
908 content
= await self
._get
_item
(
910 self
.client_to_RO
[item
],
912 extra_item
=extra_item
,
913 extra_item_id
=extra_item_id
,
914 all_tenants
=all_tenants
,
916 return remove_envelop(item
, content
)
917 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
918 raise ROClientException(e
, http_code
=504)
919 except asyncio
.TimeoutError
:
920 raise ROClientException("Timeout", http_code
=504)
922 async def delete(self
, item
, item_id_name
=None, all_tenants
=False):
924 Delete the information of an item from its id or name
925 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
926 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
927 :param all_tenants: True if not filtering by tenant. Only allowed for admin
928 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
931 if item
not in self
.client_to_RO
:
932 raise ROClientException("Invalid item {}".format(item
))
933 if item
in ("tenant", "vim", "wim"):
936 async with aiohttp
.ClientSession() as session
:
937 result
= await self
._del
_item
(
939 self
.client_to_RO
[item
],
941 all_tenants
=all_tenants
,
943 # in case of ns delete, get the action_id embeded in text
944 if item
== "ns" and result
.get("result"):
945 _
, _
, action_id
= result
["result"].partition("action_id=")
946 action_id
, _
, _
= action_id
.partition(" ")
948 result
["action_id"] = action_id
950 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
951 raise ROClientException(e
, http_code
=504)
952 except asyncio
.TimeoutError
:
953 raise ROClientException("Timeout", http_code
=504)
956 self
, item
, item_id_name
, descriptor
=None, descriptor_format
=None, **kwargs
959 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
960 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
961 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
962 :param descriptor_format: Can be 'json' or 'yaml'
963 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
964 keys can be a dot separated list to specify elements inside dict
965 :return: dictionary with the information or raises ROClientException on Error
968 if isinstance(descriptor
, str):
969 descriptor
= self
._parse
(descriptor
, descriptor_format
)
975 if item
not in self
.client_to_RO
:
976 raise ROClientException("Invalid item {}".format(item
))
977 desc
= remove_envelop(item
, descriptor
)
979 # Override descriptor with kwargs
981 desc
= self
.update_descriptor(desc
, kwargs
)
983 if item
in ("tenant", "vim"):
986 create_desc
= self
._create
_envelop
(item
, desc
)
988 async with aiohttp
.ClientSession() as session
:
989 _all_tenants
= all_tenants
992 item_id
= await self
._get
_item
_uuid
(
994 self
.client_to_RO
[item
],
996 all_tenants
=_all_tenants
,
1000 # await self._get_tenant(session)
1001 outdata
= await self
._edit
_item
(
1003 self
.client_to_RO
[item
],
1006 all_tenants
=_all_tenants
,
1008 return remove_envelop(item
, outdata
)
1009 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
1010 raise ROClientException(e
, http_code
=504)
1011 except asyncio
.TimeoutError
:
1012 raise ROClientException("Timeout", http_code
=504)
1014 async def create(self
, item
, descriptor
=None, descriptor_format
=None, **kwargs
):
1016 Creates an item from its descriptor
1017 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
1018 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
1019 :param descriptor_format: Can be 'json' or 'yaml'
1020 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
1021 keys can be a dot separated list to specify elements inside dict
1022 :return: dictionary with the information or raises ROClientException on Error
1025 if isinstance(descriptor
, str):
1026 descriptor
= self
._parse
(descriptor
, descriptor_format
)
1032 if item
not in self
.client_to_RO
:
1033 raise ROClientException("Invalid item {}".format(item
))
1034 desc
= remove_envelop(item
, descriptor
)
1036 # Override descriptor with kwargs
1038 desc
= self
.update_descriptor(desc
, kwargs
)
1040 for mandatory
in self
.mandatory_for_create
[item
]:
1041 if mandatory
not in desc
:
1042 raise ROClientException(
1043 "'{}' is mandatory parameter for {}".format(mandatory
, item
)
1047 if item
in ("tenant", "vim", "wim"):
1050 create_desc
= self
._create
_envelop
(item
, desc
)
1052 async with aiohttp
.ClientSession() as session
:
1053 outdata
= await self
._create
_item
(
1055 self
.client_to_RO
[item
],
1057 all_tenants
=all_tenants
,
1059 return remove_envelop(item
, outdata
)
1060 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
1061 raise ROClientException(e
, http_code
=504)
1062 except asyncio
.TimeoutError
:
1063 raise ROClientException("Timeout", http_code
=504)
1065 async def create_action(
1066 self
, item
, item_id_name
, descriptor
=None, descriptor_format
=None, **kwargs
1069 Performs an action over an item
1070 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
1071 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
1072 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
1073 :param descriptor_format: Can be 'json' or 'yaml'
1074 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
1075 keys can be a dot separated list to specify elements inside dict
1076 :return: dictionary with the information or raises ROClientException on Error
1079 if isinstance(descriptor
, str):
1080 descriptor
= self
._parse
(descriptor
, descriptor_format
)
1086 if item
not in self
.client_to_RO
:
1087 raise ROClientException("Invalid item {}".format(item
))
1088 desc
= remove_envelop(item
, descriptor
)
1090 # Override descriptor with kwargs
1092 desc
= self
.update_descriptor(desc
, kwargs
)
1095 if item
in ("tenant", "vim"):
1100 action
= "sdn_mapping"
1101 elif item
in ("vim_account", "ns"):
1104 # create_desc = self._create_envelop(item, desc)
1107 async with aiohttp
.ClientSession() as session
:
1108 _all_tenants
= all_tenants
1111 # item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
1112 # all_tenants=_all_tenants)
1113 outdata
= await self
._create
_item
(
1115 self
.client_to_RO
[item
],
1117 item_id_name
=item_id_name
, # item_id_name=item_id
1119 all_tenants
=_all_tenants
,
1121 return remove_envelop(item
, outdata
)
1122 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
1123 raise ROClientException(e
, http_code
=504)
1124 except asyncio
.TimeoutError
:
1125 raise ROClientException("Timeout", http_code
=504)
1128 self
, item
, item_id_name
=None, descriptor
=None, descriptor_format
=None, **kwargs
1131 Attach a datacenter or wim to a tenant, creating a vim_account, wim_account
1132 :param item: can be vim_account or wim_account
1133 :param item_id_name: id or name of the datacenter, wim
1135 :param descriptor_format:
1140 if isinstance(descriptor
, str):
1141 descriptor
= self
._parse
(descriptor
, descriptor_format
)
1147 desc
= remove_envelop(item
, descriptor
)
1149 # # check that exist
1150 # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
1151 # tenant_text = "/" + self._get_tenant()
1153 desc
= self
.update_descriptor(desc
, kwargs
)
1155 if item
== "vim_account":
1156 if not desc
.get("vim_tenant_name") and not desc
.get("vim_tenant_id"):
1157 raise ROClientException(
1158 "Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
1161 elif item
!= "wim_account":
1162 raise ROClientException(
1163 "Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".format(
1167 create_desc
= self
._create
_envelop
(item
, desc
)
1168 payload_req
= yaml
.safe_dump(create_desc
)
1169 async with aiohttp
.ClientSession() as session
:
1171 item_id
= await self
._get
_item
_uuid
(
1172 session
, self
.client_to_RO
[item
], item_id_name
, all_tenants
=True
1174 await self
._get
_tenant
(session
)
1176 url
= "{}/{tenant}/{item}/{item_id}".format(
1179 item
=self
.client_to_RO
[item
],
1182 self
.logger
.debug("RO POST %s %s", url
, payload_req
)
1183 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1184 async with session
.post(
1185 url
, headers
=self
.headers_req
, data
=payload_req
1187 response_text
= await response
.read()
1189 "POST {} [{}] {}".format(
1190 url
, response
.status
, response_text
[:100]
1193 if response
.status
>= 300:
1194 raise ROClientException(
1195 self
._parse
_error
_yaml
(response_text
),
1196 http_code
=response
.status
,
1199 response_desc
= self
._parse
_yaml
(response_text
, response
=True)
1200 desc
= remove_envelop(item
, response_desc
)
1202 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
1203 raise ROClientException(e
, http_code
=504)
1204 except asyncio
.TimeoutError
:
1205 raise ROClientException("Timeout", http_code
=504)
1207 async def detach(self
, item
, item_id_name
=None):
1208 # TODO replace the code with delete_item(vim_account,...)
1210 async with aiohttp
.ClientSession() as session
:
1212 item_id
= await self
._get
_item
_uuid
(
1213 session
, self
.client_to_RO
[item
], item_id_name
, all_tenants
=False
1215 tenant
= await self
._get
_tenant
(session
)
1217 url
= "{}/{tenant}/{item}/{datacenter}".format(
1220 item
=self
.client_to_RO
[item
],
1223 self
.logger
.debug("RO DELETE %s", url
)
1225 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
1226 async with session
.delete(url
, headers
=self
.headers_req
) as response
:
1227 response_text
= await response
.read()
1229 "DELETE {} [{}] {}".format(
1230 url
, response
.status
, response_text
[:100]
1233 if response
.status
>= 300:
1234 raise ROClientException(
1235 self
._parse
_error
_yaml
(response_text
),
1236 http_code
=response
.status
,
1239 response_desc
= self
._parse
_yaml
(response_text
, response
=True)
1240 desc
= remove_envelop(item
, response_desc
)
1242 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
1243 raise ROClientException(e
, http_code
=504)
1244 except asyncio
.TimeoutError
:
1245 raise ROClientException("Timeout", http_code
=504)
1247 # TODO convert to asyncio
1250 def edit_datacenter(
1255 descriptor_format
=None,
1259 """Edit the parameters of a datacenter
1260 Params: must supply a descriptor or/and a parameter to change
1261 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1262 descriptor: with format {'datacenter':{params to change info}}
1263 must be a dictionary or a json/yaml text.
1264 parameters to change can be supplyied by the descriptor or as parameters:
1265 new_name: the datacenter name
1266 vim_url: the datacenter URL
1267 vim_url_admin: the datacenter URL for administrative issues
1268 vim_type: the datacenter type, can be openstack or openvim.
1269 public: boolean, available to other tenants
1270 description: datacenter description
1271 Return: Raises an exception on error, not found or found several
1272 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
1275 if isinstance(descriptor
, str):
1276 descriptor
= self
._parse
(descriptor
, descriptor_format
)
1280 descriptor
= {"datacenter": {}}
1282 raise ROClientException("Missing descriptor")
1284 if "datacenter" not in descriptor
or len(descriptor
) != 1:
1285 raise ROClientException(
1286 "Descriptor must contain only one 'datacenter' field"
1288 for param
in kwargs
:
1289 if param
== "new_name":
1290 descriptor
["datacenter"]["name"] = kwargs
[param
]
1292 descriptor
["datacenter"][param
] = kwargs
[param
]
1293 return self
._edit
_item
("datacenters", descriptor
, uuid
, name
, all_tenants
=None)
1300 descriptor_format
=None,
1304 """Edit the parameters of a scenario
1305 Params: must supply a descriptor or/and a parameters to change
1306 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1307 descriptor: with format {'scenario':{params to change info}}
1308 must be a dictionary or a json/yaml text.
1309 parameters to change can be supplyied by the descriptor or as parameters:
1310 new_name: the scenario name
1311 public: boolean, available to other tenants
1312 description: scenario description
1313 tenant_id. Propietary tenant
1314 Return: Raises an exception on error, not found or found several
1315 Obtain a dictionary with format {'scenario':{new_scenario_info}}
1318 if isinstance(descriptor
, str):
1319 descriptor
= self
._parse
(descriptor
, descriptor_format
)
1323 descriptor
= {"scenario": {}}
1325 raise ROClientException("Missing descriptor")
1327 if "scenario" not in descriptor
or len(descriptor
) > 2:
1328 raise ROClientException("Descriptor must contain only one 'scenario' field")
1329 for param
in kwargs
:
1330 if param
== "new_name":
1331 descriptor
["scenario"]["name"] = kwargs
[param
]
1333 descriptor
["scenario"][param
] = kwargs
[param
]
1334 return self
._edit
_item
("scenarios", descriptor
, uuid
, name
, all_tenants
=None)
1337 def vim_action(self
, action
, item
, uuid
=None, all_tenants
=False, **kwargs
):
1338 """Perform an action over a vim
1340 action: can be 'list', 'get'/'show', 'delete' or 'create'
1341 item: can be 'tenants' or 'networks'
1342 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
1344 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
1345 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
1346 must be a dictionary or a json/yaml text.
1347 name: for created tenant/net Overwrite descriptor name if any
1348 description: tenant descriptor. Overwrite descriptor description if any
1350 Return: Raises an exception on error
1351 Obtain a dictionary with format {'tenant':{new_tenant_info}}
1353 session
= None # TODO remove when changed to asyncio
1354 if item
not in ("tenants", "networks", "images"):
1355 raise ROClientException(
1356 "Unknown value for item '{}', must be 'tenants', 'nets' or "
1357 "images".format(str(item
))
1360 image_actions
= ["list", "get", "show", "delete"]
1361 if item
== "images" and action
not in image_actions
:
1362 raise ROClientException(
1363 "Only available actions for item '{}' are {}\n"
1364 "Requested action was '{}'".format(
1365 item
, ", ".join(image_actions
), action
1369 tenant_text
= "/any"
1371 tenant_text
= "/" + self
._get
_tenant
(session
)
1373 if "datacenter_id" in kwargs
or "datacenter_name" in kwargs
:
1374 datacenter
= self
._get
_item
_uuid
(
1377 kwargs
.get("datacenter"),
1378 all_tenants
=all_tenants
,
1381 datacenter
= self
._get
_datacenter
(session
)
1383 if action
== "list":
1384 url
= "{}{}/vim/{}/{}".format(self
.uri
, tenant_text
, datacenter
, item
)
1385 self
.logger
.debug("GET %s", url
)
1386 mano_response
= requests
.get(url
, headers
=self
.headers_req
)
1387 self
.logger
.debug("RO response: %s", mano_response
.text
)
1388 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
1389 if mano_response
.status_code
== 200:
1392 raise ROClientException(str(content
), http_code
=mano_response
.status
)
1393 elif action
== "get" or action
== "show":
1394 url
= "{}{}/vim/{}/{}/{}".format(
1395 self
.uri
, tenant_text
, datacenter
, item
, uuid
1397 self
.logger
.debug("GET %s", url
)
1398 mano_response
= requests
.get(url
, headers
=self
.headers_req
)
1399 self
.logger
.debug("RO response: %s", mano_response
.text
)
1400 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
1401 if mano_response
.status_code
== 200:
1404 raise ROClientException(str(content
), http_code
=mano_response
.status
)
1405 elif action
== "delete":
1406 url
= "{}{}/vim/{}/{}/{}".format(
1407 self
.uri
, tenant_text
, datacenter
, item
, uuid
1409 self
.logger
.debug("DELETE %s", url
)
1410 mano_response
= requests
.delete(url
, headers
=self
.headers_req
)
1411 self
.logger
.debug("RO response: %s", mano_response
.text
)
1412 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
1413 if mano_response
.status_code
== 200:
1416 raise ROClientException(str(content
), http_code
=mano_response
.status
)
1417 elif action
== "create":
1418 if "descriptor" in kwargs
:
1419 if isinstance(kwargs
["descriptor"], str):
1420 descriptor
= self
._parse
(
1421 kwargs
["descriptor"], kwargs
.get("descriptor_format")
1424 descriptor
= kwargs
["descriptor"]
1425 elif "name" in kwargs
:
1426 descriptor
= {item
[:-1]: {"name": kwargs
["name"]}}
1428 raise ROClientException("Missing descriptor")
1430 if item
[:-1] not in descriptor
or len(descriptor
) != 1:
1431 raise ROClientException(
1432 "Descriptor must contain only one 'tenant' field"
1434 if "name" in kwargs
:
1435 descriptor
[item
[:-1]]["name"] = kwargs
["name"]
1436 if "description" in kwargs
:
1437 descriptor
[item
[:-1]]["description"] = kwargs
["description"]
1438 payload_req
= yaml
.safe_dump(descriptor
)
1440 url
= "{}{}/vim/{}/{}".format(self
.uri
, tenant_text
, datacenter
, item
)
1441 self
.logger
.debug("RO POST %s %s", url
, payload_req
)
1442 mano_response
= requests
.post(
1443 url
, headers
=self
.headers_req
, data
=payload_req
1445 self
.logger
.debug("RO response: %s", mano_response
.text
)
1446 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
1447 if mano_response
.status_code
== 200:
1450 raise ROClientException(str(content
), http_code
=mano_response
.status
)
1452 raise ROClientException("Unknown value for action '{}".format(str(action
)))