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