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 first_vnfd
= vnfd
[k
]['vnfd'][0]
208 vdu_list
= first_vnfd
.get('vdu',[])
210 for vdu_number
, vdu
in enumerate(vdu_list
):
213 guest_epa
["mempage-size"] = "LARGE"
214 guest_epa
["cpu-pinning-policy"] = "DEDICATED"
215 guest_epa
["cpu-thread-pinning-policy"] = "PREFER"
216 guest_epa
["numa-node-policy"] = {}
217 guest_epa
["numa-node-policy"]["node-cnt"] = 1
218 guest_epa
["numa-node-policy"]["mem-policy"] = "STRICT"
219 #guest_epa["numa-node-policy"]["node"] = []
220 #guest_epa["numa-node-policy"]["node"].append({"id": "0", "paired-threads": {"num-paired-threads": 1} })
221 special_ow_string
= "{}vdu.{}.guest-epa={};".format(special_ow_string
,vdu_number
,quote(yaml
.safe_dump(guest_epa
)))
222 headers
['Query-String-Format'] = 'yaml'
224 special_ow_string
= "{}vdu.{}.guest-epa=;".format(special_ow_string
,vdu_number
)
225 if override_paravirt
:
226 for iface_number
in range(len(vdu
['interface'])):
227 special_ow_string
= "{}vdu.{}.interface.{}.virtual-interface.type=PARAVIRT;".format(
228 special_ow_string
,vdu_number
,iface_number
)
229 special_ow_string
= special_ow_string
.rstrip(";")
231 headers
["Content-File-MD5"] = utils
.md5(filename
)
232 http_header
= ['{}: {}'.format(key
,val
)
233 for (key
,val
) in list(headers
.items())]
234 self
._http
.set_http_header(http_header
)
236 http_code
, resp
= self
._http
.put_cmd(endpoint
=update_endpoint
, filename
=filename
)
239 if special_ow_string
:
241 overwrite
= "{};{}".format(overwrite
,special_ow_string
)
243 overwrite
= special_ow_string
245 ow_string
= '?{}'.format(overwrite
)
246 self
._apiResource
= '/vnf_packages_content'
247 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
248 self
._apiVersion
, self
._apiResource
)
249 endpoint
= '{}{}'.format(self
._apiBase
,ow_string
)
250 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, filename
=filename
)
251 #print('HTTP CODE: {}'.format(http_code))
252 #print('RESP: {}'.format(resp))
253 if http_code
in (200, 201, 202):
255 resp
= json
.loads(resp
)
256 if not resp
or 'id' not in resp
:
257 raise ClientException('unexpected response from server: {}'.format(resp
))
259 elif http_code
== 204:
262 # msg = "Error {}".format(http_code)
265 # msg = "{} - {}".format(msg, json.loads(resp))
267 # msg = "{} - {}".format(msg, resp)
268 # raise ClientException("failed to create/update vnfd - {}".format(msg))
270 def update(self
, name
, filename
):
271 self
._logger
.debug("")
272 self
._client
.get_token()
273 vnfd
= self
.get(name
)
274 endpoint
= '{}/{}/package_content'.format(self
._apiBase
, vnfd
['_id'])
275 self
.create(filename
=filename
, update_endpoint
=endpoint
)