Coverage for osmclient/sol005/nsi.py: 9%

208 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2024-06-30 09:54 +0000

1# Copyright 2018 Telefonica 

2# 

3# All Rights Reserved. 

4# 

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 

8# 

9# http://www.apache.org/licenses/LICENSE-2.0 

10# 

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 

15# under the License. 

16 

17""" 

18OSM NSI (Network Slice Instance) API handling 

19""" 

20 

21from osmclient.common import utils 

22from osmclient.common import wait as WaitForStatus 

23from osmclient.common.exceptions import ClientException 

24from osmclient.common.exceptions import NotFound 

25import yaml 

26import json 

27import logging 

28 

29 

30class Nsi(object): 

31 def __init__(self, http=None, client=None): 

32 self._http = http 

33 self._client = client 

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 

40 ) 

41 

42 # NSI '--wait' option 

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" 

49 ) 

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( 

54 "NSI", 

55 str(id), 

56 wait_time, 

57 apiUrlStatus, 

58 self._http.get2_cmd, 

59 deleteFlag=deleteFlag, 

60 ) 

61 

62 def list(self, filter=None): 

63 """Returns a list of NSI""" 

64 self._logger.debug("") 

65 self._client.get_token() 

66 filter_string = "" 

67 if filter: 

68 filter_string = "?{}".format(filter) 

69 _, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string)) 

70 if resp: 

71 return json.loads(resp) 

72 return list() 

73 

74 def get(self, name): 

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"]: 

81 return nsi 

82 else: 

83 for nsi in self.list(): 

84 if name == nsi["name"]: 

85 return nsi 

86 raise NotFound("nsi {} not found".format(name)) 

87 

88 def get_individual(self, name): 

89 self._logger.debug("") 

90 nsi_id = name 

91 self._client.get_token() 

92 if not utils.validate_uuid4(name): 

93 for nsi in self.list(): 

94 if name == nsi["name"]: 

95 nsi_id = nsi["_id"] 

96 break 

97 try: 

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)) 

101 if resp: 

102 return json.loads(resp) 

103 except NotFound: 

104 raise NotFound("nsi '{}' not found".format(name)) 

105 raise NotFound("nsi {} not found".format(name)) 

106 

107 def delete(self, name, force=False, wait=False): 

108 self._logger.debug("") 

109 nsi = self.get(name) 

110 querystring = "" 

111 if force: 

112 querystring = "?FORCE=True" 

113 http_code, resp = self._http.delete_cmd( 

114 "{}/{}{}".format(self._apiBase, nsi["_id"], querystring) 

115 ) 

116 # print('HTTP CODE: {}'.format(http_code)) 

117 # print('RESP: {}'.format(resp)) 

118 if http_code == 202: 

119 if wait and 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) 

124 else: 

125 print("Deletion in progress") 

126 elif http_code == 204: 

127 print("Deleted") 

128 else: 

129 msg = resp or "" 

130 # if resp: 

131 # try: 

132 # msg = json.loads(resp) 

133 # except ValueError: 

134 # msg = resp 

135 raise ClientException("failed to delete nsi {} - {}".format(name, msg)) 

136 

137 def create( 

138 self, 

139 nst_name, 

140 nsi_name, 

141 account, 

142 config=None, 

143 ssh_keys=None, 

144 description="default description", 

145 admin_status="ENABLED", 

146 wait=False, 

147 ): 

148 self._logger.debug("") 

149 self._client.get_token() 

150 nst = self._client.nst.get(nst_name) 

151 

152 vim_account_id = {} 

153 

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] 

158 

159 vim = self._client.vim.get(vim_account) 

160 if vim is None: 

161 raise NotFound("cannot find vim account '{}'".format(vim_account)) 

162 vim_account_id[vim_account] = vim["_id"] 

163 return vim["_id"] 

164 

165 nsi = {} 

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' 

173 

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}) 

179 # 

180 # ns['ssh-authorized-key'] = ssh_keys_format 

181 nsi["ssh_keys"] = [] 

182 for pubkeyfile in ssh_keys.split(","): 

183 with open(pubkeyfile, "r") as f: 

184 nsi["ssh_keys"].append(f.read()) 

185 if config: 

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() 

194 ): 

195 vim_network_name_dict[ 

196 get_vim_account_id(vim_account) 

197 ] = vim_net 

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() 

209 ): 

210 vim_network_name_dict[ 

211 get_vim_account_id(vim_account) 

212 ] = vim_net 

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") 

219 ) 

220 nsi["netslice-subnet"] = nsi_config["netslice-subnet"] 

221 

222 if "additionalParamsForNsi" in nsi_config: 

223 nsi["additionalParamsForNsi"] = nsi_config.pop("additionalParamsForNsi") 

224 if not isinstance(nsi["additionalParamsForNsi"], dict): 

225 raise ValueError( 

226 "Error at --config 'additionalParamsForNsi' must be a dictionary" 

227 ) 

228 if "additionalParamsForSubnet" in nsi_config: 

229 nsi["additionalParamsForSubnet"] = nsi_config.pop( 

230 "additionalParamsForSubnet" 

231 ) 

232 if not isinstance(nsi["additionalParamsForSubnet"], list): 

233 raise ValueError( 

234 "Error at --config 'additionalParamsForSubnet' must be a list" 

235 ) 

236 for additional_param_subnet in nsi["additionalParamsForSubnet"]: 

237 if not isinstance(additional_param_subnet, dict): 

