blob: de61c23d60c657c7a9636b54206bbc7f5bd54871 [file] [log] [blame]
tiernoc0e42e22018-05-11 11:36:10 +02001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4##
5# Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
tiernoc0e42e22018-05-11 11:36:10 +02006#
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#
tiernoc0e42e22018-05-11 11:36:10 +020019##
20
21"""
22asyncio RO python client to interact with RO-server
23"""
24
25import asyncio
26import aiohttp
tiernoc0e42e22018-05-11 11:36:10 +020027import json
28import yaml
29import logging
tiernoc0e42e22018-05-11 11:36:10 +020030from urllib.parse import quote
31from uuid import UUID
32from copy import deepcopy
33
tierno275411e2018-05-16 14:33:32 +020034__author__ = "Alfonso Tierno"
tiernoc0e42e22018-05-11 11:36:10 +020035__date__ = "$09-Jan-2018 09:09:48$"
tierno275411e2018-05-16 14:33:32 +020036__version__ = "0.1.2"
37version_date = "2018-05-16"
tiernoc0e42e22018-05-11 11:36:10 +020038requests = None
39
tierno750b2452018-05-17 16:39:29 +020040
tiernoc0e42e22018-05-11 11:36:10 +020041class ROClientException(Exception):
42 def __init__(self, message, http_code=400):
tierno750b2452018-05-17 16:39:29 +020043 """Common Exception for all RO client exceptions"""
tiernoc0e42e22018-05-11 11:36:10 +020044 self.http_code = http_code
45 Exception.__init__(self, message)
tiernoc0e42e22018-05-11 11:36:10 +020046
47
48def 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 not isinstance(clean_indata['vnfd'], list) or len(clean_indata['vnfd']) != 1:
66 raise ROClientException("'vnfd' must be a list only one element")
67 clean_indata = clean_indata['vnfd'][0]
68 elif item == "nsd":
69 if clean_indata.get('nsd:nsd-catalog'):
70 clean_indata = clean_indata['nsd:nsd-catalog']
71 elif clean_indata.get('nsd-catalog'):
72 clean_indata = clean_indata['nsd-catalog']
73 if clean_indata.get('nsd'):
74 if not isinstance(clean_indata['nsd'], list) or len(clean_indata['nsd']) != 1:
75 raise ROClientException("'nsd' must be a list only one element")
76 clean_indata = clean_indata['nsd'][0]
77 elif item == "sdn":
78 if len(indata) == 1 and "sdn_controller" in indata:
79 clean_indata = indata["sdn_controller"]
80 elif item == "tenant":
81 if len(indata) == 1 and "tenant" in indata:
82 clean_indata = indata["tenant"]
83 elif item in ("vim", "vim_account", "datacenters"):
84 if len(indata) == 1 and "datacenter" in indata:
85 clean_indata = indata["datacenter"]
tiernoe37b57d2018-12-11 17:22:51 +000086 elif item == "wim":
87 if len(indata) == 1 and "wim" in indata:
88 clean_indata = indata["wim"]
89 elif item == "wim_account":
90 if len(indata) == 1 and "wim_account" in indata:
91 clean_indata = indata["wim_account"]
tiernoc0e42e22018-05-11 11:36:10 +020092 elif item == "ns" or item == "instances":
93 if len(indata) == 1 and "instance" in indata:
94 clean_indata = indata["instance"]
95 else:
96 assert False, "remove_envelop with unknown item {}".format(item)
97
98 return clean_indata
99
100
101class ROClient:
102 headers_req = {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
103 client_to_RO = {'tenant': 'tenants', 'vim': 'datacenters', 'vim_account': 'datacenters', 'sdn': 'sdn_controllers',
tiernoe37b57d2018-12-11 17:22:51 +0000104 'vnfd': 'vnfs', 'nsd': 'scenarios', 'wim': 'wims', 'wim_account': 'wims',
tiernoc0e42e22018-05-11 11:36:10 +0200105 'ns': 'instances'}
106 mandatory_for_create = {
107 'tenant': ("name", ),
gcalvino911ff7d2018-11-13 17:19:32 +0100108 'vnfd': ("name", "id"),
gcalvinoea0cc0a2018-11-06 13:20:29 +0100109 'nsd': ("name", "id"),
tiernoc0e42e22018-05-11 11:36:10 +0200110 'ns': ("name", "scenario", "datacenter"),
111 'vim': ("name", "vim_url"),
tiernoe37b57d2018-12-11 17:22:51 +0000112 'wim': ("name", "wim_url"),
tiernoc0e42e22018-05-11 11:36:10 +0200113 'vim_account': (),
tiernoe37b57d2018-12-11 17:22:51 +0000114 'wim_account': (),
tiernof186b442019-12-17 16:43:32 +0000115 'sdn': ("name", 'type'),
tiernoc0e42e22018-05-11 11:36:10 +0200116 }
117 timeout_large = 120
118 timeout_short = 30
119
tierno69f0d382020-05-07 13:08:09 +0000120 def __init__(self, loop, uri, **kwargs):
tiernoc0e42e22018-05-11 11:36:10 +0200121 self.loop = loop
tierno69f0d382020-05-07 13:08:09 +0000122 self.uri = uri
tiernoc0e42e22018-05-11 11:36:10 +0200123
124 self.username = kwargs.get("username")
125 self.password = kwargs.get("password")
126 self.tenant_id_name = kwargs.get("tenant")
127 self.tenant = None
128 self.datacenter_id_name = kwargs.get("datacenter")
129 self.datacenter = None
tierno69f0d382020-05-07 13:08:09 +0000130 logger_name = kwargs.get('logger_name', 'lcm.ro')
tiernoc0e42e22018-05-11 11:36:10 +0200131 self.logger = logging.getLogger(logger_name)
132 if kwargs.get("loglevel"):
133 self.logger.setLevel(kwargs["loglevel"])
134 global requests
135 requests = kwargs.get("TODO remove")
136
137 def __getitem__(self, index):
138 if index == 'tenant':
139 return self.tenant_id_name
140 elif index == 'datacenter':
141 return self.datacenter_id_name
142 elif index == 'username':
143 return self.username
144 elif index == 'password':
145 return self.password
tierno69f0d382020-05-07 13:08:09 +0000146 elif index == 'uri':
147 return self.uri
tiernoc0e42e22018-05-11 11:36:10 +0200148 else:
tierno750b2452018-05-17 16:39:29 +0200149 raise KeyError("Invalid key '{}'".format(index))
endikac2950402020-09-14 11:20:00 +0200150
tierno750b2452018-05-17 16:39:29 +0200151 def __setitem__(self, index, value):
tiernoc0e42e22018-05-11 11:36:10 +0200152 if index == 'tenant':
153 self.tenant_id_name = value
154 elif index == 'datacenter' or index == 'vim':
155 self.datacenter_id_name = value
156 elif index == 'username':
157 self.username = value
158 elif index == 'password':
159 self.password = value
tierno69f0d382020-05-07 13:08:09 +0000160 elif index == 'uri':
161 self.uri = value
tiernoc0e42e22018-05-11 11:36:10 +0200162 else:
163 raise KeyError("Invalid key '{}'".format(index))
164 self.tenant = None # force to reload tenant with different credentials
165 self.datacenter = None # force to reload datacenter with different credentials
tierno750b2452018-05-17 16:39:29 +0200166
tiernob5203912020-08-11 11:20:13 +0000167 @staticmethod
168 def _parse(descriptor, descriptor_format, response=False):
tiernoc0e42e22018-05-11 11:36:10 +0200169 if descriptor_format and descriptor_format != "json" and descriptor_format != "yaml":
tierno750b2452018-05-17 16:39:29 +0200170 raise ROClientException("'descriptor_format' must be a 'json' or 'yaml' text")
tiernoc0e42e22018-05-11 11:36:10 +0200171 if descriptor_format != "json":
172 try:
173 return yaml.load(descriptor)
174 except yaml.YAMLError as exc:
175 error_pos = ""
176 if hasattr(exc, 'problem_mark'):
177 mark = exc.problem_mark
tierno750b2452018-05-17 16:39:29 +0200178 error_pos = " at line:{} column:{}s".format(mark.line + 1, mark.column + 1)
tiernoc0e42e22018-05-11 11:36:10 +0200179 error_text = "yaml format error" + error_pos
180 elif descriptor_format != "yaml":
181 try:
tierno750b2452018-05-17 16:39:29 +0200182 return json.loads(descriptor)
tiernoc0e42e22018-05-11 11:36:10 +0200183 except Exception as e:
184 if response:
185 error_text = "json format error" + str(e)
186
187 if response:
188 raise ROClientException(error_text)
tierno750b2452018-05-17 16:39:29 +0200189 raise ROClientException(error_text)
tiernob5203912020-08-11 11:20:13 +0000190
191 @staticmethod
192 def _parse_error_yaml(descriptor):
193 json_error = None
194 try:
195 json_error = yaml.load(descriptor, Loader=yaml.Loader)
196 return json_error["error"]["description"]
197 except Exception:
198 return str(json_error or descriptor)
199
200 @staticmethod
201 def _parse_yaml(descriptor, response=False):
tiernoc0e42e22018-05-11 11:36:10 +0200202 try:
tierno626e0152019-11-29 14:16:16 +0000203 return yaml.load(descriptor, Loader=yaml.Loader)
tiernoc0e42e22018-05-11 11:36:10 +0200204 except yaml.YAMLError as exc:
205 error_pos = ""
206 if hasattr(exc, 'problem_mark'):
207 mark = exc.problem_mark
tierno750b2452018-05-17 16:39:29 +0200208 error_pos = " at line:{} column:{}s".format(mark.line + 1, mark.column + 1)
tiernoc0e42e22018-05-11 11:36:10 +0200209 error_text = "yaml format error" + error_pos
210 if response:
211 raise ROClientException(error_text)
tierno750b2452018-05-17 16:39:29 +0200212 raise ROClientException(error_text)
tiernoc0e42e22018-05-11 11:36:10 +0200213
214 @staticmethod
215 def check_if_uuid(uuid_text):
216 """
217 Check if text correspond to an uuid foramt
218 :param uuid_text:
219 :return: True if it is an uuid False if not
220 """
221 try:
222 UUID(uuid_text)
223 return True
tierno98768132018-09-11 12:07:21 +0200224 except Exception:
tiernoc0e42e22018-05-11 11:36:10 +0200225 return False
226
227 @staticmethod
228 def _create_envelop(item, indata=None):
229 """
230 Returns a new dict that incledes indata with the expected envelop
231 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
232 :param indata: Content to be enveloped
233 :return: a new dic with {<envelop>: {indata} } where envelop can be e.g. tenant, datacenter, ...
234 """
235 if item == "vnfd":
236 return {'vnfd-catalog': {'vnfd': [indata]}}
237 elif item == "nsd":
238 return {'nsd-catalog': {'nsd': [indata]}}
239 elif item == "tenant":
240 return {'tenant': indata}
241 elif item in ("vim", "vim_account", "datacenter"):
242 return {'datacenter': indata}
tiernoe37b57d2018-12-11 17:22:51 +0000243 elif item == "wim":
244 return {'wim': indata}
245 elif item == "wim_account":
246 return {'wim_account': indata}
tiernoc0e42e22018-05-11 11:36:10 +0200247 elif item == "ns" or item == "instances":
248 return {'instance': indata}
249 elif item == "sdn":
250 return {'sdn_controller': indata}
251 else:
252 assert False, "_create_envelop with unknown item {}".format(item)
253
254 @staticmethod
255 def update_descriptor(desc, kwargs):
256 desc = deepcopy(desc) # do not modify original descriptor
257 try:
258 for k, v in kwargs.items():
259 update_content = desc
260 kitem_old = None
261 klist = k.split(".")
262 for kitem in klist:
263 if kitem_old is not None:
264 update_content = update_content[kitem_old]
265 if isinstance(update_content, dict):
266 kitem_old = kitem
267 elif isinstance(update_content, list):
268 kitem_old = int(kitem)
269 else:
270 raise ROClientException(
271 "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(k, kitem))
272 if v == "__DELETE__":
273 del update_content[kitem_old]
274 else:
275 update_content[kitem_old] = v
276 return desc
277 except KeyError:
278 raise ROClientException(
279 "Invalid query string '{}'. Descriptor does not contain '{}'".format(k, kitem_old))
280 except ValueError:
281 raise ROClientException("Invalid query string '{}'. Expected integer index list instead of '{}'".format(
282 k, kitem))
283 except IndexError:
284 raise ROClientException(
285 "Invalid query string '{}'. Index '{}' out of range".format(k, kitem_old))
286
287 @staticmethod
288 def check_ns_status(ns_descriptor):
289 """
290 Inspect RO instance descriptor and indicates the status
291 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
292 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
293 """
tiernof186b442019-12-17 16:43:32 +0000294 error_list = []
295 total = {"VMs": 0, "networks": 0, "SDN_networks": 0}
296 done = {"VMs": 0, "networks": 0, "SDN_networks": 0}
tiernoc0e42e22018-05-11 11:36:10 +0200297
tiernof186b442019-12-17 16:43:32 +0000298 def _get_ref(desc):
299 # return an identification for the network or vm. Try vim_id if exist, if not descriptor id for net
tierno15b1cf12019-08-29 13:21:40 +0000300 if desc.get("vim_net_id"):
endikac2950402020-09-14 11:20:00 +0200301 return "'vim-net-id={}'".format(desc["vim_net_id"])
tierno15b1cf12019-08-29 13:21:40 +0000302 elif desc.get("ns_net_osm_id"):
303 return "'nsd-vld-id={}'".format(desc["ns_net_osm_id"])
304 elif desc.get("vnf_net_osm_id"):
305 return "'vnfd-vld-id={}'".format(desc["vnf_net_osm_id"])
306 # for VM
307 elif desc.get("vim_vm_id"):
endikac2950402020-09-14 11:20:00 +0200308 return "'vim-vm-id={}'".format(desc["vim_vm_id"])
tierno15b1cf12019-08-29 13:21:40 +0000309 elif desc.get("vdu_osm_id"):
310 return "'vnfd-vdu-id={}'".format(desc["vdu_osm_id"])
311 else:
312 return ""
313
tiernof186b442019-12-17 16:43:32 +0000314 def _get_sdn_ref(sce_net_id):
315 # look for the network associated to the SDN network and obtain the identification
316 net = next((x for x in ns_descriptor["nets"] if x.get("sce_net_id") == sce_net_id), None)
317 if not sce_net_id or not net:
318 return ""
319 return _get_ref(net)
tiernoc0e42e22018-05-11 11:36:10 +0200320
tiernof186b442019-12-17 16:43:32 +0000321 try:
322 total["networks"] = len(ns_descriptor["nets"])
323 for net in ns_descriptor["nets"]:
324 if net["status"] in ("ERROR", "VIM_ERROR"):
tiernob5203912020-08-11 11:20:13 +0000325 error_list.append("Error at VIM network {}: {}".format(_get_ref(net), net["error_msg"]))
tiernof186b442019-12-17 16:43:32 +0000326 elif net["status"] == "ACTIVE":
327 done["networks"] += 1
328
329 total["SDN_networks"] = len(ns_descriptor["sdn_nets"])
330 for sdn_net in ns_descriptor["sdn_nets"]:
331 if sdn_net["status"] in ("ERROR", "VIM_ERROR", "WIM_ERROR"):
tiernob5203912020-08-11 11:20:13 +0000332 error_list.append("Error at SDN network {}: {}".format(_get_sdn_ref(sdn_net.get("sce_net_id")),
333 sdn_net["error_msg"]))
tiernof186b442019-12-17 16:43:32 +0000334 elif sdn_net["status"] == "ACTIVE":
335 done["SDN_networks"] += 1
336
337 for vnf in ns_descriptor["vnfs"]:
338 for vm in vnf["vms"]:
339 total["VMs"] += 1
340 if vm["status"] in ("ERROR", "VIM_ERROR"):
tiernob5203912020-08-11 11:20:13 +0000341 error_list.append("Error at VIM VM {}: {}".format(_get_ref(vm), vm["error_msg"]))
tiernof186b442019-12-17 16:43:32 +0000342 elif vm["status"] == "ACTIVE":
343 done["VMs"] += 1
344 if error_list:
tiernob5203912020-08-11 11:20:13 +0000345 # skip errors caused because other dependendent task is on error
346 return "ERROR", "; ".join([el for el in error_list if "because depends on failed ACTION" not in el])
tiernof186b442019-12-17 16:43:32 +0000347 if all(total[x] == done[x] for x in total): # DONE == TOTAL for all items
348 return "ACTIVE", str({x: total[x] for x in total if total[x]}) # print only those which value is not 0
349 else:
350 return "BUILD", str({x: "{}/{}".format(done[x], total[x]) for x in total if total[x]})
351 # print done/total for each item if total is not 0
352 except Exception as e:
353 raise ROClientException("Unexpected RO ns descriptor. Wrong version? {}".format(e)) from e
tiernoc0e42e22018-05-11 11:36:10 +0200354
355 @staticmethod
tierno22f4f9c2018-06-11 18:53:39 +0200356 def check_action_status(action_descriptor):
357 """
358 Inspect RO instance descriptor and indicates the status
359 :param action_descriptor: action instance descriptor obtained with self.show("ns", "action")
360 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
361 """
362 net_total = 0
363 vm_total = 0
364 net_done = 0
365 vm_done = 0
366 other_total = 0
367 other_done = 0
368
369 for vim_action_set in action_descriptor["actions"]:
tierno1af43fc2019-03-08 08:54:58 +0000370 for vim_action in vim_action_set["vim_wim_actions"]:
tierno22f4f9c2018-06-11 18:53:39 +0200371 if vim_action["item"] == "instance_vms":
372 vm_total += 1
373 elif vim_action["item"] == "instance_nets":
374 net_total += 1
375 else:
376 other_total += 1
377 if vim_action["status"] == "FAILED":
378 return "ERROR", vim_action["error_msg"]
tierno1af43fc2019-03-08 08:54:58 +0000379 elif vim_action["status"] in ("DONE", "SUPERSEDED", "FINISHED"):
tierno22f4f9c2018-06-11 18:53:39 +0200380 if vim_action["item"] == "instance_vms":
381 vm_done += 1
382 elif vim_action["item"] == "instance_nets":
383 net_done += 1
384 else:
385 other_done += 1
386
387 if net_total == net_done and vm_total == vm_done and other_total == other_done:
388 return "ACTIVE", "VMs {}, networks: {}, other: {} ".format(vm_total, net_total, other_total)
389 else:
390 return "BUILD", "VMs: {}/{}, networks: {}/{}, other: {}/{}".format(vm_done, vm_total, net_done, net_total,
391 other_done, other_total)
392
393 @staticmethod
tiernoc0e42e22018-05-11 11:36:10 +0200394 def get_ns_vnf_info(ns_descriptor):
395 """
396 Get a dict with the VIM_id, ip_addresses, mac_addresses of every vnf and vdu
397 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
tierno22f4f9c2018-06-11 18:53:39 +0200398 :return: dict with:
399 <member_vnf_index>:
400 ip_address: XXXX,
401 vdur:
402 <vdu_osm_id>:
403 ip_address: XXX
404 vim_id: XXXX
405 interfaces:
406 <name>:
407 ip_address: XXX
408 mac_address: XXX
tiernoc0e42e22018-05-11 11:36:10 +0200409 """
410 ns_info = {}
411 for vnf in ns_descriptor["vnfs"]:
gcalvino911ff7d2018-11-13 17:19:32 +0100412 if not vnf.get("ip_address") and vnf.get("vms"):
tierno275411e2018-05-16 14:33:32 +0200413 raise ROClientException("ns member_vnf_index '{}' has no IP address".format(
414 vnf["member_vnf_index"]), http_code=409)
tiernoc0e42e22018-05-11 11:36:10 +0200415 vnfr_info = {
416 "ip_address": vnf.get("ip_address"),
417 "vdur": {}
418 }
419 for vm in vnf["vms"]:
420 vdur = {
421 "vim_id": vm.get("vim_vm_id"),
tierno22f4f9c2018-06-11 18:53:39 +0200422 "ip_address": vm.get("ip_address"),
423 "interfaces": {}
tiernoc0e42e22018-05-11 11:36:10 +0200424 }
tierno275411e2018-05-16 14:33:32 +0200425 for iface in vm["interfaces"]:
426 if iface.get("type") == "mgmt" and not iface.get("ip_address"):
427 raise ROClientException("ns member_vnf_index '{}' vm '{}' management interface '{}' has no IP "
428 "address".format(vnf["member_vnf_index"], vm["vdu_osm_id"],
429 iface["external_name"]), http_code=409)
tierno22f4f9c2018-06-11 18:53:39 +0200430 vdur["interfaces"][iface["internal_name"]] = {"ip_address": iface.get("ip_address"),
431 "mac_address": iface.get("mac_address"),
432 "vim_id": iface.get("vim_interface_id"),
433 }
tiernoc0e42e22018-05-11 11:36:10 +0200434 vnfr_info["vdur"][vm["vdu_osm_id"]] = vdur
435 ns_info[str(vnf["member_vnf_index"])] = vnfr_info
436 return ns_info
437
tiernoc0e42e22018-05-11 11:36:10 +0200438 async def _get_item_uuid(self, session, item, item_id_name, all_tenants=False):
439 if all_tenants:
440 tenant_text = "/any"
441 elif all_tenants is None:
442 tenant_text = ""
443 else:
444 if not self.tenant:
445 await self._get_tenant(session)
446 tenant_text = "/" + self.tenant
447
448 item_id = 0
tierno69f0d382020-05-07 13:08:09 +0000449 url = "{}{}/{}".format(self.uri, tenant_text, item)
tiernoc0e42e22018-05-11 11:36:10 +0200450 if self.check_if_uuid(item_id_name):
451 item_id = item_id_name
452 url += "/" + item_id_name
453 elif item_id_name and item_id_name.startswith("'") and item_id_name.endswith("'"):
454 item_id_name = item_id_name[1:-1]
tierno750b2452018-05-17 16:39:29 +0200455 self.logger.debug("RO GET %s", url)
calvinosanchd5916fd2020-01-09 17:19:53 +0100456 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
457 async with session.get(url, headers=self.headers_req) as response:
458 response_text = await response.read()
459 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
460 if response.status == 404: # NOT_FOUND
461 raise ROClientException("No {} found with id '{}'".format(item[:-1], item_id_name),
462 http_code=404)
463 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000464 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
calvinosanchd5916fd2020-01-09 17:19:53 +0100465 content = self._parse_yaml(response_text, response=True)
tiernoc0e42e22018-05-11 11:36:10 +0200466
467 if item_id:
468 return item_id
469 desc = content[item]
470 assert isinstance(desc, list), "_get_item_uuid get a non dict with a list inside {}".format(type(desc))
471 uuid = None
472 for i in desc:
473 if item_id_name and i["name"] != item_id_name:
474 continue
475 if uuid: # found more than one
476 raise ROClientException(
477 "Found more than one {} with name '{}'. uuid must be used".format(item, item_id_name),
478 http_code=404)
479 uuid = i["uuid"]
480 if not uuid:
481 raise ROClientException("No {} found with name '{}'".format(item[:-1], item_id_name), http_code=404)
482 return uuid
483
tierno22f4f9c2018-06-11 18:53:39 +0200484 async def _get_item(self, session, item, item_id_name, extra_item=None, extra_item_id=None, all_tenants=False):
tiernoc0e42e22018-05-11 11:36:10 +0200485 if all_tenants:
486 tenant_text = "/any"
487 elif all_tenants is None:
488 tenant_text = ""
489 else:
490 if not self.tenant:
491 await self._get_tenant(session)
492 tenant_text = "/" + self.tenant
493
494 if self.check_if_uuid(item_id_name):
495 uuid = item_id_name
496 else:
497 # check that exist
498 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
tierno750b2452018-05-17 16:39:29 +0200499
tierno69f0d382020-05-07 13:08:09 +0000500 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, uuid)
tierno22f4f9c2018-06-11 18:53:39 +0200501 if extra_item:
502 url += "/" + extra_item
503 if extra_item_id:
504 url += "/" + extra_item_id
tierno750b2452018-05-17 16:39:29 +0200505 self.logger.debug("GET %s", url)
calvinosanchd5916fd2020-01-09 17:19:53 +0100506 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
507 async with session.get(url, headers=self.headers_req) as response:
508 response_text = await response.read()
509 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
510 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000511 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
tiernoc0e42e22018-05-11 11:36:10 +0200512
513 return self._parse_yaml(response_text, response=True)
514
515 async def _get_tenant(self, session):
516 if not self.tenant:
517 self.tenant = await self._get_item_uuid(session, "tenants", self.tenant_id_name, None)
518 return self.tenant
endikac2950402020-09-14 11:20:00 +0200519
tiernoc0e42e22018-05-11 11:36:10 +0200520 async def _get_datacenter(self, session):
521 if not self.tenant:
522 await self._get_tenant(session)
523 if not self.datacenter:
524 self.datacenter = await self._get_item_uuid(session, "datacenters", self.datacenter_id_name, True)
525 return self.datacenter
526
527 async def _create_item(self, session, item, descriptor, item_id_name=None, action=None, all_tenants=False):
528 if all_tenants:
529 tenant_text = "/any"
530 elif all_tenants is None:
531 tenant_text = ""
532 else:
533 if not self.tenant:
534 await self._get_tenant(session)
535 tenant_text = "/" + self.tenant
536 payload_req = yaml.safe_dump(descriptor)
tierno750b2452018-05-17 16:39:29 +0200537 # print payload_req
tiernoc0e42e22018-05-11 11:36:10 +0200538
539 api_version_text = ""
540 if item == "vnfs":
541 # assumes version v3 only
542 api_version_text = "/v3"
543 item = "vnfd"
544 elif item == "scenarios":
545 # assumes version v3 only
546 api_version_text = "/v3"
547 item = "nsd"
548
549 if not item_id_name:
tierno750b2452018-05-17 16:39:29 +0200550 uuid = ""
tiernoc0e42e22018-05-11 11:36:10 +0200551 elif self.check_if_uuid(item_id_name):
552 uuid = "/{}".format(item_id_name)
553 else:
554 # check that exist
555 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
556 uuid = "/{}".format(uuid)
557 if not action:
558 action = ""
559 else:
tierno22f4f9c2018-06-11 18:53:39 +0200560 action = "/{}".format(action)
tiernoc0e42e22018-05-11 11:36:10 +0200561
tierno69f0d382020-05-07 13:08:09 +0000562 url = "{}{apiver}{tenant}/{item}{id}{action}".format(self.uri, apiver=api_version_text,
tierno750b2452018-05-17 16:39:29 +0200563 tenant=tenant_text, item=item, id=uuid, action=action)
564 self.logger.debug("RO POST %s %s", url, payload_req)
calvinosanchd5916fd2020-01-09 17:19:53 +0100565 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
566 async with session.post(url, headers=self.headers_req, data=payload_req) as response:
567 response_text = await response.read()
568 self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
569 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000570 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
tiernoc0e42e22018-05-11 11:36:10 +0200571
572 return self._parse_yaml(response_text, response=True)
573
574 async def _del_item(self, session, item, item_id_name, all_tenants=False):
575 if all_tenants:
576 tenant_text = "/any"
577 elif all_tenants is None:
578 tenant_text = ""
579 else:
580 if not self.tenant:
581 await self._get_tenant(session)
582 tenant_text = "/" + self.tenant
583 if not self.check_if_uuid(item_id_name):
584 # check that exist
585 _all_tenants = all_tenants
tiernoe37b57d2018-12-11 17:22:51 +0000586 if item in ("datacenters", 'wims'):
tiernoc0e42e22018-05-11 11:36:10 +0200587 _all_tenants = True
588 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants=_all_tenants)
589 else:
590 uuid = item_id_name
tierno750b2452018-05-17 16:39:29 +0200591
tierno69f0d382020-05-07 13:08:09 +0000592 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, uuid)
tiernoc0e42e22018-05-11 11:36:10 +0200593 self.logger.debug("DELETE %s", url)
calvinosanchd5916fd2020-01-09 17:19:53 +0100594 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
595 async with session.delete(url, headers=self.headers_req) as response:
596 response_text = await response.read()
597 self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
598 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000599 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
calvinosanchd5916fd2020-01-09 17:19:53 +0100600
tiernoc0e42e22018-05-11 11:36:10 +0200601 return self._parse_yaml(response_text, response=True)
602
603 async def _list_item(self, session, item, all_tenants=False, filter_dict=None):
604 if all_tenants:
605 tenant_text = "/any"
606 elif all_tenants is None:
607 tenant_text = ""
608 else:
609 if not self.tenant:
610 await self._get_tenant(session)
611 tenant_text = "/" + self.tenant
tierno750b2452018-05-17 16:39:29 +0200612
tierno69f0d382020-05-07 13:08:09 +0000613 url = "{}{}/{}".format(self.uri, tenant_text, item)
tiernoc0e42e22018-05-11 11:36:10 +0200614 separator = "?"
615 if filter_dict:
616 for k in filter_dict:
tierno750b2452018-05-17 16:39:29 +0200617 url += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
tiernoc0e42e22018-05-11 11:36:10 +0200618 separator = "&"
tierno750b2452018-05-17 16:39:29 +0200619 self.logger.debug("RO GET %s", url)
calvinosanchd5916fd2020-01-09 17:19:53 +0100620 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
621 async with session.get(url, headers=self.headers_req) as response:
622 response_text = await response.read()
623 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
624 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000625 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
calvinosanchd5916fd2020-01-09 17:19:53 +0100626
tiernoc0e42e22018-05-11 11:36:10 +0200627 return self._parse_yaml(response_text, response=True)
628
629 async def _edit_item(self, session, item, item_id, descriptor, all_tenants=False):
630 if all_tenants:
631 tenant_text = "/any"
632 elif all_tenants is None:
633 tenant_text = ""
634 else:
635 if not self.tenant:
636 await self._get_tenant(session)
637 tenant_text = "/" + self.tenant
638
639 payload_req = yaml.safe_dump(descriptor)
endikac2950402020-09-14 11:20:00 +0200640
tierno750b2452018-05-17 16:39:29 +0200641 # print payload_req
tierno69f0d382020-05-07 13:08:09 +0000642 url = "{}{}/{}/{}".format(self.uri, tenant_text, item, item_id)
tierno750b2452018-05-17 16:39:29 +0200643 self.logger.debug("RO PUT %s %s", url, payload_req)
calvinosanchd5916fd2020-01-09 17:19:53 +0100644 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
645 async with session.put(url, headers=self.headers_req, data=payload_req) as response:
646 response_text = await response.read()
647 self.logger.debug("PUT {} [{}] {}".format(url, response.status, response_text[:100]))
648 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000649 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
calvinosanchd5916fd2020-01-09 17:19:53 +0100650
tiernoc0e42e22018-05-11 11:36:10 +0200651 return self._parse_yaml(response_text, response=True)
652
tierno22f4f9c2018-06-11 18:53:39 +0200653 async def get_version(self):
654 """
655 Obtain RO server version.
656 :return: a list with integers ["major", "minor", "release"]. Raises ROClientException on Error,
657 """
658 try:
tiernoc231a872020-01-21 08:49:05 +0000659 response_text = ""
bravof922c4172020-11-24 21:21:43 -0300660 async with aiohttp.ClientSession() as session:
tierno69f0d382020-05-07 13:08:09 +0000661 url = "{}/version".format(self.uri)
tierno22f4f9c2018-06-11 18:53:39 +0200662 self.logger.debug("RO GET %s", url)
calvinosanchd5916fd2020-01-09 17:19:53 +0100663 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
664 async with session.get(url, headers=self.headers_req) as response:
665 response_text = await response.read()
666 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
667 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000668 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
calvinosanchd5916fd2020-01-09 17:19:53 +0100669
tierno22f4f9c2018-06-11 18:53:39 +0200670 for word in str(response_text).split(" "):
671 if "." in word:
672 version_text, _, _ = word.partition("-")
tierno8069ce52019-08-28 15:34:33 +0000673 return version_text
tierno22f4f9c2018-06-11 18:53:39 +0200674 raise ROClientException("Got invalid version text: '{}'".format(response_text), http_code=500)
calvinosanch30ccee32020-01-13 12:01:36 +0100675 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tierno22f4f9c2018-06-11 18:53:39 +0200676 raise ROClientException(e, http_code=504)
677 except asyncio.TimeoutError:
678 raise ROClientException("Timeout", http_code=504)
679 except Exception as e:
680 raise ROClientException("Got invalid version text: '{}'; causing exception {}".format(response_text, e),
681 http_code=500)
682
tiernoc0e42e22018-05-11 11:36:10 +0200683 async def get_list(self, item, all_tenants=False, filter_by=None):
684 """
bravof922c4172020-11-24 21:21:43 -0300685 List of items filtered by the contents in the dictionary "filter_by".
tiernoc0e42e22018-05-11 11:36:10 +0200686 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
687 :param all_tenants: True if not filtering by tenant. Only allowed for admin
688 :param filter_by: dictionary with filtering
689 :return: a list of dict. It can be empty. Raises ROClientException on Error,
690 """
691 try:
692 if item not in self.client_to_RO:
693 raise ROClientException("Invalid item {}".format(item))
694 if item == 'tenant':
695 all_tenants = None
calvinosanchd5916fd2020-01-09 17:19:53 +0100696 async with aiohttp.ClientSession(loop=self.loop) as session:
tiernoc0e42e22018-05-11 11:36:10 +0200697 content = await self._list_item(session, self.client_to_RO[item], all_tenants=all_tenants,
698 filter_dict=filter_by)
699 if isinstance(content, dict):
700 if len(content) == 1:
701 for _, v in content.items():
702 return v
703 return content.values()[0]
704 else:
705 raise ROClientException("Output not a list neither dict with len equal 1", http_code=500)
706 return content
calvinosanch30ccee32020-01-13 12:01:36 +0100707 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tiernoc0e42e22018-05-11 11:36:10 +0200708 raise ROClientException(e, http_code=504)
tierno22f4f9c2018-06-11 18:53:39 +0200709 except asyncio.TimeoutError:
710 raise ROClientException("Timeout", http_code=504)
tiernoc0e42e22018-05-11 11:36:10 +0200711
tierno22f4f9c2018-06-11 18:53:39 +0200712 async def show(self, item, item_id_name=None, extra_item=None, extra_item_id=None, all_tenants=False):
tiernoc0e42e22018-05-11 11:36:10 +0200713 """
714 Obtain the information of an item from its id or name
715 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
716 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
tierno22f4f9c2018-06-11 18:53:39 +0200717 :param extra_item: if supplied, it is used to add to the URL.
718 Can be 'action' if item='ns'; 'networks' or'images' if item='vim'
719 :param extra_item_id: if supplied, it is used get details of a concrete extra_item.
tiernoc0e42e22018-05-11 11:36:10 +0200720 :param all_tenants: True if not filtering by tenant. Only allowed for admin
721 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
722 """
723 try:
724 if item not in self.client_to_RO:
725 raise ROClientException("Invalid item {}".format(item))
726 if item == 'tenant':
727 all_tenants = None
728 elif item == 'vim':
729 all_tenants = True
730 elif item == 'vim_account':
731 all_tenants = False
732
calvinosanchd5916fd2020-01-09 17:19:53 +0100733 async with aiohttp.ClientSession(loop=self.loop) as session:
tierno22f4f9c2018-06-11 18:53:39 +0200734 content = await self._get_item(session, self.client_to_RO[item], item_id_name, extra_item=extra_item,
735 extra_item_id=extra_item_id, all_tenants=all_tenants)
tiernoc0e42e22018-05-11 11:36:10 +0200736 return remove_envelop(item, content)
calvinosanch30ccee32020-01-13 12:01:36 +0100737 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tiernoc0e42e22018-05-11 11:36:10 +0200738 raise ROClientException(e, http_code=504)
tierno22f4f9c2018-06-11 18:53:39 +0200739 except asyncio.TimeoutError:
740 raise ROClientException("Timeout", http_code=504)
tiernoc0e42e22018-05-11 11:36:10 +0200741
742 async def delete(self, item, item_id_name=None, all_tenants=False):
743 """
744 Delete the information of an item from its id or name
745 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
746 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
747 :param all_tenants: True if not filtering by tenant. Only allowed for admin
748 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
749 """
750 try:
751 if item not in self.client_to_RO:
752 raise ROClientException("Invalid item {}".format(item))
tiernoe37b57d2018-12-11 17:22:51 +0000753 if item in ('tenant', 'vim', 'wim'):
tiernoc0e42e22018-05-11 11:36:10 +0200754 all_tenants = None
755
calvinosanchd5916fd2020-01-09 17:19:53 +0100756 async with aiohttp.ClientSession(loop=self.loop) as session:
tiernofa66d152018-08-28 10:13:45 +0000757 result = await self._del_item(session, self.client_to_RO[item], item_id_name, all_tenants=all_tenants)
758 # in case of ns delete, get the action_id embeded in text
759 if item == "ns" and result.get("result"):
760 _, _, action_id = result["result"].partition("action_id=")
761 action_id, _, _ = action_id.partition(" ")
762 if action_id:
763 result["action_id"] = action_id
764 return result
calvinosanch30ccee32020-01-13 12:01:36 +0100765 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tiernoc0e42e22018-05-11 11:36:10 +0200766 raise ROClientException(e, http_code=504)
tierno22f4f9c2018-06-11 18:53:39 +0200767 except asyncio.TimeoutError:
768 raise ROClientException("Timeout", http_code=504)
tiernoc0e42e22018-05-11 11:36:10 +0200769
770 async def edit(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
771 """ Edit an item
772 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
tierno22f4f9c2018-06-11 18:53:39 +0200773 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
tiernoc0e42e22018-05-11 11:36:10 +0200774 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
775 :param descriptor_format: Can be 'json' or 'yaml'
776 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
777 keys can be a dot separated list to specify elements inside dict
778 :return: dictionary with the information or raises ROClientException on Error
779 """
780 try:
781 if isinstance(descriptor, str):
782 descriptor = self._parse(descriptor, descriptor_format)
783 elif descriptor:
784 pass
785 else:
786 descriptor = {}
787
788 if item not in self.client_to_RO:
789 raise ROClientException("Invalid item {}".format(item))
790 desc = remove_envelop(item, descriptor)
791
792 # Override descriptor with kwargs
793 if kwargs:
794 desc = self.update_descriptor(desc, kwargs)
795 all_tenants = False
796 if item in ('tenant', 'vim'):
797 all_tenants = None
798
799 create_desc = self._create_envelop(item, desc)
800
calvinosanchd5916fd2020-01-09 17:19:53 +0100801 async with aiohttp.ClientSession(loop=self.loop) as session:
tiernoc0e42e22018-05-11 11:36:10 +0200802 _all_tenants = all_tenants
803 if item == 'vim':
804 _all_tenants = True
tierno750b2452018-05-17 16:39:29 +0200805 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
806 all_tenants=_all_tenants)
tiernofe1c37f2018-05-17 22:58:04 +0200807 if item == 'vim':
808 _all_tenants = None
tiernoc0e42e22018-05-11 11:36:10 +0200809 # await self._get_tenant(session)
tierno750b2452018-05-17 16:39:29 +0200810 outdata = await self._edit_item(session, self.client_to_RO[item], item_id, create_desc,
811 all_tenants=_all_tenants)
tiernoc0e42e22018-05-11 11:36:10 +0200812 return remove_envelop(item, outdata)
calvinosanch30ccee32020-01-13 12:01:36 +0100813 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tiernoc0e42e22018-05-11 11:36:10 +0200814 raise ROClientException(e, http_code=504)
tierno22f4f9c2018-06-11 18:53:39 +0200815 except asyncio.TimeoutError:
816 raise ROClientException("Timeout", http_code=504)
tiernoc0e42e22018-05-11 11:36:10 +0200817
818 async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
819 """
820 Creates an item from its descriptor
821 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
822 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
823 :param descriptor_format: Can be 'json' or 'yaml'
824 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
825 keys can be a dot separated list to specify elements inside dict
826 :return: dictionary with the information or raises ROClientException on Error
827 """
828 try:
829 if isinstance(descriptor, str):
830 descriptor = self._parse(descriptor, descriptor_format)
831 elif descriptor:
832 pass
833 else:
834 descriptor = {}
835
836 if item not in self.client_to_RO:
837 raise ROClientException("Invalid item {}".format(item))
838 desc = remove_envelop(item, descriptor)
839
840 # Override descriptor with kwargs
841 if kwargs:
842 desc = self.update_descriptor(desc, kwargs)
843
844 for mandatory in self.mandatory_for_create[item]:
845 if mandatory not in desc:
846 raise ROClientException("'{}' is mandatory parameter for {}".format(mandatory, item))
847
848 all_tenants = False
tiernoe37b57d2018-12-11 17:22:51 +0000849 if item in ('tenant', 'vim', 'wim'):
tiernoc0e42e22018-05-11 11:36:10 +0200850 all_tenants = None
851
852 create_desc = self._create_envelop(item, desc)
853
calvinosanchd5916fd2020-01-09 17:19:53 +0100854 async with aiohttp.ClientSession(loop=self.loop) as session:
tiernoc0e42e22018-05-11 11:36:10 +0200855 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
856 all_tenants=all_tenants)
857 return remove_envelop(item, outdata)
calvinosanch30ccee32020-01-13 12:01:36 +0100858 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tiernoc0e42e22018-05-11 11:36:10 +0200859 raise ROClientException(e, http_code=504)
tierno22f4f9c2018-06-11 18:53:39 +0200860 except asyncio.TimeoutError:
861 raise ROClientException("Timeout", http_code=504)
862
863 async def create_action(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
864 """
865 Performs an action over an item
866 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
867 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
868 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
869 :param descriptor_format: Can be 'json' or 'yaml'
870 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
871 keys can be a dot separated list to specify elements inside dict
872 :return: dictionary with the information or raises ROClientException on Error
873 """
874 try:
875 if isinstance(descriptor, str):
876 descriptor = self._parse(descriptor, descriptor_format)
877 elif descriptor:
878 pass
879 else:
880 descriptor = {}
881
882 if item not in self.client_to_RO:
883 raise ROClientException("Invalid item {}".format(item))
884 desc = remove_envelop(item, descriptor)
885
886 # Override descriptor with kwargs
887 if kwargs:
888 desc = self.update_descriptor(desc, kwargs)
889
890 all_tenants = False
891 if item in ('tenant', 'vim'):
892 all_tenants = None
893
894 action = None
895 if item == "vims":
896 action = "sdn_mapping"
897 elif item in ("vim_account", "ns"):
898 action = "action"
899
900 # create_desc = self._create_envelop(item, desc)
901 create_desc = desc
902
calvinosanchd5916fd2020-01-09 17:19:53 +0100903 async with aiohttp.ClientSession(loop=self.loop) as session:
tierno22f4f9c2018-06-11 18:53:39 +0200904 _all_tenants = all_tenants
905 if item == 'vim':
906 _all_tenants = True
907 # item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
908 # all_tenants=_all_tenants)
909 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
910 item_id_name=item_id_name, # item_id_name=item_id
911 action=action, all_tenants=_all_tenants)
912 return remove_envelop(item, outdata)
calvinosanch30ccee32020-01-13 12:01:36 +0100913 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tierno22f4f9c2018-06-11 18:53:39 +0200914 raise ROClientException(e, http_code=504)
915 except asyncio.TimeoutError:
916 raise ROClientException("Timeout", http_code=504)
tiernoc0e42e22018-05-11 11:36:10 +0200917
tiernoe37b57d2018-12-11 17:22:51 +0000918 async def attach(self, item, item_id_name=None, descriptor=None, descriptor_format=None, **kwargs):
919 """
920 Attach a datacenter or wim to a tenant, creating a vim_account, wim_account
921 :param item: can be vim_account or wim_account
922 :param item_id_name: id or name of the datacenter, wim
923 :param descriptor:
924 :param descriptor_format:
925 :param kwargs:
926 :return:
927 """
tierno22f4f9c2018-06-11 18:53:39 +0200928 try:
929 if isinstance(descriptor, str):
930 descriptor = self._parse(descriptor, descriptor_format)
931 elif descriptor:
932 pass
933 else:
934 descriptor = {}
tiernoe37b57d2018-12-11 17:22:51 +0000935
936 desc = remove_envelop(item, descriptor)
tiernoc0e42e22018-05-11 11:36:10 +0200937
tierno22f4f9c2018-06-11 18:53:39 +0200938 # # check that exist
939 # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
940 # tenant_text = "/" + self._get_tenant()
941 if kwargs:
942 desc = self.update_descriptor(desc, kwargs)
tiernoc0e42e22018-05-11 11:36:10 +0200943
tiernoe37b57d2018-12-11 17:22:51 +0000944 if item == "vim_account":
945 if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
946 raise ROClientException("Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
947 "provided")
948 elif item != "wim_account":
949 raise ROClientException("Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".
950 format(item))
951 create_desc = self._create_envelop(item, desc)
tierno22f4f9c2018-06-11 18:53:39 +0200952 payload_req = yaml.safe_dump(create_desc)
calvinosanchd5916fd2020-01-09 17:19:53 +0100953 async with aiohttp.ClientSession(loop=self.loop) as session:
tierno22f4f9c2018-06-11 18:53:39 +0200954 # check that exist
tiernoe37b57d2018-12-11 17:22:51 +0000955 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=True)
tierno22f4f9c2018-06-11 18:53:39 +0200956 await self._get_tenant(session)
tiernoc0e42e22018-05-11 11:36:10 +0200957
tierno69f0d382020-05-07 13:08:09 +0000958 url = "{}/{tenant}/{item}/{item_id}".format(self.uri, tenant=self.tenant,
tiernoe37b57d2018-12-11 17:22:51 +0000959 item=self.client_to_RO[item], item_id=item_id)
tierno22f4f9c2018-06-11 18:53:39 +0200960 self.logger.debug("RO POST %s %s", url, payload_req)
calvinosanchd5916fd2020-01-09 17:19:53 +0100961 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
962 async with session.post(url, headers=self.headers_req, data=payload_req) as response:
963 response_text = await response.read()
964 self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
965 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000966 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
tiernoc0e42e22018-05-11 11:36:10 +0200967
tierno22f4f9c2018-06-11 18:53:39 +0200968 response_desc = self._parse_yaml(response_text, response=True)
tiernoe37b57d2018-12-11 17:22:51 +0000969 desc = remove_envelop(item, response_desc)
tierno22f4f9c2018-06-11 18:53:39 +0200970 return desc
calvinosanch30ccee32020-01-13 12:01:36 +0100971 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tierno22f4f9c2018-06-11 18:53:39 +0200972 raise ROClientException(e, http_code=504)
973 except asyncio.TimeoutError:
974 raise ROClientException("Timeout", http_code=504)
tiernoc0e42e22018-05-11 11:36:10 +0200975
tiernoe37b57d2018-12-11 17:22:51 +0000976 async def detach(self, item, item_id_name=None):
tierno750b2452018-05-17 16:39:29 +0200977 # TODO replace the code with delete_item(vim_account,...)
tierno22f4f9c2018-06-11 18:53:39 +0200978 try:
calvinosanchd5916fd2020-01-09 17:19:53 +0100979 async with aiohttp.ClientSession(loop=self.loop) as session:
tierno22f4f9c2018-06-11 18:53:39 +0200980 # check that exist
tiernoe37b57d2018-12-11 17:22:51 +0000981 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=False)
tierno22f4f9c2018-06-11 18:53:39 +0200982 tenant = await self._get_tenant(session)
tiernoc0e42e22018-05-11 11:36:10 +0200983
tierno69f0d382020-05-07 13:08:09 +0000984 url = "{}/{tenant}/{item}/{datacenter}".format(self.uri, tenant=tenant,
tiernoe37b57d2018-12-11 17:22:51 +0000985 item=self.client_to_RO[item], datacenter=item_id)
tierno22f4f9c2018-06-11 18:53:39 +0200986 self.logger.debug("RO DELETE %s", url)
endikac2950402020-09-14 11:20:00 +0200987
calvinosanchd5916fd2020-01-09 17:19:53 +0100988 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
989 async with session.delete(url, headers=self.headers_req) as response:
990 response_text = await response.read()
991 self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
992 if response.status >= 300:
tiernob5203912020-08-11 11:20:13 +0000993 raise ROClientException(self._parse_error_yaml(response_text), http_code=response.status)
endikac2950402020-09-14 11:20:00 +0200994
tierno22f4f9c2018-06-11 18:53:39 +0200995 response_desc = self._parse_yaml(response_text, response=True)
tiernoe37b57d2018-12-11 17:22:51 +0000996 desc = remove_envelop(item, response_desc)
tierno22f4f9c2018-06-11 18:53:39 +0200997 return desc
calvinosanch30ccee32020-01-13 12:01:36 +0100998 except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
tierno22f4f9c2018-06-11 18:53:39 +0200999 raise ROClientException(e, http_code=504)
1000 except asyncio.TimeoutError:
1001 raise ROClientException("Timeout", http_code=504)
tiernoc0e42e22018-05-11 11:36:10 +02001002
tiernoc0e42e22018-05-11 11:36:10 +02001003 # TODO convert to asyncio
tierno750b2452018-05-17 16:39:29 +02001004 # DATACENTERS
tiernoc0e42e22018-05-11 11:36:10 +02001005
tierno750b2452018-05-17 16:39:29 +02001006 def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False,
1007 **kwargs):
tiernoc0e42e22018-05-11 11:36:10 +02001008 """Edit the parameters of a datacenter
1009 Params: must supply a descriptor or/and a parameter to change
1010 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1011 descriptor: with format {'datacenter':{params to change info}}
1012 must be a dictionary or a json/yaml text.
1013 parameters to change can be supplyied by the descriptor or as parameters:
1014 new_name: the datacenter name
1015 vim_url: the datacenter URL
1016 vim_url_admin: the datacenter URL for administrative issues
1017 vim_type: the datacenter type, can be openstack or openvim.
1018 public: boolean, available to other tenants
1019 description: datacenter description
1020 Return: Raises an exception on error, not found or found several
1021 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
1022 """
1023
1024 if isinstance(descriptor, str):
1025 descriptor = self.parse(descriptor, descriptor_format)
1026 elif descriptor:
1027 pass
1028 elif kwargs:
tierno750b2452018-05-17 16:39:29 +02001029 descriptor = {"datacenter": {}}
tiernoc0e42e22018-05-11 11:36:10 +02001030 else:
1031 raise ROClientException("Missing descriptor")
1032
tierno750b2452018-05-17 16:39:29 +02001033 if 'datacenter' not in descriptor or len(descriptor) != 1:
tiernoc0e42e22018-05-11 11:36:10 +02001034 raise ROClientException("Descriptor must contain only one 'datacenter' field")
1035 for param in kwargs:
tierno750b2452018-05-17 16:39:29 +02001036 if param == 'new_name':
tiernoc0e42e22018-05-11 11:36:10 +02001037 descriptor['datacenter']['name'] = kwargs[param]
1038 else:
1039 descriptor['datacenter'][param] = kwargs[param]
1040 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
tiernoc0e42e22018-05-11 11:36:10 +02001041
1042 def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
1043 """Edit the parameters of a scenario
1044 Params: must supply a descriptor or/and a parameters to change
1045 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1046 descriptor: with format {'scenario':{params to change info}}
1047 must be a dictionary or a json/yaml text.
1048 parameters to change can be supplyied by the descriptor or as parameters:
1049 new_name: the scenario name
1050 public: boolean, available to other tenants
1051 description: scenario description
1052 tenant_id. Propietary tenant
1053 Return: Raises an exception on error, not found or found several
1054 Obtain a dictionary with format {'scenario':{new_scenario_info}}
1055 """
1056
1057 if isinstance(descriptor, str):
1058 descriptor = self.parse(descriptor, descriptor_format)
1059 elif descriptor:
1060 pass
1061 elif kwargs:
tierno750b2452018-05-17 16:39:29 +02001062 descriptor = {"scenario": {}}
tiernoc0e42e22018-05-11 11:36:10 +02001063 else:
1064 raise ROClientException("Missing descriptor")
1065
tierno750b2452018-05-17 16:39:29 +02001066 if 'scenario' not in descriptor or len(descriptor) > 2:
tiernoc0e42e22018-05-11 11:36:10 +02001067 raise ROClientException("Descriptor must contain only one 'scenario' field")
1068 for param in kwargs:
tierno750b2452018-05-17 16:39:29 +02001069 if param == 'new_name':
tiernoc0e42e22018-05-11 11:36:10 +02001070 descriptor['scenario']['name'] = kwargs[param]
1071 else:
1072 descriptor['scenario'][param] = kwargs[param]
1073 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
1074
tierno750b2452018-05-17 16:39:29 +02001075 # VIM ACTIONS
tiernoc0e42e22018-05-11 11:36:10 +02001076 def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
1077 """Perform an action over a vim
tierno750b2452018-05-17 16:39:29 +02001078 Params:
tiernoc0e42e22018-05-11 11:36:10 +02001079 action: can be 'list', 'get'/'show', 'delete' or 'create'
1080 item: can be 'tenants' or 'networks'
1081 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
1082 other parameters:
tierno750b2452018-05-17 16:39:29 +02001083 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
1084 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
tiernoc0e42e22018-05-11 11:36:10 +02001085 must be a dictionary or a json/yaml text.
1086 name: for created tenant/net Overwrite descriptor name if any
1087 description: tenant descriptor. Overwrite descriptor description if any
tierno750b2452018-05-17 16:39:29 +02001088
tiernoc0e42e22018-05-11 11:36:10 +02001089 Return: Raises an exception on error
1090 Obtain a dictionary with format {'tenant':{new_tenant_info}}
1091 """
tierno750b2452018-05-17 16:39:29 +02001092 session = None # TODO remove when changed to asyncio
tiernoc0e42e22018-05-11 11:36:10 +02001093 if item not in ("tenants", "networks", "images"):
1094 raise ROClientException("Unknown value for item '{}', must be 'tenants', 'nets' or "
tierno750b2452018-05-17 16:39:29 +02001095 "images".format(str(item)))
tiernoc0e42e22018-05-11 11:36:10 +02001096
tierno750b2452018-05-17 16:39:29 +02001097 image_actions = ['list', 'get', 'show', 'delete']
tiernoc0e42e22018-05-11 11:36:10 +02001098 if item == "images" and action not in image_actions:
1099 raise ROClientException("Only available actions for item '{}' are {}\n"
tierno750b2452018-05-17 16:39:29 +02001100 "Requested action was '{}'".format(item, ', '.join(image_actions), action))
tiernoc0e42e22018-05-11 11:36:10 +02001101 if all_tenants:
1102 tenant_text = "/any"
1103 else:
tierno750b2452018-05-17 16:39:29 +02001104 tenant_text = "/" + self._get_tenant()
1105
tiernoc0e42e22018-05-11 11:36:10 +02001106 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
1107 datacenter = self._get_item_uuid(session, "datacenters", kwargs.get("datacenter"), all_tenants=all_tenants)
1108 else:
1109 datacenter = self.get_datacenter(session)
1110
tierno750b2452018-05-17 16:39:29 +02001111 if action == "list":
tierno69f0d382020-05-07 13:08:09 +00001112 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
tierno750b2452018-05-17 16:39:29 +02001113 self.logger.debug("GET %s", url)
tiernoc0e42e22018-05-11 11:36:10 +02001114 mano_response = requests.get(url, headers=self.headers_req)
tierno750b2452018-05-17 16:39:29 +02001115 self.logger.debug("RO response: %s", mano_response.text)
endikac2950402020-09-14 11:20:00 +02001116 content = self._parse_yaml(mano_response.text, response=True)
tierno750b2452018-05-17 16:39:29 +02001117 if mano_response.status_code == 200:
tiernoc0e42e22018-05-11 11:36:10 +02001118 return content
1119 else:
endikac2950402020-09-14 11:20:00 +02001120 raise ROClientException(str(content), http_code=mano_response.status)
tierno750b2452018-05-17 16:39:29 +02001121 elif action == "get" or action == "show":
tierno69f0d382020-05-07 13:08:09 +00001122 url = "{}{}/vim/{}/{}/{}".format(self.uri, tenant_text, datacenter, item, uuid)
tierno750b2452018-05-17 16:39:29 +02001123 self.logger.debug("GET %s", url)
tiernoc0e42e22018-05-11 11:36:10 +02001124 mano_response = requests.get(url, headers=self.headers_req)
tierno750b2452018-05-17 16:39:29 +02001125 self.logger.debug("RO response: %s", mano_response.text)
endikac2950402020-09-14 11:20:00 +02001126 content = self._parse_yaml(mano_response.text, response=True)
tierno750b2452018-05-17 16:39:29 +02001127 if mano_response.status_code == 200:
tiernoc0e42e22018-05-11 11:36:10 +02001128 return content
1129 else:
endikac2950402020-09-14 11:20:00 +02001130 raise ROClientException(str(content), http_code=mano_response.status)
tierno750b2452018-05-17 16:39:29 +02001131 elif action == "delete":
tierno69f0d382020-05-07 13:08:09 +00001132 url = "{}{}/vim/{}/{}/{}".format(self.uri, tenant_text, datacenter, item, uuid)
tierno750b2452018-05-17 16:39:29 +02001133 self.logger.debug("DELETE %s", url)
tiernoc0e42e22018-05-11 11:36:10 +02001134 mano_response = requests.delete(url, headers=self.headers_req)
tierno750b2452018-05-17 16:39:29 +02001135 self.logger.debug("RO response: %s", mano_response.text)
endikac2950402020-09-14 11:20:00 +02001136 content = self._parse_yaml(mano_response.text, response=True)
tierno750b2452018-05-17 16:39:29 +02001137 if mano_response.status_code == 200:
tiernoc0e42e22018-05-11 11:36:10 +02001138 return content
1139 else:
endikac2950402020-09-14 11:20:00 +02001140 raise ROClientException(str(content), http_code=mano_response.status)
tierno750b2452018-05-17 16:39:29 +02001141 elif action == "create":
tiernoc0e42e22018-05-11 11:36:10 +02001142 if "descriptor" in kwargs:
1143 if isinstance(kwargs["descriptor"], str):
tierno750b2452018-05-17 16:39:29 +02001144 descriptor = self._parse(kwargs["descriptor"], kwargs.get("descriptor_format"))
tiernoc0e42e22018-05-11 11:36:10 +02001145 else:
1146 descriptor = kwargs["descriptor"]
1147 elif "name" in kwargs:
tierno750b2452018-05-17 16:39:29 +02001148 descriptor = {item[:-1]: {"name": kwargs["name"]}}
tiernoc0e42e22018-05-11 11:36:10 +02001149 else:
1150 raise ROClientException("Missing descriptor")
endikac2950402020-09-14 11:20:00 +02001151
tierno750b2452018-05-17 16:39:29 +02001152 if item[:-1] not in descriptor or len(descriptor) != 1:
tiernoc0e42e22018-05-11 11:36:10 +02001153 raise ROClientException("Descriptor must contain only one 'tenant' field")
1154 if "name" in kwargs:
tierno750b2452018-05-17 16:39:29 +02001155 descriptor[item[:-1]]['name'] = kwargs["name"]
tiernoc0e42e22018-05-11 11:36:10 +02001156 if "description" in kwargs:
tierno750b2452018-05-17 16:39:29 +02001157 descriptor[item[:-1]]['description'] = kwargs["description"]
tiernoc0e42e22018-05-11 11:36:10 +02001158 payload_req = yaml.safe_dump(descriptor)
tierno750b2452018-05-17 16:39:29 +02001159 # print payload_req
tierno69f0d382020-05-07 13:08:09 +00001160 url = "{}{}/vim/{}/{}".format(self.uri, tenant_text, datacenter, item)
tierno750b2452018-05-17 16:39:29 +02001161 self.logger.debug("RO POST %s %s", url, payload_req)
1162 mano_response = requests.post(url, headers=self.headers_req, data=payload_req)
1163 self.logger.debug("RO response: %s", mano_response.text)
tiernoc0e42e22018-05-11 11:36:10 +02001164 content = self._parse_yaml(mano_response.text, response=True)
tierno750b2452018-05-17 16:39:29 +02001165 if mano_response.status_code == 200:
tiernoc0e42e22018-05-11 11:36:10 +02001166 return content
1167 else:
1168 raise ROClientException(str(content), http_code=mano_response.status)
1169 else:
tierno750b2452018-05-17 16:39:29 +02001170 raise ROClientException("Unknown value for action '{}".format(str(action)))
tiernoc0e42e22018-05-11 11:36:10 +02001171
1172
1173if __name__ == '__main__':
1174 RO_URL = "http://localhost:9090/openmano"
1175 TEST_TENANT = "myTenant"
1176 TEST_VIM1 = "myvim"
1177 TEST_URL1 = "https://localhost:5000/v1"
1178 TEST_TYPE1 = "openstack"
1179 TEST_CONFIG1 = {"use_floating_ip": True}
1180 TEST_VIM2 = "myvim2"
1181 TEST_URL2 = "https://localhost:5000/v2"
1182 TEST_TYPE2 = "openvim"
1183 TEST_CONFIG2 = {"config2": "config2", "config3": True}
1184
1185 streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1186 logging.basicConfig(format=streamformat)
1187 logger = logging.getLogger("ROClient")
1188
1189 tenant_id = None
1190 vim_id = False
1191 loop = asyncio.get_event_loop()
tierno69f0d382020-05-07 13:08:09 +00001192 myClient = ROClient(uri=RO_URL, loop=loop, loglevel="DEBUG")
tiernoc0e42e22018-05-11 11:36:10 +02001193 try:
1194 # test tenant
1195 content = loop.run_until_complete(myClient.get_list("tenant"))
1196 print("tenants", content)
1197 content = loop.run_until_complete(myClient.create("tenant", name=TEST_TENANT))
1198 tenant_id = True
1199 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1200 print("tenant", TEST_TENANT, content)
tierno750b2452018-05-17 16:39:29 +02001201 content = loop.run_until_complete(myClient.edit("tenant", TEST_TENANT, description="another description"))
tiernoc0e42e22018-05-11 11:36:10 +02001202 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1203 print("tenant edited", TEST_TENANT, content)
1204 myClient["tenant"] = TEST_TENANT
1205
tiernoc0e42e22018-05-11 11:36:10 +02001206 # test VIM
tierno750b2452018-05-17 16:39:29 +02001207 content = loop.run_until_complete(myClient.create("vim", name=TEST_VIM1, type=TEST_TYPE1, vim_url=TEST_URL1,
1208 config=TEST_CONFIG1))
tiernoc0e42e22018-05-11 11:36:10 +02001209 vim_id = True
1210 content = loop.run_until_complete(myClient.get_list("vim"))
1211 print("vim", content)
1212 content = loop.run_until_complete(myClient.show("vim", TEST_VIM1))
1213 print("vim", TEST_VIM1, content)
tierno750b2452018-05-17 16:39:29 +02001214 content = loop.run_until_complete(myClient.edit("vim", TEST_VIM1, description="another description",
tiernoc0e42e22018-05-11 11:36:10 +02001215 name=TEST_VIM2, type=TEST_TYPE2, vim_url=TEST_URL2,
1216 config=TEST_CONFIG2))
1217 content = loop.run_until_complete(myClient.show("vim", TEST_VIM2))
1218 print("vim edited", TEST_VIM2, content)
1219
1220 # test VIM_ACCOUNT
1221 content = loop.run_until_complete(myClient.attach_datacenter(TEST_VIM2, vim_username='user',
tierno750b2452018-05-17 16:39:29 +02001222 vim_password='pass', vim_tenant_name='vimtenant1',
1223 config=TEST_CONFIG1))
tiernoc0e42e22018-05-11 11:36:10 +02001224 vim_id = True
1225 content = loop.run_until_complete(myClient.get_list("vim_account"))
1226 print("vim_account", content)
1227 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1228 print("vim_account", TEST_VIM2, content)
tierno750b2452018-05-17 16:39:29 +02001229 content = loop.run_until_complete(myClient.edit("vim_account", TEST_VIM2, vim_username='user2',
1230 vim_password='pass2', vim_tenant_name="vimtenant2",
1231 config=TEST_CONFIG2))
tiernoc0e42e22018-05-11 11:36:10 +02001232 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1233 print("vim_account edited", TEST_VIM2, content)
1234
1235 myClient["vim"] = TEST_VIM2
1236
1237 except Exception as e:
1238 logger.error("Error {}".format(e), exc_info=True)
1239
1240 for item in (("vim_account", TEST_VIM1), ("vim", TEST_VIM1),
1241 ("vim_account", TEST_VIM2), ("vim", TEST_VIM2),
1242 ("tenant", TEST_TENANT)):
1243 try:
1244 content = loop.run_until_complete(myClient.delete(item[0], item[1]))
1245 print("{} {} deleted; {}".format(item[0], item[1], content))
1246 except Exception as e:
1247 if e.http_code == 404:
1248 print("{} {} not present or already deleted".format(item[0], item[1]))
1249 else:
1250 logger.error("Error {}".format(e), exc_info=True)
1251
1252 loop.close()