Reformats code on NSD and VNFD clients and fixes small validation bug on VNFD create...
[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
47 def list(self, filter=None):
48 self._logger.debug("")
49 self._client.get_token()
50 filter_string = ''
51 if filter:
52 filter_string = '?{}'.format(filter)
53 _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase, filter_string))
54 if resp:
55 return json.loads(resp)
56 return list()
57
58 def get(self, name):
59 self._logger.debug("")
60 self._client.get_token()
61 if utils.validate_uuid4(name):
62 for vnfd in self.list():
63 if name == vnfd['_id']:
64 return vnfd
65 else:
66 for vnfd in self.list():
67 if 'name' in vnfd and name == vnfd['name']:
68 return vnfd
69 raise NotFound("vnfd {} not found".format(name))
70
71 def get_individual(self, name):
72 self._logger.debug("")
73 vnfd = self.get(name)
74 # It is redundant, since the previous one already gets the whole vnfpkginfo
75 # The only difference is that a different primitive is exercised
76 try:
77 _, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, vnfd['_id']))
78 if resp:
79 return json.loads(resp)
80 except NotFound:
81 raise NotFound("vnfd '{}' not found".format(name))
82 raise NotFound("vnfd '{}' not found".format(name))
83
84 def get_thing(self, name, thing, filename):
85 self._logger.debug("")
86 vnfd = self.get(name)
87 headers = self._client._headers
88 headers['Accept'] = 'application/binary'
89 http_code, resp = self._http.get2_cmd('{}/{}/{}'.format(self._apiBase, vnfd['_id'], thing))
90
91 if resp:
92 return json.loads(resp)
93
94 def get_descriptor(self, name, filename):
95 self._logger.debug("")
96 self.get_thing(name, 'vnfd', filename)
97
98 def get_package(self, name, filename):
99 self._logger.debug("")
100 self.get_thing(name, 'package_content', filename)
101
102 def get_artifact(self, name, artifact, filename):
103 self._logger.debug("")
104 self.get_thing(name, 'artifacts/{}'.format(artifact), filename)
105
106 def delete(self, name, force=False):
107 self._logger.debug("")
108 self._client.get_token()
109 vnfd = self.get(name)
110 querystring = ''
111 if force:
112 querystring = '?FORCE=True'
113 http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
114 vnfd['_id'], querystring))
115
116 if http_code == 202:
117 print('Deletion in progress')
118 elif http_code == 204:
119 print('Deleted')
120 else:
121 msg = resp or ""
122 raise ClientException("failed to delete vnfd {} - {}".format(name, msg))
123
124 def create(self, filename, overwrite=None, update_endpoint=None, skip_charm_build=False,
125 override_epa=False, override_nonepa=False, override_paravirt=False):
126 self._logger.debug("")
127 if os.path.isdir(filename):
128 filename = filename.rstrip('/')
129 filename = self._client.package_tool.build(filename, skip_validation=False,
130 skip_charm_build=skip_charm_build)
131 print('Uploading package {}'.format(filename))
132 self.create(filename, overwrite=overwrite, update_endpoint=update_endpoint,
133 override_epa=override_epa, override_nonepa=override_nonepa,
134 override_paravirt=override_paravirt)
135 else:
136 self._client.get_token()
137 mime_type = magic.from_file(filename, mime=True)
138 if mime_type is None:
139 raise ClientException(
140 "Unexpected MIME type for file {}: MIME type {}".format(
141 filename, mime_type)
142 )
143 headers = self._client._headers
144 headers['Content-Filename'] = basename(filename)
145 if mime_type in ['application/yaml', 'text/plain', 'application/json']:
146 headers['Content-Type'] = 'text/plain'
147 elif mime_type in ['application/gzip', 'application/x-gzip']:
148 headers['Content-Type'] = 'application/gzip'
149 else:
150 raise ClientException(
151 "Unexpected MIME type for file {}: MIME type {}".format(
152 filename, mime_type)
153 )
154 special_ow_string = ''
155 if override_epa or override_nonepa or override_paravirt:
156 # If override for EPA, non-EPA or paravirt is required, get the descriptor data
157 descriptor_data = None
158 if mime_type in ['application/yaml', 'text/plain', 'application/json']:
159 with open(filename) as df:
160 descriptor_data = df.read()
161 elif mime_type in ['application/gzip', 'application/x-gzip']:
162 tar_object = tarfile.open(filename, "r:gz")
163 descriptor_list = []
164 for member in tar_object:
165 if member.isreg():
166 if '/' not in os.path.dirname(member.name) and member.name.endswith('.yaml'):
167 descriptor_list.append(member.name)
168 if len(descriptor_list) > 1:
169 raise ClientException('Found more than one potential descriptor in the tar.gz file')
170 elif len(descriptor_list) == 0:
171 raise ClientException('No descriptor was found in the tar.gz file')
172 with tar_object.extractfile(descriptor_list[0]) as df:
173 descriptor_data = df.read()
174 tar_object.close()
175 if not descriptor_data:
176 raise ClientException('Descriptor could not be read')
177 desc_type, vnfd = validation_im().yaml_validation(descriptor_data)
178 validation_im().pyangbind_validation(desc_type, vnfd)
179 vnfd = yaml.safe_load(descriptor_data)
180 vdu_list = []
181 for k in vnfd:
182 # Get only the first descriptor in case there are many in the yaml file
183 # k can be vnfd:vnfd-catalog or vnfd-catalog. This check is skipped
184 first_vnfd = vnfd[k]['vnfd'][0]
185 vdu_list = first_vnfd.get('vdu', [])
186 break
187 for vdu_number, vdu in enumerate(vdu_list):
188 if override_epa:
189 guest_epa = {}
190 guest_epa["mempage-size"] = "LARGE"
191 guest_epa["cpu-pinning-policy"] = "DEDICATED"
192 guest_epa["cpu-thread-pinning-policy"] = "PREFER"
193 guest_epa["numa-node-policy"] = {}
194 guest_epa["numa-node-policy"]["node-cnt"] = 1
195 guest_epa["numa-node-policy"]["mem-policy"] = "STRICT"
196 special_ow_string = "{}vdu.{}.guest-epa={};".format(special_ow_string, vdu_number,
197 quote(yaml.safe_dump(guest_epa)))
198 headers['Query-String-Format'] = 'yaml'
199 if override_nonepa:
200 special_ow_string = "{}vdu.{}.guest-epa=;".format(special_ow_string, vdu_number)
201 if override_paravirt:
202 for iface_number in range(len(vdu['interface'])):
203 special_ow_string = "{}vdu.{}.interface.{}.virtual-interface.type=PARAVIRT;".format(
204 special_ow_string, vdu_number, iface_number)
205 special_ow_string = special_ow_string.rstrip(";")
206
207 headers["Content-File-MD5"] = utils.md5(filename)
208 http_header = ['{}: {}'.format(key, val)
209 for (key, val) in list(headers.items())]
210 self._http.set_http_header(http_header)
211 if update_endpoint:
212 http_code, resp = self._http.put_cmd(endpoint=update_endpoint, filename=filename)
213 else:
214 ow_string = ''
215 if special_ow_string:
216 if overwrite:
217 overwrite = "{};{}".format(overwrite, special_ow_string)
218 else:
219 overwrite = special_ow_string
220 if overwrite:
221 ow_string = '?{}'.format(overwrite)
222 self._apiResource = '/vnf_packages_content'
223 self._apiBase = '{}{}{}'.format(self._apiName,
224 self._apiVersion, self._apiResource)
225 endpoint = '{}{}'.format(self._apiBase, ow_string)
226 http_code, resp = self._http.post_cmd(endpoint=endpoint, filename=filename)
227
228 if http_code in (200, 201, 202):
229 if resp:
230 resp = json.loads(resp)
231 if not resp or 'id' not in resp:
232 raise ClientException('unexpected response from server: {}'.format(resp))
233 print(resp['id'])
234 elif http_code == 204:
235 print('Updated')
236
237 def update(self, name, filename):
238 self._logger.debug("")
239 self._client.get_token()
240 vnfd = self.get(name)
241 endpoint = '{}/{}/package_content'.format(self._apiBase, vnfd['_id'])
242 self.create(filename=filename, update_endpoint=endpoint)