2 # -*- coding: utf-8 -*-
5 # Copyright 2020 Telefónica Investigación y Desarrollo, S.A.U.
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
11 # http://www.apache.org/licenses/LICENSE-2.0
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
22 asyncio RO python client to interact with New Generation RO server
30 __author__
= "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com"
31 __date__
= "$09-Jan-2018 09:09:48$"
33 version_date
= "2020-05-08"
36 class NgRoException(Exception):
37 def __init__(self
, message
, http_code
=400):
38 """Common Exception for all RO client exceptions"""
39 self
.http_code
= http_code
40 Exception.__init
__(self
, message
)
44 headers_req
= {"Accept": "application/yaml", "content-type": "application/yaml"}
48 "vim_account": "datacenters",
49 "sdn": "sdn_controllers",
53 "wim_account": "wims",
56 mandatory_for_create
= {
58 "vnfd": ("name", "id"),
59 "nsd": ("name", "id"),
60 "ns": ("name", "scenario", "datacenter"),
61 "vim": ("name", "vim_url"),
62 "wim": ("name", "wim_url"),
65 "sdn": ("name", "type"),
70 def __init__(self
, loop
, uri
, **kwargs
):
72 self
.endpoint_url
= uri
73 if not self
.endpoint_url
.endswith("/"):
74 self
.endpoint_url
+= "/"
75 if not self
.endpoint_url
.startswith("http"):
76 self
.endpoint_url
= "http://" + self
.endpoint_url
78 self
.username
= kwargs
.get("username")
79 self
.password
= kwargs
.get("password")
80 self
.tenant_id_name
= kwargs
.get("tenant")
82 self
.datacenter_id_name
= kwargs
.get("datacenter")
83 self
.datacenter
= None
84 logger_name
= kwargs
.get("logger_name", "lcm.ro")
85 self
.logger
= logging
.getLogger(logger_name
)
86 if kwargs
.get("loglevel"):
87 self
.logger
.setLevel(kwargs
["loglevel"])
89 async def deploy(self
, nsr_id
, target
):
91 Performs an action over an item
92 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
93 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
94 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
95 :param descriptor_format: Can be 'json' or 'yaml'
96 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
97 keys can be a dot separated list to specify elements inside dict
98 :return: dictionary with the information or raises NgRoException on Error
101 if isinstance(target
, str):
102 target
= self
._parse
_yaml
(target
)
103 payload_req
= yaml
.safe_dump(target
)
105 url
= "{}/ns/v1/deploy/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
106 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
107 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
108 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
109 async with session
.post(
110 url
, headers
=self
.headers_req
, data
=payload_req
112 response_text
= await response
.read()
114 "POST {} [{}] {}".format(
115 url
, response
.status
, response_text
[:100]
118 if response
.status
>= 300:
119 raise NgRoException(response_text
, http_code
=response
.status
)
120 return self
._parse
_yaml
(response_text
, response
=True)
121 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
122 raise NgRoException(e
, http_code
=504)
123 except asyncio
.TimeoutError
:
124 raise NgRoException("Timeout", http_code
=504)
126 async def migrate(self
, nsr_id
, target
):
128 Performs migration of VNFs
129 :param nsr_id: NS Instance Id
130 :param target: payload data for migrate operation
131 :return: dictionary with the information or raises NgRoException on Error
134 if isinstance(target
, str):
135 target
= self
._parse
_yaml
(target
)
136 payload_req
= yaml
.safe_dump(target
)
138 url
= "{}/ns/v1/migrate/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
139 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
140 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
141 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
142 async with session
.post(
143 url
, headers
=self
.headers_req
, data
=payload_req
145 response_text
= await response
.read()
147 "POST {} [{}] {}".format(
148 url
, response
.status
, response_text
[:100]
151 if response
.status
>= 300:
152 raise NgRoException(response_text
, http_code
=response
.status
)
153 return self
._parse
_yaml
(response_text
, response
=True)
154 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
155 raise NgRoException(e
, http_code
=504)
156 except asyncio
.TimeoutError
:
157 raise NgRoException("Timeout", http_code
=504)
159 async def operate(self
, nsr_id
, target
, operation_type
):
161 Performs start/stop/rebuil of VNFs
162 :param nsr_id: NS Instance Id
163 :param target: payload data for migrate operation
164 :param operation_type: start/stop/rebuil of VNFs
165 :return: dictionary with the information or raises NgRoException on Error
168 if isinstance(target
, str):
169 target
= self
._parse
_yaml
(target
)
170 payload_req
= yaml
.safe_dump(target
)
172 url
= "{}/ns/v1/{operation_type}/{nsr_id}".format(
173 self
.endpoint_url
, operation_type
=operation_type
, nsr_id
=nsr_id
175 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
176 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
177 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
178 async with session
.post(
179 url
, headers
=self
.headers_req
, data
=payload_req
181 response_text
= await response
.read()
183 "POST {} [{}] {}".format(
184 url
, response
.status
, response_text
[:100]
187 if response
.status
>= 300:
188 raise NgRoException(response_text
, http_code
=response
.status
)
189 return self
._parse
_yaml
(response_text
, response
=True)
190 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
191 raise NgRoException(e
, http_code
=504)
192 except asyncio
.TimeoutError
:
193 raise NgRoException("Timeout", http_code
=504)
195 async def status(self
, nsr_id
, action_id
):
197 url
= "{}/ns/v1/deploy/{nsr_id}/{action_id}".format(
198 self
.endpoint_url
, nsr_id
=nsr_id
, action_id
=action_id
200 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
201 self
.logger
.debug("GET %s", url
)
202 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
203 async with session
.get(url
, headers
=self
.headers_req
) as response
:
204 response_text
= await response
.read()
206 "GET {} [{}] {}".format(
207 url
, response
.status
, response_text
[:100]
210 if response
.status
>= 300:
211 raise NgRoException(response_text
, http_code
=response
.status
)
212 return self
._parse
_yaml
(response_text
, response
=True)
214 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
215 raise NgRoException(e
, http_code
=504)
216 except asyncio
.TimeoutError
:
217 raise NgRoException("Timeout", http_code
=504)
219 async def delete(self
, nsr_id
):
221 url
= "{}/ns/v1/deploy/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
222 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
223 self
.logger
.debug("DELETE %s", url
)
224 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
225 async with session
.delete(url
, headers
=self
.headers_req
) as response
:
226 self
.logger
.debug("DELETE {} [{}]".format(url
, response
.status
))
227 if response
.status
>= 300:
229 "Delete {}".format(nsr_id
), http_code
=response
.status
233 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
234 raise NgRoException(e
, http_code
=504)
235 except asyncio
.TimeoutError
:
236 raise NgRoException("Timeout", http_code
=504)
238 async def get_version(self
):
240 Obtain RO server version.
241 :return: a list with integers ["major", "minor", "release"]. Raises NgRoException on Error,
245 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
246 url
= "{}/version".format(self
.endpoint_url
)
247 self
.logger
.debug("RO GET %s", url
)
248 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
249 async with session
.get(url
, headers
=self
.headers_req
) as response
:
250 response_text
= await response
.read()
252 "GET {} [{}] {}".format(
253 url
, response
.status
, response_text
[:100]
256 if response
.status
>= 300:
257 raise NgRoException(response_text
, http_code
=response
.status
)
259 for word
in str(response_text
).split(" "):
261 version_text
, _
, _
= word
.partition("-")
264 "Got invalid version text: '{}'".format(response_text
),
267 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
268 raise NgRoException(e
, http_code
=504)
269 except asyncio
.TimeoutError
:
270 raise NgRoException("Timeout", http_code
=504)
271 except Exception as e
:
273 "Got invalid version text: '{}'; causing exception {}".format(
279 async def recreate(self
, nsr_id
, target
):
281 Performs an action over an item
282 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
283 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
284 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
285 :param descriptor_format: Can be 'json' or 'yaml'
286 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
287 keys can be a dot separated list to specify elements inside dict
288 :return: dictionary with the information or raises NgRoException on Error
291 if isinstance(target
, str):
292 target
= self
._parse
_yaml
(target
)
293 payload_req
= yaml
.safe_dump(target
)
295 url
= "{}/ns/v1/recreate/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
296 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
297 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
298 async with session
.post(
299 url
, headers
=self
.headers_req
, data
=payload_req
301 response_text
= await response
.read()
303 "POST {} [{}] {}".format(
304 url
, response
.status
, response_text
[:100]
307 if response
.status
>= 300:
308 raise NgRoException(response_text
, http_code
=response
.status
)
309 return self
._parse
_yaml
(response_text
, response
=True)
310 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
311 raise NgRoException(e
, http_code
=504)
312 except asyncio
.TimeoutError
:
313 raise NgRoException("Timeout", http_code
=504)
315 async def recreate_status(self
, nsr_id
, action_id
):
317 url
= "{}/ns/v1/recreate/{nsr_id}/{action_id}".format(
318 self
.endpoint_url
, nsr_id
=nsr_id
, action_id
=action_id
320 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
321 self
.logger
.debug("GET %s", url
)
322 async with session
.get(url
, headers
=self
.headers_req
) as response
:
323 response_text
= await response
.read()
325 "GET {} [{}] {}".format(
326 url
, response
.status
, response_text
[:100]
329 if response
.status
>= 300:
330 raise NgRoException(response_text
, http_code
=response
.status
)
331 return self
._parse
_yaml
(response_text
, response
=True)
333 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
334 raise NgRoException(e
, http_code
=504)
335 except asyncio
.TimeoutError
:
336 raise NgRoException("Timeout", http_code
=504)
338 async def vertical_scale(self
, nsr_id
, target
):
340 Performs migration of VNFs
341 :param nsr_id: NS Instance Id
342 :param target: payload data for migrate operation
343 :return: dictionary with the information or raises NgRoException on Error
346 if isinstance(target
, str):
347 target
= self
._parse
_yaml
(target
)
348 payload_req
= yaml
.safe_dump(target
)
350 url
= "{}/ns/v1/verticalscale/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
351 async with aiohttp
.ClientSession(loop
=self
.loop
) as session
:
352 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
353 async with session
.post(
354 url
, headers
=self
.headers_req
, data
=payload_req
356 response_text
= await response
.read()
358 "POST {} [{}] {}".format(
359 url
, response
.status
, response_text
[:100]
362 if response
.status
>= 300:
363 raise NgRoException(response_text
, http_code
=response
.status
)
364 return self
._parse
_yaml
(response_text
, response
=True)
365 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
366 raise NgRoException(e
, http_code
=504)
367 except asyncio
.TimeoutError
:
368 raise NgRoException("Timeout", http_code
=504)
371 def _parse_yaml(descriptor
, response
=False):
373 return yaml
.safe_load(descriptor
)
374 except yaml
.YAMLError
as exc
:
376 if hasattr(exc
, "problem_mark"):
377 mark
= exc
.problem_mark
378 error_pos
= " at line:{} column:{}s".format(
379 mark
.line
+ 1, mark
.column
+ 1
381 error_text
= "yaml format error" + error_pos
383 raise NgRoException("reponse with " + error_text
)
384 raise NgRoException(error_text
)