Code Coverage

Cobertura Coverage Report > osmclient.sol005 >

vnfd.py

Trend

Classes100%
 
Lines13%
   
Conditionals100%
 

File Coverage summary

NameClassesLinesConditionals
vnfd.py
100%
1/1
13%
26/208
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
vnfd.py
13%
26/208
N/A

Source

osmclient/sol005/vnfd.py
1 # Copyright 2018 Telefonica
2 #
3 # All Rights Reserved.
4 #
5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
6 #    not use this file except in compliance with the License. You may obtain
7 #    a copy of the License at
8 #
9 #         http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #    Unless required by applicable law or agreed to in writing, software
12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 #    License for the specific language governing permissions and limitations
15 #    under the License.
16
17 1 """
18 OSM vnfd API handling
19 """
20
21 1 from osmclient.common.exceptions import NotFound
22 1 from osmclient.common.exceptions import ClientException
23 1 from osmclient.common import utils
24 1 import json
25 1 import yaml
26 1 import magic
27 1 from os.path import basename
28 1 import logging
29 1 import os.path
30 1 from urllib.parse import quote
31 1 import tarfile
32 1 from osm_im.validation import Validation as validation_im
33 1 from zipfile import ZipFile
34
35
36 1 class Vnfd(object):
37 1     def __init__(self, http=None, client=None):
38 0         self._http = http
39 0         self._client = client
40 0         self._logger = logging.getLogger("osmclient")
41 0         self._apiName = "/vnfpkgm"
42 0         self._apiVersion = "/v1"
43 0         self._apiResource = "/vnf_packages"
44 0         self._apiBase = "{}{}{}".format(
45             self._apiName, self._apiVersion, self._apiResource
46         )
47
48 1     def list(self, filter=None):
49 0         self._logger.debug("")
50 0         self._client.get_token()
51 0         filter_string = ""
52 0         if filter:
53 0             filter_string = "?{}".format(filter)
54 0         _, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string))
55
56 0         if resp:
57 0             return json.loads(resp)
58 0         return list()
59
60 1     def get(self, name):
61 0         self._logger.debug("")
62 0         self._client.get_token()
63 0         if utils.validate_uuid4(name):
64 0             for vnfd in self.list():
65 0                 if name == vnfd["_id"]:
66 0                     return vnfd
67         else:
68 0             for vnfd in self.list():
69 0                 if "product-name" in vnfd and name == vnfd["product-name"]:
70 0                     return vnfd
71 0                 elif "name" in vnfd and name == vnfd["name"]:
72 0                     return vnfd
73 0         raise NotFound("vnfd {} not found".format(name))
74
75 1     def get_individual(self, name):
76 0         self._logger.debug("")
77 0         vnfd = self.get(name)
78         # It is redundant, since the previous one already gets the whole vnfpkginfo
79         # The only difference is that a different primitive is exercised
80 0         try:
81 0             _, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, vnfd["_id"]))
82 0             if resp:
83 0                 return json.loads(resp)
84 0         except NotFound:
85 0             raise NotFound("vnfd '{}' not found".format(name))
86 0         raise NotFound("vnfd '{}' not found".format(name))
87
88 1     def get_thing(self, name, thing, filename):
89 0         self._logger.debug("")
90 0         vnfd = self.get(name)
91 0         headers = self._client._headers
92 0         headers["Accept"] = "application/binary"
93 0         http_code, resp = self._http.get2_cmd(
94             "{}/{}/{}".format(self._apiBase, vnfd["_id"], thing)
95         )
96
97 0         if resp:
98 0             return json.loads(resp)
99
100 1     def get_descriptor(self, name, filename):
101 0         self._logger.debug("")
102 0         self.get_thing(name, "vnfd", filename)
103
104 1     def get_package(self, name, filename):
105 0         self._logger.debug("")
106 0         self.get_thing(name, "package_content", filename)
107
108 1     def get_artifact(self, name, artifact, filename):
109 0         self._logger.debug("")
110 0         self.get_thing(name, "artifacts/{}".format(artifact), filename)
111
112 1     def delete(self, name, force=False):
113 0         self._logger.debug("")
114 0         self._client.get_token()
115 0         vnfd = self.get(name)
116 0         querystring = ""
117 0         if force:
118 0             querystring = "?FORCE=True"
119 0         http_code, resp = self._http.delete_cmd(
120             "{}/{}{}".format(self._apiBase, vnfd["_id"], querystring)
121         )
122
123 0         if http_code == 202:
124 0             print("Deletion in progress")
125 0         elif http_code == 204:
126 0             print("Deleted")
127         else:
128 0             msg = resp or ""
129 0             raise ClientException("failed to delete vnfd {} - {}".format(name, msg))
130
131 1     def create(
132         self,
133         filename,
134         overwrite=None,
135         update_endpoint=None,
136         skip_charm_build=False,
137         override_epa=False,
138         override_nonepa=False,
139         override_paravirt=False,
140     ):
141 0         self._logger.debug("")
142 0         if os.path.isdir(filename):
143 0             filename = filename.rstrip("/")
144 0             filename = self._client.package_tool.build(
145                 filename, skip_validation=False, skip_charm_build=skip_charm_build
146             )
147
148 0             print("Uploading package {}".format(filename))
149 0             self.create(
150                 filename,
151                 overwrite=overwrite,
152                 update_endpoint=update_endpoint,
153                 override_epa=override_epa,
154                 override_nonepa=override_nonepa,
155                 override_paravirt=override_paravirt,
156             )
157         else:
158 0             self._client.get_token()
159 0             mime_type = magic.from_file(filename, mime=True)
160 0             if mime_type is None:
161 0                 raise ClientException(
162                     "Unexpected MIME type for file {}: MIME type {}".format(
163                         filename, mime_type
164                     )
165                 )
166 0             headers = self._client._headers
167 0             headers["Content-Filename"] = basename(filename)
168 0             if mime_type in ["application/yaml", "text/plain", "application/json"]:
169 0                 headers["Content-Type"] = "text/plain"
170 0             elif mime_type in ["application/gzip", "application/x-gzip"]:
171 0                 headers["Content-Type"] = "application/gzip"
172 0             elif mime_type in ["application/zip"]:
173 0                 headers["Content-Type"] = "application/zip"
174             else:
175 0                 raise ClientException(
176                     "Unexpected MIME type for file {}: MIME type {}".format(
177                         filename, mime_type
178                     )
179                 )
180
181 0             special_override_string = ""
182 0             if override_epa or override_nonepa or override_paravirt:
183                 # If override for EPA, non-EPA or paravirt is required, get the descriptor data
184 0                 descriptor_data = None
185 0                 if mime_type in ["application/yaml", "text/plain", "application/json"]:
186 0                     with open(filename) as df:
187 0                         descriptor_data = df.read()
188 0                 elif mime_type in ["application/gzip", "application/x-gzip"]:
189 0                     tar_object = tarfile.open(filename, "r:gz")
190 0                     descriptor_list = []
191 0                     for member in tar_object:
192 0                         if member.isreg():
193 0                             if "/" not in os.path.dirname(
194                                 member.name
195                             ) and member.name.endswith(".yaml"):
196 0                                 descriptor_list.append(member.name)
197 0                     if len(descriptor_list) > 1:
198 0                         raise ClientException(
199                             "Found more than one potential descriptor in the tar.gz file"
200                         )
201 0                     elif len(descriptor_list) == 0:
202 0                         raise ClientException(
203                             "No descriptor was found in the tar.gz file"
204                         )
205 0                     with tar_object.extractfile(descriptor_list[0]) as df:
206 0                         descriptor_data = df.read()
207 0                     tar_object.close()
208 0                 elif mime_type in ["application/zip"]:
209 0                     package_zip = ZipFile(filename)
210 0                     package_files = package_zip.infolist()
211
212 0                     descriptors = []
213 0                     if (
214                         "Definitions" in package_files
215                         and "TOSCA-Metadata" in package_files
216                     ):
217 0                         descriptors = [
218                             definition
219                             for definition in package_files
220                             if "Definitions/" in definition
221                             and (
222                                 definition.endswith(".yaml")
223                                 or definition.endswith(".yml")
224                             )
225                         ]
226                     else:
227 0                         descriptors = [
228                             definition
229                             for definition in package_files
230                             if definition.endswith(".yaml")
231                             or definition.endswith(".yml")
232                         ]
233 0                     if len(descriptors) < 1:
234 0                         raise ClientException(
235                             "No descriptor found on this package, OSM was expecting at least 1"
236                         )
237 0                     descriptor_data = package_zip.open(descriptors[0])
238 0                     package_zip.close()
239
240 0                 if not descriptor_data:
241 0                     raise ClientException("Descriptor could not be read")
242 0                 desc_type, vnfd = validation_im().yaml_validation(descriptor_data)
243 0                 validation_im().pyangbind_validation(desc_type, vnfd)
244
245 0                 vnfd = yaml.safe_load(descriptor_data)
246 0                 vcd_list = []
247 0                 vdu_list = []
248 0                 for k in vnfd:
249                     # Get only the first descriptor in case there are many in the yaml file
250                     # k can be vnfd or  etsi-nfv-vnfd:vnfd. This check is skipped
251 0                     first_vnfd = vnfd.get(k, {})
252 0                     vcd_list = first_vnfd.get("virtual-compute-desc", [])
253 0                     vdu_list = first_vnfd.get("vdu", [])
254 0                     break
255
256 0                 for vcd_number, vcd in enumerate(vcd_list):
257 0                     if override_epa:
258 0                         virtual_memory = vcd["virtual-memory"]
259 0                         virtual_memory["mempage-size"] = "LARGE"
260 0                         virtual_memory["numa-enabled"] = True
261 0                         virtual_memory["numa-node-policy"] = {
262                             "node-cnt": 1,
263                             "mem-policy": "STRICT",
264                         }
265 0                         virtual_cpu = vcd["virtual-cpu"]
266 0                         virtual_cpu["pinning"] = {
267                             "policy": "static",
268                             "thread-policy": "PREFER",
269                         }
270
271 0                         cpu_override_string = (
272                             "virtual-compute-desc.{}.virtual-cpu={};".format(
273                                 vcd_number, quote(yaml.safe_dump(virtual_cpu))
274                             )
275                         )
276 0                         memory_override_string = (
277                             "virtual-compute-desc.{}.virtual-memory={};".format(
278                                 vcd_number, quote(yaml.safe_dump(virtual_memory))
279                             )
280                         )
281 0                         special_override_string = "{}{}{}".format(
282                             special_override_string,
283                             cpu_override_string,
284                             memory_override_string,
285                         )
286
287 0                     headers["Query-String-Format"] = "yaml"
288 0                     if override_nonepa:
289 0                         virtual_memory = vcd["virtual-memory"]
290 0                         virtual_memory["mempage-size"] = ""
291 0                         virtual_memory["numa-enabled"] = ""
292 0                         virtual_memory["numa-node-policy"] = {}
293 0                         virtual_cpu = vcd["virtual-cpu"]
294 0                         virtual_cpu["pinning"] = {}
295
296 0                         cpu_override_string = (
297                             "virtual-compute-desc.{}.virtual-cpu={};".format(
298                                 vcd_number, quote(yaml.safe_dump(virtual_cpu))
299                             )
300                         )
301 0                         memory_override_string = (
302                             "virtual-compute-desc.{}.virtual-memory={};".format(
303                                 vcd_number, quote(yaml.safe_dump(virtual_memory))
304                             )
305                         )
306 0                         special_override_string = "{}{}{}".format(
307                             special_override_string,
308                             cpu_override_string,
309                             memory_override_string,
310                         )
311
312 0                 if override_paravirt:
313 0                     for vdu_number, vdu in enumerate(vdu_list):
314 0                         for cpd_number, cpd in enumerate(vdu["int-cpd"]):
315 0                             for vnir_number, vnir in enumerate(
316                                 cpd["virtual-network-interface-requirement"]
317                             ):
318 0                                 special_override_string = (
319                                     "{}vdu.{}.int-cpd.{}.virtual-network-interface-"
320                                     "requirement.{}.virtual-interface.type="
321                                     "PARAVIRT;".format(
322                                         special_override_string,
323                                         vdu_number,
324                                         cpd_number,
325                                         vnir_number,
326                                     )
327                                 )
328
329 0                 special_override_string = special_override_string.rstrip(";")
330
331 0             headers["Content-File-MD5"] = utils.md5(filename)
332 0             http_header = [
333                 "{}: {}".format(key, val) for (key, val) in list(headers.items())
334             ]
335
336 0             self._http.set_http_header(http_header)
337 0             if update_endpoint:
338 0                 http_code, resp = self._http.put_cmd(
339                     endpoint=update_endpoint, filename=filename
340                 )
341             else:
342 0                 ow_string = ""
343 0                 if special_override_string:
344 0                     if overwrite:
345 0                         overwrite = "{};{}".format(overwrite, special_override_string)
346                     else:
347 0                         overwrite = special_override_string
348
349 0                 if overwrite:
350 0                     ow_string = "?{}".format(overwrite)
351 0                 self._apiResource = "/vnf_packages_content"
352 0                 self._apiBase = "{}{}{}".format(
353                     self._apiName, self._apiVersion, self._apiResource
354                 )
355 0                 endpoint = "{}{}".format(self._apiBase, ow_string)
356 0                 http_code, resp = self._http.post_cmd(
357                     endpoint=endpoint, filename=filename
358                 )
359
360 0             if http_code in (200, 201, 202):
361 0                 if resp:
362 0                     resp = json.loads(resp)
363 0                 if not resp or "id" not in resp:
364 0                     raise ClientException(
365                         "unexpected response from server: {}".format(resp)
366                     )
367 0                 print(resp["id"])
368 0             elif http_code == 204:
369 0                 print("Updated")
370
371 1     def update(self, name, filename):
372 0         self._logger.debug("")
373 0         self._client.get_token()
374 0         vnfd = self.get(name)
375 0         endpoint = "{}/{}/package_content".format(self._apiBase, vnfd["_id"])
376 0         self.create(filename=filename, update_endpoint=endpoint)