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, 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(
52 WaitForStatus
.TIMEOUT_NSI_OPERATION
,
55 deleteFlag
=deleteFlag
)
57 def list(self
, filter=None):
58 """Returns a list of NSI
60 self
._logger
.debug("")
61 self
._client
.get_token()
64 filter_string
= '?{}'.format(filter)
65 resp
= self
._http
.get_cmd('{}{}'.format(self
._apiBase
,filter_string
))
71 """Returns an NSI based on name or id
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']:
80 for nsi
in self
.list():
81 if name
== nsi
['name']:
83 raise NotFound("nsi {} not found".format(name
))
85 def get_individual(self
, name
):
86 self
._logger
.debug("")
88 self
._client
.get_token()
89 if not utils
.validate_uuid4(name
):
90 for nsi
in self
.list():
91 if name
== nsi
['name']:
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))
99 raise NotFound("nsi {} not found".format(name
))
101 def delete(self
, name
, force
=False, wait
=False):
102 self
._logger
.debug("")
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))
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)
118 print('Deletion in progress')
119 elif http_code
== 204:
125 msg
= json
.loads(resp
)
128 raise ClientException("failed to delete nsi {} - {}".format(name
, msg
))
130 def create(self
, nst_name
, nsi_name
, account
, config
=None,
131 ssh_keys
=None, description
='default description',
132 admin_status
='ENABLED', wait
=False):
134 self
._logger
.debug("")
135 self
._client
.get_token()
136 nst
= self
._client
.nst
.get(nst_name
)
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
]
145 vim
= self
._client
.vim
.get(vim_account
)
147 raise NotFound("cannot find vim account '{}'".format(vim_account
))
148 vim_account_id
[vim_account
] = vim
['_id']
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'
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})
166 # ns['ssh-authorized-key'] = ssh_keys_format
168 for pubkeyfile
in ssh_keys
.split(','):
169 with
open(pubkeyfile
, 'r') as f
:
170 nsi
['ssh_keys'].append(f
.read())
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"]
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'")
216 # print(yaml.safe_dump(nsi))
218 self
._apiResource
= '/netslice_instances_content'
219 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
220 self
._apiVersion
, self
._apiResource
)
221 headers
= self
._client
._headers
222 headers
['Content-Type'] = 'application/yaml'
223 http_header
= ['{}: {}'.format(key
,val
)
224 for (key
,val
) in list(headers
.items())]
225 self
._http
.set_http_header(http_header
)
226 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
228 #print('HTTP CODE: {}'.format(http_code))
229 #print('RESP: {}'.format(resp))
230 if http_code
in (200, 201, 202, 204):
232 resp
= json
.loads(resp
)
233 if not resp
or 'id' not in resp
:
234 raise ClientException('unexpected response from server - {} '.format(
237 # Wait for status for NSI instance creation
238 self
._wait
(resp
.get('nsilcmop_id'))
244 msg
= json
.loads(resp
)
247 raise ClientException(msg
)
248 except ClientException
as exc
:
249 message
="failed to create nsi: {} nst: {}\nerror:\n{}".format(
253 raise ClientException(message
)
255 def list_op(self
, name
, filter=None):
256 """Returns the list of operations of a NSI
258 self
._logger
.debug("")
261 self
._apiResource
= '/nsi_lcm_op_occs'
262 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
263 self
._apiVersion
, self
._apiResource
)
266 filter_string
= '&{}'.format(filter)
267 http_code
, resp
= self
._http
.get2_cmd('{}?netsliceInstanceId={}'.format(
268 self
._apiBase
, nsi
['_id'],
270 #print('HTTP CODE: {}'.format(http_code))
271 #print('RESP: {}'.format(resp))
274 resp
= json
.loads(resp
)
277 raise ClientException('unexpected response from server')
282 resp
= json
.loads(resp
)
286 raise ClientException(msg
)
287 except ClientException
as exc
:
288 message
="failed to get operation list of NSI {}:\nerror:\n{}".format(
291 raise ClientException(message
)
293 def get_op(self
, operationId
):
294 """Returns the status of an operation
296 self
._logger
.debug("")
297 self
._client
.get_token()
299 self
._apiResource
= '/nsi_lcm_op_occs'
300 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
301 self
._apiVersion
, self
._apiResource
)
302 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
303 #print('HTTP CODE: {}'.format(http_code))
304 #print('RESP: {}'.format(resp))
307 resp
= json
.loads(resp
)
310 raise ClientException('unexpected response from server')
315 resp
= json
.loads(resp
)
319 raise ClientException(msg
)
320 except ClientException
as exc
:
321 message
="failed to get status of operation {}:\nerror:\n{}".format(
324 raise ClientException(message
)
326 def exec_op(self
, name
, op_name
, op_data
=None):
327 """Executes an operation on a NSI
329 self
._logger
.debug("")
332 self
._apiResource
= '/netslice_instances'
333 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
334 self
._apiVersion
, self
._apiResource
)
335 endpoint
= '{}/{}/{}'.format(self
._apiBase
, nsi
['_id'], op_name
)
336 #print('OP_NAME: {}'.format(op_name))
337 #print('OP_DATA: {}'.format(json.dumps(op_data)))
338 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
339 #print('HTTP CODE: {}'.format(http_code))
340 #print('RESP: {}'.format(resp))
341 if http_code
in (200, 201, 202, 204):
343 resp
= json
.loads(resp
)
344 if not resp
or 'id' not in resp
:
345 raise ClientException('unexpected response from server - {}'.format(
352 msg
= json
.loads(resp
)
355 raise ClientException(msg
)
356 except ClientException
as exc
:
357 message
="failed to exec operation {}:\nerror:\n{}".format(
360 raise ClientException(message
)