2 # -*- coding: utf-8 -*-
5 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
6 # This file is part of openmano
9 # Licensed under the Apache License, Version 2.0 (the "License"); you may
10 # not use this file except in compliance with the License. You may obtain
11 # a copy of the License at
13 # http://www.apache.org/licenses/LICENSE-2.0
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 # License for the specific language governing permissions and limitations
21 # For those usages not covered by the Apache License, Version 2.0 please
22 # contact with: nfvlabs@tid.es
26 asyncio RO python client to interact with RO-server
36 from urllib
.parse
import quote
38 from copy
import deepcopy
40 __author__
= "Alfonso Tierno, Pablo Montes"
41 __date__
= "$09-Jan-2018 09:09:48$"
42 __version__
= "0.1.0-r470"
43 version_date
= "Jan 2018"
46 class ROClientException(Exception):
47 def __init__(self
, message
, http_code
=400):
48 self
.http_code
= http_code
49 Exception.__init
__(self
, message
)
50 """Common Exception for all openmano client exceptions"""
53 def remove_envelop(item
, indata
=None):
55 Obtain the useful data removing the envelop. It goes through the vnfd or nsd catalog and returns the
57 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
58 :param indata: Content to be inspected
59 :return: the useful part of indata (a reference, not a new dictionay) plus boolean indicating if it was enveloped
66 if clean_indata
.get('vnfd:vnfd-catalog'):
68 clean_indata
= clean_indata
['vnfd:vnfd-catalog']
69 elif clean_indata
.get('vnfd-catalog'):
71 clean_indata
= clean_indata
['vnfd-catalog']
72 if clean_indata
.get('vnfd'):
73 if not isinstance(clean_indata
['vnfd'], list) or len(clean_indata
['vnfd']) != 1:
74 raise ROClientException("'vnfd' must be a list only one element")
75 clean_indata
= clean_indata
['vnfd'][0]
77 if clean_indata
.get('nsd:nsd-catalog'):
79 clean_indata
= clean_indata
['nsd:nsd-catalog']
80 elif clean_indata
.get('nsd-catalog'):
82 clean_indata
= clean_indata
['nsd-catalog']
83 if clean_indata
.get('nsd'):
84 if not isinstance(clean_indata
['nsd'], list) or len(clean_indata
['nsd']) != 1:
85 raise ROClientException("'nsd' must be a list only one element")
86 clean_indata
= clean_indata
['nsd'][0]
87 elif item
== "tenant":
88 if len(indata
) == 1 and "tenant" in indata
:
90 clean_indata
= indata
["tenant"]
91 elif item
== "vim" or item
== "datacenter":
92 if len(indata
) == 1 and "datacenter" in indata
:
94 clean_indata
= indata
["datacenter"]
95 elif item
== "ns" or item
== "instances":
96 if len(indata
) == 1 and "instance" in indata
:
98 clean_indata
= indata
["instance"]
100 assert False, "remove_envelop with unknown item {}".format(item
)
102 return clean_indata
, enveloped
106 headers_req
= {'Accept': 'application/yaml', 'content-type': 'application/yaml'}
107 client_to_RO
= {'tenant': 'tenants', 'vim': 'datacenters', 'vnfd': 'vnfs', 'nsd': 'scenarios',
109 mandatory_for_create
= {
110 'tenant': ("name", ),
111 'vim': ("name", "vim_url"),
112 'vnfd': ("name", "id", "connection-point", "vdu"),
113 'nsd': ("name", "id", "constituent-vnfd"),
114 'ns': ("name", "scenario", "datacenter"),
119 def __init__(self
, loop
, endpoint_url
, **kwargs
):
121 self
.endpoint_url
= endpoint_url
123 self
.username
= kwargs
.get("username")
124 self
.password
= kwargs
.get("password")
125 self
.tenant_id_name
= kwargs
.get("tenant")
127 self
.datacenter_id_name
= kwargs
.get("datacenter")
128 self
.datacenter
= None
129 logger_name
= kwargs
.get('logger_name', 'ROClient')
130 self
.logger
= logging
.getLogger(logger_name
)
131 if kwargs
.get("loglevel"):
132 self
.logger
.setLevel(kwargs
["loglevel"])
134 requests
= kwargs
.get("TODO remove")
136 def __getitem__(self
, index
):
137 if index
== 'tenant':
138 return self
.tenant_id_name
139 elif index
== 'datacenter':
140 return self
.datacenter_id_name
141 elif index
== 'username':
143 elif index
== 'password':
145 elif index
== 'endpoint_url':
146 return self
.endpoint_url
148 raise KeyError("Invalid key '%s'" %str
(index
))
150 def __setitem__(self
,index
, value
):
151 if index
== 'tenant':
152 self
.tenant_id_name
= value
153 elif index
== 'datacenter':
154 self
.datacenter_id_name
= value
155 elif index
== 'username':
156 self
.username
= value
157 elif index
== 'password':
158 self
.password
= value
159 elif index
== 'endpoint_url':
160 self
.endpoint_url
= value
162 raise KeyError("Invalid key '{}'".format(index
))
163 self
.tenant
= None # force to reload tenant with different credentials
164 self
.datacenter
= None # force to reload datacenter with different credentials
166 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":
172 return yaml
.load(descriptor
)
173 except yaml
.YAMLError
as exc
:
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":
181 return json
.loads(descriptor
)
182 except Exception as e
:
184 error_text
= "json format error" + str(e
)
187 raise ROClientException(error_text
)
188 raise ROClientException(error_text
)
190 def _parse_yaml(self
, descriptor
, response
=False):
192 return yaml
.load(descriptor
)
193 except yaml
.YAMLError
as exc
:
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
200 raise ROClientException(error_text
)
201 raise ROClientException(error_text
)
204 def check_if_uuid(uuid_text
):
206 Check if text correspond to an uuid foramt
208 :return: True if it is an uuid False if not
213 except (ValueError, TypeError):
217 def _create_envelop(item
, indata
=None):
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, ...
225 return {'vnfd-catalog': {'vnfd': [indata
]}}
227 return {'nsd-catalog': {'nsd': [indata
]}}
228 elif item
== "tenant":
229 return {'tenant': indata
}
230 elif item
== "vim" or item
== "datacenter":
231 return {'datacenter': indata
}
232 elif item
== "ns" or item
== "instances":
233 return {'instance': indata
}
235 assert False, "_create_envelop with unknown item {}".format(item
)
238 def check_ns_status(ns_descriptor
):
240 Inspect RO instance descriptor and indicates the status
241 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
242 :return: status, message: status can be BUILD,ACTIVE,ERROR, message is a text message
249 for net
in ns_descriptor
["nets"]:
251 if net
["status"] == "ERROR":
252 return "ERROR", net
["error_msg"]
253 elif net
["status"] == "ACTIVE":
255 for vnf
in ns_descriptor
["vnfs"]:
256 for vm
in vnf
["vms"]:
258 if vm
["status"] == "ERROR":
259 return "ERROR", vm
["error_msg"]
260 elif vm
["status"] == "ACTIVE":
263 if net_total
== net_done
and vm_total
== vm_done
:
264 return "ACTIVE", "VMs {}, networks: {}".format(vm_total
, net_total
)
266 return "BUILD", "VMs: {}/{}, networks: {}/{}".format(vm_done
, vm_total
, net_done
, net_total
)
269 def get_ns_vnf_ip(ns_descriptor
):
271 Get a dict with the IPs of every vnf.
272 :param ns_descriptor: instance descriptor obtained with self.show("ns", )
273 :return: dict iwth key member_vnf_index, value ip_address
276 for vnf
in ns_descriptor
["vnfs"]:
277 ns_ip
[str(vnf
["member_vnf_index"])] = vnf
["ip_address"]
279 # vnf[mgmt_access]: '{interface_id: cf3cbf00-385c-49b4-9a3f-b400b7b15dc6, vm_id: d0dd22a9-91ef-46f1-8e8f-8cf4b2d5b2d7}'
283 async def get_list(self
, item
, all_tenants
=False, filter_by
=None):
285 Obtain a list of items filtering by the specigy filter_by.
286 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
287 :param all_tenants: True if not filtering by tenant. Only allowed for admin
288 :param filter_by: dictionary with filtering
289 :return: a list of dict. It can be empty. Raises ROClientException on Error,
292 if item
not in self
.client_to_RO
:
293 raise ROClientException("Invalid item {}".format(item
))
296 with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
297 content
= await self
._list
_item
(session
, self
.client_to_RO
[item
], all_tenants
=all_tenants
,
298 filter_dict
=filter_by
)
299 if isinstance(content
, dict):
300 if len(content
) == 1:
301 for _
, v
in content
.items():
303 return content
.values()[0]
305 raise ROClientException("Output not a list neither dict with len equal 1", http_code
=500)
307 except aiohttp
.errors
.ClientOSError
as e
:
308 raise ROClientException(e
, http_code
=504)
310 async def _get_item_uuid(self
, session
, item
, item_id_name
, all_tenants
=False):
313 elif all_tenants
is None:
317 await self
._get
_tenant
(session
)
318 tenant_text
= "/" + self
.tenant
321 url
= "{}{}/{}".format(self
.endpoint_url
, tenant_text
, item
)
322 if self
.check_if_uuid(item_id_name
):
323 item_id
= item_id_name
324 url
+= "/" + item_id_name
325 elif item_id_name
and item_id_name
.startswith("'") and item_id_name
.endswith("'"):
326 item_id_name
= item_id_name
[1:-1]
327 self
.logger
.debug("openmano GET %s", url
)
328 with aiohttp
.Timeout(self
.timeout_short
):
329 async with session
.get(url
, headers
=self
.headers_req
) as response
:
330 response_text
= await response
.read()
331 self
.logger
.debug("GET {} [{}] {}".format(url
, response
.status
, response_text
[:100]))
332 if response
.status
== 404: # NOT_FOUND
333 raise ROClientException("No {} found with id '{}'".format(item
[:-1], item_id_name
),
335 if response
.status
>= 300:
336 raise ROClientException(response_text
, http_code
=response
.status
)
337 content
= self
._parse
_yaml
(response_text
, response
=True)
342 assert isinstance(desc
, list), "_get_item_uuid get a non dict with a list inside {}".format(type(desc
))
345 if item_id_name
and i
["name"] != item_id_name
:
347 if uuid
: # found more than one
348 raise ROClientException(
349 "Found more than one {} with name '{}'. uuid must be used".format(item
, item_id_name
),
353 raise ROClientException("No {} found with name '{}'".format(item
[:-1], item_id_name
), http_code
=404)
356 async def _get_item(self
, session
, item
, item_id_name
, all_tenants
=False):
359 elif all_tenants
is None:
363 await self
._get
_tenant
(session
)
364 tenant_text
= "/" + self
.tenant
366 if self
.check_if_uuid(item_id_name
):
370 uuid
= self
._get
_item
_uuid
(session
, item
, item_id_name
, all_tenants
)
372 url
= "{}{}/{}/{}".format(self
.endpoint_url
, tenant_text
, item
, uuid
)
373 self
.logger
.debug("GET %s", url
)
374 with aiohttp
.Timeout(self
.timeout_short
):
375 async with session
.get(url
, headers
=self
.headers_req
) as response
:
376 response_text
= await response
.read()
377 self
.logger
.debug("GET {} [{}] {}".format(url
, response
.status
, response_text
[:100]))
378 if response
.status
>= 300:
379 raise ROClientException(response_text
, http_code
=response
.status
)
381 return self
._parse
_yaml
(response_text
, response
=True)
383 async def _get_tenant(self
, session
):
385 self
.tenant
= await self
._get
_item
_uuid
(session
, "tenants", self
.tenant_id_name
, None)
388 async def _get_datacenter(self
, session
):
390 await self
._get
_tenant
(session
)
391 if not self
.datacenter
:
392 self
.datacenter
= await self
._get
_item
_uuid
(session
, "datacenters", self
.datacenter_id_name
, True)
393 return self
.datacenter
395 async def _create_item(self
, session
, item
, descriptor
, all_tenants
=False):
398 elif all_tenants
is None:
402 await self
._get
_tenant
(session
)
403 tenant_text
= "/" + self
.tenant
404 payload_req
= yaml
.safe_dump(descriptor
)
406 api_version_text
= ""
408 # assumes version v3 only
409 api_version_text
= "/v3"
411 elif item
== "scenarios":
412 # assumes version v3 only
413 api_version_text
= "/v3"
418 url
= "{}{apiver}{tenant}/{item}".format(self
.endpoint_url
, apiver
=api_version_text
, tenant
=tenant_text
,
420 self
.logger
.debug("openmano POST %s %s", url
, payload_req
)
421 with aiohttp
.Timeout(self
.timeout_large
):
422 async with session
.post(url
, headers
=self
.headers_req
, data
=payload_req
) as response
:
423 response_text
= await response
.read()
424 self
.logger
.debug("POST {} [{}] {}".format(url
, response
.status
, response_text
[:100]))
425 if response
.status
>= 300:
426 raise ROClientException(response_text
, http_code
=response
.status
)
428 response_desc
= self
._parse
_yaml
(response_text
, response
=True)
429 desc
, _
= remove_envelop(item
, response_desc
)
432 async def _del_item(self
, session
, item
, item_id_name
, all_tenants
=False):
435 elif all_tenants
is None:
439 await self
._get
_tenant
(session
)
440 tenant_text
= "/" + self
.tenant
441 if not self
.check_if_uuid(item_id_name
):
443 uuid
= await self
._get
_item
_uuid
(session
, item
, item_id_name
, all_tenants
)
447 url
= "{}{}/{}/{}".format(self
.endpoint_url
, tenant_text
, item
, uuid
)
448 self
.logger
.debug("DELETE %s", url
)
449 with aiohttp
.Timeout(self
.timeout_short
):
450 async with session
.delete(url
, headers
=self
.headers_req
) as response
:
451 response_text
= await response
.read()
452 self
.logger
.debug("DELETE {} [{}] {}".format(url
, response
.status
, response_text
[:100]))
453 if response
.status
>= 300:
454 raise ROClientException(response_text
, http_code
=response
.status
)
455 return self
._parse
_yaml
(response_text
, response
=True)
457 async def _list_item(self
, session
, item
, all_tenants
=False, filter_dict
=None):
460 elif all_tenants
is None:
464 await self
._get
_tenant
(session
)
465 tenant_text
= "/" + self
.tenant
467 url
= "{}{}/{}".format(self
.endpoint_url
, tenant_text
, item
)
470 for k
in filter_dict
:
471 url
+= separator
+ quote(str(k
)) + "=" + quote(str(filter_dict
[k
]))
473 self
.logger
.debug("openmano GET %s", url
)
474 with aiohttp
.Timeout(self
.timeout_short
):
475 async with session
.get(url
, headers
=self
.headers_req
) as response
:
476 response_text
= await response
.read()
477 self
.logger
.debug("GET {} [{}] {}".format(url
, response
.status
, response_text
[:100]))
478 if response
.status
>= 300:
479 raise ROClientException(response_text
, http_code
=response
.status
)
480 return self
._parse
_yaml
(response_text
, response
=True)
482 async def _edit_item(self
, session
, item
, descriptor
, item_id_name
, all_tenants
=False):
485 elif all_tenants
is None:
489 await self
._get
_tenant
(session
)
490 tenant_text
= "/" + self
.tenant
494 uuid
= self
._get
_item
_uuid
(session
, "tenants", item_id_name
, all_tenants
)
496 payload_req
= yaml
.safe_dump(descriptor
)
500 url
= "{}{}/{}/{}".format(self
.endpoint_url
, tenant_text
, item
, uuid
)
501 self
.logger
.debug("openmano PUT %s %s", url
, payload_req
)
502 with aiohttp
.Timeout(self
.timeout_large
):
503 async with session
.put(url
, headers
=self
.headers_req
, data
=payload_req
) as response
:
504 response_text
= await response
.read()
505 self
.logger
.debug("PUT {} [{}] {}".format(url
, response
.status
, response_text
[:100]))
506 if response
.status
>= 300:
507 raise ROClientException(response_text
, http_code
=response
.status
)
508 return self
._parse
_yaml
(response_text
, response
=True)
510 async def show(self
, item
, item_id_name
=None, all_tenants
=False):
512 Obtain the information of an item from its id or name
513 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
514 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
515 :param all_tenants: True if not filtering by tenant. Only allowed for admin
516 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
519 if item
not in self
.client_to_RO
:
520 raise ROClientException("Invalid item {}".format(item
))
524 with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
525 content
= await self
._get
_item
(session
, self
.client_to_RO
[item
], item_id_name
, all_tenants
=all_tenants
)
526 if len(content
) == 1:
527 return content
.values()[0]
530 except aiohttp
.errors
.ClientOSError
as e
:
531 raise ROClientException(e
, http_code
=504)
533 async def delete(self
, item
, item_id_name
=None, all_tenants
=False):
535 Delete the information of an item from its id or name
536 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
537 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
538 :param all_tenants: True if not filtering by tenant. Only allowed for admin
539 :return: dictionary with the information or raises ROClientException on Error, NotFound, found several
542 if item
not in self
.client_to_RO
:
543 raise ROClientException("Invalid item {}".format(item
))
547 with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
550 item_id
= await self
._get
_item
_uuid
(session
, "datacenters", item_id_name
, all_tenants
=True)
552 return await self
._del
_item
(session
, self
.client_to_RO
[item
], item_id_name
, all_tenants
=all_tenants
)
553 except aiohttp
.errors
.ClientOSError
as e
:
554 raise ROClientException(e
, http_code
=504)
556 async def create(self
, item
, descriptor
=None, descriptor_format
=None, **kwargs
):
558 Creates an item from its descriptor
559 :param item: can be 'tenant', 'vim', 'vnfd', 'nsd', 'ns'
560 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
561 :param descriptor_format: Can be 'json' or 'yaml'
562 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
563 keys can be a dot separated list to specify elements inside dict
564 :return: dictionary with the information or raises ROClientException on Error
567 if isinstance(descriptor
, str):
568 descriptor
= self
._parse
(descriptor
, descriptor_format
)
574 if item
not in self
.client_to_RO
:
575 raise ROClientException("Invalid item {}".format(item
))
576 desc
, enveloped
= remove_envelop(item
, descriptor
)
578 # Override descriptor with kwargs
580 desc
= deepcopy(desc
) # do not modify original descriptor
582 for k
, v
in kwargs
.items():
583 update_content
= desc
587 if kitem_old
is not None:
588 update_content
= update_content
[kitem_old
]
589 if isinstance(update_content
, dict):
591 elif isinstance(update_content
, list):
592 kitem_old
= int(kitem
)
594 raise ROClientException(
595 "Invalid query string '{}'. Descriptor is not a list nor dict at '{}'".format(k
, kitem
))
596 if v
== "__DELETE__":
597 del update_content
[kitem_old
]
599 update_content
[kitem_old
] = v
601 raise ROClientException(
602 "Invalid query string '{}'. Descriptor does not contain '{}'".format(k
, kitem_old
))
604 raise ROClientException("Invalid query string '{}'. Expected integer index list instead of '{}'".format(
607 raise ROClientException(
608 "Invalid query string '{}'. Index '{}' out of range".format(k
, kitem_old
))
610 for mandatory
in self
.mandatory_for_create
[item
]:
611 if mandatory
not in desc
:
612 raise ROClientException("'{}' is mandatory parameter for {}".format(mandatory
, item
))
615 if item
in ('tenant', 'vim'):
619 create_desc
= self
._create
_envelop
(item
, desc
)
621 create_desc
= descriptor
623 with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
624 return await self
._create
_item
(session
, self
.client_to_RO
[item
], create_desc
, all_tenants
)
625 except aiohttp
.errors
.ClientOSError
as e
:
626 raise ROClientException(e
, http_code
=504)
628 def edit_tenant(self
, uuid
=None, name
=None, descriptor
=None, descriptor_format
=None, new_name
=None, new_description
=None):
629 """Edit the parameters of a tenant
630 Params: must supply a descriptor or/and a new_name or new_description
631 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
632 descriptor: with format {'tenant':{params to change info}}
633 must be a dictionary or a json/yaml text.
634 name: the tenant name. Overwrite descriptor name if any
635 description: tenant descriptor.. Overwrite descriptor description if any
636 Return: Raises an exception on error, not found or found several
637 Obtain a dictionary with format {'tenant':{newtenant_info}}
641 if isinstance(descriptor
, str):
642 descriptor
= self
.parse(descriptor
, descriptor_format
)
645 elif new_name
or new_description
:
646 descriptor
={"tenant": {}}
648 raise ROClientException("Missing descriptor")
650 if 'tenant' not in descriptor
or len(descriptor
)!=1:
651 raise ROClientException("Descriptor must contain only one 'tenant' field")
653 descriptor
['tenant']['name'] = new_name
655 descriptor
['tenant']['description'] = new_description
657 return self
._edit
_item
("tenants", descriptor
, uuid
, name
, all_tenants
=None)
658 except aiohttp
.errors
.ClientOSError
as e
:
659 raise ROClientException(e
, http_code
=504)
663 def edit_datacenter(self
, uuid
=None, name
=None, descriptor
=None, descriptor_format
=None, all_tenants
=False, **kwargs
):
664 """Edit the parameters of a datacenter
665 Params: must supply a descriptor or/and a parameter to change
666 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
667 descriptor: with format {'datacenter':{params to change info}}
668 must be a dictionary or a json/yaml text.
669 parameters to change can be supplyied by the descriptor or as parameters:
670 new_name: the datacenter name
671 vim_url: the datacenter URL
672 vim_url_admin: the datacenter URL for administrative issues
673 vim_type: the datacenter type, can be openstack or openvim.
674 public: boolean, available to other tenants
675 description: datacenter description
676 Return: Raises an exception on error, not found or found several
677 Obtain a dictionary with format {'datacenter':{new_datacenter_info}}
680 if isinstance(descriptor
, str):
681 descriptor
= self
.parse(descriptor
, descriptor_format
)
685 descriptor
={"datacenter": {}}
687 raise ROClientException("Missing descriptor")
689 if 'datacenter' not in descriptor
or len(descriptor
)!=1:
690 raise ROClientException("Descriptor must contain only one 'datacenter' field")
692 if param
=='new_name':
693 descriptor
['datacenter']['name'] = kwargs
[param
]
695 descriptor
['datacenter'][param
] = kwargs
[param
]
696 return self
._edit
_item
("datacenters", descriptor
, uuid
, name
, all_tenants
=None)
698 def attach_datacenter(self
, uuid_name
=None, descriptor
=None, descriptor_format
=None, vim_user
=None, vim_password
=None, vim_tenant_name
=None, vim_tenant_id
=None):
700 uuid
= self
._get
_item
_uuid
(session
, "datacenters", uuid_name
, all_tenants
=True)
701 tenant_text
= "/"+self
._get
_tenant
()
703 if isinstance(descriptor
, str):
704 descriptor
= self
.parse(descriptor
, descriptor_format
)
707 elif vim_user
or vim_password
or vim_tenant_name
or vim_tenant_id
:
708 descriptor
={"datacenter": {}}
710 raise ROClientException("Missing descriptor or params")
712 if vim_user
or vim_password
or vim_tenant_name
or vim_tenant_id
:
716 descriptor
['datacenter']['vim_user'] = vim_user
718 descriptor
['datacenter']['vim_password'] = vim_password
720 descriptor
['datacenter']['vim_tenant_name'] = vim_tenant_name
722 descriptor
['datacenter']['vim_tenant'] = vim_tenant_id
723 except (KeyError, TypeError) as e
:
724 if str(e
)=='datacenter': error_pos
= "missing field 'datacenter'"
725 else: error_pos
="wrong format"
726 raise ROClientException("Wrong datacenter descriptor: " + error_pos
)
728 payload_req
= yaml
.safe_dump(descriptor
)
730 url
= "{}{}/datacenters/{}".format(self
.endpoint_url
, tenant_text
, uuid
)
731 self
.logger
.debug("openmano POST %s %s", url
, payload_req
)
732 mano_response
= requests
.post(url
, headers
= self
.headers_req
, data
=payload_req
)
733 self
.logger
.debug("openmano response: %s", mano_response
.text
)
735 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
736 if mano_response
.status_code
==200:
739 raise ROClientException(str(content
), http_code
=mano_response
.status
)
741 def detach_datacenter(self
, uuid_name
=None):
744 uuid
= self
._get
_item
_uuid
(session
, "datacenters", uuid_name
, all_tenants
=False)
745 tenant_text
= "/"+self
._get
_tenant
()
746 url
= "{}{}/datacenters/{}".format(self
.endpoint_url
, tenant_text
, uuid
)
747 self
.logger
.debug("openmano DELETE %s", url
)
748 mano_response
= requests
.delete(url
, headers
= self
.headers_req
)
749 self
.logger
.debug("openmano response: %s", mano_response
.text
)
751 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
752 if mano_response
.status_code
==200:
755 raise ROClientException(str(content
), http_code
=mano_response
.status
)
759 def edit_scenario(self
, uuid
=None, name
=None, descriptor
=None, descriptor_format
=None, all_tenants
=False, **kwargs
):
760 """Edit the parameters of a scenario
761 Params: must supply a descriptor or/and a parameters to change
762 uuid or/and name. If only name is supplied, there must be only one or an exception is raised
763 descriptor: with format {'scenario':{params to change info}}
764 must be a dictionary or a json/yaml text.
765 parameters to change can be supplyied by the descriptor or as parameters:
766 new_name: the scenario name
767 public: boolean, available to other tenants
768 description: scenario description
769 tenant_id. Propietary tenant
770 Return: Raises an exception on error, not found or found several
771 Obtain a dictionary with format {'scenario':{new_scenario_info}}
774 if isinstance(descriptor
, str):
775 descriptor
= self
.parse(descriptor
, descriptor_format
)
779 descriptor
={"scenario": {}}
781 raise ROClientException("Missing descriptor")
783 if 'scenario' not in descriptor
or len(descriptor
)>2:
784 raise ROClientException("Descriptor must contain only one 'scenario' field")
786 if param
=='new_name':
787 descriptor
['scenario']['name'] = kwargs
[param
]
789 descriptor
['scenario'][param
] = kwargs
[param
]
790 return self
._edit
_item
("scenarios", descriptor
, uuid
, name
, all_tenants
=None)
793 def vim_action(self
, action
, item
, uuid
=None, all_tenants
=False, **kwargs
):
794 """Perform an action over a vim
796 action: can be 'list', 'get'/'show', 'delete' or 'create'
797 item: can be 'tenants' or 'networks'
798 uuid: uuid of the tenant/net to show or to delete. Ignore otherwise
800 datacenter_name, datacenter_id: datacenters to act on, if missing uses classes store datacenter
801 descriptor, descriptor_format: descriptor needed on creation, can be a dict or a yaml/json str
802 must be a dictionary or a json/yaml text.
803 name: for created tenant/net Overwrite descriptor name if any
804 description: tenant descriptor. Overwrite descriptor description if any
806 Return: Raises an exception on error
807 Obtain a dictionary with format {'tenant':{new_tenant_info}}
809 if item
not in ("tenants", "networks", "images"):
810 raise ROClientException("Unknown value for item '{}', must be 'tenants', 'nets' or "
811 "images".format(str(item
)))
813 image_actions
= ['list','get','show','delete']
814 if item
== "images" and action
not in image_actions
:
815 raise ROClientException("Only available actions for item '{}' are {}\n"
816 "Requested action was '{}'".format(item
, ', '.join(image_actions
), action
))
820 tenant_text
= "/"+self
._get
_tenant
()
822 if "datacenter_id" in kwargs
or "datacenter_name" in kwargs
:
823 datacenter
= self
._get
_item
_uuid
(session
, "datacenters", kwargs
.get("datacenter"), all_tenants
=all_tenants
)
825 datacenter
= self
.get_datacenter(session
)
828 url
= "{}{}/vim/{}/{}".format(self
.endpoint_url
, tenant_text
, datacenter
, item
)
829 self
.logger
.debug("GET %s", url
)
830 mano_response
= requests
.get(url
, headers
=self
.headers_req
)
831 self
.logger
.debug("openmano response: %s", mano_response
.text
)
832 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
833 if mano_response
.status_code
==200:
836 raise ROClientException(str(content
), http_code
=mano_response
.status
)
837 elif action
=="get" or action
=="show":
838 url
= "{}{}/vim/{}/{}/{}".format(self
.endpoint_url
, tenant_text
, datacenter
, item
, uuid
)
839 self
.logger
.debug("GET %s", url
)
840 mano_response
= requests
.get(url
, headers
=self
.headers_req
)
841 self
.logger
.debug("openmano response: %s", mano_response
.text
)
842 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
843 if mano_response
.status_code
==200:
846 raise ROClientException(str(content
), http_code
=mano_response
.status
)
847 elif action
=="delete":
848 url
= "{}{}/vim/{}/{}/{}".format(self
.endpoint_url
, tenant_text
, datacenter
, item
, uuid
)
849 self
.logger
.debug("DELETE %s", url
)
850 mano_response
= requests
.delete(url
, headers
=self
.headers_req
)
851 self
.logger
.debug("openmano response: %s", mano_response
.text
)
852 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
853 if mano_response
.status_code
==200:
856 raise ROClientException(str(content
), http_code
=mano_response
.status
)
857 elif action
=="create":
858 if "descriptor" in kwargs
:
859 if isinstance(kwargs
["descriptor"], str):
860 descriptor
= self
._parse
(kwargs
["descriptor"], kwargs
.get("descriptor_format") )
862 descriptor
= kwargs
["descriptor"]
863 elif "name" in kwargs
:
864 descriptor
={item
[:-1]: {"name": kwargs
["name"]}}
866 raise ROClientException("Missing descriptor")
868 if item
[:-1] not in descriptor
or len(descriptor
)!=1:
869 raise ROClientException("Descriptor must contain only one 'tenant' field")
871 descriptor
[ item
[:-1] ]['name'] = kwargs
["name"]
872 if "description" in kwargs
:
873 descriptor
[ item
[:-1] ]['description'] = kwargs
["description"]
874 payload_req
= yaml
.safe_dump(descriptor
)
876 url
= "{}{}/vim/{}/{}".format(self
.endpoint_url
, tenant_text
, datacenter
, item
)
877 self
.logger
.debug("openmano POST %s %s", url
, payload_req
)
878 mano_response
= requests
.post(url
, headers
= self
.headers_req
, data
=payload_req
)
879 self
.logger
.debug("openmano response: %s", mano_response
.text
)
880 content
= self
._parse
_yaml
(mano_response
.text
, response
=True)
881 if mano_response
.status_code
==200:
884 raise ROClientException(str(content
), http_code
=mano_response
.status
)
886 raise ROClientException("Unknown value for action '{}".format(str(action
)))
889 if __name__
== '__main__':
890 RO_URL
= "http://localhost:9090/openmano"
894 streamformat
= "%(asctime)s %(name)s %(levelname)s: %(message)s"
895 logging
.basicConfig(format
=streamformat
)
897 loop
= asyncio
.get_event_loop()
898 myClient
= ROClient(endpoint_url
=RO_URL
, loop
=loop
, tenant_id
=RO_TENANT
, datacenter_id
=RO_VIM
, debug
=True)
899 content
= loop
.run_until_complete(myClient
.list_tenants())