238 raise ValueError( 

239 "Error at --config 'additionalParamsForSubnet' items must be dictionaries" 

240 ) 

241 if not additional_param_subnet.get("id"): 

242 raise ValueError( 

243 "Error at --config 'additionalParamsForSubnet' items must contain subnet 'id'" 

244 ) 

245 if not additional_param_subnet.get( 

246 "additionalParamsForNs" 

247 ) and not additional_param_subnet.get("additionalParamsForVnf"): 

248 raise ValueError( 

249 "Error at --config 'additionalParamsForSubnet' items must contain " 

250 "'additionalParamsForNs' and/or 'additionalParamsForVnf'" 

251 ) 

252 if "timeout_nsi_deploy" in nsi_config: 

253 nsi["timeout_nsi_deploy"] = nsi_config.pop("timeout_nsi_deploy") 

254 

255 # print(yaml.safe_dump(nsi)) 

256 try: 

257 self._apiResource = "/netslice_instances_content" 

258 self._apiBase = "{}{}{}".format( 

259 self._apiName, self._apiVersion, self._apiResource 

260 ) 

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 

266 ) 

267 # print('HTTP CODE: {}'.format(http_code)) 

268 # print('RESP: {}'.format(resp)) 

269 # if http_code in (200, 201, 202, 204): 

270 if resp: 

271 resp = json.loads(resp) 

272 if not resp or "id" not in resp: 

273 raise ClientException( 

274 "unexpected response from server - {} ".format(resp) 

275 ) 

276 if wait: 

277 # Wait for status for NSI instance creation 

278 self._wait(resp.get("nsilcmop_id"), wait) 

279 print(resp["id"]) 

280 # else: 

281 # msg = "" 

282 # if resp: 

283 # try: 

284 # msg = json.loads(resp) 

285 # except ValueError: 

286 # msg = 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) 

291 ) 

292 raise ClientException(message) 

293 

294 def list_op(self, name, filter=None): 

295 """Returns the list of operations of a NSI""" 

296 self._logger.debug("") 

297 nsi = self.get(name) 

298 try: 

299 self._apiResource = "/nsi_lcm_op_occs" 

300 self._apiBase = "{}{}{}".format( 

301 self._apiName, self._apiVersion, self._apiResource 

302 ) 

303 filter_string = "" 

304 if filter: 

305 filter_string = "&{}".format(filter) 

306 http_code, resp = self._http.get2_cmd( 

307 "{}?netsliceInstanceId={}{}".format( 

308 self._apiBase, nsi["_id"], filter_string 

309 ) 

310 ) 

311 # print('HTTP CODE: {}'.format(http_code)) 

312 # print('RESP: {}'.format(resp)) 

313 # if http_code == 200: 

314 if resp: 

315 resp = json.loads(resp) 

316 return resp 

317 else: 

318 raise ClientException("unexpected response from server") 

319 # else: 

320 # msg = "" 

321 # if resp: 

322 # try: 

323 # resp = json.loads(resp) 

324 # msg = resp['detail'] 

325 # except ValueError: 

326 # msg = resp 

327 # raise ClientException(msg) 

328 except ClientException as exc: 

329 message = "failed to get operation list of NSI {}:\nerror:\n{}".format( 

330 name, str(exc) 

331 ) 

332 raise ClientException(message) 

333 

334 def get_op(self, operationId): 

335 """Returns the status of an operation""" 

336 self._logger.debug("") 

337 self._client.get_token() 

338 try: 

339 self._apiResource = "/nsi_lcm_op_occs" 

340 self._apiBase = "{}{}{}".format( 

341 self._apiName, self._apiVersion, self._apiResource 

342 ) 

343 http_code, resp = self._http.get2_cmd( 

344 "{}/{}".format(self._apiBase, operationId) 

345 ) 

346 # print('HTTP CODE: {}'.format(http_code)) 

347 # print('RESP: {}'.format(resp)) 

348 # if http_code == 200: 

349 if resp: 

350 resp = json.loads(resp) 

351 return resp 

352 else: 

353 raise ClientException("unexpected response from server") 

354 # else: 

355 # msg = "" 

356 # if resp: 

357 # try: 

358 # resp = json.loads(resp) 

359 # msg = resp['detail'] 

360 # except ValueError: 

361 # msg = resp 

362 # raise ClientException(msg) 

363 except ClientException as exc: 

364 message = "failed to get status of operation {}:\nerror:\n{}".format( 

365 operationId, str(exc) 

366 ) 

367 raise ClientException(message) 

368 

369 def exec_op(self, name, op_name, op_data=None): 

370 """Executes an operation on a NSI""" 

371 self._logger.debug("") 

372 nsi = self.get(name) 

373 try: 

374 self._apiResource = "/netslice_instances" 

375 self._apiBase = "{}{}{}".format( 

376 self._apiName, self._apiVersion, self._apiResource 

377 ) 

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 

383 ) 

384 # print('HTTP CODE: {}'.format(http_code)) 

385 # print('RESP: {}'.format(resp)) 

386 # if http_code in (200, 201, 202, 204): 

387 if resp: 

388 resp = json.loads(resp) 

389 if not resp or "id" not in resp: 

390 raise ClientException( 

391 "unexpected response from server - {}".format(resp) 

392 ) 

393 print(resp["id"]) 

394 # else: 

395 # msg = "" 

396 # if resp: 

397 # try: 

398 # msg = json.loads(resp) 

399 # except ValueError: 

400 # msg = 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)