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
._apiName
= '/nsilcm'
35 self
._apiVersion
= '/v1'
36 self
._apiResource
= '/netslice_instances_content'
37 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
38 self
._apiVersion
, self
._apiResource
)
41 def _wait(self
, id, deleteFlag
=False):
42 self
._client
.get_token()
43 # Endpoint to get operation status
44 apiUrlStatus
= '{}{}{}'.format(self
._apiName
, self
._apiVersion
, '/nsi_lcm_op_occs')
45 # Wait for status for NSI instance creation/update/deletion
46 WaitForStatus
.wait_for_status(
49 WaitForStatus
.TIMEOUT_NSI_OPERATION
,
52 deleteFlag
=deleteFlag
)
54 def list(self
, filter=None):
55 """Returns a list of NSI
57 self
._client
.get_token()
60 filter_string
= '?{}'.format(filter)
61 resp
= self
._http
.get_cmd('{}{}'.format(self
._apiBase
,filter_string
))
67 """Returns an NSI based on name or id
69 self
._client
.get_token()
70 if utils
.validate_uuid4(name
):
71 for nsi
in self
.list():
72 if name
== nsi
['_id']:
75 for nsi
in self
.list():
76 if name
== nsi
['name']:
78 raise NotFound("nsi {} not found".format(name
))
80 def get_individual(self
, name
):
82 self
._client
.get_token()
83 if not utils
.validate_uuid4(name
):
84 for nsi
in self
.list():
85 if name
== nsi
['name']:
88 resp
= self
._http
.get_cmd('{}/{}'.format(self
._apiBase
, nsi_id
))
89 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, nsi_id))
90 #print(yaml.safe_dump(resp))
93 raise NotFound("nsi {} not found".format(name
))
95 def delete(self
, name
, force
=False, wait
=False):
99 querystring
= '?FORCE=True'
100 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
101 nsi
['_id'], querystring
))
102 # print('HTTP CODE: {}'.format(http_code))
103 # print('RESP: {}'.format(resp))
106 resp
= json
.loads(resp
)
107 # Wait for status for NSI instance deletion
108 # For the 'delete' operation, '_id' is used
109 self
._wait
(resp
.get('_id'), deleteFlag
=True)
111 print('Deletion in progress')
112 elif http_code
== 204:
118 msg
= json
.loads(resp
)
121 raise ClientException("failed to delete nsi {} - {}".format(name
, msg
))
123 def create(self
, nst_name
, nsi_name
, account
, config
=None,
124 ssh_keys
=None, description
='default description',
125 admin_status
='ENABLED', wait
=False):
127 self
._client
.get_token()
128 nst
= self
._client
.nst
.get(nst_name
)
132 def get_vim_account_id(vim_account
):
133 if vim_account_id
.get(vim_account
):
134 return vim_account_id
[vim_account
]
136 vim
= self
._client
.vim
.get(vim_account
)
138 raise NotFound("cannot find vim account '{}'".format(vim_account
))
139 vim_account_id
[vim_account
] = vim
['_id']
143 nsi
['nstId'] = nst
['_id']
144 nsi
['nsiName'] = nsi_name
145 nsi
['nsiDescription'] = description
146 nsi
['vimAccountId'] = get_vim_account_id(account
)
147 #nsi['userdata'] = {}
148 #nsi['userdata']['key1']='value1'
149 #nsi['userdata']['key2']='value2'
151 if ssh_keys
is not None:
152 # ssh_keys is comma separate list
153 # ssh_keys_format = []
154 # for key in ssh_keys.split(','):
155 # ssh_keys_format.append({'key-pair-ref': key})
157 # ns['ssh-authorized-key'] = ssh_keys_format
159 for pubkeyfile
in ssh_keys
.split(','):
160 with
open(pubkeyfile
, 'r') as f
:
161 nsi
['ssh_keys'].append(f
.read())
163 nsi_config
= yaml
.safe_load(config
)
164 if "netslice-vld" in nsi_config
:
165 for vld
in nsi_config
["netslice-vld"]:
166 if vld
.get("vim-network-name"):
167 if isinstance(vld
["vim-network-name"], dict):
168 vim_network_name_dict
= {}
169 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
170 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
171 vld
["vim-network-name"] = vim_network_name_dict
172 nsi
["netslice-vld"] = nsi_config
["netslice-vld"]
173 if "netslice-subnet" in nsi_config
:
174 for nssubnet
in nsi_config
["netslice-subnet"]:
175 if "vld" in nssubnet
:
176 for vld
in nssubnet
["vld"]:
177 if vld
.get("vim-network-name"):
178 if isinstance(vld
["vim-network-name"], dict):
179 vim_network_name_dict
= {}
180 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
181 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
182 vld
["vim-network-name"] = vim_network_name_dict
183 if "vnf" in nssubnet
:
184 for vnf
in nsi_config
["vnf"]:
185 if vnf
.get("vim_account"):
186 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
187 nsi
["netslice-subnet"] = nsi_config
["netslice-subnet"]
189 if "additionalParamsForNsi" in nsi_config
:
190 nsi
["additionalParamsForNsi"] = nsi_config
.pop("additionalParamsForNsi")
191 if not isinstance(nsi
["additionalParamsForNsi"], dict):
192 raise ValueError("Error at --config 'additionalParamsForNsi' must be a dictionary")
193 if "additionalParamsForSubnet" in nsi_config
:
194 nsi
["additionalParamsForSubnet"] = nsi_config
.pop("additionalParamsForSubnet")
195 if not isinstance(nsi
["additionalParamsForSubnet"], list):
196 raise ValueError("Error at --config 'additionalParamsForSubnet' must be a list")
197 for additional_param_subnet
in nsi
["additionalParamsForSubnet"]:
198 if not isinstance(additional_param_subnet
, dict):
199 raise ValueError("Error at --config 'additionalParamsForSubnet' items must be dictionaries")
200 if not additional_param_subnet
.get("id"):
201 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain subnet 'id'")
202 if not additional_param_subnet
.get("additionalParamsForNs") and\
203 not additional_param_subnet
.get("additionalParamsForVnf"):
204 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain "
205 "'additionalParamsForNs' and/or 'additionalParamsForVnf'")
207 # print(yaml.safe_dump(nsi))
209 self
._apiResource
= '/netslice_instances_content'
210 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
211 self
._apiVersion
, self
._apiResource
)
212 headers
= self
._client
._headers
213 headers
['Content-Type'] = 'application/yaml'
214 http_header
= ['{}: {}'.format(key
,val
)
215 for (key
,val
) in list(headers
.items())]
216 self
._http
.set_http_header(http_header
)
217 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
219 #print('HTTP CODE: {}'.format(http_code))
220 #print('RESP: {}'.format(resp))
221 if http_code
in (200, 201, 202, 204):
223 resp
= json
.loads(resp
)
224 if not resp
or 'id' not in resp
:
225 raise ClientException('unexpected response from server - {} '.format(
228 # Wait for status for NSI instance creation
229 self
._wait
(resp
.get('nsilcmop_id'))
235 msg
= json
.loads(resp
)
238 raise ClientException(msg
)
239 except ClientException
as exc
:
240 message
="failed to create nsi: {} nst: {}\nerror:\n{}".format(
244 raise ClientException(message
)
246 def list_op(self
, name
, filter=None):
247 """Returns the list of operations of a NSI
251 self
._apiResource
= '/nsi_lcm_op_occs'
252 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
253 self
._apiVersion
, self
._apiResource
)
256 filter_string
= '&{}'.format(filter)
257 http_code
, resp
= self
._http
.get2_cmd('{}?netsliceInstanceId={}'.format(
258 self
._apiBase
, nsi
['_id'],
260 #print('HTTP CODE: {}'.format(http_code))
261 #print('RESP: {}'.format(resp))
264 resp
= json
.loads(resp
)
267 raise ClientException('unexpected response from server')
272 resp
= json
.loads(resp
)
276 raise ClientException(msg
)
277 except ClientException
as exc
:
278 message
="failed to get operation list of NSI {}:\nerror:\n{}".format(
281 raise ClientException(message
)
283 def get_op(self
, operationId
):
284 """Returns the status of an operation
286 self
._client
.get_token()
288 self
._apiResource
= '/nsi_lcm_op_occs'
289 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
290 self
._apiVersion
, self
._apiResource
)
291 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
292 #print('HTTP CODE: {}'.format(http_code))
293 #print('RESP: {}'.format(resp))
296 resp
= json
.loads(resp
)
299 raise ClientException('unexpected response from server')
304 resp
= json
.loads(resp
)
308 raise ClientException(msg
)
309 except ClientException
as exc
:
310 message
="failed to get status of operation {}:\nerror:\n{}".format(
313 raise ClientException(message
)
315 def exec_op(self
, name
, op_name
, op_data
=None):
316 """Executes an operation on a NSI
320 self
._apiResource
= '/netslice_instances'
321 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
322 self
._apiVersion
, self
._apiResource
)
323 endpoint
= '{}/{}/{}'.format(self
._apiBase
, nsi
['_id'], op_name
)
324 #print('OP_NAME: {}'.format(op_name))
325 #print('OP_DATA: {}'.format(json.dumps(op_data)))
326 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
327 #print('HTTP CODE: {}'.format(http_code))
328 #print('RESP: {}'.format(resp))
329 if http_code
in (200, 201, 202, 204):
331 resp
= json
.loads(resp
)
332 if not resp
or 'id' not in resp
:
333 raise ClientException('unexpected response from server - {}'.format(
340 msg
= json
.loads(resp
)
343 raise ClientException(msg
)
344 except ClientException
as exc
:
345 message
="failed to exec operation {}:\nerror:\n{}".format(
348 raise ClientException(message
)