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
, uri
, **kwargs
):
71 self
.endpoint_url
= uri
72 if not self
.endpoint_url
.endswith("/"):
73 self
.endpoint_url
+= "/"
74 if not self
.endpoint_url
.startswith("http"):
75 self
.endpoint_url
= "http://" + self
.endpoint_url
77 self
.username
= kwargs
.get("username")
78 self
.password
= kwargs
.get("password")
79 self
.tenant_id_name
= kwargs
.get("tenant")
81 self
.datacenter_id_name
= kwargs
.get("datacenter")
82 self
.datacenter
= None
83 logger_name
= kwargs
.get("logger_name", "lcm.ro")
84 self
.logger
= logging
.getLogger(logger_name
)
85 if kwargs
.get("loglevel"):
86 self
.logger
.setLevel(kwargs
["loglevel"])
88 async def deploy(self
, nsr_id
, target
):
90 Performs an action over an item
91 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
92 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
93 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
94 :param descriptor_format: Can be 'json' or 'yaml'
95 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
96 keys can be a dot separated list to specify elements inside dict
97 :return: dictionary with the information or raises NgRoException on Error
100 if isinstance(target
, str):
101 target
= self
._parse
_yaml
(target
)
102 payload_req
= yaml
.safe_dump(target
)
104 url
= "{}/ns/v1/deploy/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
105 async with aiohttp
.ClientSession() as session
:
106 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
107 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
108 async with session
.post(
109 url
, headers
=self
.headers_req
, data
=payload_req
111 response_text
= await response
.read()
113 "POST {} [{}] {}".format(
114 url
, response
.status
, response_text
[:100]
117 if response
.status
>= 300:
118 raise NgRoException(response_text
, http_code
=response
.status
)
119 return self
._parse
_yaml
(response_text
, response
=True)
120 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
121 raise NgRoException(e
, http_code
=504)
122 except asyncio
.TimeoutError
:
123 raise NgRoException("Timeout", http_code
=504)
125 async def migrate(self
, nsr_id
, target
):
127 Performs migration of VNFs
128 :param nsr_id: NS Instance Id
129 :param target: payload data for migrate operation
130 :return: dictionary with the information or raises NgRoException on Error
133 if isinstance(target
, str):
134 target
= self
._parse
_yaml
(target
)
135 payload_req
= yaml
.safe_dump(target
)
137 url
= "{}/ns/v1/migrate/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
138 async with aiohttp
.ClientSession() as session
:
139 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
140 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
141 async with session
.post(
142 url
, headers
=self
.headers_req
, data
=payload_req
144 response_text
= await response
.read()
146 "POST {} [{}] {}".format(
147 url
, response
.status
, response_text
[:100]
150 if response
.status
>= 300:
151 raise NgRoException(response_text
, http_code
=response
.status
)
152 return self
._parse
_yaml
(response_text
, response
=True)
153 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
154 raise NgRoException(e
, http_code
=504)
155 except asyncio
.TimeoutError
:
156 raise NgRoException("Timeout", http_code
=504)
158 async def operate(self
, nsr_id
, target
, operation_type
):
160 Performs start/stop/rebuil of VNFs
161 :param nsr_id: NS Instance Id
162 :param target: payload data for migrate operation
163 :param operation_type: start/stop/rebuil of VNFs
164 :return: dictionary with the information or raises NgRoException on Error
167 if isinstance(target
, str):
168 target
= self
._parse
_yaml
(target
)
169 payload_req
= yaml
.safe_dump(target
)
171 url
= "{}/ns/v1/{operation_type}/{nsr_id}".format(
172 self
.endpoint_url
, operation_type
=operation_type
, nsr_id
=nsr_id
174 async with aiohttp
.ClientSession() as session
:
175 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
176 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
177 async with session
.post(
178 url
, headers
=self
.headers_req
, data
=payload_req
180 response_text
= await response
.read()
182 "POST {} [{}] {}".format(
183 url
, response
.status
, response_text
[:100]
186 if response
.status
>= 300:
187 raise NgRoException(response_text
, http_code
=response
.status
)
188 return self
._parse
_yaml
(response_text
, response
=True)
189 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
190 raise NgRoException(e
, http_code
=504)
191 except asyncio
.TimeoutError
:
192 raise NgRoException("Timeout", http_code
=504)
194 async def status(self
, nsr_id
, action_id
):
196 url
= "{}/ns/v1/deploy/{nsr_id}/{action_id}".format(
197 self
.endpoint_url
, nsr_id
=nsr_id
, action_id
=action_id
199 async with aiohttp
.ClientSession() as session
:
200 self
.logger
.debug("GET %s", url
)
201 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
202 async with session
.get(url
, headers
=self
.headers_req
) as response
:
203 response_text
= await response
.read()
205 "GET {} [{}] {}".format(
206 url
, response
.status
, response_text
[:100]
209 if response
.status
>= 300:
210 raise NgRoException(response_text
, http_code
=response
.status
)
211 return self
._parse
_yaml
(response_text
, response
=True)
213 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
214 raise NgRoException(e
, http_code
=504)
215 except asyncio
.TimeoutError
:
216 raise NgRoException("Timeout", http_code
=504)
218 async def delete(self
, nsr_id
):
220 url
= "{}/ns/v1/deploy/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
221 async with aiohttp
.ClientSession() as session
:
222 self
.logger
.debug("DELETE %s", url
)
223 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
224 async with session
.delete(url
, headers
=self
.headers_req
) as response
:
225 self
.logger
.debug("DELETE {} [{}]".format(url
, response
.status
))
226 if response
.status
>= 300:
228 "Delete {}".format(nsr_id
), http_code
=response
.status
232 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
233 raise NgRoException(e
, http_code
=504)
234 except asyncio
.TimeoutError
:
235 raise NgRoException("Timeout", http_code
=504)
237 async def get_version(self
):
239 Obtain RO server version.
240 :return: a list with integers ["major", "minor", "release"]. Raises NgRoException on Error,
244 async with aiohttp
.ClientSession() as session
:
245 url
= "{}/version".format(self
.endpoint_url
)
246 self
.logger
.debug("RO GET %s", url
)
247 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
248 async with session
.get(url
, headers
=self
.headers_req
) as response
:
249 response_text
= await response
.read()
251 "GET {} [{}] {}".format(
252 url
, response
.status
, response_text
[:100]
255 if response
.status
>= 300:
256 raise NgRoException(response_text
, http_code
=response
.status
)
258 for word
in str(response_text
).split(" "):
260 version_text
, _
, _
= word
.partition("-")
263 "Got invalid version text: '{}'".format(response_text
),
266 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
267 raise NgRoException(e
, http_code
=504)
268 except asyncio
.TimeoutError
:
269 raise NgRoException("Timeout", http_code
=504)
270 except Exception as e
:
272 "Got invalid version text: '{}'; causing exception {}".format(
278 async def recreate(self
, nsr_id
, target
):
280 Performs an action over an item
281 :param item: can be 'tenant', 'vnfd', 'nsd', 'ns', 'vim', 'vim_account', 'sdn'
282 :param item_id_name: RO id or name of the item. Raise and exception if more than one found
283 :param descriptor: can be a dict, or a yaml/json text. Autodetect unless descriptor_format is provided
284 :param descriptor_format: Can be 'json' or 'yaml'
285 :param kwargs: Overrides descriptor with values as name, description, vim_url, vim_url_admin, vim_type
286 keys can be a dot separated list to specify elements inside dict
287 :return: dictionary with the information or raises NgRoException on Error
290 if isinstance(target
, str):
291 target
= self
._parse
_yaml
(target
)
292 payload_req
= yaml
.safe_dump(target
)
294 url
= "{}/ns/v1/recreate/{nsr_id}".format(self
.endpoint_url
, nsr_id
=nsr_id
)
295 async with aiohttp
.ClientSession() as session
:
296 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
297 async with session
.post(
298 url
, headers
=self
.headers_req
, data
=payload_req
300 response_text
= await response
.read()
302 "POST {} [{}] {}".format(
303 url
, response
.status
, response_text
[:100]
306 if response
.status
>= 300:
307 raise NgRoException(response_text
, http_code
=response
.status
)
308 return self
._parse
_yaml
(response_text
, response
=True)
309 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
310 raise NgRoException(e
, http_code
=504)
311 except asyncio
.TimeoutError
:
312 raise NgRoException("Timeout", http_code
=504)
314 async def recreate_status(self
, nsr_id
, action_id
):
316 url
= "{}/ns/v1/recreate/{nsr_id}/{action_id}".format(
317 self
.endpoint_url
, nsr_id
=nsr_id
, action_id
=action_id
319 async with aiohttp
.ClientSession() as session
:
320 self
.logger
.debug("GET %s", url
)
321 async with session
.get(url
, headers
=self
.headers_req
) as response
:
322 response_text
= await response
.read()
324 "GET {} [{}] {}".format(
325 url
, response
.status
, response_text
[:100]
328 if response
.status
>= 300:
329 raise NgRoException(response_text
, http_code
=response
.status
)
330 return self
._parse
_yaml
(response_text
, response
=True)
332 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
333 raise NgRoException(e
, http_code
=504)
334 except asyncio
.TimeoutError
:
335 raise NgRoException("Timeout", http_code
=504)
337 async def vertical_scale(self
, nsr_id
, target
):
339 Performs migration of VNFs
340 :param nsr_id: NS Instance Id
341 :param target: payload data for migrate operation
342 :return: dictionary with the information or raises NgRoException on Error
345 if isinstance(target
, str):
346 target
= self
._parse
_yaml
(target
)
347 payload_req
= yaml
.safe_dump(target
)
349 url
= "{}/ns/v1/verticalscale/{nsr_id}".format(
350 self
.endpoint_url
, nsr_id
=nsr_id
352 async with aiohttp
.ClientSession() as session
:
353 self
.logger
.debug("NG-RO POST %s %s", url
, payload_req
)
354 async with session
.post(
355 url
, headers
=self
.headers_req
, data
=payload_req
357 response_text
= await response
.read()
359 "POST {} [{}] {}".format(
360 url
, response
.status
, response_text
[:100]
363 if response
.status
>= 300:
364 raise NgRoException(response_text
, http_code
=response
.status
)
365 return self
._parse
_yaml
(response_text
, response
=True)
366 except (aiohttp
.ClientOSError
, aiohttp
.ClientError
) as e
:
367 raise NgRoException(e
, http_code
=504)
368 except asyncio
.TimeoutError
:
369 raise NgRoException("Timeout", http_code
=504)
372 def _parse_yaml(descriptor
, response
=False):
374 return yaml
.safe_load(descriptor
)
375 except yaml
.YAMLError
as exc
:
377 if hasattr(exc
, "problem_mark"):
378 mark
= exc
.problem_mark
379 error_pos
= " at line:{} column:{}s".format(
380 mark
.line
+ 1, mark
.column
+ 1
382 error_text
= "yaml format error" + error_pos
384 raise NgRoException("reponse with " + error_text
)
385 raise NgRoException(error_text
)