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