54dbbf1f00e9d8100cf40024512b811dd06835d1
[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 with aiohttp.Timeout(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 with aiohttp.Timeout(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 with aiohttp.Timeout(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 with aiohttp.Timeout(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 return self._parse_yaml(response_text, response=True)
589
590 async def _list_item(self, session, item, all_tenants=False, filter_dict=None):
591 if all_tenants:
592 tenant_text = "/any"
593 elif all_tenants is None:
594 tenant_text = ""
595 else:
596 if not self.tenant:
597 await self._get_tenant(session)
598 tenant_text = "/" + self.tenant
599
600 url = "{}{}/{}".format(self.endpoint_url, tenant_text, item)
601 separator = "?"
602 if filter_dict:
603 for k in filter_dict:
604 url += separator + quote(str(k)) + "=" + quote(str(filter_dict[k]))
605 separator = "&"
606 self.logger.debug("RO GET %s", url)
607 with aiohttp.Timeout(self.timeout_short):
608 async with session.get(url, headers=self.headers_req) as response:
609 response_text = await response.read()
610 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
611 if response.status >= 300:
612 raise ROClientException(response_text, http_code=response.status)
613 return self._parse_yaml(response_text, response=True)
614
615 async def _edit_item(self, session, item, item_id, descriptor, all_tenants=False):
616 if all_tenants:
617 tenant_text = "/any"
618 elif all_tenants is None:
619 tenant_text = ""
620 else:
621 if not self.tenant:
622 await self._get_tenant(session)
623 tenant_text = "/" + self.tenant
624
625 payload_req = yaml.safe_dump(descriptor)
626
627 # print payload_req
628 url = "{}{}/{}/{}".format(self.endpoint_url, tenant_text, item, item_id)
629 self.logger.debug("RO PUT %s %s", url, payload_req)
630 with aiohttp.Timeout(self.timeout_large):
631 async with session.put(url, headers=self.headers_req, data=payload_req) as response:
632 response_text = await response.read()
633 self.logger.debug("PUT {} [{}] {}".format(url, response.status, response_text[:100]))
634 if response.status >= 300:
635 raise ROClientException(response_text, http_code=response.status)
636 return self._parse_yaml(response_text, response=True)
637
638 async def get_version(self):
639 """
640 Obtain RO server version.
641 :return: a list with integers ["major", "minor", "release"]. Raises ROClientException on Error,
642 """
643 try:
644 with aiohttp.ClientSession(loop=self.loop) as session:
645 url = "{}/version".format(self.endpoint_url)
646 self.logger.debug("RO GET %s", url)
647 with aiohttp.Timeout(self.timeout_short):
648 async with session.get(url, headers=self.headers_req) as response:
649 response_text = await response.read()
650 self.logger.debug("GET {} [{}] {}".format(url, response.status, response_text[:100]))
651 if response.status >= 300:
652 raise ROClientException(response_text, http_code=response.status)
653 for word in str(response_text).split(" "):
654 if "." in word:
655 version_text, _, _ = word.partition("-")
656 return version_text
657 raise ROClientException("Got invalid version text: '{}'".format(response_text), http_code=500)
658 except aiohttp.errors.ClientOSError as e:
659 raise ROClientException(e, http_code=504)
660 except asyncio.TimeoutError:
661 raise ROClientException("Timeout", http_code=504)
662 except Exception as e:
663 raise ROClientException("Got invalid version text: '{}'; causing exception {}".format(response_text, e),
664 http_code=500)
665
666 async def get_list(self, item, all_tenants=False, filter_by=None):
667 """
668 Obtain a list of items filtering by the specigy filter_by.
669 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
670 :param all_tenants: True if not filtering by tenant. Only allowed for admin
671 :param filter_by: dictionary with filtering
672 :return: a list of dict. It can be empty. Raises ROClientException on Error,
673 """
674 try:
675 if item not in self.client_to_RO:
676 raise ROClientException("Invalid item {}".format(item))
677 if item == 'tenant':
678 all_tenants = None
679 with aiohttp.ClientSession(loop=self.loop) as session:
680 content = await self._list_item(session, self.client_to_RO[item], all_tenants=all_tenants,
681 filter_dict=filter_by)
682 if isinstance(content, dict):
683 if len(content) == 1:
684 for _, v in content.items():
685 return v
686 return content.values()[0]
687 else:
688 raise ROClientException("Output not a list neither dict with len equal 1", http_code=500)
689 return content
690 except aiohttp.errors.ClientOSError as e:
691 raise ROClientException(e, http_code=504)
692 except asyncio.TimeoutError:
693 raise ROClientException("Timeout", http_code=504)
694
695 async def show(self, item, item_id_name=None, extra_item=None, extra_item_id=None, all_tenants=False):
696 """
697 Obtain the information of an item from its id or name
698 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
699 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
700 :param extra_item: if supplied, it is used to add to the URL.
701 Can be 'action' if item='ns'; 'networks' or'images' if item='vim'
702 :param extra_item_id: if supplied, it is used get details of a concrete extra_item.
703 :param all_tenants: True if not filtering by tenant. Only allowed for admin
704 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
705 """
706 try:
707 if item not in self.client_to_RO:
708 raise ROClientException("Invalid item {}".format(item))
709 if item == 'tenant':
710 all_tenants = None
711 elif item == 'vim':
712 all_tenants = True
713 elif item == 'vim_account':
714 all_tenants = False
715
716 with aiohttp.ClientSession(loop=self.loop) as session:
717 content = await self._get_item(session, self.client_to_RO[item], item_id_name, extra_item=extra_item,
718 extra_item_id=extra_item_id, all_tenants=all_tenants)
719 return remove_envelop(item, content)
720 except aiohttp.errors.ClientOSError as e:
721 raise ROClientException(e, http_code=504)
722 except asyncio.TimeoutError:
723 raise ROClientException("Timeout", http_code=504)
724
725 async def delete(self, item, item_id_name=None, all_tenants=False):
726 """
727 Delete the information of an item from its id or name
728 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
729 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
730 :param all_tenants: True if not filtering by tenant. Only allowed for admin
731 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
732 """
733 try:
734 if item not in self.client_to_RO:
735 raise ROClientException("Invalid item {}".format(item))
736 if item in ('tenant', 'vim', 'wim'):
737 all_tenants = None
738
739 with aiohttp.ClientSession(loop=self.loop) as session:
740 result = await self._del_item(session, self.client_to_RO[item], item_id_name, all_tenants=all_tenants)
741 # in case of ns delete, get the action_id embeded in text
742 if item == "ns" and result.get("result"):
743 _, _, action_id = result["result"].partition("action_id=")
744 action_id, _, _ = action_id.partition(" ")
745 if action_id:
746 result["action_id"] = action_id
747 return result
748 except aiohttp.errors.ClientOSError as e:
749 raise ROClientException(e, http_code=504)
750 except asyncio.TimeoutError:
751 raise ROClientException("Timeout", http_code=504)
752
753 async def edit(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
754 """ Edit an item
755 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns', 'vim'
756 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
757 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
758 :param descriptor_format: Can be 'json' or 'yaml'
759 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
760 keys can be a dot separated list to specify elements inside dict
761 :return: dictionary with the information or raises ROClientException on Error
762 """
763 try:
764 if isinstance(descriptor, str):
765 descriptor = self._parse(descriptor, descriptor_format)
766 elif descriptor:
767 pass
768 else:
769 descriptor = {}
770
771 if item not in self.client_to_RO:
772 raise ROClientException("Invalid item {}".format(item))
773 desc = remove_envelop(item, descriptor)
774
775 # Override descriptor with kwargs
776 if kwargs:
777 desc = self.update_descriptor(desc, kwargs)
778 all_tenants = False
779 if item in ('tenant', 'vim'):
780 all_tenants = None
781
782 create_desc = self._create_envelop(item, desc)
783
784 with aiohttp.ClientSession(loop=self.loop) as session:
785 _all_tenants = all_tenants
786 if item == 'vim':
787 _all_tenants = True
788 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
789 all_tenants=_all_tenants)
790 if item == 'vim':
791 _all_tenants = None
792 # await self._get_tenant(session)
793 outdata = await self._edit_item(session, self.client_to_RO[item], item_id, create_desc,
794 all_tenants=_all_tenants)
795 return remove_envelop(item, outdata)
796 except aiohttp.errors.ClientOSError as e:
797 raise ROClientException(e, http_code=504)
798 except asyncio.TimeoutError:
799 raise ROClientException("Timeout", http_code=504)
800
801 async def create(self, item, descriptor=None, descriptor_format=None, **kwargs):
802 """
803 Creates an item from its descriptor
804 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
805 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
806 :param descriptor_format: Can be 'json' or 'yaml'
807 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
808 keys can be a dot separated list to specify elements inside dict
809 :return: dictionary with the information or raises ROClientException on Error
810 """
811 try:
812 if isinstance(descriptor, str):
813 descriptor = self._parse(descriptor, descriptor_format)
814 elif descriptor:
815 pass
816 else:
817 descriptor = {}
818
819 if item not in self.client_to_RO:
820 raise ROClientException("Invalid item {}".format(item))
821 desc = remove_envelop(item, descriptor)
822
823 # Override descriptor with kwargs
824 if kwargs:
825 desc = self.update_descriptor(desc, kwargs)
826
827 for mandatory in self.mandatory_for_create[item]:
828 if mandatory not in desc:
829 raise ROClientException("'{}' is mandatory parameter for {}".format(mandatory, item))
830
831 all_tenants = False
832 if item in ('tenant', 'vim', 'wim'):
833 all_tenants = None
834
835 create_desc = self._create_envelop(item, desc)
836
837 with aiohttp.ClientSession(loop=self.loop) as session:
838 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
839 all_tenants=all_tenants)
840 return remove_envelop(item, outdata)
841 except aiohttp.errors.ClientOSError as e:
842 raise ROClientException(e, http_code=504)
843 except asyncio.TimeoutError:
844 raise ROClientException("Timeout", http_code=504)
845
846 async def create_action(self, item, item_id_name, descriptor=None, descriptor_format=None, **kwargs):
847 """
848 Performs an action over an item
849 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
850 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
851 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
852 :param descriptor_format: Can be 'json' or 'yaml'
853 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
854 keys can be a dot separated list to specify elements inside dict
855 :return: dictionary with the information or raises ROClientException on Error
856 """
857 try:
858 if isinstance(descriptor, str):
859 descriptor = self._parse(descriptor, descriptor_format)
860 elif descriptor:
861 pass
862 else:
863 descriptor = {}
864
865 if item not in self.client_to_RO:
866 raise ROClientException("Invalid item {}".format(item))
867 desc = remove_envelop(item, descriptor)
868
869 # Override descriptor with kwargs
870 if kwargs:
871 desc = self.update_descriptor(desc, kwargs)
872
873 all_tenants = False
874 if item in ('tenant', 'vim'):
875 all_tenants = None
876
877 action = None
878 if item == "vims":
879 action = "sdn_mapping"
880 elif item in ("vim_account", "ns"):
881 action = "action"
882
883 # create_desc = self._create_envelop(item, desc)
884 create_desc = desc
885
886 with aiohttp.ClientSession(loop=self.loop) as session:
887 _all_tenants = all_tenants
888 if item == 'vim':
889 _all_tenants = True
890 # item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name,
891 # all_tenants=_all_tenants)
892 outdata = await self._create_item(session, self.client_to_RO[item], create_desc,
893 item_id_name=item_id_name, # item_id_name=item_id
894 action=action, all_tenants=_all_tenants)
895 return remove_envelop(item, outdata)
896 except aiohttp.errors.ClientOSError as e:
897 raise ROClientException(e, http_code=504)
898 except asyncio.TimeoutError:
899 raise ROClientException("Timeout", http_code=504)
900
901 async def attach(self, item, item_id_name=None, descriptor=None, descriptor_format=None, **kwargs):
902 """
903 Attach a datacenter or wim to a tenant, creating a vim_account, wim_account
904 :param item: can be vim_account or wim_account
905 :param item_id_name: id or name of the datacenter, wim
906 :param descriptor:
907 :param descriptor_format:
908 :param kwargs:
909 :return:
910 """
911 try:
912 if isinstance(descriptor, str):
913 descriptor = self._parse(descriptor, descriptor_format)
914 elif descriptor:
915 pass
916 else:
917 descriptor = {}
918
919 desc = remove_envelop(item, descriptor)
920
921 # # check that exist
922 # uuid = self._get_item_uuid(session, "datacenters", uuid_name, all_tenants=True)
923 # tenant_text = "/" + self._get_tenant()
924 if kwargs:
925 desc = self.update_descriptor(desc, kwargs)
926
927 if item == "vim_account":
928 if not desc.get("vim_tenant_name") and not desc.get("vim_tenant_id"):
929 raise ROClientException("Wrong descriptor. At least vim_tenant_name or vim_tenant_id must be "
930 "provided")
931 elif item != "wim_account":
932 raise ROClientException("Attach with unknown item {}. Must be 'vim_account' or 'wim_account'".
933 format(item))
934 create_desc = self._create_envelop(item, desc)
935 payload_req = yaml.safe_dump(create_desc)
936 with aiohttp.ClientSession(loop=self.loop) as session:
937 # check that exist
938 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=True)
939 await self._get_tenant(session)
940
941 url = "{}/{tenant}/{item}/{item_id}".format(self.endpoint_url, tenant=self.tenant,
942 item=self.client_to_RO[item], item_id=item_id)
943 self.logger.debug("RO POST %s %s", url, payload_req)
944 with aiohttp.Timeout(self.timeout_large):
945 async with session.post(url, headers=self.headers_req, data=payload_req) as response:
946 response_text = await response.read()
947 self.logger.debug("POST {} [{}] {}".format(url, response.status, response_text[:100]))
948 if response.status >= 300:
949 raise ROClientException(response_text, http_code=response.status)
950
951 response_desc = self._parse_yaml(response_text, response=True)
952 desc = remove_envelop(item, response_desc)
953 return desc
954 except aiohttp.errors.ClientOSError as e:
955 raise ROClientException(e, http_code=504)
956 except asyncio.TimeoutError:
957 raise ROClientException("Timeout", http_code=504)
958
959 async def detach(self, item, item_id_name=None):
960 # TODO replace the code with delete_item(vim_account,...)
961 try:
962 with aiohttp.ClientSession(loop=self.loop) as session:
963 # check that exist
964 item_id = await self._get_item_uuid(session, self.client_to_RO[item], item_id_name, all_tenants=False)
965 tenant = await self._get_tenant(session)
966
967 url = "{}/{tenant}/{item}/{datacenter}".format(self.endpoint_url, tenant=tenant,
968 item=self.client_to_RO[item], datacenter=item_id)
969 self.logger.debug("RO DELETE %s", url)
970 with aiohttp.Timeout(self.timeout_large):
971 async with session.delete(url, headers=self.headers_req) as response:
972 response_text = await response.read()
973 self.logger.debug("DELETE {} [{}] {}".format(url, response.status, response_text[:100]))
974 if response.status >= 300:
975 raise ROClientException(response_text, http_code=response.status)
976
977 response_desc = self._parse_yaml(response_text, response=True)
978 desc = remove_envelop(item, response_desc)
979 return desc
980 except aiohttp.errors.ClientOSError as e:
981 raise ROClientException(e, http_code=504)
982 except asyncio.TimeoutError:
983 raise ROClientException("Timeout", http_code=504)
984
985 # TODO convert to asyncio
986 # DATACENTERS
987
988 def edit_datacenter(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False,
989 **kwargs):
990 """Edit the parameters of a datacenter
991 Params: must supply a descriptor or/and a parameter to change
992 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
993 descriptor: with format {'datacenter':{params to change info}}
994 must be a dictionary or a json/yaml text.
995 parameters to change can be supplyied by the descriptor or as parameters:
996 new_name: the datacenter name
997 vim_url: the datacenter URL
998 vim_url_admin: the datacenter URL for administrative issues
999 vim_type: the datacenter type, can be openstack or openvim.
1000 public: boolean, available to other tenants
1001 description: datacenter description
1002 Return: Raises an exception on error, not found or found several
1003 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
1004 """
1005
1006 if isinstance(descriptor, str):
1007 descriptor = self.parse(descriptor, descriptor_format)
1008 elif descriptor:
1009 pass
1010 elif kwargs:
1011 descriptor = {"datacenter": {}}
1012 else:
1013 raise ROClientException("Missing descriptor")
1014
1015 if 'datacenter' not in descriptor or len(descriptor) != 1:
1016 raise ROClientException("Descriptor must contain only one 'datacenter' field")
1017 for param in kwargs:
1018 if param == 'new_name':
1019 descriptor['datacenter']['name'] = kwargs[param]
1020 else:
1021 descriptor['datacenter'][param] = kwargs[param]
1022 return self._edit_item("datacenters", descriptor, uuid, name, all_tenants=None)
1023
1024 def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs):
1025 """Edit the parameters of a scenario
1026 Params: must supply a descriptor or/and a parameters to change
1027 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
1028 descriptor: with format {'scenario':{params to change info}}
1029 must be a dictionary or a json/yaml text.
1030 parameters to change can be supplyied by the descriptor or as parameters:
1031 new_name: the scenario name
1032 public: boolean, available to other tenants
1033 description: scenario description
1034 tenant_id. Propietary tenant
1035 Return: Raises an exception on error, not found or found several
1036 Obtain a dictionary with format {'scenario':{new_scenario_info}}
1037 """
1038
1039 if isinstance(descriptor, str):
1040 descriptor = self.parse(descriptor, descriptor_format)
1041 elif descriptor:
1042 pass
1043 elif kwargs:
1044 descriptor = {"scenario": {}}
1045 else:
1046 raise ROClientException("Missing descriptor")
1047
1048 if 'scenario' not in descriptor or len(descriptor) > 2:
1049 raise ROClientException("Descriptor must contain only one 'scenario' field")
1050 for param in kwargs:
1051 if param == 'new_name':
1052 descriptor['scenario']['name'] = kwargs[param]
1053 else:
1054 descriptor['scenario'][param] = kwargs[param]
1055 return self._edit_item("scenarios", descriptor, uuid, name, all_tenants=None)
1056
1057 # VIM ACTIONS
1058 def vim_action(self, action, item, uuid=None, all_tenants=False, **kwargs):
1059 """Perform an action over a vim
1060 Params:
1061 action: can be 'list', 'get'/'show', 'delete' or 'create'
1062 item: can be 'tenants' or 'networks'
1063 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
1064 other parameters:
1065 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
1066 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
1067 must be a dictionary or a json/yaml text.
1068 name: for created tenant/net Overwrite descriptor name if any
1069 description: tenant descriptor. Overwrite descriptor description if any
1070
1071 Return: Raises an exception on error
1072 Obtain a dictionary with format {'tenant':{new_tenant_info}}
1073 """
1074 session = None # TODO remove when changed to asyncio
1075 if item not in ("tenants", "networks", "images"):
1076 raise ROClientException("Unknown value for item '{}', must be 'tenants', 'nets' or "
1077 "images".format(str(item)))
1078
1079 image_actions = ['list', 'get', 'show', 'delete']
1080 if item == "images" and action not in image_actions:
1081 raise ROClientException("Only available actions for item '{}' are {}\n"
1082 "Requested action was '{}'".format(item, ', '.join(image_actions), action))
1083 if all_tenants:
1084 tenant_text = "/any"
1085 else:
1086 tenant_text = "/" + self._get_tenant()
1087
1088 if "datacenter_id" in kwargs or "datacenter_name" in kwargs:
1089 datacenter = self._get_item_uuid(session, "datacenters", kwargs.get("datacenter"), all_tenants=all_tenants)
1090 else:
1091 datacenter = self.get_datacenter(session)
1092
1093 if action == "list":
1094 url = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1095 self.logger.debug("GET %s", url)
1096 mano_response = requests.get(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 == "get" or action == "show":
1104 url = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1105 self.logger.debug("GET %s", url)
1106 mano_response = requests.get(url, headers=self.headers_req)
1107 self.logger.debug("RO response: %s", mano_response.text)
1108 content = self._parse_yaml(mano_response.text, response=True)
1109 if mano_response.status_code == 200:
1110 return content
1111 else:
1112 raise ROClientException(str(content), http_code=mano_response.status)
1113 elif action == "delete":
1114 url = "{}{}/vim/{}/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item, uuid)
1115 self.logger.debug("DELETE %s", url)
1116 mano_response = requests.delete(url, headers=self.headers_req)
1117 self.logger.debug("RO response: %s", mano_response.text)
1118 content = self._parse_yaml(mano_response.text, response=True)
1119 if mano_response.status_code == 200:
1120 return content
1121 else:
1122 raise ROClientException(str(content), http_code=mano_response.status)
1123 elif action == "create":
1124 if "descriptor" in kwargs:
1125 if isinstance(kwargs["descriptor"], str):
1126 descriptor = self._parse(kwargs["descriptor"], kwargs.get("descriptor_format"))
1127 else:
1128 descriptor = kwargs["descriptor"]
1129 elif "name" in kwargs:
1130 descriptor = {item[:-1]: {"name": kwargs["name"]}}
1131 else:
1132 raise ROClientException("Missing descriptor")
1133
1134 if item[:-1] not in descriptor or len(descriptor) != 1:
1135 raise ROClientException("Descriptor must contain only one 'tenant' field")
1136 if "name" in kwargs:
1137 descriptor[item[:-1]]['name'] = kwargs["name"]
1138 if "description" in kwargs:
1139 descriptor[item[:-1]]['description'] = kwargs["description"]
1140 payload_req = yaml.safe_dump(descriptor)
1141 # print payload_req
1142 url = "{}{}/vim/{}/{}".format(self.endpoint_url, tenant_text, datacenter, item)
1143 self.logger.debug("RO POST %s %s", url, payload_req)
1144 mano_response = requests.post(url, headers=self.headers_req, data=payload_req)
1145 self.logger.debug("RO response: %s", mano_response.text)
1146 content = self._parse_yaml(mano_response.text, response=True)
1147 if mano_response.status_code == 200:
1148 return content
1149 else:
1150 raise ROClientException(str(content), http_code=mano_response.status)
1151 else:
1152 raise ROClientException("Unknown value for action '{}".format(str(action)))
1153
1154
1155 if __name__ == '__main__':
1156 RO_URL = "http://localhost:9090/openmano"
1157 TEST_TENANT = "myTenant"
1158 TEST_VIM1 = "myvim"
1159 TEST_URL1 = "https://localhost:5000/v1"
1160 TEST_TYPE1 = "openstack"
1161 TEST_CONFIG1 = {"use_floating_ip": True}
1162 TEST_VIM2 = "myvim2"
1163 TEST_URL2 = "https://localhost:5000/v2"
1164 TEST_TYPE2 = "openvim"
1165 TEST_CONFIG2 = {"config2": "config2", "config3": True}
1166
1167 streamformat = "%(asctime)s %(name)s %(levelname)s: %(message)s"
1168 logging.basicConfig(format=streamformat)
1169 logger = logging.getLogger("ROClient")
1170
1171 tenant_id = None
1172 vim_id = False
1173 loop = asyncio.get_event_loop()
1174 myClient = ROClient(endpoint_url=RO_URL, loop=loop, loglevel="DEBUG")
1175 try:
1176 # test tenant
1177 content = loop.run_until_complete(myClient.get_list("tenant"))
1178 print("tenants", content)
1179 content = loop.run_until_complete(myClient.create("tenant", name=TEST_TENANT))
1180 tenant_id = True
1181 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1182 print("tenant", TEST_TENANT, content)
1183 content = loop.run_until_complete(myClient.edit("tenant", TEST_TENANT, description="another description"))
1184 content = loop.run_until_complete(myClient.show("tenant", TEST_TENANT))
1185 print("tenant edited", TEST_TENANT, content)
1186 myClient["tenant"] = TEST_TENANT
1187
1188 # test VIM
1189 content = loop.run_until_complete(myClient.create("vim", name=TEST_VIM1, type=TEST_TYPE1, vim_url=TEST_URL1,
1190 config=TEST_CONFIG1))
1191 vim_id = True
1192 content = loop.run_until_complete(myClient.get_list("vim"))
1193 print("vim", content)
1194 content = loop.run_until_complete(myClient.show("vim", TEST_VIM1))
1195 print("vim", TEST_VIM1, content)
1196 content = loop.run_until_complete(myClient.edit("vim", TEST_VIM1, description="another description",
1197 name=TEST_VIM2, type=TEST_TYPE2, vim_url=TEST_URL2,
1198 config=TEST_CONFIG2))
1199 content = loop.run_until_complete(myClient.show("vim", TEST_VIM2))
1200 print("vim edited", TEST_VIM2, content)
1201
1202 # test VIM_ACCOUNT
1203 content = loop.run_until_complete(myClient.attach_datacenter(TEST_VIM2, vim_username='user',
1204 vim_password='pass', vim_tenant_name='vimtenant1',
1205 config=TEST_CONFIG1))
1206 vim_id = True
1207 content = loop.run_until_complete(myClient.get_list("vim_account"))
1208 print("vim_account", content)
1209 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1210 print("vim_account", TEST_VIM2, content)
1211 content = loop.run_until_complete(myClient.edit("vim_account", TEST_VIM2, vim_username='user2',
1212 vim_password='pass2', vim_tenant_name="vimtenant2",
1213 config=TEST_CONFIG2))
1214 content = loop.run_until_complete(myClient.show("vim_account", TEST_VIM2))
1215 print("vim_account edited", TEST_VIM2, content)
1216
1217 myClient["vim"] = TEST_VIM2
1218
1219 except Exception as e:
1220 logger.error("Error {}".format(e), exc_info=True)
1221
1222 for item in (("vim_account", TEST_VIM1), ("vim", TEST_VIM1),
1223 ("vim_account", TEST_VIM2), ("vim", TEST_VIM2),
1224 ("tenant", TEST_TENANT)):
1225 try:
1226 content = loop.run_until_complete(myClient.delete(item[0], item[1]))
1227 print("{} {} deleted; {}".format(item[0], item[1], content))
1228 except Exception as e:
1229 if e.http_code == 404:
1230 print("{} {} not present or already deleted".format(item[0], item[1]))
1231 else:
1232 logger.error("Error {}".format(e), exc_info=True)
1233
1234 loop.close()