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 # Endpoint to get operation status
43 apiUrlStatus
= '{}{}{}'.format(self
._apiName
, self
._apiVersion
, '/nsi_lcm_op_occs')
44 # Wait for status for NSI instance creation/update/deletion
45 WaitForStatus
.wait_for_status(
48 WaitForStatus
.TIMEOUT_NSI_OPERATION
,
51 deleteFlag
=deleteFlag
)
53 def list(self
, filter=None):
54 """Returns a list of NSI
58 filter_string
= '?{}'.format(filter)
59 resp
= self
._http
.get_cmd('{}{}'.format(self
._apiBase
,filter_string
))
65 """Returns an NSI based on name or id
67 if utils
.validate_uuid4(name
):
68 for nsi
in self
.list():
69 if name
== nsi
['_id']:
72 for nsi
in self
.list():
73 if name
== nsi
['name']:
75 raise NotFound("nsi {} not found".format(name
))
77 def get_individual(self
, name
):
79 if not utils
.validate_uuid4(name
):
80 for nsi
in self
.list():
81 if name
== nsi
['name']:
84 resp
= self
._http
.get_cmd('{}/{}'.format(self
._apiBase
, nsi_id
))
85 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, nsi_id))
86 #print yaml.safe_dump(resp)
89 raise NotFound("nsi {} not found".format(name
))
91 def delete(self
, name
, force
=False, wait
=False):
95 querystring
= '?FORCE=True'
96 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
97 nsi
['_id'], querystring
))
98 # print 'HTTP CODE: {}'.format(http_code)
99 # print 'RESP: {}'.format(resp)
102 resp
= json
.loads(resp
)
103 # Wait for status for NSI instance deletion
104 # For the 'delete' operation, '_id' is used
105 self
._wait
(resp
.get('_id'), deleteFlag
=True)
107 print('Deletion in progress')
108 elif http_code
== 204:
114 msg
= json
.loads(resp
)
117 raise ClientException("failed to delete nsi {} - {}".format(name
, msg
))
119 def create(self
, nst_name
, nsi_name
, account
, config
=None,
120 ssh_keys
=None, description
='default description',
121 admin_status
='ENABLED', wait
=False):
123 nst
= self
._client
.nst
.get(nst_name
)
127 def get_vim_account_id(vim_account
):
128 if vim_account_id
.get(vim_account
):
129 return vim_account_id
[vim_account
]
131 vim
= self
._client
.vim
.get(vim_account
)
133 raise NotFound("cannot find vim account '{}'".format(vim_account
))
134 vim_account_id
[vim_account
] = vim
['_id']
138 nsi
['nstId'] = nst
['_id']
139 nsi
['nsiName'] = nsi_name
140 nsi
['nsiDescription'] = description
141 nsi
['vimAccountId'] = get_vim_account_id(account
)
142 #nsi['userdata'] = {}
143 #nsi['userdata']['key1']='value1'
144 #nsi['userdata']['key2']='value2'
146 if ssh_keys
is not None:
147 # ssh_keys is comma separate list
148 # ssh_keys_format = []
149 # for key in ssh_keys.split(','):
150 # ssh_keys_format.append({'key-pair-ref': key})
152 # ns['ssh-authorized-key'] = ssh_keys_format
154 for pubkeyfile
in ssh_keys
.split(','):
155 with
open(pubkeyfile
, 'r') as f
:
156 nsi
['ssh_keys'].append(f
.read())
158 nsi_config
= yaml
.load(config
)
159 if "netslice-vld" in nsi_config
:
160 for vld
in nsi_config
["netslice-vld"]:
161 if vld
.get("vim-network-name"):
162 if isinstance(vld
["vim-network-name"], dict):
163 vim_network_name_dict
= {}
164 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
165 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
166 vld
["vim-network-name"] = vim_network_name_dict
167 nsi
["netslice-vld"] = nsi_config
["netslice-vld"]
168 if "netslice-subnet" in nsi_config
:
169 for nssubnet
in nsi_config
["netslice-subnet"]:
170 if "vld" in nssubnet
:
171 for vld
in nssubnet
["vld"]:
172 if vld
.get("vim-network-name"):
173 if isinstance(vld
["vim-network-name"], dict):
174 vim_network_name_dict
= {}
175 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
176 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
177 vld
["vim-network-name"] = vim_network_name_dict
178 if "vnf" in nssubnet
:
179 for vnf
in nsi_config
["vnf"]:
180 if vnf
.get("vim_account"):
181 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
182 nsi
["netslice-subnet"] = nsi_config
["netslice-subnet"]
184 if "additionalParamsForNsi" in nsi_config
:
185 nsi
["additionalParamsForNsi"] = nsi_config
.pop("additionalParamsForNsi")
186 if not isinstance(nsi
["additionalParamsForNsi"], dict):
187 raise ValueError("Error at --config 'additionalParamsForNsi' must be a dictionary")
188 if "additionalParamsForSubnet" in nsi_config
:
189 nsi
["additionalParamsForSubnet"] = nsi_config
.pop("additionalParamsForSubnet")
190 if not isinstance(nsi
["additionalParamsForSubnet"], list):
191 raise ValueError("Error at --config 'additionalParamsForSubnet' must be a list")
192 for additional_param_subnet
in nsi
["additionalParamsForSubnet"]:
193 if not isinstance(additional_param_subnet
, dict):
194 raise ValueError("Error at --config 'additionalParamsForSubnet' items must be dictionaries")
195 if not additional_param_subnet
.get("id"):
196 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain subnet 'id'")
197 if not additional_param_subnet
.get("additionalParamsForNs") and\
198 not additional_param_subnet
.get("additionalParamsForVnf"):
199 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain "
200 "'additionalParamsForNs' and/or 'additionalParamsForVnf'")
202 # print yaml.safe_dump(nsi)
204 self
._apiResource
= '/netslice_instances_content'
205 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
206 self
._apiVersion
, self
._apiResource
)
207 headers
= self
._client
._headers
208 headers
['Content-Type'] = 'application/yaml'
209 http_header
= ['{}: {}'.format(key
,val
)
210 for (key
,val
) in list(headers
.items())]
211 self
._http
.set_http_header(http_header
)
212 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
214 #print 'HTTP CODE: {}'.format(http_code)
215 #print 'RESP: {}'.format(resp)
216 if http_code
in (200, 201, 202, 204):
218 resp
= json
.loads(resp
)
219 if not resp
or 'id' not in resp
:
220 raise ClientException('unexpected response from server - {} '.format(
223 # Wait for status for NSI instance creation
224 self
._wait
(resp
.get('nsilcmop_id'))
230 msg
= json
.loads(resp
)
233 raise ClientException(msg
)
234 except ClientException
as exc
:
235 message
="failed to create nsi: {} nst: {}\nerror:\n{}".format(
239 raise ClientException(message
)
241 def list_op(self
, name
, filter=None):
242 """Returns the list of operations of a NSI
246 self
._apiResource
= '/nsi_lcm_op_occs'
247 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
248 self
._apiVersion
, self
._apiResource
)
251 filter_string
= '&{}'.format(filter)
252 http_code
, resp
= self
._http
.get2_cmd('{}?netsliceInstanceId={}'.format(
253 self
._apiBase
, nsi
['_id'],
255 #print 'HTTP CODE: {}'.format(http_code)
256 #print 'RESP: {}'.format(resp)
259 resp
= json
.loads(resp
)
262 raise ClientException('unexpected response from server')
267 resp
= json
.loads(resp
)
271 raise ClientException(msg
)
272 except ClientException
as exc
:
273 message
="failed to get operation list of NSI {}:\nerror:\n{}".format(
276 raise ClientException(message
)
278 def get_op(self
, operationId
):
279 """Returns the status of an operation
282 self
._apiResource
= '/nsi_lcm_op_occs'
283 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
284 self
._apiVersion
, self
._apiResource
)
285 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
286 #print 'HTTP CODE: {}'.format(http_code)
287 #print 'RESP: {}'.format(resp)
290 resp
= json
.loads(resp
)
293 raise ClientException('unexpected response from server')
298 resp
= json
.loads(resp
)
302 raise ClientException(msg
)
303 except ClientException
as exc
:
304 message
="failed to get status of operation {}:\nerror:\n{}".format(
307 raise ClientException(message
)
309 def exec_op(self
, name
, op_name
, op_data
=None):
310 """Executes an operation on a NSI
314 self
._apiResource
= '/netslice_instances'
315 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
316 self
._apiVersion
, self
._apiResource
)
317 endpoint
= '{}/{}/{}'.format(self
._apiBase
, nsi
['_id'], op_name
)
318 #print 'OP_NAME: {}'.format(op_name)
319 #print 'OP_DATA: {}'.format(json.dumps(op_data))
320 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
321 #print 'HTTP CODE: {}'.format(http_code)
322 #print 'RESP: {}'.format(resp)
323 if http_code
in (200, 201, 202, 204):
325 resp
= json
.loads(resp
)
326 if not resp
or 'id' not in resp
:
327 raise ClientException('unexpected response from server - {}'.format(
334 msg
= json
.loads(resp
)
337 raise ClientException(msg
)
338 except ClientException
as exc
:
339 message
="failed to exec operation {}:\nerror:\n{}".format(
342 raise ClientException(message
)