1 # Copyright 2018 Telefonica
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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
18 OSM NSI (Network Slice Instance) API handling
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
32 def __init__(self
, http
=None, client
=None):
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
)
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(
57 deleteFlag
=deleteFlag
)
59 def list(self
, filter=None):
60 """Returns a list of NSI
62 self
._logger
.debug("")
63 self
._client
.get_token()
66 filter_string
= '?{}'.format(filter)
67 _
, resp
= self
._http
.get2_cmd('{}{}'.format(self
._apiBase
,filter_string
))
69 return json
.loads(resp
)
73 """Returns an NSI based on name or id
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']:
82 for nsi
in self
.list():
83 if name
== nsi
['name']:
85 raise NotFound("nsi {} not found".format(name
))
87 def get_individual(self
, name
):
88 self
._logger
.debug("")
90 self
._client
.get_token()
91 if not utils
.validate_uuid4(name
):
92 for nsi
in self
.list():
93 if name
== nsi
['name']:
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))
101 return json
.loads(resp
)
103 raise NotFound("nsi '{}' not found".format(name
))
104 raise NotFound("nsi {} not found".format(name
))
106 def delete(self
, name
, force
=False, wait
=False):
107 self
._logger
.debug("")
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))
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)
123 print('Deletion in progress')
124 elif http_code
== 204:
130 # msg = json.loads(resp)
133 raise ClientException("failed to delete nsi {} - {}".format(name
, msg
))
135 def create(self
, nst_name
, nsi_name
, account
, config
=None,
136 ssh_keys
=None, description
='default description',
137 admin_status
='ENABLED', wait
=False):
139 self
._logger
.debug("")
140 self
._client
.get_token()
141 nst
= self
._client
.nst
.get(nst_name
)
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
]
150 vim
= self
._client
.vim
.get(vim_account
)
152 raise NotFound("cannot find vim account '{}'".format(vim_account
))
153 vim_account_id
[vim_account
] = vim
['_id']
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'
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})
171 # ns['ssh-authorized-key'] = ssh_keys_format
173 for pubkeyfile
in ssh_keys
.split(','):
174 with
open(pubkeyfile
, 'r') as f
:
175 nsi
['ssh_keys'].append(f
.read())
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"]
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")
223 # print(yaml.safe_dump(nsi))
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
,
235 #print('HTTP CODE: {}'.format(http_code))
236 #print('RESP: {}'.format(resp))
237 #if http_code in (200, 201, 202, 204):
239 resp
= json
.loads(resp
)
240 if not resp
or 'id' not in resp
:
241 raise ClientException('unexpected response from server - {} '.format(
244 # Wait for status for NSI instance creation
245 self
._wait
(resp
.get('nsilcmop_id'), wait
)
251 # msg = json.loads(resp)
254 # raise ClientException(msg)
255 except ClientException
as exc
:
256 message
="failed to create nsi: {} nst: {}\nerror:\n{}".format(
260 raise ClientException(message
)
262 def list_op(self
, name
, filter=None):
263 """Returns the list of operations of a NSI
265 self
._logger
.debug("")
268 self
._apiResource
= '/nsi_lcm_op_occs'
269 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
270 self
._apiVersion
, self
._apiResource
)
273 filter_string
= '&{}'.format(filter)
274 http_code
, resp
= self
._http
.get2_cmd('{}?netsliceInstanceId={}{}'.format(
275 self
._apiBase
, nsi
['_id'],
277 #print('HTTP CODE: {}'.format(http_code))
278 #print('RESP: {}'.format(resp))
279 #if http_code == 200:
281 resp
= json
.loads(resp
)
284 raise ClientException('unexpected response from server')
289 # resp = json.loads(resp)
290 # msg = resp['detail']
293 # raise ClientException(msg)
294 except ClientException
as exc
:
295 message
="failed to get operation list of NSI {}:\nerror:\n{}".format(
298 raise ClientException(message
)
300 def get_op(self
, operationId
):
301 """Returns the status of an operation
303 self
._logger
.debug("")
304 self
._client
.get_token()
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:
314 resp
= json
.loads(resp
)
317 raise ClientException('unexpected response from server')
322 # resp = json.loads(resp)
323 # msg = resp['detail']
326 # raise ClientException(msg)
327 except ClientException
as exc
:
328 message
="failed to get status of operation {}:\nerror:\n{}".format(
331 raise ClientException(message
)
333 def exec_op(self
, name
, op_name
, op_data
=None):
334 """Executes an operation on a NSI
336 self
._logger
.debug("")
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):
350 resp
= json
.loads(resp
)
351 if not resp
or 'id' not in resp
:
352 raise ClientException('unexpected response from server - {}'.format(
359 # msg = json.loads(resp)
362 # raise ClientException(msg)
363 except ClientException
as exc
:
364 message
="failed to exec operation {}:\nerror:\n{}".format(
367 raise ClientException(message
)