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
31 def __init__(self
, http
=None, client
=None):
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
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"
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(
59 deleteFlag
=deleteFlag
,
62 def list(self
, filter=None):
63 """Returns a list of NSI"""
64 self
._logger
.debug("")
65 self
._client
.get_token()
68 filter_string
= "?{}".format(filter)
69 _
, resp
= self
._http
.get2_cmd("{}{}".format(self
._apiBase
, filter_string
))
71 return json
.loads(resp
)
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"]:
83 for nsi
in self
.list():
84 if name
== nsi
["name"]:
86 raise NotFound("nsi {} not found".format(name
))
88 def get_individual(self
, name
):
89 self
._logger
.debug("")
91 self
._client
.get_token()
92 if not utils
.validate_uuid4(name
):
93 for nsi
in self
.list():
94 if name
== nsi
["name"]:
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))
102 return json
.loads(resp
)
104 raise NotFound("nsi '{}' not found".format(name
))
105 raise NotFound("nsi {} not found".format(name
))
107 def delete(self
, name
, force
=False, wait
=False):
108 self
._logger
.debug("")
112 querystring
= "?FORCE=True"
113 http_code
, resp
= self
._http
.delete_cmd(
114 "{}/{}{}".format(self
._apiBase
, nsi
["_id"], querystring
)
116 # print('HTTP CODE: {}'.format(http_code))
117 # print('RESP: {}'.format(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)
125 print("Deletion in progress")
126 elif http_code
== 204:
132 # msg = json.loads(resp)
135 raise ClientException("failed to delete nsi {} - {}".format(name
, msg
))
144 description
="default description",
145 admin_status
="ENABLED",
148 self
._logger
.debug("")
149 self
._client
.get_token()
150 nst
= self
._client
.nst
.get(nst_name
)
154 def get_vim_account_id(vim_account
):
155 self
._logger
.debug("")
156 if vim_account_id
.get(vim_account
):
157 return vim_account_id
[vim_account
]
159 vim
= self
._client
.vim
.get(vim_account
)
161 raise NotFound("cannot find vim account '{}'".format(vim_account
))
162 vim_account_id
[vim_account
] = vim
["_id"]
166 nsi
["nstId"] = nst
["_id"]
167 nsi
["nsiName"] = nsi_name
168 nsi
["nsiDescription"] = description
169 nsi
["vimAccountId"] = get_vim_account_id(account
)
170 # nsi['userdata'] = {}
171 # nsi['userdata']['key1']='value1'
172 # nsi['userdata']['key2']='value2'
174 if ssh_keys
is not None:
175 # ssh_keys is comma separate list
176 # ssh_keys_format = []
177 # for key in ssh_keys.split(','):
178 # ssh_keys_format.append({'key-pair-ref': key})
180 # ns['ssh-authorized-key'] = ssh_keys_format
182 for pubkeyfile
in ssh_keys
.split(","):
183 with
open(pubkeyfile
, "r") as f
:
184 nsi
["ssh_keys"].append(f
.read())
186 nsi_config
= yaml
.safe_load(config
)
187 if "netslice-vld" in nsi_config
:
188 for vld
in nsi_config
["netslice-vld"]:
189 if vld
.get("vim-network-name"):
190 if isinstance(vld
["vim-network-name"], dict):
191 vim_network_name_dict
= {}
192 for vim_account
, vim_net
in list(
193 vld
["vim-network-name"].items()
195 vim_network_name_dict
[
196 get_vim_account_id(vim_account
)
198 vld
["vim-network-name"] = vim_network_name_dict
199 nsi
["netslice-vld"] = nsi_config
["netslice-vld"]
200 if "netslice-subnet" in nsi_config
:
201 for nssubnet
in nsi_config
["netslice-subnet"]:
202 if "vld" in nssubnet
:
203 for vld
in nssubnet
["vld"]:
204 if vld
.get("vim-network-name"):
205 if isinstance(vld
["vim-network-name"], dict):
206 vim_network_name_dict
= {}
207 for vim_account
, vim_net
in list(
208 vld
["vim-network-name"].items()
210 vim_network_name_dict
[
211 get_vim_account_id(vim_account
)
213 vld
["vim-network-name"] = vim_network_name_dict
214 if "vnf" in nssubnet
:
215 for vnf
in nssubnet
["vnf"]:
216 if vnf
.get("vim_account"):
217 vnf
["vimAccountId"] = get_vim_account_id(
218 vnf
.pop("vim_account")
220 nsi
["netslice-subnet"] = nsi_config
["netslice-subnet"]
222 if "additionalParamsForNsi" in nsi_config
:
223 nsi
["additionalParamsForNsi"] = nsi_config
.pop("additionalParamsForNsi")
224 if not isinstance(nsi
["additionalParamsForNsi"], dict):
226 "Error at --config 'additionalParamsForNsi' must be a dictionary"
228 if "additionalParamsForSubnet" in nsi_config
:
229 nsi
["additionalParamsForSubnet"] = nsi_config
.pop(
230 "additionalParamsForSubnet"
232 if not isinstance(nsi
["additionalParamsForSubnet"], list):
234 "Error at --config 'additionalParamsForSubnet' must be a list"
236 for additional_param_subnet
in nsi
["additionalParamsForSubnet"]:
237 if not isinstance(additional_param_subnet
, dict):
239 "Error at --config 'additionalParamsForSubnet' items must be dictionaries"
241 if not additional_param_subnet
.get("id"):
243 "Error at --config 'additionalParamsForSubnet' items must contain subnet 'id'"
245 if not additional_param_subnet
.get(
246 "additionalParamsForNs"
247 ) and not additional_param_subnet
.get("additionalParamsForVnf"):
249 "Error at --config 'additionalParamsForSubnet' items must contain "
250 "'additionalParamsForNs' and/or 'additionalParamsForVnf'"
252 if "timeout_nsi_deploy" in nsi_config
:
253 nsi
["timeout_nsi_deploy"] = nsi_config
.pop("timeout_nsi_deploy")
255 # print(yaml.safe_dump(nsi))
257 self
._apiResource
= "/netslice_instances_content"
258 self
._apiBase
= "{}{}{}".format(
259 self
._apiName
, self
._apiVersion
, self
._apiResource
261 headers
= self
._client
._headers
262 headers
["Content-Type"] = "application/yaml"
263 self
._http
.set_http_header(headers
)
264 http_code
, resp
= self
._http
.post_cmd(
265 endpoint
=self
._apiBase
, postfields_dict
=nsi
267 # print('HTTP CODE: {}'.format(http_code))
268 # print('RESP: {}'.format(resp))
269 # if http_code in (200, 201, 202, 204):
271 resp
= json
.loads(resp
)
272 if not resp
or "id" not in resp
:
273 raise ClientException(
274 "unexpected response from server - {} ".format(resp
)
277 # Wait for status for NSI instance creation
278 self
._wait
(resp
.get("nsilcmop_id"), wait
)
284 # msg = json.loads(resp)
287 # raise ClientException(msg)
288 except ClientException
as exc
:
289 message
= "failed to create nsi: {} nst: {}\nerror:\n{}".format(
290 nsi_name
, nst_name
, str(exc
)
292 raise ClientException(message
)
294 def list_op(self
, name
, filter=None):
295 """Returns the list of operations of a NSI"""
296 self
._logger
.debug("")
299 self
._apiResource
= "/nsi_lcm_op_occs"
300 self
._apiBase
= "{}{}{}".format(
301 self
._apiName
, self
._apiVersion
, self
._apiResource
305 filter_string
= "&{}".format(filter)
306 http_code
, resp
= self
._http
.get2_cmd(
307 "{}?netsliceInstanceId={}{}".format(
308 self
._apiBase
, nsi
["_id"], filter_string
311 # print('HTTP CODE: {}'.format(http_code))
312 # print('RESP: {}'.format(resp))
313 # if http_code == 200:
315 resp
= json
.loads(resp
)
318 raise ClientException("unexpected response from server")
323 # resp = json.loads(resp)
324 # msg = resp['detail']
327 # raise ClientException(msg)
328 except ClientException
as exc
:
329 message
= "failed to get operation list of NSI {}:\nerror:\n{}".format(
332 raise ClientException(message
)
334 def get_op(self
, operationId
):
335 """Returns the status of an operation"""
336 self
._logger
.debug("")
337 self
._client
.get_token()
339 self
._apiResource
= "/nsi_lcm_op_occs"
340 self
._apiBase
= "{}{}{}".format(
341 self
._apiName
, self
._apiVersion
, self
._apiResource
343 http_code
, resp
= self
._http
.get2_cmd(
344 "{}/{}".format(self
._apiBase
, operationId
)
346 # print('HTTP CODE: {}'.format(http_code))
347 # print('RESP: {}'.format(resp))
348 # if http_code == 200:
350 resp
= json
.loads(resp
)
353 raise ClientException("unexpected response from server")
358 # resp = json.loads(resp)
359 # msg = resp['detail']
362 # raise ClientException(msg)
363 except ClientException
as exc
:
364 message
= "failed to get status of operation {}:\nerror:\n{}".format(
365 operationId
, str(exc
)
367 raise ClientException(message
)
369 def exec_op(self
, name
, op_name
, op_data
=None):
370 """Executes an operation on a NSI"""
371 self
._logger
.debug("")
374 self
._apiResource
= "/netslice_instances"
375 self
._apiBase
= "{}{}{}".format(
376 self
._apiName
, self
._apiVersion
, self
._apiResource
378 endpoint
= "{}/{}/{}".format(self
._apiBase
, nsi
["_id"], op_name
)
379 # print('OP_NAME: {}'.format(op_name))
380 # print('OP_DATA: {}'.format(json.dumps(op_data)))
381 http_code
, resp
= self
._http
.post_cmd(
382 endpoint
=endpoint
, postfields_dict
=op_data
384 # print('HTTP CODE: {}'.format(http_code))
385 # print('RESP: {}'.format(resp))
386 # if http_code in (200, 201, 202, 204):
388 resp
= json
.loads(resp
)
389 if not resp
or "id" not in resp
:
390 raise ClientException(
391 "unexpected response from server - {}".format(resp
)
398 # msg = json.loads(resp)
401 # raise ClientException(msg)
402 except ClientException
as exc
:
403 message
= "failed to exec operation {}:\nerror:\n{}".format(name
, str(exc
))
404 raise ClientException(message
)