Code Coverage

Cobertura Coverage Report > osm_lcm >

ng_ro.py

Trend

File Coverage summary

NameClassesLinesConditionals
ng_ro.py
100%
1/1
26%
54/205
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
ng_ro.py
26%
54/205
N/A

Source

osm_lcm/ng_ro.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 ##
5 # Copyright 2020 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 1 """
22 asyncio RO python client to interact with New Generation RO server
23 """
24
25 1 import asyncio
26 1 import aiohttp
27 1 import yaml
28 1 import logging
29
30 1 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com"
31 1 __date__ = "$09-Jan-2018 09:09:48$"
32 1 __version__ = "0.1.2"
33 1 version_date = "2020-05-08"
34
35
36 1 class NgRoException(Exception):
37 1     def __init__(self, message, http_code=400):
38         """Common Exception for all RO client exceptions"""
39 1         self.http_code = http_code
40 1         Exception.__init__(self, message)
41
42
43 1 class NgRoClient:
44 1     headers_req = {"Accept": "application/yaml", "content-type": "application/yaml"}
45 1     client_to_RO = {
46         "tenant": "tenants",
47         "vim": "datacenters",
48         "vim_account": "datacenters",
49         "sdn": "sdn_controllers",
50         "vnfd": "vnfs",
51         "nsd": "scenarios",
52         "wim": "wims",
53         "wim_account": "wims",
54         "ns": "instances",
55     }
56 1     mandatory_for_create = {
57         "tenant": ("name",),
58         "vnfd": ("name", "id"),
59         "nsd": ("name", "id"),
60         "ns": ("name", "scenario", "datacenter"),
61         "vim": ("name", "vim_url"),
62         "wim": ("name", "wim_url"),
63         "vim_account": (),
64         "wim_account": (),
65         "sdn": ("name", "type"),
66     }
67 1     timeout_large = 120
68 1     timeout_short = 30
69
70 1     def __init__(self, loop, uri, **kwargs):
71 1         self.loop = loop
72 1         self.endpoint_url = uri
73 1         if not self.endpoint_url.endswith("/"):
74 1             self.endpoint_url += "/"
75 1         if not self.endpoint_url.startswith("http"):
76 1             self.endpoint_url = "http://" + self.endpoint_url
77
78 1         self.username = kwargs.get("username")
79 1         self.password = kwargs.get("password")
80 1         self.tenant_id_name = kwargs.get("tenant")
81 1         self.tenant = None
82 1         self.datacenter_id_name = kwargs.get("datacenter")
83 1         self.datacenter = None
84 1         logger_name = kwargs.get("logger_name", "lcm.ro")
85 1         self.logger = logging.getLogger(logger_name)
86 1         if kwargs.get("loglevel"):
87 1             self.logger.setLevel(kwargs["loglevel"])
88
89 1     async def deploy(self, nsr_id, target):
90         """
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
99         """
100 0         try:
101 0             if isinstance(target, str):
102 0                 target = self._parse_yaml(target)
103 0             payload_req = yaml.safe_dump(target)
104
105 0             url = "{}/ns/v1/deploy/{nsr_id}".format(self.endpoint_url, nsr_id=nsr_id)
106 0             async with aiohttp.ClientSession(loop=self.loop) as session:
107 0                 self.logger.debug("NG-RO POST %s %s", url, payload_req)
108                 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
109 0                 async with session.post(
110                     url, headers=self.headers_req, data=payload_req
111                 ) as response:
112 0                     response_text = await response.read()
113 0                     self.logger.debug(
114                         "POST {} [{}] {}".format(
115                             url, response.status, response_text[:100]
116                         )
117                     )
118 0                     if response.status >= 300:
119 0                         raise NgRoException(response_text, http_code=response.status)
120 0                     return self._parse_yaml(response_text, response=True)
121 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
122 0             raise NgRoException(e, http_code=504)
123 0         except asyncio.TimeoutError:
124 0             raise NgRoException("Timeout", http_code=504)
125
126 1     async def migrate(self, nsr_id, target):
127         """
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
132         """
133 0         try:
134 0             if isinstance(target, str):
135 0                 target = self._parse_yaml(target)
136 0             payload_req = yaml.safe_dump(target)
137
138 0             url = "{}/ns/v1/migrate/{nsr_id}".format(self.endpoint_url, nsr_id=nsr_id)
139 0             async with aiohttp.ClientSession(loop=self.loop) as session:
140 0                 self.logger.debug("NG-RO POST %s %s", url, payload_req)
141                 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
142 0                 async with session.post(
143                     url, headers=self.headers_req, data=payload_req
144                 ) as response:
145 0                     response_text = await response.read()
146 0                     self.logger.debug(
147                         "POST {} [{}] {}".format(
148                             url, response.status, response_text[:100]
149                         )
150                     )
151 0                     if response.status >= 300:
152 0                         raise NgRoException(response_text, http_code=response.status)
153 0                     return self._parse_yaml(response_text, response=True)
154 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
155 0             raise NgRoException(e, http_code=504)
156 0         except asyncio.TimeoutError:
157 0             raise NgRoException("Timeout", http_code=504)
158
159 1     async def operate(self, nsr_id, target, operation_type):
160         """
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
166         """
167 0         try:
168 0             if isinstance(target, str):
169 0                 target = self._parse_yaml(target)
170 0             payload_req = yaml.safe_dump(target)
171
172 0             url = "{}/ns/v1/{operation_type}/{nsr_id}".format(
173                 self.endpoint_url, operation_type=operation_type, nsr_id=nsr_id
174             )
175 0             async with aiohttp.ClientSession(loop=self.loop) as session:
176 0                 self.logger.debug("NG-RO POST %s %s", url, payload_req)
177                 # timeout = aiohttp.ClientTimeout(total=self.timeout_large)
178 0                 async with session.post(
179                     url, headers=self.headers_req, data=payload_req
180                 ) as response:
181 0                     response_text = await response.read()
182 0                     self.logger.debug(
183                         "POST {} [{}] {}".format(
184                             url, response.status, response_text[:100]
185                         )
186                     )
187 0                     if response.status >= 300:
188 0                         raise NgRoException(response_text, http_code=response.status)
189 0                     return self._parse_yaml(response_text, response=True)
190 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
191 0             raise NgRoException(e, http_code=504)
192 0         except asyncio.TimeoutError:
193 0             raise NgRoException("Timeout", http_code=504)
194
195 1     async def status(self, nsr_id, action_id):
196 1         try:
197 1             url = "{}/ns/v1/deploy/{nsr_id}/{action_id}".format(
198                 self.endpoint_url, nsr_id=nsr_id, action_id=action_id
199             )
200 1             async with aiohttp.ClientSession(loop=self.loop) as session:
201 1                 self.logger.debug("GET %s", url)
202                 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
203 1                 async with session.get(url, headers=self.headers_req) as response:
204 0                     response_text = await response.read()
205 0                     self.logger.debug(
206                         "GET {} [{}] {}".format(
207                             url, response.status, response_text[:100]
208                         )
209                     )
210 0                     if response.status >= 300:
211 0                         raise NgRoException(response_text, http_code=response.status)
212 0                     return self._parse_yaml(response_text, response=True)
213
214 1         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
215 1             raise NgRoException(e, http_code=504)
216 0         except asyncio.TimeoutError:
217 0             raise NgRoException("Timeout", http_code=504)
218
219 1     async def delete(self, nsr_id):
220 0         try:
221 0             url = "{}/ns/v1/deploy/{nsr_id}".format(self.endpoint_url, nsr_id=nsr_id)
222 0             async with aiohttp.ClientSession(loop=self.loop) as session:
223 0                 self.logger.debug("DELETE %s", url)
224                 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
225 0                 async with session.delete(url, headers=self.headers_req) as response:
226 0                     self.logger.debug("DELETE {} [{}]".format(url, response.status))
227 0                     if response.status >= 300:
228 0                         raise NgRoException(
229                             "Delete {}".format(nsr_id), http_code=response.status
230                         )
231 0                     return
232
233 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
234 0             raise NgRoException(e, http_code=504)
235 0         except asyncio.TimeoutError:
236 0             raise NgRoException("Timeout", http_code=504)
237
238 1     async def get_version(self):
239         """
240         Obtain RO server version.
241         :return: a list with integers ["major", "minor", "release"]. Raises NgRoException on Error,
242         """
243 0         try:
244 0             response_text = ""
245 0             async with aiohttp.ClientSession(loop=self.loop) as session:
246 0                 url = "{}/version".format(self.endpoint_url)
247 0                 self.logger.debug("RO GET %s", url)
248                 # timeout = aiohttp.ClientTimeout(total=self.timeout_short)
249 0                 async with session.get(url, headers=self.headers_req) as response:
250 0                     response_text = await response.read()
251 0                     self.logger.debug(
252                         "GET {} [{}] {}".format(
253                             url, response.status, response_text[:100]
254                         )
255                     )
256 0                     if response.status >= 300:
257 0                         raise NgRoException(response_text, http_code=response.status)
258
259 0                 for word in str(response_text).split(" "):
260 0                     if "." in word:
261 0                         version_text, _, _ = word.partition("-")
262 0                         return version_text
263 0                 raise NgRoException(
264                     "Got invalid version text: '{}'".format(response_text),
265                     http_code=500,
266                 )
267 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
268 0             raise NgRoException(e, http_code=504)
269 0         except asyncio.TimeoutError:
270 0             raise NgRoException("Timeout", http_code=504)
271 0         except Exception as e:
272 0             raise NgRoException(
273                 "Got invalid version text: '{}'; causing exception {}".format(
274                     response_text, e
275                 ),
276                 http_code=500,
277             )
278
279 1     async def recreate(self, nsr_id, target):
280         """
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
289         """
290 0         try:
291 0             if isinstance(target, str):
292 0                 target = self._parse_yaml(target)
293 0             payload_req = yaml.safe_dump(target)
294
295 0             url = "{}/ns/v1/recreate/{nsr_id}".format(self.endpoint_url, nsr_id=nsr_id)
296 0             async with aiohttp.ClientSession(loop=self.loop) as session:
297 0                 self.logger.debug("NG-RO POST %s %s", url, payload_req)
298 0                 async with session.post(
299                     url, headers=self.headers_req, data=payload_req
300                 ) as response:
301 0                     response_text = await response.read()
302 0                     self.logger.debug(
303                         "POST {} [{}] {}".format(
304                             url, response.status, response_text[:100]
305                         )
306                     )
307 0                     if response.status >= 300:
308 0                         raise NgRoException(response_text, http_code=response.status)
309 0                     return self._parse_yaml(response_text, response=True)
310 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
311 0             raise NgRoException(e, http_code=504)
312 0         except asyncio.TimeoutError:
313 0             raise NgRoException("Timeout", http_code=504)
314
315 1     async def recreate_status(self, nsr_id, action_id):
316 0         try:
317 0             url = "{}/ns/v1/recreate/{nsr_id}/{action_id}".format(
318                 self.endpoint_url, nsr_id=nsr_id, action_id=action_id
319             )
320 0             async with aiohttp.ClientSession(loop=self.loop) as session:
321 0                 self.logger.debug("GET %s", url)
322 0                 async with session.get(url, headers=self.headers_req) as response:
323 0                     response_text = await response.read()
324 0                     self.logger.debug(
325                         "GET {} [{}] {}".format(
326                             url, response.status, response_text[:100]
327                         )
328                     )
329 0                     if response.status >= 300:
330 0                         raise NgRoException(response_text, http_code=response.status)
331 0                     return self._parse_yaml(response_text, response=True)
332
333 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
334 0             raise NgRoException(e, http_code=504)
335 0         except asyncio.TimeoutError:
336 0             raise NgRoException("Timeout", http_code=504)
337
338 1     async def vertical_scale(self, nsr_id, target):
339         """
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
344         """
345 0         try:
346 0             if isinstance(target, str):
347 0                 target = self._parse_yaml(target)
348 0             payload_req = yaml.safe_dump(target)
349
350 0             url = "{}/ns/v1/verticalscale/{nsr_id}".format(
351                 self.endpoint_url, nsr_id=nsr_id
352             )
353 0             async with aiohttp.ClientSession(loop=self.loop) as session:
354 0                 self.logger.debug("NG-RO POST %s %s", url, payload_req)
355 0                 async with session.post(
356                     url, headers=self.headers_req, data=payload_req
357                 ) as response:
358 0                     response_text = await response.read()
359 0                     self.logger.debug(
360                         "POST {} [{}] {}".format(
361                             url, response.status, response_text[:100]
362                         )
363                     )
364 0                     if response.status >= 300:
365 0                         raise NgRoException(response_text, http_code=response.status)
366 0                     return self._parse_yaml(response_text, response=True)
367 0         except (aiohttp.ClientOSError, aiohttp.ClientError) as e:
368 0             raise NgRoException(e, http_code=504)
369 0         except asyncio.TimeoutError:
370 0             raise NgRoException("Timeout", http_code=504)
371
372 1     @staticmethod
373 1     def _parse_yaml(descriptor, response=False):
374 0         try:
375 0             return yaml.safe_load(descriptor)
376 0         except yaml.YAMLError as exc:
377 0             error_pos = ""
378 0             if hasattr(exc, "problem_mark"):
379 0                 mark = exc.problem_mark
380 0                 error_pos = " at line:{} column:{}s".format(
381                     mark.line + 1, mark.column + 1
382                 )
383 0             error_text = "yaml format error" + error_pos
384 0             if response:
385 0                 raise NgRoException("reponse with " + error_text)
386 0             raise NgRoException(error_text)