61985270019419358e9533f8a221ee37ab701c89
[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", "port", 'ip', 'dpid', '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 net_total = 0
284 vm_total = 0
285 net_done = 0
286 vm_done = 0
287
288 def _get_ref(desc): # return an identification for the network or vm. Try vim_id if exist, if not descriptor id
289 # for net
290 if desc.get("vim_net_id"):
291 return "'vim-id={}'".format(desc["vim_net_id"])
292 elif desc.get("ns_net_osm_id"):
293 return "'nsd-vld-id={}'".format(desc["ns_net_osm_id"])
294 elif desc.get("vnf_net_osm_id"):
295 return "'vnfd-vld-id={}'".format(desc["vnf_net_osm_id"])
296 # for VM
297 elif desc.get("vim_vm_id"):
298 return "'vim-id={}'".format(desc["vim_vm_id"])
299 elif desc.get("vdu_osm_id"):
300 return "'vnfd-vdu-id={}'".format(desc["vdu_osm_id"])
301 else:
302 return ""
303
304 for net in ns_descriptor["nets"]:
305 net_total += 1
306 if net["status"] in ("ERROR", "VIM_ERROR"):
307 return "ERROR", "VIM network ({}) on error: {}".format(_get_ref(net), net["error_msg"])
308 elif net["status"] == "ACTIVE":
309 net_done += 1
310 for vnf in ns_descriptor["vnfs"]:
311 for vm in vnf["vms"]:
312 vm_total += 1
313 if vm["status"] in ("ERROR", "VIM_ERROR"):
314 return "ERROR", "VIM VM ({}) on error: {}".format(_get_ref(vm), vm["error_msg"])
315 elif vm["status"] == "ACTIVE":
316 vm_done += 1
317
318 if net_total == net_done and vm_total == vm_done:
319 return "ACTIVE", "VMs {}, networks: {}".format(vm_total, net_total)
320 else:
321 return "BUILD", "VMs: {}/{}, networks: {}/{}".format(vm_done, vm_total, net_done, net_total)
322
323 @staticmethod
324 def check_action_status(action_descriptor):
325 """
326 Inspect RO instance descriptor and indicates the status
327 :param action_descriptor: action instance descriptor obtained with self.show("ns", "action")
328 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
329 """
330 net_total = 0
331 vm_total = 0
332 net_done = 0
333 vm_done = 0
334 other_total = 0
335 other_done = 0
336
337 for vim_action_set in action_descriptor["actions"]:
338 for vim_action in vim_action_set["vim_wim_actions"]:
339 if vim_action["item"] == "instance_vms":
340 vm_total += 1
341 elif vim_action["item"] == "instance_nets":
342 net_total += 1
343 else:
344 other_total += 1
345 if vim_action["status"] == "FAILED":
346 return "ERROR", vim_action["error_msg"]
347 elif vim_action["status"] in ("DONE", "SUPERSEDED", "FINISHED"):
348 if vim_action["item"] == "instance_vms":
349 vm_done += 1
350 elif vim_action["item"] == "instance_nets":
351 net_done += 1
352 else:
353 other_done += 1
354
355 if net_total == net_done and vm_total == vm_done and other_total == other_done:
356 return "ACTIVE", "VMs {}, networks: {}, other: {} ".format(vm_total, net_total, other_total)
357 else:
358 return "BUILD", "VMs: {}/{}, networks: {}/{}, other: {}/{}".format(vm_done, vm_total, net_done, net_total,
359 other_done, other_total)
360
361 @staticmethod
362 def get_ns_vnf_info(ns_descriptor):
363 """
364 Get a dict with the VIM_id, ip_addresses, mac_addresses of every vnf and vdu
365 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
366 :return: dict with:
367 <member_vnf_index>:
368 ip_address: XXXX,
369 vdur:
370 <vdu_osm_id>:
371 ip_address: XXX
372 vim_id: XXXX
373 interfaces:
374 <name>:
375 ip_address: XXX
376 mac_address: XXX
377 """
378 ns_info = {}
379 for vnf in ns_descriptor["vnfs"]:
380 if not vnf.get("ip_address") and vnf.get("vms"):
381 raise ROClientException("ns member_vnf_index '{}' has no IP address".format(
382 vnf["member_vnf_index"]), http_code=409)
383 vnfr_info = {
384 "ip_address": vnf.get("ip_address"),
385 "vdur": {}
386 }
387 for vm in vnf["vms"]:
388 vdur = {
389 "vim_id": vm.get("vim_vm_id"),
390 "ip_address": vm.get("ip_address"),
391 "interfaces": {}
392 }
393 for iface in vm["interfaces"]:
394 if iface.get("type") == "mgmt" and not iface.get("ip_address"):
395 raise ROClientException("ns member_vnf_index '{}' vm '{}' management interface '{}' has no IP "
396 "address".format(vnf["member_vnf_index"], vm["vdu_osm_id"],
397 iface["external_name"]), http_code=409)
398 vdur["interfaces"][iface["internal_name"]] = {"ip_address": iface.get("ip_address"),
399 "mac_address": iface.get("mac_address"),
400 "vim_id": iface.get("vim_interface_id"),
401 }
402 vnfr_info["vdur"][vm["vdu_osm_id"]] = vdur
403 ns_info[str(vnf["member_vnf_index"])] = vnfr_info
404 return ns_info
405
406 async def _get_item_uuid(self, session, item, item_id_name, all_tenants=False):
407 if all_tenants:
408 tenant_text = "/any"
409 elif all_tenants is None:
410 tenant_text = ""
411 else:
412 if not self.tenant:
413 await self._get_tenant(session)
414 tenant_text = "/" + self.tenant
415
416 item_id = 0
417 url = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
418 if self.check_if_uuid(item_id_name):
419 item_id = item_id_name
420 url += "/" + item_id_name
421 elif item_id_name and item_id_name.startswith("'") and item_id_name.endswith("'"):
422 item_id_name = item_id_name[1:-1]
423 self.logger.debug("RO GET %s", url)
424 with aiohttp.Timeout(self.timeout_short):
425 async with session.get(url, headers=self.headers_req) as response:
426 response_text = await response.read()
427 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
428 if response.status == 404: # NOT_FOUND
429 raise ROClientException("No {} found with id '{}'".format(item[:-1], item_id_name),
430 http_code=404)
431 if response.status >= 300:
432 raise ROClientException(response_text, http_code=response.status)
433 content = self._parse_yaml(response_text, response=True)
434
435 if item_id:
436 return item_id
437 desc = content[item]
438 assert isinstance(desc, list), "_get_item_uuid get a non dict with a list inside {}".format(type(desc))
439 uuid = None
440 for i in desc:
441 if item_id_name and i["name"] != item_id_name:
442 continue
443 if uuid: # found more than one
444 raise ROClientException(
445 "Found more than one {} with name '{}'. uuid must be used".format(item, item_id_name),
446 http_code=404)
447 uuid = i["uuid"]
448 if not uuid:
449 raise ROClientException("No {} found with name '{}'".format(item[:-1], item_id_name), http_code=404)
450 return uuid
451
452 async def _get_item(self, session, item, item_id_name, extra_item=None, extra_item_id=None, all_tenants=False):
453 if all_tenants:
454 tenant_text = "/any"
455 elif all_tenants is None:
456 tenant_text = ""
457 else:
458 if not self.tenant:
459 await self._get_tenant(session)
460 tenant_text = "/" + self.tenant
461
462 if self.check_if_uuid(item_id_name):
463 uuid = item_id_name
464 else:
465 # check that exist
466 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
467
468 url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
469 if extra_item:
470 url += "/" + extra_item
471 if extra_item_id:
472 url += "/" + extra_item_id
473 self.logger.debug("GET %s", url)
474 with aiohttp.Timeout(self.timeout_short):
475 async with session.get(url, headers=self.headers_req) as response:
476 response_text = await response.read()
477 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
478 if response.status >= 300:
479 raise ROClientException(response_text, http_code=response.status)
480
481 return self._parse_yaml(response_text, response=True)
482
483 async def _get_tenant(self, session):
484 if not self.tenant:
485 self.tenant = await self._get_item_uuid(session, "tenants", self.tenant_id_name, None)
486 return self.tenant
487
488 async def _get_datacenter(self, session):
489 if not self.tenant:
490 await self._get_tenant(session)
491 if not self.datacenter:
492 self.datacenter = await self._get_item_uuid(session, "datacenters", self.datacenter_id_name, True)
493 return self.datacenter
494
495 async def _create_item(self, session, item, descriptor, item_id_name=None, action=None, all_tenants=False):
496 if all_tenants:
497 tenant_text = "/any"
498 elif all_tenants is None:
499 tenant_text = ""
500 else:
501 if not self.tenant:
502 await self._get_tenant(session)
503 tenant_text = "/" + self.tenant
504 payload_req = yaml.safe_dump(descriptor)
505 # print payload_req
506
507 api_version_text = ""
508 if item == "vnfs":
509 # assumes version v3 only
510 api_version_text = "/v3"
511 item = "vnfd"
512 elif item == "scenarios":
513 # assumes version v3 only
514 api_version_text = "/v3"
515 item = "nsd"
516
517 if not item_id_name:
518 uuid = ""
519 elif self.check_if_uuid(item_id_name):
520 uuid = "/{}".format(item_id_name)
521 else:
522 # check that exist
523 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants)
524 uuid = "/{}".format(uuid)
525 if not action:
526 action = ""
527 else:
528 action = "/{}".format(action)
529
530 url = "{}{apiver}{tenant}/{item}{id}{action}".format(self.endpoint_url, apiver=api_version_text,
531 tenant=tenant_text, item=item, id=uuid, action=action)
532 self.logger.debug("RO POST %s %s", url, payload_req)
533 with aiohttp.Timeout(self.timeout_large):
534 async with session.post(url, headers=self.headers_req, data=payload_req) as response:
535 response_text = await response.read()
536 self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
537 if response.status >= 300:
538 raise ROClientException(response_text, http_code=response.status)
539
540 return self._parse_yaml(response_text, response=True)
541
542 async def _del_item(self, session, item, item_id_name, all_tenants=False):
543 if all_tenants:
544 tenant_text = "/any"
545 elif all_tenants is None:
546 tenant_text = ""
547 else:
548 if not self.tenant:
549 await self._get_tenant(session)
550 tenant_text = "/" + self.tenant
551 if not self.check_if_uuid(item_id_name):
552 # check that exist
553 _all_tenants = all_tenants
554 if item in ("datacenters", 'wims'):
555 _all_tenants = True
556 uuid = await self._get_item_uuid(session, item, item_id_name, all_tenants=_all_tenants)
557 else:
558 uuid = item_id_name
559
560 url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, uuid)
561 self.logger.debug("DELETE %s", url)
562 with aiohttp.Timeout(self.timeout_short):
563 async with session.delete(url, headers=self.headers_req) as response:
564 response_text = await response.read()
565 self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
566 if response.status >= 300:
567 raise ROClientException(response_text, http_code=response.status)
568 return self._parse_yaml(response_text, response=True)
569
570 async def _list_item(self, session, item, all_tenants=False, filter_dict=None):
571 if all_tenants:
572 tenant_text = "/any"
573 elif all_tenants is None:
574 tenant_text = ""
575 else:
576 if not self.tenant:
577 await self._get_tenant(session)
578 tenant_text = "/" + self.tenant
579
580 url = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
581 separator = "?"
582 if filter_dict:
583 for k in filter_dict:
584 url += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
585 separator = "&"
586 self.logger.debug("RO GET %s", url)
587 with aiohttp.Timeout(self.timeout_short):
588 async with session.get(url, headers=self.headers_req) as response:
589 response_text = await response.read()
590 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
591 if response.status >= 300:
592 raise ROClientException(response_text, http_code=response.status)
593 return self._parse_yaml(response_text, response=True)
594
595 async def _edit_item(self, session, item, item_id, descriptor, all_tenants=False):
596 if all_tenants:
597 tenant_text = "/any"
598 elif all_tenants is None:
599 tenant_text = ""
600 else:
601 if not self.tenant:
602 await self._get_tenant(session)
603 tenant_text = "/" + self.tenant
604
605 payload_req = yaml.safe_dump(descriptor)
606
607 # print payload_req
608 url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, item_id)
609 self.logger.debug("RO PUT %s %s", url, payload_req)
610 with aiohttp.Timeout(self.timeout_large):
611 async with session.put(url, headers=self.headers_req, data=payload_req) as response:
612 response_text = await response.read()
613 self.logger.debug("PUT {} [{}] {}".format(url, response.status, response_text[:100]))
614 if response.status >= 300:
615 raise ROClientException(response_text, http_code=response.status)
616 return self._parse_yaml(response_text, response=True)
617
618 async def get_version(self):
619 """
620 Obtain RO server version.
621 :return: a list with integers ["major", "minor", "release"]. Raises ROClientException on Error,
622 """
623 try:
624 with aiohttp.ClientSession(loop=self.loop) as session:
625 url = "{}/version".format(self.endpoint_url)
626 self.logger.debug("RO GET %s", url)
627 with aiohttp.Timeout(self.timeout_short):
628 async with session.get(url, headers=self.headers_req) as response:
629 response_text = await response.read()
630 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
631 if response.status >= 300:
632 raise ROClientException(response_text, http_code=response.status)
633 for word in str(response_text).split(" "):
634 if "." in word:
635 version_text, _, _ = word.partition("-")
636 return version_text
637 raise ROClientException("Got invalid version text: '{}'".format(response_text), http_code=500)
638 except aiohttp.errors.ClientOSError as e:
639 raise ROClientException(e, http_code=504)
640 except asyncio.TimeoutError:
641 raise ROClientException("Timeout", http_code=504)
642 except Exception as e:
643 raise ROClientException("Got invalid version text: '{}'; causing exception {}".format(response_text, e),
644 http_code=500)
645
646 async def get_list(self, item, all_tenants=False, filter_by=None):
647 """
648 Obtain a list of items filtering by the specigy filter_by.
649 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
650 :param all_tenants: True if not filtering by tenant. Only allowed for admin
651 :param filter_by: dictionary with filtering
652 :return: a list of dict. It can be empty. Raises ROClientException on Error,
653 """
654 try:
655 if item not in self.client_to_RO:
656 raise ROClientException("Invalid item {}".format(item))
657 if item == 'tenant':
658 all_tenants = None
659 with aiohttp.ClientSession(loop=self.loop) as session:
660 content = await self._list_item(session, self.client_to_RO[item], all_tenants=all_tenants,
661 filter_dict=filter_by)
662 if isinstance(content, dict):
663 if len(content) == 1:
664 for _, v in content.items():
665 return v
666 return content.values()[0]
667 else:
668 raise ROClientException("Output not a list neither dict with len equal 1", http_code=500)
669 return content
670 except aiohttp.errors.ClientOSError as e:
671 raise ROClientException(e, http_code=504)
672 except asyncio.TimeoutError:
673 raise ROClientException("Timeout", http_code=504)
674
675 async def show(self, item, item_id_name=None, extra_item=None, extra_item_id=None, all_tenants=False):
676 """
677 Obtain the information of an item from its id or name
678 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
679 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
680 :param extra_item: if supplied, it is used to add to the URL.
681 Can be 'action' if item='ns'; 'networks' or'images' if item='vim'
682 :param extra_item_id: if supplied, it is used get details of a concrete extra_item.
683 :param all_tenants: True if not filtering by tenant. Only allowed for admin
684 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
685 """
686 try:
687 if item not in self.client_to_RO:
688 raise ROClientException("Invalid item {}".format(item))
689 if item == 'tenant':
690 all_tenants = None
691 elif item == 'vim':
692 all_tenants = True
693 elif item == 'vim_account':
694 all_tenants = False
695
696 with aiohttp.ClientSession(loop=self.loop) as session:
697 content = await self._get_item(session, self.client_to_RO[item], item_id_name, extra_item=extra_item,
698 extra_item_id=extra_item_id, all_tenants=all_tenants)
699 return remove_envelop(item, content)
700 except aiohttp.errors.ClientOSError as e:
701 raise ROClientException(e, http_code=504)
702 except asyncio.TimeoutError:
703 raise ROClientException("Timeout", http_code=504)
704
705 async def delete(self, item, item_id_name=None, all_tenants=False):
706 """
707 Delete the information of an item from its id or name
708 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
709 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
710 :param all_tenants: True if not filtering by tenant. Only allowed for admin
711 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
712 """
713 try:
714 if item not in self.client_to_RO:
715 raise ROClientException("Invalid item {}".format(item))
716 if item in ('tenant', 'vim', 'wim'):
717 all_tenants = None
718
719 with aiohttp.ClientSession(loop=self.loop) as session:
720 result = await self._del_item(session, self.client_to_RO[item], item_id_name, all_tenants=all_tenants)
721 # in case of ns delete, get the action_id embeded in text
722 if item == "ns" and result.get("result"):
723 _, _, action_id = result["result"].partition("action_id=")
724 action_id, _, _ = action_id.partition(" ")
725 if action_id:
726 result["action_id"] = action_id
727 return result
728 except aiohttp.errors.ClientOSError as e:
729 raise ROClientException(e, http_code=504)
730 except asyncio.TimeoutError:
731 raise ROClientException("Timeout", http_code=504)
732
733 async def edit(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
734 """ Edit an item
735 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
736 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
737 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
738 :param descriptor_format: Can be 'json' or 'yaml'
739 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
740 keys can be a dot separated list to specify elements inside dict
741 :return: dictionary with the information or raises ROClientException on Error
742 """
743 try:
744 if isinstance(descriptor, str):
745 descriptor = self._parse(descriptor, descriptor_format)
746 elif descriptor:
747 pass
748 else:
749 descriptor = {}
750
751 if item not in self.client_to_RO:
752 raise ROClientException("Invalid item {}".format(item))
753 desc = remove_envelop(item, descriptor)
754
755 # Override descriptor with kwargs
756 if kwargs:
757 desc = self.update_descriptor(desc, kwargs)
758 all_tenants = False
759 if item in ('tenant', 'vim'):
760 all_tenants = None
761
762 create_desc = self._create_envelop(item, desc)
763
764 with aiohttp.ClientSession(loop=self.loop) as session:
765 _all_tenants = all_tenants
766 if item == 'vim':
767 _all_tenants = True
768 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
769 all_tenants=_all_tenants)
770 if item == 'vim':
771 _all_tenants = None
772 # await self._get_tenant(session)
773 outdata = await self._edit_item(session, self.client_to_RO[item], item_id, create_desc,
774 all_tenants=_all_tenants)
775 return remove_envelop(item, outdata)
776 except aiohttp.errors.ClientOSError as e:
777 raise ROClientException(e, http_code=504)
778 except asyncio.TimeoutError:
779 raise ROClientException("Timeout", http_code=504)
780
781 async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
782 """
783 Creates an item from its descriptor
784 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
785 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
786 :param descriptor_format: Can be 'json' or 'yaml'
787 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
788 keys can be a dot separated list to specify elements inside dict
789 :return: dictionary with the information or raises ROClientException on Error
790 """
791 try:
792 if isinstance(descriptor, str):
793 descriptor = self._parse(descriptor, descriptor_format)
794 elif descriptor:
795 pass
796 else:
797 descriptor = {}
798
799 if item not in self.client_to_RO:
800 raise ROClientException("Invalid item {}".format(item))
801 desc = remove_envelop(item, descriptor)
802
803 # Override descriptor with kwargs
804 if kwargs:
805 desc = self.update_descriptor(desc, kwargs)
806
807 for mandatory in self.mandatory_for_create[item]:
808 if mandatory not in desc:
809 raise ROClientException("'{}' is mandatory parameter for {}".format(mandatory, item))
810
811 all_tenants = False
812 if item in ('tenant', 'vim', 'wim'):
813 all_tenants = None
814
815 create_desc = self._create_envelop(item, desc)
816
817 with aiohttp.ClientSession(loop=self.loop) as session:
818 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
819 all_tenants=all_tenants)
820 return remove_envelop(item, outdata)
821 except aiohttp.errors.ClientOSError as e:
822 raise ROClientException(e, http_code=504)
823 except asyncio.TimeoutError:
824 raise ROClientException("Timeout", http_code=504)
825
826 async def create_action(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
827 """
828 Performs an action over an item
829 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
830 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
831 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
832 :param descriptor_format: Can be 'json' or 'yaml'
833 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
834 keys can be a dot separated list to specify elements inside dict
835 :return: dictionary with the information or raises ROClientException on Error
836 """
837 try:
838 if isinstance(descriptor, str):
839 descriptor = self._parse(descriptor, descriptor_format)
840 elif descriptor:
841 pass
842 else:
843 descriptor = {}
844
845 if item not in self.client_to_RO:
846 raise ROClientException("Invalid item {}".format(item))
847 desc = remove_envelop(item, descriptor)
848
849 # Override descriptor with kwargs
850 if kwargs:
851 desc = self.update_descriptor(desc, kwargs)
852
853 all_tenants = False
854 if item in ('tenant', 'vim'):
855 all_tenants = None
856
857 action = None
858 if item == "vims":
859 action = "sdn_mapping"
860 elif item in ("vim_account", "ns"):
861 action = "action"
862
863 # create_desc = self._create_envelop(item, desc)
864 create_desc = desc
865
866 with aiohttp.ClientSession(loop=self.loop) as session:
867 _all_tenants = all_tenants
868 if item == 'vim':
869 _all_tenants = True
870 # item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
871 # all_tenants=_all_tenants)
872 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
873 item_id_name=item_id_name, # item_id_name=item_id
874 action=action, all_tenants=_all_tenants)
875 return remove_envelop(item, outdata)
876 except aiohttp.errors.ClientOSError as e:
877 raise ROClientException(e, http_code=504)
878 except asyncio.TimeoutError:
879 raise ROClientException("Timeout", http_code=504)
880
881 async def attach(self, item, item_id_name=None, descriptor=None, descriptor_format=None, **kwargs):
882 """
883 Attach a datacenter or wim to a tenant, creating a vim_account, wim_account
884 :param item: can be vim_account or wim_account
885 :param item_id_name: id or name of the datacenter, wim
886 :param descriptor:
887 :param descriptor_format:
888 :param kwargs:
889 :return:
890 """
891 try:
892 if isinstance(descriptor, str):
893 descriptor = self._parse(descriptor, descriptor_format)
894 elif descriptor:
895 pass
896 else:
897 descriptor = {}
898
899 desc = remove_envelop(item, descriptor)
900
901 # # check that exist
902 # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
903 # tenant_text = "/" + self._get_tenant()
904 if kwargs:
905 desc = self.update_descriptor(desc, kwargs)
906
907 if item == "vim_account":
908 if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
909 raise ROClientException("Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
910 "provided")
911 elif item != "wim_account":
912 raise ROClientException("Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".
913 format(item))
914 create_desc = self._create_envelop(item, desc)
915 payload_req = yaml.safe_dump(create_desc)
916 with aiohttp.ClientSession(loop=self.loop) as session:
917 # check that exist
918 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=True)
919 await self._get_tenant(session)
920
921 url = "{}/{tenant}/{item}/{item_id}".format(self.endpoint_url, tenant=self.tenant,
922 item=self.client_to_RO[item], item_id=item_id)
923 self.logger.debug("RO POST %s %s", url, payload_req)
924 with aiohttp.Timeout(self.timeout_large):
925 async with session.post(url, headers=self.headers_req, data=payload_req) as response:
926 response_text = await response.read()
927 self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
928 if response.status >= 300:
929 raise ROClientException(response_text, http_code=response.status)
930
931 response_desc = self._parse_yaml(response_text, response=True)
932 desc = remove_envelop(item, response_desc)
933 return desc
934 except aiohttp.errors.ClientOSError as e:
935 raise ROClientException(e, http_code=504)
936 except asyncio.TimeoutError:
937 raise ROClientException("Timeout", http_code=504)
938
939 async def detach(self, item, item_id_name=None):
940 # TODO replace the code with delete_item(vim_account,...)
941 try:
942 with aiohttp.ClientSession(loop=self.loop) as session:
943 # check that exist
944 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=False)
945 tenant = await self._get_tenant(session)
946
947 url = "{}/{tenant}/{item}/{datacenter}".format(self.endpoint_url, tenant=tenant,
948 item=self.client_to_RO[item], datacenter=item_id)
949 self.logger.debug("RO DELETE %s", url)
950 with aiohttp.Timeout(self.timeout_large):
951 async with session.delete(url, headers=self.headers_req) as response:
952 response_text = await response.read()
953 self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
954 if response.status >= 300:
955 raise ROClientException(response_text, http_code=response.status)
956
957 response_desc = self._parse_yaml(response_text, response=True)
958 desc = remove_envelop(item, response_desc)
959 return desc
960 except aiohttp.errors.ClientOSError as e:
961 raise ROClientException(e, http_code=504)
962 except asyncio.TimeoutError:
963 raise ROClientException("Timeout", http_code=504)
964
965 # TODO convert to asyncio
966 # DATACENTERS
967
968 def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False,
969 **kwargs):
970 """Edit the parameters of a datacenter
971 Params: must supply a descriptor or/and a parameter to change
972 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
973 descriptor: with format {'datacenter':{params to change info}}
974 must be a dictionary or a json/yaml text.
975 parameters to change can be supplyied by the descriptor or as parameters:
976 new_name: the datacenter name
977 vim_url: the datacenter URL
978 vim_url_admin: the datacenter URL for administrative issues
979 vim_type: the datacenter type, can be openstack or openvim.
980 public: boolean, available to other tenants
981 description: datacenter description
982 Return: Raises an exception on error, not found or found several
983 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
984 """
985
986 if isinstance(descriptor, str):
987 descriptor = self.parse(descriptor, descriptor_format)
988 elif descriptor:
989 pass
990 elif kwargs:
991 descriptor = {"datacenter": {}}
992 else:
993 raise ROClientException("Missing descriptor")
994
995 if 'datacenter' not in descriptor or len(descriptor) != 1:
996 raise ROClientException("Descriptor must contain only one 'datacenter' field")
997 for param in kwargs:
998 if param == 'new_name':
999 descriptor['datacenter']['name'] = kwargs[param]
1000 else:
1001 descriptor['datacenter'][param] = kwargs[param]
1002 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
1003
1004 def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
1005 """Edit the parameters of a scenario
1006 Params: must supply a descriptor or/and a parameters to change
1007 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1008 descriptor: with format {'scenario':{params to change info}}
1009 must be a dictionary or a json/yaml text.
1010 parameters to change can be supplyied by the descriptor or as parameters:
1011 new_name: the scenario name
1012 public: boolean, available to other tenants
1013 description: scenario description
1014 tenant_id. Propietary tenant
1015 Return: Raises an exception on error, not found or found several
1016 Obtain a dictionary with format {'scenario':{new_scenario_info}}
1017 """
1018
1019 if isinstance(descriptor, str):
1020 descriptor = self.parse(descriptor, descriptor_format)
1021 elif descriptor:
1022 pass
1023 elif kwargs:
1024 descriptor = {"scenario": {}}
1025 else:
1026 raise ROClientException("Missing descriptor")
1027
1028 if 'scenario' not in descriptor or len(descriptor) > 2:
1029 raise ROClientException("Descriptor must contain only one 'scenario' field")
1030 for param in kwargs:
1031 if param == 'new_name':
1032 descriptor['scenario']['name'] = kwargs[param]
1033 else:
1034 descriptor['scenario'][param] = kwargs[param]
1035 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
1036
1037 # VIM ACTIONS
1038 def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
1039 """Perform an action over a vim
1040 Params:
1041 action: can be 'list', 'get'/'show', 'delete' or 'create'
1042 item: can be 'tenants' or 'networks'
1043 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
1044 other parameters:
1045 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
1046 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
1047 must be a dictionary or a json/yaml text.
1048 name: for created tenant/net Overwrite descriptor name if any
1049 description: tenant descriptor. Overwrite descriptor description if any
1050
1051 Return: Raises an exception on error
1052 Obtain a dictionary with format {'tenant':{new_tenant_info}}
1053 """
1054 session = None # TODO remove when changed to asyncio
1055 if item not in ("tenants", "networks", "images"):
1056 raise ROClientException("Unknown value for item '{}', must be 'tenants', 'nets' or "
1057 "images".format(str(item)))
1058
1059 image_actions = ['list', 'get', 'show', 'delete']
1060 if item == "images" and action not in image_actions:
1061 raise ROClientException("Only available actions for item '{}' are {}\n"
1062 "Requested action was '{}'".format(item, ', '.join(image_actions), action))
1063 if all_tenants:
1064 tenant_text = "/any"
1065 else:
1066 tenant_text = "/" + self._get_tenant()
1067
1068 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
1069 datacenter = self._get_item_uuid(session, "datacenters", kwargs.get("datacenter"), all_tenants=all_tenants)
1070 else:
1071 datacenter = self.get_datacenter(session)
1072
1073 if action == "list":
1074 url = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1075 self.logger.debug("GET %s", url)
1076 mano_response = requests.get(url, headers=self.headers_req)
1077 self.logger.debug("RO response: %s", mano_response.text)
1078 content = self._parse_yaml(mano_response.text, response=True)
1079 if mano_response.status_code == 200:
1080 return content
1081 else:
1082 raise ROClientException(str(content), http_code=mano_response.status)
1083 elif action == "get" or action == "show":
1084 url = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1085 self.logger.debug("GET %s", url)
1086 mano_response = requests.get(url, headers=self.headers_req)
1087 self.logger.debug("RO response: %s", mano_response.text)
1088 content = self._parse_yaml(mano_response.text, response=True)
1089 if mano_response.status_code == 200:
1090 return content
1091 else:
1092 raise ROClientException(str(content), http_code=mano_response.status)
1093 elif action == "delete":
1094 url = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1095 self.logger.debug("DELETE %s", url)
1096 mano_response = requests.delete(url, headers=self.headers_req)
1097 self.logger.debug("RO response: %s", mano_response.text)
1098 content = self._parse_yaml(mano_response.text, response=True)
1099 if mano_response.status_code == 200:
1100 return content
1101 else:
1102 raise ROClientException(str(content), http_code=mano_response.status)
1103 elif action == "create":
1104 if "descriptor" in kwargs:
1105 if isinstance(kwargs["descriptor"], str):
1106 descriptor = self._parse(kwargs["descriptor"], kwargs.get("descriptor_format"))
1107 else:
1108 descriptor = kwargs["descriptor"]
1109 elif "name" in kwargs:
1110 descriptor = {item[:-1]: {"name": kwargs["name"]}}
1111 else:
1112 raise ROClientException("Missing descriptor")
1113
1114 if item[:-1] not in descriptor or len(descriptor) != 1:
1115 raise ROClientException("Descriptor must contain only one 'tenant' field")
1116 if "name" in kwargs:
1117 descriptor[item[:-1]]['name'] = kwargs["name"]
1118 if "description" in kwargs:
1119 descriptor[item[:-1]]['description'] = kwargs["description"]
1120 payload_req = yaml.safe_dump(descriptor)
1121 # print payload_req
1122 url = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1123 self.logger.debug("RO POST %s %s", url, payload_req)
1124 mano_response = requests.post(url, headers=self.headers_req, data=payload_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 else:
1132 raise ROClientException("Unknown value for action '{}".format(str(action)))
1133
1134
1135 if __name__ == '__main__':
1136 RO_URL = "http://localhost:9090/openmano"
1137 TEST_TENANT = "myTenant"
1138 TEST_VIM1 = "myvim"
1139 TEST_URL1 = "https://localhost:5000/v1"
1140 TEST_TYPE1 = "openstack"
1141 TEST_CONFIG1 = {"use_floating_ip": True}
1142 TEST_VIM2 = "myvim2"
1143 TEST_URL2 = "https://localhost:5000/v2"
1144 TEST_TYPE2 = "openvim"
1145 TEST_CONFIG2 = {"config2": "config2", "config3": True}
1146
1147 streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1148 logging.basicConfig(format=streamformat)
1149 logger = logging.getLogger("ROClient")
1150
1151 tenant_id = None
1152 vim_id = False
1153 loop = asyncio.get_event_loop()
1154 myClient = ROClient(endpoint_url=RO_URL, loop=loop, loglevel="DEBUG")
1155 try:
1156 # test tenant
1157 content = loop.run_until_complete(myClient.get_list("tenant"))
1158 print("tenants", content)
1159 content = loop.run_until_complete(myClient.create("tenant", name=TEST_TENANT))
1160 tenant_id = True
1161 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1162 print("tenant", TEST_TENANT, content)
1163 content = loop.run_until_complete(myClient.edit("tenant", TEST_TENANT, description="another description"))
1164 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1165 print("tenant edited", TEST_TENANT, content)
1166 myClient["tenant"] = TEST_TENANT
1167
1168 # test VIM
1169 content = loop.run_until_complete(myClient.create("vim", name=TEST_VIM1, type=TEST_TYPE1, vim_url=TEST_URL1,
1170 config=TEST_CONFIG1))
1171 vim_id = True
1172 content = loop.run_until_complete(myClient.get_list("vim"))
1173 print("vim", content)
1174 content = loop.run_until_complete(myClient.show("vim", TEST_VIM1))
1175 print("vim", TEST_VIM1, content)
1176 content = loop.run_until_complete(myClient.edit("vim", TEST_VIM1, description="another description",
1177 name=TEST_VIM2, type=TEST_TYPE2, vim_url=TEST_URL2,
1178 config=TEST_CONFIG2))
1179 content = loop.run_until_complete(myClient.show("vim", TEST_VIM2))
1180 print("vim edited", TEST_VIM2, content)
1181
1182 # test VIM_ACCOUNT
1183 content = loop.run_until_complete(myClient.attach_datacenter(TEST_VIM2, vim_username='user',
1184 vim_password='pass', vim_tenant_name='vimtenant1',
1185 config=TEST_CONFIG1))
1186 vim_id = True
1187 content = loop.run_until_complete(myClient.get_list("vim_account"))
1188 print("vim_account", content)
1189 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1190 print("vim_account", TEST_VIM2, content)
1191 content = loop.run_until_complete(myClient.edit("vim_account", TEST_VIM2, vim_username='user2',
1192 vim_password='pass2', vim_tenant_name="vimtenant2",
1193 config=TEST_CONFIG2))
1194 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1195 print("vim_account edited", TEST_VIM2, content)
1196
1197 myClient["vim"] = TEST_VIM2
1198
1199 except Exception as e:
1200 logger.error("Error {}".format(e), exc_info=True)
1201
1202 for item in (("vim_account", TEST_VIM1), ("vim", TEST_VIM1),
1203 ("vim_account", TEST_VIM2), ("vim", TEST_VIM2),
1204 ("tenant", TEST_TENANT)):
1205 try:
1206 content = loop.run_until_complete(myClient.delete(item[0], item[1]))
1207 print("{} {} deleted; {}".format(item[0], item[1], content))
1208 except Exception as e:
1209 if e.http_code == 404:
1210 print("{} {} not present or already deleted".format(item[0], item[1]))
1211 else:
1212 logger.error("Error {}".format(e), exc_info=True)
1213
1214 loop.close()