Fix bug 1721: charms can include reference to .charm files
[osm/osmclient.git] / osmclient / sol005 / nsi.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 NSI (Network Slice Instance) API handling
19 """
20
21 from osmclient.common import utils
22 from osmclient.common import wait as WaitForStatus
23 from osmclient.common.exceptions import ClientException
24 from osmclient.common.exceptions import NotFound
25 import yaml
26 import json
27 import logging
28
29
30 class Nsi(object):
31 def __init__(self, http=None, client=None):
32 self._http = http
33 self._client = client
34 self._logger = logging.getLogger("osmclient")
35 self._apiName = "/nsilcm"
36 self._apiVersion = "/v1"
37 self._apiResource = "/netslice_instances_content"
38 self._apiBase = "{}{}{}".format(
39 self._apiName, self._apiVersion, self._apiResource
40 )
41
42 # NSI '--wait' option
43 def _wait(self, id, wait_time, deleteFlag=False):
44 self._logger.debug("")
45 self._client.get_token()
46 # Endpoint to get operation status
47 apiUrlStatus = "{}{}{}".format(
48 self._apiName, self._apiVersion, "/nsi_lcm_op_occs"
49 )
50 # Wait for status for NSI instance creation/update/deletion
51 if isinstance(wait_time, bool):
52 wait_time = WaitForStatus.TIMEOUT_NSI_OPERATION
53 WaitForStatus.wait_for_status(
54 "NSI",
55 str(id),
56 wait_time,
57 apiUrlStatus,
58 self._http.get2_cmd,
59 deleteFlag=deleteFlag,
60 )
61
62 def list(self, filter=None):
63 """Returns a list of NSI"""
64 self._logger.debug("")
65 self._client.get_token()
66 filter_string = ""
67 if filter:
68 filter_string = "?{}".format(filter)
69 _, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string))
70 if resp:
71 return json.loads(resp)
72 return list()
73
74 def get(self, name):
75 """Returns an NSI based on name or id"""
76 self._logger.debug("")
77 self._client.get_token()
78 if utils.validate_uuid4(name):
79 for nsi in self.list():
80 if name == nsi["_id"]:
81 return nsi
82 else:
83 for nsi in self.list():
84 if name == nsi["name"]:
85 return nsi
86 raise NotFound("nsi {} not found".format(name))
87
88 def get_individual(self, name):
89 self._logger.debug("")
90 nsi_id = name
91 self._client.get_token()
92 if not utils.validate_uuid4(name):
93 for nsi in self.list():
94 if name == nsi["name"]:
95 nsi_id = nsi["_id"]
96 break
97 try:
98 _, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, nsi_id))
99 # resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, nsi_id))
100 # print(yaml.safe_dump(resp))
101 if resp:
102 return json.loads(resp)
103 except NotFound:
104 raise NotFound("nsi '{}' not found".format(name))
105 raise NotFound("nsi {} not found".format(name))
106
107 def delete(self, name, force=False, wait=False):
108 self._logger.debug("")
109 nsi = self.get(name)
110 querystring = ""
111 if force:
112 querystring = "?FORCE=True"
113 http_code, resp = self._http.delete_cmd(
114 "{}/{}{}".format(self._apiBase, nsi["_id"], querystring)
115 )
116 # print('HTTP CODE: {}'.format(http_code))
117 # print('RESP: {}'.format(resp))
118 if http_code == 202:
119 if wait and resp:
120 resp = json.loads(resp)
121 # Wait for status for NSI instance deletion
122 # For the 'delete' operation, '_id' is used
123 self._wait(resp.get("_id"), wait, deleteFlag=True)
124 else:
125 print("Deletion in progress")
126 elif http_code == 204:
127 print("Deleted")
128 else:
129 msg = resp or ""
130 # if resp:
131 # try:
132 # msg = json.loads(resp)
133 # except ValueError:
134 # msg = resp
135 raise ClientException("failed to delete nsi {} - {}".format(name, msg))
136
137 def create(
138 self,
139 nst_name,
140 nsi_name,
141 account,
142 config=None,
143 ssh_keys=None,
144 description="default description",
145 admin_status="ENABLED",
146 wait=False,
147 ):
148
149 self._logger.debug("")
150 self._client.get_token()
151 nst = self._client.nst.get(nst_name)
152
153 vim_account_id = {}
154
155 def get_vim_account_id(vim_account):
156 self._logger.debug("")
157 if vim_account_id.get(vim_account):
158 return vim_account_id[vim_account]
159
160 vim = self._client.vim.get(vim_account)
161 if vim is None:
162 raise NotFound("cannot find vim account '{}'".format(vim_account))
163 vim_account_id[vim_account] = vim["_id"]
164 return vim["_id"]
165
166 nsi = {}
167 nsi["nstId"] = nst["_id"]
168 nsi["nsiName"] = nsi_name
169 nsi["nsiDescription"] = description
170 nsi["vimAccountId"] = get_vim_account_id(account)
171 # nsi['userdata'] = {}
172 # nsi['userdata']['key1']='value1'
173 # nsi['userdata']['key2']='value2'
174
175 if ssh_keys is not None:
176 # ssh_keys is comma separate list
177 # ssh_keys_format = []
178 # for key in ssh_keys.split(','):
179 # ssh_keys_format.append({'key-pair-ref': key})
180 #
181 # ns['ssh-authorized-key'] = ssh_keys_format
182 nsi["ssh_keys"] = []
183 for pubkeyfile in ssh_keys.split(","):
184 with open(pubkeyfile, "r") as f:
185 nsi["ssh_keys"].append(f.read())
186 if config:
187 nsi_config = yaml.safe_load(config)
188 if "netslice-vld" in nsi_config:
189 for vld in nsi_config["netslice-vld"]:
190 if vld.get("vim-network-name"):
191 if isinstance(vld["vim-network-name"], dict):
192 vim_network_name_dict = {}
193 for vim_account, vim_net in list(
194 vld["vim-network-name"].items()
195 ):
196 vim_network_name_dict[
197 get_vim_account_id(vim_account)
198 ] = vim_net
199 vld["vim-network-name"] = vim_network_name_dict
200 nsi["netslice-vld"] = nsi_config["netslice-vld"]
201 if "netslice-subnet" in nsi_config:
202 for nssubnet in nsi_config["netslice-subnet"]:
203 if "vld" in nssubnet:
204 for vld in nssubnet["vld"]:
205 if vld.get("vim-network-name"):
206 if isinstance(vld["vim-network-name"], dict):
207 vim_network_name_dict = {}
208 for vim_account, vim_net in list(
209 vld["vim-network-name"].items()
210 ):
211 vim_network_name_dict[
212 get_vim_account_id(vim_account)
213 ] = vim_net
214 vld["vim-network-name"] = vim_network_name_dict
215 if "vnf" in nssubnet:
216 for vnf in nssubnet["vnf"]:
217 if vnf.get("vim_account"):
218 vnf["vimAccountId"] = get_vim_account_id(
219 vnf.pop("vim_account")
220 )
221 nsi["netslice-subnet"] = nsi_config["netslice-subnet"]
222
223 if "additionalParamsForNsi" in nsi_config:
224 nsi["additionalParamsForNsi"] = nsi_config.pop("additionalParamsForNsi")
225 if not isinstance(nsi["additionalParamsForNsi"], dict):
226 raise ValueError(
227 "Error at --config 'additionalParamsForNsi' must be a dictionary"
228 )
229 if "additionalParamsForSubnet" in nsi_config:
230 nsi["additionalParamsForSubnet"] = nsi_config.pop(
231 "additionalParamsForSubnet"
232 )
233 if not isinstance(nsi["additionalParamsForSubnet"], list):
234 raise ValueError(
235 "Error at --config 'additionalParamsForSubnet' must be a list"
236 )
237 for additional_param_subnet in nsi["additionalParamsForSubnet"]:
238 if not isinstance(additional_param_subnet, dict):
239 raise ValueError(
240 "Error at --config 'additionalParamsForSubnet' items must be dictionaries"
241 )
242 if not additional_param_subnet.get("id"):
243 raise ValueError(
244 "Error at --config 'additionalParamsForSubnet' items must contain subnet 'id'"
245 )
246 if not additional_param_subnet.get(
247 "additionalParamsForNs"
248 ) and not additional_param_subnet.get("additionalParamsForVnf"):
249 raise ValueError(
250 "Error at --config 'additionalParamsForSubnet' items must contain "
251 "'additionalParamsForNs' and/or 'additionalParamsForVnf'"
252 )
253 if "timeout_nsi_deploy" in nsi_config:
254 nsi["timeout_nsi_deploy"] = nsi_config.pop("timeout_nsi_deploy")
255
256 # print(yaml.safe_dump(nsi))
257 try:
258 self._apiResource = "/netslice_instances_content"
259 self._apiBase = "{}{}{}".format(
260 self._apiName, self._apiVersion, self._apiResource
261 )
262 headers = self._client._headers
263 headers["Content-Type"] = "application/yaml"
264 http_header = [
265 "{}: {}".format(key, val) for (key, val) in list(headers.items())
266 ]
267 self._http.set_http_header(http_header)
268 http_code, resp = self._http.post_cmd(
269 endpoint=self._apiBase, postfields_dict=nsi
270 )
271 # print('HTTP CODE: {}'.format(http_code))
272 # print('RESP: {}'.format(resp))
273 # if http_code in (200, 201, 202, 204):
274 if resp:
275 resp = json.loads(resp)
276 if not resp or "id" not in resp:
277 raise ClientException(
278 "unexpected response from server - {} ".format(resp)
279 )
280 if wait:
281 # Wait for status for NSI instance creation
282 self._wait(resp.get("nsilcmop_id"), wait)
283 print(resp["id"])
284 # else:
285 # msg = ""
286 # if resp:
287 # try:
288 # msg = json.loads(resp)
289 # except ValueError:
290 # msg = resp
291 # raise ClientException(msg)
292 except ClientException as exc:
293 message = "failed to create nsi: {} nst: {}\nerror:\n{}".format(
294 nsi_name, nst_name, str(exc)
295 )
296 raise ClientException(message)
297
298 def list_op(self, name, filter=None):
299 """Returns the list of operations of a NSI"""
300 self._logger.debug("")
301 nsi = self.get(name)
302 try:
303 self._apiResource = "/nsi_lcm_op_occs"
304 self._apiBase = "{}{}{}".format(
305 self._apiName, self._apiVersion, self._apiResource
306 )
307 filter_string = ""
308 if filter:
309 filter_string = "&{}".format(filter)
310 http_code, resp = self._http.get2_cmd(
311 "{}?netsliceInstanceId={}{}".format(
312 self._apiBase, nsi["_id"], filter_string
313 )
314 )
315 # print('HTTP CODE: {}'.format(http_code))
316 # print('RESP: {}'.format(resp))
317 # if http_code == 200:
318 if resp:
319 resp = json.loads(resp)
320 return resp
321 else:
322 raise ClientException("unexpected response from server")
323 # else:
324 # msg = ""
325 # if resp:
326 # try:
327 # resp = json.loads(resp)
328 # msg = resp['detail']
329 # except ValueError:
330 # msg = resp
331 # raise ClientException(msg)
332 except ClientException as exc:
333 message = "failed to get operation list of NSI {}:\nerror:\n{}".format(
334 name, str(exc)
335 )
336 raise ClientException(message)
337
338 def get_op(self, operationId):
339 """Returns the status of an operation"""
340 self._logger.debug("")
341 self._client.get_token()
342 try:
343 self._apiResource = "/nsi_lcm_op_occs"
344 self._apiBase = "{}{}{}".format(
345 self._apiName, self._apiVersion, self._apiResource
346 )
347 http_code, resp = self._http.get2_cmd(
348 "{}/{}".format(self._apiBase, operationId)
349 )
350 # print('HTTP CODE: {}'.format(http_code))
351 # print('RESP: {}'.format(resp))
352 # if http_code == 200:
353 if resp:
354 resp = json.loads(resp)
355 return resp
356 else:
357 raise ClientException("unexpected response from server")
358 # else:
359 # msg = ""
360 # if resp:
361 # try:
362 # resp = json.loads(resp)
363 # msg = resp['detail']
364 # except ValueError:
365 # msg = resp
366 # raise ClientException(msg)
367 except ClientException as exc:
368 message = "failed to get status of operation {}:\nerror:\n{}".format(
369 operationId, str(exc)
370 )
371 raise ClientException(message)
372
373 def exec_op(self, name, op_name, op_data=None):
374 """Executes an operation on a NSI"""
375 self._logger.debug("")
376 nsi = self.get(name)
377 try:
378 self._apiResource = "/netslice_instances"
379 self._apiBase = "{}{}{}".format(
380 self._apiName, self._apiVersion, self._apiResource
381 )
382 endpoint = "{}/{}/{}".format(self._apiBase, nsi["_id"], op_name)
383 # print('OP_NAME: {}'.format(op_name))
384 # print('OP_DATA: {}'.format(json.dumps(op_data)))
385 http_code, resp = self._http.post_cmd(
386 endpoint=endpoint, postfields_dict=op_data
387 )
388 # print('HTTP CODE: {}'.format(http_code))
389 # print('RESP: {}'.format(resp))
390 # if http_code in (200, 201, 202, 204):
391 if resp:
392 resp = json.loads(resp)
393 if not resp or "id" not in resp:
394 raise ClientException(
395 "unexpected response from server - {}".format(resp)
396 )
397 print(resp["id"])
398 # else:
399 # msg = ""
400 # if resp:
401 # try:
402 # msg = json.loads(resp)
403 # except ValueError:
404 # msg = resp
405 # raise ClientException(msg)
406 except ClientException as exc:
407 message = "failed to exec operation {}:\nerror:\n{}".format(name, str(exc))
408 raise ClientException(message)