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
37 def __init__(self
, http
=None, client
=None):
40 self
._logger
= logging
.getLogger('osmclient')
41 self
._apiName
= '/vnfpkgm'
42 self
._apiVersion
= '/v1'
43 self
._apiResource
= '/vnf_packages'
44 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
45 self
._apiVersion
, self
._apiResource
)
46 #self._apiBase='/vnfds'
48 def list(self
, filter=None):
49 self
._logger
.debug("")
50 self
._client
.get_token()
53 filter_string
= '?{}'.format(filter)
54 _
, 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 'name' in vnfd
and name
== vnfd
['name']:
70 raise NotFound("vnfd {} not found".format(name
))
72 def get_individual(self
, name
):
73 self
._logger
.debug("")
75 # It is redundant, since the previous one already gets the whole vnfpkginfo
76 # The only difference is that a different primitive is exercised
78 _
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, vnfd
['_id']))
79 #print(yaml.safe_dump(resp))
81 return json
.loads(resp
)
83 raise NotFound("vnfd '{}' not found".format(name
))
84 raise NotFound("vnfd '{}' not found".format(name
))
86 def get_thing(self
, name
, thing
, filename
):
87 self
._logger
.debug("")
89 headers
= self
._client
._headers
90 headers
['Accept'] = 'application/binary'
91 http_code
, resp
= self
._http
.get2_cmd('{}/{}/{}'.format(self
._apiBase
, vnfd
['_id'], thing
))
92 #print('HTTP CODE: {}'.format(http_code))
93 #print('RESP: {}'.format(resp))
94 #if http_code in (200, 201, 202, 204):
97 return json
.loads(resp
)
102 # msg = json.loads(resp)
105 # raise ClientException("failed to get {} from {} - {}".format(thing, name, msg))
107 def get_descriptor(self
, name
, filename
):
108 self
._logger
.debug("")
109 self
.get_thing(name
, 'vnfd', filename
)
111 def get_package(self
, name
, filename
):
112 self
._logger
.debug("")
113 self
.get_thing(name
, 'package_content', filename
)
115 def get_artifact(self
, name
, artifact
, filename
):
116 self
._logger
.debug("")
117 self
.get_thing(name
, 'artifacts/{}'.format(artifact
), filename
)
119 def delete(self
, name
, force
=False):
120 self
._logger
.debug("")
121 self
._client
.get_token()
122 vnfd
= self
.get(name
)
125 querystring
= '?FORCE=True'
126 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
127 vnfd
['_id'], querystring
))
128 #print('HTTP CODE: {}'.format(http_code))
129 #print('RESP: {}'.format(resp))
131 print('Deletion in progress')
132 elif http_code
== 204:
138 # msg = json.loads(resp)
141 raise ClientException("failed to delete vnfd {} - {}".format(name
, msg
))
143 def create(self
, filename
, overwrite
=None, update_endpoint
=None, skip_charm_build
=False,
144 override_epa
=False, override_nonepa
=False, override_paravirt
=False):
145 self
._logger
.debug("")
146 if os
.path
.isdir(filename
):
147 filename
= filename
.rstrip('/')
148 filename
= self
._client
.package_tool
.build(filename
, skip_validation
=False, skip_charm_build
=skip_charm_build
)
149 print('Uploading package {}'.format(filename
))
150 self
.create(filename
, overwrite
=overwrite
, update_endpoint
=update_endpoint
,
151 override_epa
=override_epa
, override_nonepa
=override_nonepa
,
152 override_paravirt
=override_paravirt
)
154 self
._client
.get_token()
155 mime_type
= magic
.from_file(filename
, mime
=True)
156 if mime_type
is None:
157 raise ClientException(
158 "Unexpected MIME type for file {}: MIME type {}".format(
161 headers
= self
._client
._headers
162 headers
['Content-Filename'] = basename(filename
)
163 if mime_type
in ['application/yaml', 'text/plain', 'application/json']:
164 headers
['Content-Type'] = 'text/plain'
165 elif mime_type
in ['application/gzip', 'application/x-gzip']:
166 headers
['Content-Type'] = 'application/gzip'
167 #headers['Content-Type'] = 'application/binary'
168 # Next three lines are to be removed in next version
169 #headers['Content-Filename'] = basename(filename)
170 #file_size = stat(filename).st_size
171 #headers['Content-Range'] = 'bytes 0-{}/{}'.format(file_size - 1, file_size)
173 raise ClientException(
174 "Unexpected MIME type for file {}: MIME type {}".format(
177 special_ow_string
= ''
178 if override_epa
or override_nonepa
or override_paravirt
:
179 # If override for EPA, non-EPA or paravirt is required, get the descriptor data
180 descriptor_data
= None
181 if mime_type
in ['application/yaml', 'text/plain', 'application/json']:
182 with
open(filename
) as df
:
183 descriptor_data
= df
.read()
184 elif mime_type
in ['application/gzip', 'application/x-gzip']:
185 tar_object
= tarfile
.open(filename
, "r:gz")
187 for member
in tar_object
:
189 if '/' not in os
.path
.dirname(member
.name
) and member
.name
.endswith('.yaml'):
190 descriptor_list
.append(member
.name
)
191 if len(descriptor_list
) > 1:
192 raise ClientException('Found more than one potential descriptor in the tar.gz file')
193 elif len(descriptor_list
) == 0:
194 raise ClientException('No descriptor was found in the tar.gz file')
195 with tar_object
.extractfile(descriptor_list
[0]) as df
:
196 descriptor_data
= df
.read()
198 if not descriptor_data
:
199 raise ClientException('Descriptor could not be read')
200 desc_type
, vnfd
= validation_im
.yaml_validation(self
, descriptor_data
)
201 validation_im
.pyangbind_validation(self
, desc_type
, vnfd
)
202 vnfd
= yaml
.safe_load(descriptor_data
)
205 # Get only the first descriptor in case there are many in the yaml file
206 # k can be vnfd:vnfd-catalog or vnfd-catalog. This check is skipped
207 vdu_list
= vnfd
[k
]['vnfd'][0]['vdu']
209 for vdu_number
, vdu
in enumerate(vdu_list
):
212 guest_epa
["mempage-size"] = "LARGE"
213 guest_epa
["cpu-pinning-policy"] = "DEDICATED"
214 guest_epa
["cpu-thread-pinning-policy"] = "PREFER"
215 guest_epa
["numa-node-policy"] = {}
216 guest_epa
["numa-node-policy"]["node-cnt"] = 1
217 guest_epa
["numa-node-policy"]["mem-policy"] = "STRICT"
218 #guest_epa["numa-node-policy"]["node"] = []
219 #guest_epa["numa-node-policy"]["node"].append({"id": "0", "paired-threads": {"num-paired-threads": 1} })
220 special_ow_string
= "{}vdu.{}.guest-epa={};".format(special_ow_string
,vdu_number
,quote(yaml
.safe_dump(guest_epa
)))
221 headers
['Query-String-Format'] = 'yaml'
223 special_ow_string
= "{}vdu.{}.guest-epa=;".format(special_ow_string
,vdu_number
)
224 if override_paravirt
:
225 for iface_number
in range(len(vdu
['interface'])):
226 special_ow_string
= "{}vdu.{}.interface.{}.virtual-interface.type=PARAVIRT;".format(
227 special_ow_string
,vdu_number
,iface_number
)
228 special_ow_string
= special_ow_string
.rstrip(";")
230 headers
["Content-File-MD5"] = utils
.md5(filename
)
231 http_header
= ['{}: {}'.format(key
,val
)
232 for (key
,val
) in list(headers
.items())]
233 self
._http
.set_http_header(http_header
)
235 http_code
, resp
= self
._http
.put_cmd(endpoint
=update_endpoint
, filename
=filename
)
238 if special_ow_string
:
240 overwrite
= "{};{}".format(overwrite
,special_ow_string
)
242 overwrite
= special_ow_string
244 ow_string
= '?{}'.format(overwrite
)
245 self
._apiResource
= '/vnf_packages_content'
246 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
247 self
._apiVersion
, self
._apiResource
)
248 endpoint
= '{}{}'.format(self
._apiBase
,ow_string
)
249 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, filename
=filename
)
250 #print('HTTP CODE: {}'.format(http_code))
251 #print('RESP: {}'.format(resp))
252 if http_code
in (200, 201, 202):
254 resp
= json
.loads(resp
)
255 if not resp
or 'id' not in resp
:
256 raise ClientException('unexpected response from server: {}'.format(resp
))
258 elif http_code
== 204:
261 # msg = "Error {}".format(http_code)
264 # msg = "{} - {}".format(msg, json.loads(resp))
266 # msg = "{} - {}".format(msg, resp)
267 # raise ClientException("failed to create/update vnfd - {}".format(msg))
269 def update(self
, name
, filename
):
270 self
._logger
.debug("")
271 self
._client
.get_token()
272 vnfd
= self
.get(name
)
273 endpoint
= '{}/{}/package_content'.format(self
._apiBase
, vnfd
['_id'])
274 self
.create(filename
=filename
, update_endpoint
=endpoint
)