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