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