ac44495c856c378526d9eaeabc86c0f85fb0f2ee
[osm/osmclient.git] / 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 """
18 OSM vnfd API handling
19 """
20
21 from osmclient.common.exceptions import NotFound
22 from osmclient.common.exceptions import ClientException
23 from osmclient.common import utils
24 import json
25 import yaml
26 import magic
27 from os.path import basename
28 import logging
29 import os.path
30 from urllib.parse import quote
31 import tarfile
32 from osm_im.validation import Validation as validation_im
33
34
35 class Vnfd(object):
36
37 def __init__(self, http=None, client=None):
38 self._http = http
39 self._client = client
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'
47
48 def list(self, filter=None):
49 self._logger.debug("")
50 self._client.get_token()
51 filter_string = ''
52 if filter:
53 filter_string = '?{}'.format(filter)
54 _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase,filter_string))
55 if resp:
56 return json.loads(resp)
57 return list()
58
59 def get(self, name):
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']:
65 return vnfd
66 else:
67 for vnfd in self.list():
68 if 'name' in vnfd and name == vnfd['name']:
69 return vnfd
70 raise NotFound("vnfd {} not found".format(name))
71
72 def get_individual(self, name):
73 self._logger.debug("")
74 vnfd = self.get(name)
75 # It is redundant, since the previous one already gets the whole vnfpkginfo
76 # The only difference is that a different primitive is exercised
77 try:
78 _, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, vnfd['_id']))
79 #print(yaml.safe_dump(resp))
80 if resp:
81 return json.loads(resp)
82 except NotFound:
83 raise NotFound("vnfd '{}' not found".format(name))
84 raise NotFound("vnfd '{}' not found".format(name))
85
86 def get_thing(self, name, thing, filename):
87 self._logger.debug("")
88 vnfd = self.get(name)
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):
95 if resp:
96 #store in a file
97 return json.loads(resp)
98 #else:
99 # msg = ""
100 # if resp:
101 # try:
102 # msg = json.loads(resp)
103 # except ValueError:
104 # msg = resp
105 # raise ClientException("failed to get {} from {} - {}".format(thing, name, msg))
106
107 def get_descriptor(self, name, filename):
108 self._logger.debug("")
109 self.get_thing(name, 'vnfd', filename)
110
111 def get_package(self, name, filename):
112 self._logger.debug("")
113 self.get_thing(name, 'package_content', filename)
114
115 def get_artifact(self, name, artifact, filename):
116 self._logger.debug("")
117 self.get_thing(name, 'artifacts/{}'.format(artifact), filename)
118
119 def delete(self, name, force=False):
120 self._logger.debug("")
121 self._client.get_token()
122 vnfd = self.get(name)
123 querystring = ''
124 if force:
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))
130 if http_code == 202:
131 print('Deletion in progress')
132 elif http_code == 204:
133 print('Deleted')
134 else:
135 msg = resp or ""
136 # if resp:
137 # try:
138 # msg = json.loads(resp)
139 # except ValueError:
140 # msg = resp
141 raise ClientException("failed to delete vnfd {} - {}".format(name, msg))
142
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)
153 else:
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(
159 filename, mime_type)
160 )
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)
172 else:
173 raise ClientException(
174 "Unexpected MIME type for file {}: MIME type {}".format(
175 filename, mime_type)
176 )
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")
186 descriptor_list = []
187 for member in tar_object:
188 if member.isreg():
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()
197 tar_object.close()
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)
203 vdu_list = []
204 for k in vnfd:
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',[])
209 break;
210 for vdu_number, vdu in enumerate(vdu_list):
211 if override_epa:
212 guest_epa = {}
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'
223 if override_nonepa:
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(";")
230
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)
235 if update_endpoint:
236 http_code, resp = self._http.put_cmd(endpoint=update_endpoint, filename=filename)
237 else:
238 ow_string = ''
239 if special_ow_string:
240 if overwrite:
241 overwrite = "{};{}".format(overwrite,special_ow_string)
242 else:
243 overwrite = special_ow_string
244 if overwrite:
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):
254 if resp:
255 resp = json.loads(resp)
256 if not resp or 'id' not in resp:
257 raise ClientException('unexpected response from server: {}'.format(resp))
258 print(resp['id'])
259 elif http_code == 204:
260 print('Updated')
261 # else:
262 # msg = "Error {}".format(http_code)
263 # if resp:
264 # try:
265 # msg = "{} - {}".format(msg, json.loads(resp))
266 # except ValueError:
267 # msg = "{} - {}".format(msg, resp)
268 # raise ClientException("failed to create/update vnfd - {}".format(msg))
269
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)
276