Ubuntu18 + pip upgraded + aiohttp
[osm/LCM.git] / osm_lcm / ROclient.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
17 # under the License.
18 #
19 ##
20
21 """
22 asyncio RO python client to interact with RO-server
23 """
24
25 import asyncio
26 import aiohttp
27 import json
28 import yaml
29 import logging
30 from urllib.parse import quote
31 from uuid import UUID
32 from copy import deepcopy
33
34 __author__ = "Alfonso Tierno"
35 __date__ = "$09-Jan-2018 09:09:48$"
36 __version__ = "0.1.2"
37 version_date = "2018-05-16"
38 requests = None
39
40
41 class ROClientException(Exception):
42 def __init__(self, message, http_code=400):
43 """Common Exception for all RO client exceptions"""
44 self.http_code = http_code
45 Exception.__init__(self, message)
46
47
48 def remove_envelop(item, indata=None):
49 """
50 Obtain the useful data removing the envelop. It goes through the vnfd or nsd catalog and returns the
51 vnfd or nsd content
52 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
53 :param indata: Content to be inspected
54 :return: the useful part of indata (a reference, not a new dictionay)
55 """
56 clean_indata = indata
57 if not indata:
58 return {}
59 if item == "vnfd":
60 if clean_indata.get('vnfd:vnfd-catalog'):
61 clean_indata = clean_indata['vnfd:vnfd-catalog']
62 elif clean_indata.get('vnfd-catalog'):
63 clean_indata = clean_indata['vnfd-catalog']
64 if clean_indata.get('vnfd'):
65 if 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"]
86 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"]
92 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
101 class 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',
104 'vnfd': 'vnfs', 'nsd': 'scenarios', 'wim': 'wims', 'wim_account': 'wims',
105 'ns': 'instances'}
106 mandatory_for_create = {
107 'tenant': ("name", ),
108 'vnfd': ("name", "id"),
109 'nsd': ("name", "id"),
110 'ns': ("name", "scenario", "datacenter"),
111 'vim': ("name", "vim_url"),
112 'wim': ("name", "wim_url"),
113 'vim_account': (),
114 'wim_account': (),
115 'sdn': ("name", 'type'),
116 }
117 timeout_large = 120
118 timeout_short = 30
119
120 def __init__(self, loop, endpoint_url, **kwargs):
121 self.loop = loop
122 self.endpoint_url = endpoint_url
123
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
130 logger_name = kwargs.get('logger_name', 'ROClient')
131 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
146 elif index == 'endpoint_url':
147 return self.endpoint_url
148 else:
149 raise KeyError("Invalid key '{}'".format(index))
150
151 def __setitem__(self, index, value):
152 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
160 elif index == 'endpoint_url':
161 self.endpoint_url = value
162 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
166
167 def _parse(self, descriptor, descriptor_format, response=False):
168 if descriptor_format and descriptor_format != "json" and descriptor_format != "yaml":
169 raise ROClientException("'descriptor_format' must be a 'json' or 'yaml' text")
170 if descriptor_format != "json":
171 try:
172 return yaml.load(descriptor)
173 except yaml.YAMLError as exc:
174 error_pos = ""
175 if hasattr(exc, 'problem_mark'):
176 mark = exc.problem_mark
177 error_pos = " at line:{} column:{}s".format(mark.line + 1, mark.column + 1)
178 error_text = "yaml format error" + error_pos
179 elif descriptor_format != "yaml":
180 try:
181 return json.loads(descriptor)
182 except Exception as e:
183 if response:
184 error_text = "json format error" + str(e)
185
186 if response:
187 raise ROClientException(error_text)
188 raise ROClientException(error_text)
189
190 def _parse_yaml(self, descriptor, response=False):
191 try:
192 return yaml.load(descriptor, Loader=yaml.Loader)
193 except yaml.YAMLError as exc:
194 error_pos = ""
195 if hasattr(exc, 'problem_mark'):
196 mark = exc.problem_mark
197 error_pos = " at line:{} column:{}s".format(mark.line + 1, mark.column + 1)
198 error_text = "yaml format error" + error_pos
199 if response:
200 raise ROClientException(error_text)
201 raise ROClientException(error_text)
202
203 @staticmethod
204 def check_if_uuid(uuid_text):
205 """
206 Check if text correspond to an uuid foramt
207 :param uuid_text:
208 :return: True if it is an uuid False if not
209 """
210 try:
211 UUID(uuid_text)
212 return True
213 except Exception:
214 return False
215
216 @staticmethod
217 def _create_envelop(item, indata=None):
218 """
219 Returns a new dict that incledes indata with the expected envelop
220 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
221 :param indata: Content to be enveloped
222 :return: a new dic with {<envelop>: {indata} } where envelop can be e.g. tenant, datacenter, ...
223 """
224 if item == "vnfd":
225 return {'vnfd-catalog': {'vnfd': [indata]}}
226 elif item == "nsd":
227 return {'nsd-catalog': {'nsd': [indata]}}
228 elif item == "tenant":
229 return {'tenant': indata}
230 elif item in ("vim", "vim_account", "datacenter"):
231 return {'datacenter': indata}
232 elif item == "wim":
233 return {'wim': indata}
234 elif item == "wim_account":
235 return {'wim_account': indata}
236 elif item == "ns" or item == "instances":
237 return {'instance': indata}
238 elif item == "sdn":
239 return {'sdn_controller': indata}
240 else:
241 assert False, "_create_envelop with unknown item {}".format(item)
242
243 @staticmethod
244 def update_descriptor(desc, kwargs):
245 desc = deepcopy(desc) # do not modify original descriptor
246 try:
247 for k, v in kwargs.items():
248 update_content = desc
249 kitem_old = None
250 klist = k.split(".")
251 for kitem in klist:
252 if kitem_old is not None:
253 update_content = update_content[kitem_old]
254 if isinstance(update_content, dict):
255 kitem_old = kitem
256 elif isinstance(update_content, list):
257 kitem_old = int(kitem)
258 else:
259 raise ROClientException(
260 "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(k, kitem))
261 if v == "__DELETE__":
262 del update_content[kitem_old]
263 else:
264 update_content[kitem_old] = v
265 return desc
266 except KeyError:
267 raise ROClientException(
268 "Invalid query string '{}'. Descriptor does not contain '{}'".format(k, kitem_old))
269 except ValueError:
270 raise ROClientException("Invalid query string '{}'. Expected integer index list instead of '{}'".format(
271 k, kitem))
272 except IndexError:
273 raise ROClientException(
274 "Invalid query string '{}'. Index '{}' out of range".format(k, kitem_old))
275
276 @staticmethod
277 def check_ns_status(ns_descriptor):
278 """
279 Inspect RO instance descriptor and indicates the status
280 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
281 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
282 """
283 error_list = []
284 total = {"VMs": 0, "networks": 0, "SDN_networks": 0}
285 done = {"VMs": 0, "networks": 0, "SDN_networks": 0}
286
287 def _get_ref(desc):
288 # return an identification for the network or vm. Try vim_id if exist, if not descriptor id for net
289 if desc.get("vim_net_id"):
290 return "'vim-id={}'".format(desc["vim_net_id"])
291 elif desc.get("ns_net_osm_id"):
292 return "'nsd-vld-id={}'".format(desc["ns_net_osm_id"])
293 elif desc.get("vnf_net_osm_id"):
294 return "'vnfd-vld-id={}'".format(desc["vnf_net_osm_id"])
295 # for VM
296 elif desc.get("vim_vm_id"):
297 return "'vim-id={}'".format(desc["vim_vm_id"])
298 elif desc.get("vdu_osm_id"):
299 return "'vnfd-vdu-id={}'".format(desc["vdu_osm_id"])
300 else:
301 return ""
302
303 def _get_sdn_ref(sce_net_id):
304 # look for the network associated to the SDN network and obtain the identification
305 net = next((x for x in ns_descriptor["nets"] if x.get("sce_net_id") == sce_net_id), None)
306 if not sce_net_id or not net:
307 return ""
308 return _get_ref(net)
309
310 try:
311 total["networks"] = len(ns_descriptor["nets"])
312 for net in ns_descriptor["nets"]:
313 if net["status"] in ("ERROR", "VIM_ERROR"):
314 error_list.append("VIM network ({}) on error: {}".format(_get_ref(net), net["error_msg"]))
315 elif net["status"] == "ACTIVE":
316 done["networks"] += 1
317
318 total["SDN_networks"] = len(ns_descriptor["sdn_nets"])
319 for sdn_net in ns_descriptor["sdn_nets"]:
320 if sdn_net["status"] in ("ERROR", "VIM_ERROR", "WIM_ERROR"):
321 error_list.append("SDN network ({}) on error: {}".format(_get_sdn_ref(sdn_net.get("sce_net_id")),
322 sdn_net["error_msg"]))
323 elif sdn_net["status"] == "ACTIVE":
324 done["SDN_networks"] += 1
325
326 for vnf in ns_descriptor["vnfs"]:
327 for vm in vnf["vms"]:
328 total["VMs"] += 1
329 if vm["status"] in ("ERROR", "VIM_ERROR"):
330 error_list.append("VIM VM ({}) on error: {}".format(_get_ref(vm), vm["error_msg"]))
331 elif vm["status"] == "ACTIVE":
332 done["VMs"] += 1
333 if error_list:
334 return "ERROR", "; ".join(error_list)
335 if all(total[x] == done[x] for x in total): # DONE == TOTAL for all items
336 return "ACTIVE", str({x: total[x] for x in total if total[x]}) # print only those which value is not 0
337 else:
338 return "BUILD", str({x: "{}/{}".format(done[x], total[x]) for x in total if total[x]})
339 # print done/total for each item if total is not 0
340 except Exception as e:
341 raise ROClientException("Unexpected RO ns descriptor. Wrong version? {}".format(e)) from e
342
343 @staticmethod
344 def check_action_status(action_descriptor):
345 """
346 Inspect RO instance descriptor and indicates the status
347 :param action_descriptor: action instance descriptor obtained with self.show("ns", "action")
348 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
349 """
350 net_total = 0
351 vm_total = 0
352 net_done = 0
353 vm_done = 0
354 other_total = 0
355 other_done = 0
356
357 for vim_action_set in action_descriptor["actions"]:
358 for vim_action in vim_action_set["vim_wim_actions"]:
359 if vim_action["item"] == "instance_vms":
360 vm_total += 1
361 elif vim_action["item"] == "instance_nets":
362 net_total += 1
363 else:
364 other_total += 1
365 if vim_action["status"] == "FAILED":
366 return "ERROR", vim_action["error_msg"]
367 elif vim_action["status"] in ("DONE", "SUPERSEDED", "FINISHED"):
368 if vim_action["item"] == "instance_vms":
369 vm_done += 1
370 elif vim_action["item"] == "instance_nets":
371 net_done += 1
372 else:
373 other_done += 1
374
375 if net_total == net_done and vm_total == vm_done and other_total == other_done:
376 return "ACTIVE", "VMs {}, networks: {}, other: {} ".format(vm_total, net_total, other_total)
377 else:
378 return "BUILD", "VMs: {}/{}, networks: {}/{}, other: {}/{}".format(vm_done, vm_total, net_done, net_total,
379 other_done, other_total)
380
381 @staticmethod
382 def get_ns_vnf_info(ns_descriptor):
383 """
384 Get a dict with the VIM_id, ip_addresses, mac_addresses of every vnf and vdu
385 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
386 :return: dict with:
387 <member_vnf_index>:
388 ip_address: XXXX,
389 vdur:
390 <vdu_osm_id>:
391 ip_address: XXX
392 vim_id: XXXX
393 interfaces:
394 <name>:
395 ip_address: XXX
396 mac_address: XXX
397 """
398 ns_info = {}
399 for vnf in ns_descriptor["vnfs"]:
400 if not vnf.get("ip_address") and vnf.get("vms"):
401 raise ROClientException("ns member_vnf_index '{}' has no IP address".format(
402 vnf["member_vnf_index"]), http_code=409)
403 vnfr_info = {
404 "ip_address": vnf.get("ip_address"),
405 "vdur": {}
406 }
407 for vm in vnf["vms"]:
408 vdur = {
409 "vim_id": vm.get("vim_vm_id"),
410 "ip_address": vm.get("ip_address"),
411 "interfaces": {}
412 }
413 for iface in vm["interfaces"]:
414 if iface.get("type") == "mgmt" and not iface.get("ip_address"):
415 raise ROClientException("ns member_vnf_index '{}' vm '{}' management interface '{}' has no IP "
416 "address".format(vnf["member_vnf_index"], vm["vdu_osm_id"],
417 iface["external_name"]), http_code=409)
418 vdur["interfaces"][iface["internal_name"]] = {"ip_address": iface.get("ip_address"),
419 "mac_address": iface.get("mac_address"),
420 "vim_id": iface.get("vim_interface_id"),
421 }
422 vnfr_info["vdur"][vm["vdu_osm_id"]] = vdur
423 ns_info[str(vnf["member_vnf_index"])] = vnfr_info
424 return ns_info
425
426 async def _get_item_uuid(self, session, item, item_id_name, all_tenants=False):
427 if all_tenants:
428 tenant_text = "/any"
429 elif all_tenants is None:
430 tenant_text = ""
431 else:
432 if not self.tenant:
433 await self._get_tenant(session)
434 tenant_text = "/" + self.tenant
435
436 item_id = 0
437 url = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
438 if self.check_if_uuid(item_id_name):
439 item_id = item_id_name
440 url += "/" + item_id_name
441 elif item_id_name and item_id_name.startswith("'") and item_id_name.endswith("'"):
442 item_id_name = item_id_name[1:-1]
443 self.logger.debug("RO GET %s", url)
444 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
445 async with session.get(url, headers=self.headers_req) as response:
446 response_text = await response.read()
447 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
448 if response.status == 404: # NOT_FOUND
449 raise ROClientException("No {} found with id '{}'".format(item[:-1], item_id_name),
450 http_code=404)
451 if response.status >= 300:
452 raise ROClientException(response_text, http_code=response.status)
453 content = self._parse_yaml(response_text, response=True)
454
455 if item_id:
456 return item_id
457 desc = content[item]
458 assert isinstance(desc, list), "_get_item_uuid get a non dict with a list inside {}".format(type(desc))
459 uuid = None
460 for i in desc:
461 if item_id_name and i["name"] != item_id_name:
462 continue
463 if uuid: # found more than one
464 raise ROClientException(
465 "Found more than one {} with name '{}'. uuid must be used".format(item, item_id_name),
466 http_code=404)
467 uuid = i["uuid"]
468 if not uuid:
469 raise ROClientException("No {} found with name '{}'".format(item[:-1], item_id_name), http_code=404)
470 return uuid
471
472 async def _get_item(self, session, item, item_id_name, extra_item=None, extra_item_id=None, all_tenants=False):
473 if all_tenants:
474 tenant_text = "/any"
475 elif all_tenants is None:
476 tenant_text = ""
477 else:
478 if not self.tenant:
479 await self._get_tenant(session)
480 tenant_text = "/" + self.tenant
481
482 if self.check_if_uuid(item_id_name):
483 uuid = item_id_name
484 else:
485 # check that exist
486 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
487
488 url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
489 if extra_item:
490 url += "/" + extra_item
491 if extra_item_id:
492 url += "/" + extra_item_id
493 self.logger.debug("GET %s", url)
494 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
495 async with session.get(url, headers=self.headers_req) as response:
496 response_text = await response.read()
497 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
498 if response.status >= 300:
499 raise ROClientException(response_text, http_code=response.status)
500
501 return self._parse_yaml(response_text, response=True)
502
503 async def _get_tenant(self, session):
504 if not self.tenant:
505 self.tenant = await self._get_item_uuid(session, "tenants", self.tenant_id_name, None)
506 return self.tenant
507
508 async def _get_datacenter(self, session):
509 if not self.tenant:
510 await self._get_tenant(session)
511 if not self.datacenter:
512 self.datacenter = await self._get_item_uuid(session, "datacenters", self.datacenter_id_name, True)
513 return self.datacenter
514
515 async def _create_item(self, session, item, descriptor, item_id_name=None, action=None, all_tenants=False):
516 if all_tenants:
517 tenant_text = "/any"
518 elif all_tenants is None:
519 tenant_text = ""
520 else:
521 if not self.tenant:
522 await self._get_tenant(session)
523 tenant_text = "/" + self.tenant
524 payload_req = yaml.safe_dump(descriptor)
525 # print payload_req
526
527 api_version_text = ""
528 if item == "vnfs":
529 # assumes version v3 only
530 api_version_text = "/v3"
531 item = "vnfd"
532 elif item == "scenarios":
533 # assumes version v3 only
534 api_version_text = "/v3"
535 item = "nsd"
536
537 if not item_id_name:
538 uuid = ""
539 elif self.check_if_uuid(item_id_name):
540 uuid = "/{}".format(item_id_name)
541 else:
542 # check that exist
543 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
544 uuid = "/{}".format(uuid)
545 if not action:
546 action = ""
547 else:
548 action = "/{}".format(action)
549
550 url = "{}{apiver}{tenant}/{item}{id}{action}".format(self.endpoint_url, apiver=api_version_text,
551 tenant=tenant_text, item=item, id=uuid, action=action)
552 self.logger.debug("RO POST %s %s", url, payload_req)
553 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
554 async with session.post(url, headers=self.headers_req, data=payload_req) as response:
555 response_text = await response.read()
556 self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
557 if response.status >= 300:
558 raise ROClientException(response_text, http_code=response.status)
559
560 return self._parse_yaml(response_text, response=True)
561
562 async def _del_item(self, session, item, item_id_name, all_tenants=False):
563 if all_tenants:
564 tenant_text = "/any"
565 elif all_tenants is None:
566 tenant_text = ""
567 else:
568 if not self.tenant:
569 await self._get_tenant(session)
570 tenant_text = "/" + self.tenant
571 if not self.check_if_uuid(item_id_name):
572 # check that exist
573 _all_tenants = all_tenants
574 if item in ("datacenters", 'wims'):
575 _all_tenants = True
576 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants=_all_tenants)
577 else:
578 uuid = item_id_name
579
580 url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
581 self.logger.debug("DELETE %s", url)
582 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
583 async with session.delete(url, headers=self.headers_req) as response:
584 response_text = await response.read()
585 self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
586 if response.status >= 300:
587 raise ROClientException(response_text, http_code=response.status)
588
589 return self._parse_yaml(response_text, response=True)
590
591 async def _list_item(self, session, item, all_tenants=False, filter_dict=None):
592 if all_tenants:
593 tenant_text = "/any"
594 elif all_tenants is None:
595 tenant_text = ""
596 else:
597 if not self.tenant:
598 await self._get_tenant(session)
599 tenant_text = "/" + self.tenant
600
601 url = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
602 separator = "?"
603 if filter_dict:
604 for k in filter_dict:
605 url += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
606 separator = "&"
607 self.logger.debug("RO GET %s", url)
608 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
609 async with session.get(url, headers=self.headers_req) as response:
610 response_text = await response.read()
611 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
612 if response.status >= 300:
613 raise ROClientException(response_text, http_code=response.status)
614
615 return self._parse_yaml(response_text, response=True)
616
617 async def _edit_item(self, session, item, item_id, descriptor, all_tenants=False):
618 if all_tenants:
619 tenant_text = "/any"
620 elif all_tenants is None:
621 tenant_text = ""
622 else:
623 if not self.tenant:
624 await self._get_tenant(session)
625 tenant_text = "/" + self.tenant
626
627 payload_req = yaml.safe_dump(descriptor)
628
629 # print payload_req
630 url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, item_id)
631 self.logger.debug("RO PUT %s %s", url, payload_req)
632 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
633 async with session.put(url, headers=self.headers_req, data=payload_req) as response:
634 response_text = await response.read()
635 self.logger.debug("PUT {} [{}] {}".format(url, response.status, response_text[:100]))
636 if response.status >= 300:
637 raise ROClientException(response_text, http_code=response.status)
638
639 return self._parse_yaml(response_text, response=True)
640
641 async def get_version(self):
642 """
643 Obtain RO server version.
644 :return: a list with integers ["major", "minor", "release"]. Raises ROClientException on Error,
645 """
646 try:
647 async with aiohttp.ClientSession(loop=self.loop) as session:
648 url = "{}/version".format(self.endpoint_url)
649 self.logger.debug("RO GET %s", url)
650 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
651 async with session.get(url, headers=self.headers_req) as response:
652 response_text = await response.read()
653 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
654 if response.status >= 300:
655 raise ROClientException(response_text, http_code=response.status)
656
657 for word in str(response_text).split(" "):
658 if "." in word:
659 version_text, _, _ = word.partition("-")
660 return version_text
661 raise ROClientException("Got invalid version text: '{}'".format(response_text), http_code=500)
662 except aiohttp.ClientOSError as e:
663 raise ROClientException(e, http_code=504)
664 except asyncio.TimeoutError:
665 raise ROClientException("Timeout", http_code=504)
666 except Exception as e:
667 raise ROClientException("Got invalid version text: '{}'; causing exception {}".format(response_text, e),
668 http_code=500)
669
670 async def get_list(self, item, all_tenants=False, filter_by=None):
671 """
672 Obtain a list of items filtering by the specigy filter_by.
673 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
674 :param all_tenants: True if not filtering by tenant. Only allowed for admin
675 :param filter_by: dictionary with filtering
676 :return: a list of dict. It can be empty. Raises ROClientException on Error,
677 """
678 try:
679 if item not in self.client_to_RO:
680 raise ROClientException("Invalid item {}".format(item))
681 if item == 'tenant':
682 all_tenants = None
683 async with aiohttp.ClientSession(loop=self.loop) as session:
684 content = await self._list_item(session, self.client_to_RO[item], all_tenants=all_tenants,
685 filter_dict=filter_by)
686 if isinstance(content, dict):
687 if len(content) == 1:
688 for _, v in content.items():
689 return v
690 return content.values()[0]
691 else:
692 raise ROClientException("Output not a list neither dict with len equal 1", http_code=500)
693 return content
694 except aiohttp.ClientOSError as e:
695 raise ROClientException(e, http_code=504)
696 except asyncio.TimeoutError:
697 raise ROClientException("Timeout", http_code=504)
698
699 async def show(self, item, item_id_name=None, extra_item=None, extra_item_id=None, all_tenants=False):
700 """
701 Obtain the information of an item from its id or name
702 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
703 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
704 :param extra_item: if supplied, it is used to add to the URL.
705 Can be 'action' if item='ns'; 'networks' or'images' if item='vim'
706 :param extra_item_id: if supplied, it is used get details of a concrete extra_item.
707 :param all_tenants: True if not filtering by tenant. Only allowed for admin
708 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
709 """
710 try:
711 if item not in self.client_to_RO:
712 raise ROClientException("Invalid item {}".format(item))
713 if item == 'tenant':
714 all_tenants = None
715 elif item == 'vim':
716 all_tenants = True
717 elif item == 'vim_account':
718 all_tenants = False
719
720 async with aiohttp.ClientSession(loop=self.loop) as session:
721 content = await self._get_item(session, self.client_to_RO[item], item_id_name, extra_item=extra_item,
722 extra_item_id=extra_item_id, all_tenants=all_tenants)
723 return remove_envelop(item, content)
724 except aiohttp.ClientOSError as e:
725 raise ROClientException(e, http_code=504)
726 except asyncio.TimeoutError:
727 raise ROClientException("Timeout", http_code=504)
728
729 async def delete(self, item, item_id_name=None, all_tenants=False):
730 """
731 Delete the information of an item from its id or name
732 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
733 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
734 :param all_tenants: True if not filtering by tenant. Only allowed for admin
735 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
736 """
737 try:
738 if item not in self.client_to_RO:
739 raise ROClientException("Invalid item {}".format(item))
740 if item in ('tenant', 'vim', 'wim'):
741 all_tenants = None
742
743 async with aiohttp.ClientSession(loop=self.loop) as session:
744 result = await self._del_item(session, self.client_to_RO[item], item_id_name, all_tenants=all_tenants)
745 # in case of ns delete, get the action_id embeded in text
746 if item == "ns" and result.get("result"):
747 _, _, action_id = result["result"].partition("action_id=")
748 action_id, _, _ = action_id.partition(" ")
749 if action_id:
750 result["action_id"] = action_id
751 return result
752 except aiohttp.ClientOSError as e:
753 raise ROClientException(e, http_code=504)
754 except asyncio.TimeoutError:
755 raise ROClientException("Timeout", http_code=504)
756
757 async def edit(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
758 """ Edit an item
759 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
760 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
761 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
762 :param descriptor_format: Can be 'json' or 'yaml'
763 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
764 keys can be a dot separated list to specify elements inside dict
765 :return: dictionary with the information or raises ROClientException on Error
766 """
767 try:
768 if isinstance(descriptor, str):
769 descriptor = self._parse(descriptor, descriptor_format)
770 elif descriptor:
771 pass
772 else:
773 descriptor = {}
774
775 if item not in self.client_to_RO:
776 raise ROClientException("Invalid item {}".format(item))
777 desc = remove_envelop(item, descriptor)
778
779 # Override descriptor with kwargs
780 if kwargs:
781 desc = self.update_descriptor(desc, kwargs)
782 all_tenants = False
783 if item in ('tenant', 'vim'):
784 all_tenants = None
785
786 create_desc = self._create_envelop(item, desc)
787
788 async with aiohttp.ClientSession(loop=self.loop) as session:
789 _all_tenants = all_tenants
790 if item == 'vim':
791 _all_tenants = True
792 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
793 all_tenants=_all_tenants)
794 if item == 'vim':
795 _all_tenants = None
796 # await self._get_tenant(session)
797 outdata = await self._edit_item(session, self.client_to_RO[item], item_id, create_desc,
798 all_tenants=_all_tenants)
799 return remove_envelop(item, outdata)
800 except aiohttp.ClientOSError as e:
801 raise ROClientException(e, http_code=504)
802 except asyncio.TimeoutError:
803 raise ROClientException("Timeout", http_code=504)
804
805 async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
806 """
807 Creates an item from its descriptor
808 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
809 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
810 :param descriptor_format: Can be 'json' or 'yaml'
811 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
812 keys can be a dot separated list to specify elements inside dict
813 :return: dictionary with the information or raises ROClientException on Error
814 """
815 try:
816 if isinstance(descriptor, str):
817 descriptor = self._parse(descriptor, descriptor_format)
818 elif descriptor:
819 pass
820 else:
821 descriptor = {}
822
823 if item not in self.client_to_RO:
824 raise ROClientException("Invalid item {}".format(item))
825 desc = remove_envelop(item, descriptor)
826
827 # Override descriptor with kwargs
828 if kwargs:
829 desc = self.update_descriptor(desc, kwargs)
830
831 for mandatory in self.mandatory_for_create[item]:
832 if mandatory not in desc:
833 raise ROClientException("'{}' is mandatory parameter for {}".format(mandatory, item))
834
835 all_tenants = False
836 if item in ('tenant', 'vim', 'wim'):
837 all_tenants = None
838
839 create_desc = self._create_envelop(item, desc)
840
841 async with aiohttp.ClientSession(loop=self.loop) as session:
842 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
843 all_tenants=all_tenants)
844 return remove_envelop(item, outdata)
845 except aiohttp.ClientOSError as e:
846 raise ROClientException(e, http_code=504)
847 except asyncio.TimeoutError:
848 raise ROClientException("Timeout", http_code=504)
849
850 async def create_action(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
851 """
852 Performs an action over an item
853 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
854 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
855 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
856 :param descriptor_format: Can be 'json' or 'yaml'
857 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
858 keys can be a dot separated list to specify elements inside dict
859 :return: dictionary with the information or raises ROClientException on Error
860 """
861 try:
862 if isinstance(descriptor, str):
863 descriptor = self._parse(descriptor, descriptor_format)
864 elif descriptor:
865 pass
866 else:
867 descriptor = {}
868
869 if item not in self.client_to_RO:
870 raise ROClientException("Invalid item {}".format(item))
871 desc = remove_envelop(item, descriptor)
872
873 # Override descriptor with kwargs
874 if kwargs:
875 desc = self.update_descriptor(desc, kwargs)
876
877 all_tenants = False
878 if item in ('tenant', 'vim'):
879 all_tenants = None
880
881 action = None
882 if item == "vims":
883 action = "sdn_mapping"
884 elif item in ("vim_account", "ns"):
885 action = "action"
886
887 # create_desc = self._create_envelop(item, desc)
888 create_desc = desc
889
890 async with aiohttp.ClientSession(loop=self.loop) as session:
891 _all_tenants = all_tenants
892 if item == 'vim':
893 _all_tenants = True
894 # item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
895 # all_tenants=_all_tenants)
896 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
897 item_id_name=item_id_name, # item_id_name=item_id
898 action=action, all_tenants=_all_tenants)
899 return remove_envelop(item, outdata)
900 except aiohttp.ClientOSError as e:
901 raise ROClientException(e, http_code=504)
902 except asyncio.TimeoutError:
903 raise ROClientException("Timeout", http_code=504)
904
905 async def attach(self, item, item_id_name=None, descriptor=None, descriptor_format=None, **kwargs):
906 """
907 Attach a datacenter or wim to a tenant, creating a vim_account, wim_account
908 :param item: can be vim_account or wim_account
909 :param item_id_name: id or name of the datacenter, wim
910 :param descriptor:
911 :param descriptor_format:
912 :param kwargs:
913 :return:
914 """
915 try:
916 if isinstance(descriptor, str):
917 descriptor = self._parse(descriptor, descriptor_format)
918 elif descriptor:
919 pass
920 else:
921 descriptor = {}
922
923 desc = remove_envelop(item, descriptor)
924
925 # # check that exist
926 # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
927 # tenant_text = "/" + self._get_tenant()
928 if kwargs:
929 desc = self.update_descriptor(desc, kwargs)
930
931 if item == "vim_account":
932 if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
933 raise ROClientException("Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
934 "provided")
935 elif item != "wim_account":
936 raise ROClientException("Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".
937 format(item))
938 create_desc = self._create_envelop(item, desc)
939 payload_req = yaml.safe_dump(create_desc)
940 async with aiohttp.ClientSession(loop=self.loop) as session:
941 # check that exist
942 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=True)
943 await self._get_tenant(session)
944
945 url = "{}/{tenant}/{item}/{item_id}".format(self.endpoint_url, tenant=self.tenant,
946 item=self.client_to_RO[item], item_id=item_id)
947 self.logger.debug("RO POST %s %s", url, payload_req)
948 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
949 async with session.post(url, headers=self.headers_req, data=payload_req) as response:
950 response_text = await response.read()
951 self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
952 if response.status >= 300:
953 raise ROClientException(response_text, http_code=response.status)
954
955 response_desc = self._parse_yaml(response_text, response=True)
956 desc = remove_envelop(item, response_desc)
957 return desc
958 except aiohttp.ClientOSError as e:
959 raise ROClientException(e, http_code=504)
960 except asyncio.TimeoutError:
961 raise ROClientException("Timeout", http_code=504)
962
963 async def detach(self, item, item_id_name=None):
964 # TODO replace the code with delete_item(vim_account,...)
965 try:
966 async with aiohttp.ClientSession(loop=self.loop) as session:
967 # check that exist
968 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=False)
969 tenant = await self._get_tenant(session)
970
971 url = "{}/{tenant}/{item}/{datacenter}".format(self.endpoint_url, tenant=tenant,
972 item=self.client_to_RO[item], datacenter=item_id)
973 self.logger.debug("RO DELETE %s", url)
974
975 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
976 async with session.delete(url, headers=self.headers_req) as response:
977 response_text = await response.read()
978 self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
979 if response.status >= 300:
980 raise ROClientException(response_text, http_code=response.status)
981
982 if response.status >= 300:
983 raise ROClientException(response_text, http_code=response.status)
984
985 response_desc = self._parse_yaml(response_text, response=True)
986 desc = remove_envelop(item, response_desc)
987 return desc
988 except aiohttp.ClientOSError as e:
989 raise ROClientException(e, http_code=504)
990 except asyncio.TimeoutError:
991 raise ROClientException("Timeout", http_code=504)
992
993 # TODO convert to asyncio
994 # DATACENTERS
995
996 def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False,
997 **kwargs):
998 """Edit the parameters of a datacenter
999 Params: must supply a descriptor or/and a parameter to change
1000 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1001 descriptor: with format {'datacenter':{params to change info}}
1002 must be a dictionary or a json/yaml text.
1003 parameters to change can be supplyied by the descriptor or as parameters:
1004 new_name: the datacenter name
1005 vim_url: the datacenter URL
1006 vim_url_admin: the datacenter URL for administrative issues
1007 vim_type: the datacenter type, can be openstack or openvim.
1008 public: boolean, available to other tenants
1009 description: datacenter description
1010 Return: Raises an exception on error, not found or found several
1011 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
1012 """
1013
1014 if isinstance(descriptor, str):
1015 descriptor = self.parse(descriptor, descriptor_format)
1016 elif descriptor:
1017 pass
1018 elif kwargs:
1019 descriptor = {"datacenter": {}}
1020 else:
1021 raise ROClientException("Missing descriptor")
1022
1023 if 'datacenter' not in descriptor or len(descriptor) != 1:
1024 raise ROClientException("Descriptor must contain only one 'datacenter' field")
1025 for param in kwargs:
1026 if param == 'new_name':
1027 descriptor['datacenter']['name'] = kwargs[param]
1028 else:
1029 descriptor['datacenter'][param] = kwargs[param]
1030 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
1031
1032 def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
1033 """Edit the parameters of a scenario
1034 Params: must supply a descriptor or/and a parameters to change
1035 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1036 descriptor: with format {'scenario':{params to change info}}
1037 must be a dictionary or a json/yaml text.
1038 parameters to change can be supplyied by the descriptor or as parameters:
1039 new_name: the scenario name
1040 public: boolean, available to other tenants
1041 description: scenario description
1042 tenant_id. Propietary tenant
1043 Return: Raises an exception on error, not found or found several
1044 Obtain a dictionary with format {'scenario':{new_scenario_info}}
1045 """
1046
1047 if isinstance(descriptor, str):
1048 descriptor = self.parse(descriptor, descriptor_format)
1049 elif descriptor:
1050 pass
1051 elif kwargs:
1052 descriptor = {"scenario": {}}
1053 else:
1054 raise ROClientException("Missing descriptor")
1055
1056 if 'scenario' not in descriptor or len(descriptor) > 2:
1057 raise ROClientException("Descriptor must contain only one 'scenario' field")
1058 for param in kwargs:
1059 if param == 'new_name':
1060 descriptor['scenario']['name'] = kwargs[param]
1061 else:
1062 descriptor['scenario'][param] = kwargs[param]
1063 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
1064
1065 # VIM ACTIONS
1066 def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
1067 """Perform an action over a vim
1068 Params:
1069 action: can be 'list', 'get'/'show', 'delete' or 'create'
1070 item: can be 'tenants' or 'networks'
1071 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
1072 other parameters:
1073 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
1074 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
1075 must be a dictionary or a json/yaml text.
1076 name: for created tenant/net Overwrite descriptor name if any
1077 description: tenant descriptor. Overwrite descriptor description if any
1078
1079 Return: Raises an exception on error
1080 Obtain a dictionary with format {'tenant':{new_tenant_info}}
1081 """
1082 session = None # TODO remove when changed to asyncio
1083 if item not in ("tenants", "networks", "images"):
1084 raise ROClientException("Unknown value for item '{}', must be 'tenants', 'nets' or "
1085 "images".format(str(item)))
1086
1087 image_actions = ['list', 'get', 'show', 'delete']
1088 if item == "images" and action not in image_actions:
1089 raise ROClientException("Only available actions for item '{}' are {}\n"
1090 "Requested action was '{}'".format(item, ', '.join(image_actions), action))
1091 if all_tenants:
1092 tenant_text = "/any"
1093 else:
1094 tenant_text = "/" + self._get_tenant()
1095
1096 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
1097 datacenter = self._get_item_uuid(session, "datacenters", kwargs.get("datacenter"), all_tenants=all_tenants)
1098 else:
1099 datacenter = self.get_datacenter(session)
1100
1101 if action == "list":
1102 url = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1103 self.logger.debug("GET %s", url)
1104 mano_response = requests.get(url, headers=self.headers_req)
1105 self.logger.debug("RO response: %s", mano_response.text)
1106 content = self._parse_yaml(mano_response.text, response=True)
1107 if mano_response.status_code == 200:
1108 return content
1109 else:
1110 raise ROClientException(str(content), http_code=mano_response.status)
1111 elif action == "get" or action == "show":
1112 url = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1113 self.logger.debug("GET %s", url)
1114 mano_response = requests.get(url, headers=self.headers_req)
1115 self.logger.debug("RO response: %s", mano_response.text)
1116 content = self._parse_yaml(mano_response.text, response=True)
1117 if mano_response.status_code == 200:
1118 return content
1119 else:
1120 raise ROClientException(str(content), http_code=mano_response.status)
1121 elif action == "delete":
1122 url = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1123 self.logger.debug("DELETE %s", url)
1124 mano_response = requests.delete(url, headers=self.headers_req)
1125 self.logger.debug("RO response: %s", mano_response.text)
1126 content = self._parse_yaml(mano_response.text, response=True)
1127 if mano_response.status_code == 200:
1128 return content
1129 else:
1130 raise ROClientException(str(content), http_code=mano_response.status)
1131 elif action == "create":
1132 if "descriptor" in kwargs:
1133 if isinstance(kwargs["descriptor"], str):
1134 descriptor = self._parse(kwargs["descriptor"], kwargs.get("descriptor_format"))
1135 else:
1136 descriptor = kwargs["descriptor"]
1137 elif "name" in kwargs:
1138 descriptor = {item[:-1]: {"name": kwargs["name"]}}
1139 else:
1140 raise ROClientException("Missing descriptor")
1141
1142 if item[:-1] not in descriptor or len(descriptor) != 1:
1143 raise ROClientException("Descriptor must contain only one 'tenant' field")
1144 if "name" in kwargs:
1145 descriptor[item[:-1]]['name'] = kwargs["name"]
1146 if "description" in kwargs:
1147 descriptor[item[:-1]]['description'] = kwargs["description"]
1148 payload_req = yaml.safe_dump(descriptor)
1149 # print payload_req
1150 url = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1151 self.logger.debug("RO POST %s %s", url, payload_req)
1152 mano_response = requests.post(url, headers=self.headers_req, data=payload_req)
1153 self.logger.debug("RO response: %s", mano_response.text)
1154 content = self._parse_yaml(mano_response.text, response=True)
1155 if mano_response.status_code == 200:
1156 return content
1157 else:
1158 raise ROClientException(str(content), http_code=mano_response.status)
1159 else:
1160 raise ROClientException("Unknown value for action '{}".format(str(action)))
1161
1162
1163 if __name__ == '__main__':
1164 RO_URL = "http://localhost:9090/openmano"
1165 TEST_TENANT = "myTenant"
1166 TEST_VIM1 = "myvim"
1167 TEST_URL1 = "https://localhost:5000/v1"
1168 TEST_TYPE1 = "openstack"
1169 TEST_CONFIG1 = {"use_floating_ip": True}
1170 TEST_VIM2 = "myvim2"
1171 TEST_URL2 = "https://localhost:5000/v2"
1172 TEST_TYPE2 = "openvim"
1173 TEST_CONFIG2 = {"config2": "config2", "config3": True}
1174
1175 streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1176 logging.basicConfig(format=streamformat)
1177 logger = logging.getLogger("ROClient")
1178
1179 tenant_id = None
1180 vim_id = False
1181 loop = asyncio.get_event_loop()
1182 myClient = ROClient(endpoint_url=RO_URL, loop=loop, loglevel="DEBUG")
1183 try:
1184 # test tenant
1185 content = loop.run_until_complete(myClient.get_list("tenant"))
1186 print("tenants", content)
1187 content = loop.run_until_complete(myClient.create("tenant", name=TEST_TENANT))
1188 tenant_id = True
1189 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1190 print("tenant", TEST_TENANT, content)
1191 content = loop.run_until_complete(myClient.edit("tenant", TEST_TENANT, description="another description"))
1192 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1193 print("tenant edited", TEST_TENANT, content)
1194 myClient["tenant"] = TEST_TENANT
1195
1196 # test VIM
1197 content = loop.run_until_complete(myClient.create("vim", name=TEST_VIM1, type=TEST_TYPE1, vim_url=TEST_URL1,
1198 config=TEST_CONFIG1))
1199 vim_id = True
1200 content = loop.run_until_complete(myClient.get_list("vim"))
1201 print("vim", content)
1202 content = loop.run_until_complete(myClient.show("vim", TEST_VIM1))
1203 print("vim", TEST_VIM1, content)
1204 content = loop.run_until_complete(myClient.edit("vim", TEST_VIM1, description="another description",
1205 name=TEST_VIM2, type=TEST_TYPE2, vim_url=TEST_URL2,
1206 config=TEST_CONFIG2))
1207 content = loop.run_until_complete(myClient.show("vim", TEST_VIM2))
1208 print("vim edited", TEST_VIM2, content)
1209
1210 # test VIM_ACCOUNT
1211 content = loop.run_until_complete(myClient.attach_datacenter(TEST_VIM2, vim_username='user',
1212 vim_password='pass', vim_tenant_name='vimtenant1',
1213 config=TEST_CONFIG1))
1214 vim_id = True
1215 content = loop.run_until_complete(myClient.get_list("vim_account"))
1216 print("vim_account", content)
1217 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1218 print("vim_account", TEST_VIM2, content)
1219 content = loop.run_until_complete(myClient.edit("vim_account", TEST_VIM2, vim_username='user2',
1220 vim_password='pass2', vim_tenant_name="vimtenant2",
1221 config=TEST_CONFIG2))
1222 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1223 print("vim_account edited", TEST_VIM2, content)
1224
1225 myClient["vim"] = TEST_VIM2
1226
1227 except Exception as e:
1228 logger.error("Error {}".format(e), exc_info=True)
1229
1230 for item in (("vim_account", TEST_VIM1), ("vim", TEST_VIM1),
1231 ("vim_account", TEST_VIM2), ("vim", TEST_VIM2),
1232 ("tenant", TEST_TENANT)):
1233 try:
1234 content = loop.run_until_complete(myClient.delete(item[0], item[1]))
1235 print("{} {} deleted; {}".format(item[0], item[1], content))
1236 except Exception as e:
1237 if e.http_code == 404:
1238 print("{} {} not present or already deleted".format(item[0], item[1]))
1239 else:
1240 logger.error("Error {}".format(e), exc_info=True)
1241
1242 loop.close()