1 # Copyright 2018 Telefonica
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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
21 from osmclient
.common
.exceptions
import NotFound
22 from osmclient
.common
.exceptions
import ClientException
23 from osmclient
.common
import utils
27 from os
.path
import basename
30 from urllib
.parse
import quote
32 from osm_im
.validation
import Validation
as validation_im
36 def __init__(self
, http
=None, client
=None):
39 self
._logger
= logging
.getLogger("osmclient")
40 self
._apiName
= "/vnfpkgm"
41 self
._apiVersion
= "/v1"
42 self
._apiResource
= "/vnf_packages"
43 self
._apiBase
= "{}{}{}".format(
44 self
._apiName
, self
._apiVersion
, self
._apiResource
47 def list(self
, filter=None):
48 self
._logger
.debug("")
49 self
._client
.get_token()
52 filter_string
= "?{}".format(filter)
53 _
, resp
= self
._http
.get2_cmd("{}{}".format(self
._apiBase
, filter_string
))
56 return json
.loads(resp
)
60 self
._logger
.debug("")
61 self
._client
.get_token()
62 if utils
.validate_uuid4(name
):
63 for vnfd
in self
.list():
64 if name
== vnfd
["_id"]:
67 for vnfd
in self
.list():
68 if "product-name" in vnfd
and name
== vnfd
["product-name"]:
70 elif "name" in vnfd
and name
== vnfd
["name"]:
72 raise NotFound("vnfd {} not found".format(name
))
74 def get_individual(self
, name
):
75 self
._logger
.debug("")
77 # It is redundant, since the previous one already gets the whole vnfpkginfo
78 # The only difference is that a different primitive is exercised
80 _
, resp
= self
._http
.get2_cmd("{}/{}".format(self
._apiBase
, vnfd
["_id"]))
82 return json
.loads(resp
)
84 raise NotFound("vnfd '{}' not found".format(name
))
85 raise NotFound("vnfd '{}' not found".format(name
))
87 def get_thing(self
, name
, thing
, filename
):
88 self
._logger
.debug("")
90 headers
= self
._client
._headers
91 headers
["Accept"] = "application/binary"
92 http_code
, resp
= self
._http
.get2_cmd(
93 "{}/{}/{}".format(self
._apiBase
, vnfd
["_id"], thing
)
97 return json
.loads(resp
)
99 def get_descriptor(self
, name
, filename
):
100 self
._logger
.debug("")
101 self
.get_thing(name
, "vnfd", filename
)
103 def get_package(self
, name
, filename
):
104 self
._logger
.debug("")
105 self
.get_thing(name
, "package_content", filename
)
107 def get_artifact(self
, name
, artifact
, filename
):
108 self
._logger
.debug("")
109 self
.get_thing(name
, "artifacts/{}".format(artifact
), filename
)
111 def delete(self
, name
, force
=False):
112 self
._logger
.debug("")
113 self
._client
.get_token()
114 vnfd
= self
.get(name
)
117 querystring
= "?FORCE=True"
118 http_code
, resp
= self
._http
.delete_cmd(
119 "{}/{}{}".format(self
._apiBase
, vnfd
["_id"], querystring
)
123 print("Deletion in progress")
124 elif http_code
== 204:
128 raise ClientException("failed to delete vnfd {} - {}".format(name
, msg
))
134 update_endpoint
=None,
135 skip_charm_build
=False,
137 override_nonepa
=False,
138 override_paravirt
=False,
140 self
._logger
.debug("")
141 if os
.path
.isdir(filename
):
142 filename
= filename
.rstrip("/")
143 filename
= self
._client
.package_tool
.build(
144 filename
, skip_validation
=False, skip_charm_build
=skip_charm_build
147 print("Uploading package {}".format(filename
))
151 update_endpoint
=update_endpoint
,
152 override_epa
=override_epa
,
153 override_nonepa
=override_nonepa
,
154 override_paravirt
=override_paravirt
,
157 self
._client
.get_token()
158 mime_type
= magic
.from_file(filename
, mime
=True)
159 if mime_type
is None:
160 raise ClientException(
161 "Unexpected MIME type for file {}: MIME type {}".format(
165 headers
= self
._client
._headers
166 headers
["Content-Filename"] = basename(filename
)
167 if mime_type
in ["application/yaml", "text/plain", "application/json"]:
168 headers
["Content-Type"] = "text/plain"
169 elif mime_type
in ["application/gzip", "application/x-gzip"]:
170 headers
["Content-Type"] = "application/gzip"
172 raise ClientException(
173 "Unexpected MIME type for file {}: MIME type {}".format(
178 special_override_string
= ""
179 if override_epa
or override_nonepa
or override_paravirt
:
180 # If override for EPA, non-EPA or paravirt is required, get the descriptor data
181 descriptor_data
= None
182 if mime_type
in ["application/yaml", "text/plain", "application/json"]:
183 with
open(filename
) as df
:
184 descriptor_data
= df
.read()
185 elif mime_type
in ["application/gzip", "application/x-gzip"]:
186 tar_object
= tarfile
.open(filename
, "r:gz")
188 for member
in tar_object
:
190 if "/" not in os
.path
.dirname(
192 ) and member
.name
.endswith(".yaml"):
193 descriptor_list
.append(member
.name
)
194 if len(descriptor_list
) > 1:
195 raise ClientException(
196 "Found more than one potential descriptor in the tar.gz file"
198 elif len(descriptor_list
) == 0:
199 raise ClientException(
200 "No descriptor was found in the tar.gz file"
202 with tar_object
.extractfile(descriptor_list
[0]) as df
:
203 descriptor_data
= df
.read()
205 if not descriptor_data
:
206 raise ClientException("Descriptor could not be read")
207 desc_type
, vnfd
= validation_im().yaml_validation(descriptor_data
)
208 validation_im().pyangbind_validation(desc_type
, vnfd
)
210 vnfd
= yaml
.safe_load(descriptor_data
)
214 # Get only the first descriptor in case there are many in the yaml file
215 # k can be vnfd or etsi-nfv-vnfd:vnfd. This check is skipped
216 first_vnfd
= vnfd
.get(k
, {})
217 vcd_list
= first_vnfd
.get("virtual-compute-desc", [])
218 vdu_list
= first_vnfd
.get("vdu", [])
221 for vcd_number
, vcd
in enumerate(vcd_list
):
223 virtual_memory
= vcd
["virtual-memory"]
224 virtual_memory
["mempage-size"] = "LARGE"
225 virtual_memory
["numa-enabled"] = True
226 virtual_memory
["numa-node-policy"] = {
228 "mem-policy": "STRICT",
230 virtual_cpu
= vcd
["virtual-cpu"]
231 virtual_cpu
["pinning"] = {
233 "thread-policy": "PREFER",
236 cpu_override_string
= (
237 "virtual-compute-desc.{}.virtual-cpu={};".format(
238 vcd_number
, quote(yaml
.safe_dump(virtual_cpu
))
241 memory_override_string
= (
242 "virtual-compute-desc.{}.virtual-memory={};".format(
243 vcd_number
, quote(yaml
.safe_dump(virtual_memory
))
246 special_override_string
= "{}{}{}".format(
247 special_override_string
,
249 memory_override_string
,
252 headers
["Query-String-Format"] = "yaml"
254 virtual_memory
= vcd
["virtual-memory"]
255 virtual_memory
["mempage-size"] = ""
256 virtual_memory
["numa-enabled"] = ""
257 virtual_memory
["numa-node-policy"] = {}
258 virtual_cpu
= vcd
["virtual-cpu"]
259 virtual_cpu
["pinning"] = {}
261 cpu_override_string
= (
262 "virtual-compute-desc.{}.virtual-cpu={};".format(
263 vcd_number
, quote(yaml
.safe_dump(virtual_cpu
))
266 memory_override_string
= (
267 "virtual-compute-desc.{}.virtual-memory={};".format(
268 vcd_number
, quote(yaml
.safe_dump(virtual_memory
))
271 special_override_string
= "{}{}{}".format(
272 special_override_string
,
274 memory_override_string
,
277 if override_paravirt
:
278 for vdu_number
, vdu
in enumerate(vdu_list
):
279 for cpd_number
, cpd
in enumerate(vdu
["int-cpd"]):
280 for vnir_number
, vnir
in enumerate(
281 cpd
["virtual-network-interface-requirement"]
283 special_override_string
= (
284 "{}vdu.{}.int-cpd.{}.virtual-network-interface-"
285 "requirement.{}.virtual-interface.type="
287 special_override_string
,
294 special_override_string
= special_override_string
.rstrip(";")
296 headers
["Content-File-MD5"] = utils
.md5(filename
)
298 "{}: {}".format(key
, val
) for (key
, val
) in list(headers
.items())
301 self
._http
.set_http_header(http_header
)
303 http_code
, resp
= self
._http
.put_cmd(
304 endpoint
=update_endpoint
, filename
=filename
308 if special_override_string
:
310 overwrite
= "{};{}".format(overwrite
, special_override_string
)
312 overwrite
= special_override_string
315 ow_string
= "?{}".format(overwrite
)
316 self
._apiResource
= "/vnf_packages_content"
317 self
._apiBase
= "{}{}{}".format(
318 self
._apiName
, self
._apiVersion
, self
._apiResource
320 endpoint
= "{}{}".format(self
._apiBase
, ow_string
)
321 http_code
, resp
= self
._http
.post_cmd(
322 endpoint
=endpoint
, filename
=filename
325 if http_code
in (200, 201, 202):
327 resp
= json
.loads(resp
)
328 if not resp
or "id" not in resp
:
329 raise ClientException(
330 "unexpected response from server: {}".format(resp
)
333 elif http_code
== 204:
336 def update(self
, name
, filename
):
337 self
._logger
.debug("")
338 self
._client
.get_token()
339 vnfd
= self
.get(name
)
340 endpoint
= "{}/{}/package_content".format(self
._apiBase
, vnfd
["_id"])
341 self
.create(filename
=filename
, update_endpoint
=endpoint
)