Coverage for osmclient/sol005/ns.py: 8%

382 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2024-06-30 09:02 +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 ns 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 Ns(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 = "/nslcm" 

36 self._apiVersion = "/v1" 

37 self._apiResource = "/ns_instances_content" 

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

39 self._apiName, self._apiVersion, self._apiResource 

40 ) 

41 

42 # NS '--wait' option 

43 def _wait(self, id, wait_time, deleteFlag=False, entity="NS"): 

44 self._logger.debug("") 

45 # Endpoint to get operation status 

46 apiUrlStatus = "{}{}{}".format( 

47 self._apiName, self._apiVersion, "/ns_lcm_op_occs" 

48 ) 

49 # Wait for status for NS instance creation/update/deletion 

50 if isinstance(wait_time, bool): 

51 wait_time = WaitForStatus.TIMEOUT_NS_OPERATION 

52 WaitForStatus.wait_for_status( 

53 entity, 

54 str(id), 

55 wait_time, 

56 apiUrlStatus, 

57 self._http.get2_cmd, 

58 deleteFlag=deleteFlag, 

59 ) 

60 

61 def list(self, filter=None): 

62 """Returns a list of NS""" 

63 self._logger.debug("") 

64 self._client.get_token() 

65 filter_string = "" 

66 if filter: 

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

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

69 if resp: 

70 return json.loads(resp) 

71 return list() 

72 

73 def get(self, name): 

74 """Returns an NS based on name or id""" 

75 self._logger.debug("") 

76 self._client.get_token() 

77 if utils.validate_uuid4(name): 

78 for ns in self.list(): 

79 if name == ns["_id"]: 

80 return ns 

81 else: 

82 for ns in self.list(): 

83 if name == ns["name"]: 

84 return ns 

85 raise NotFound("ns '{}' not found".format(name)) 

86 

87 def get_individual(self, name): 

88 self._logger.debug("") 

89 self._client.get_token() 

90 ns_id = name 

91 if not utils.validate_uuid4(name): 

92 for ns in self.list(): 

93 if name == ns["name"]: 

94 ns_id = ns["_id"] 

95 break 

96 try: 

97 _, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, ns_id)) 

98 # resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id)) 

99 # print(yaml.safe_dump(resp)) 

100 if resp: 

101 return json.loads(resp) 

102 except NotFound: 

103 raise NotFound("ns '{}' not found".format(name)) 

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

105 

106 def delete(self, name, force=False, config=None, wait=False): 

107 """ 

108 Deletes a Network Service (NS) 

109 :param name: name of network service 

110 :param force: set force. Direct deletion without cleaning at VIM 

111 :param config: parameters of deletion, as: 

112 autoremove: Bool (default True) 

113 timeout_ns_terminate: int 

114 skip_terminate_primitives: Bool (default False) to not exec the terminate primitives 

115 :param wait: Make synchronous. Wait until deletion is completed: 

116 False to not wait (by default), True to wait a standard time, or int (time to wait) 

117 :return: None. Exception if fail 

118 """ 

119 self._logger.debug("") 

120 ns = self.get(name) 

121 querystring_list = [] 

122 querystring = "" 

123 if config: 

124 ns_config = yaml.safe_load(config) 

125 querystring_list += ["{}={}".format(k, v) for k, v in ns_config.items()] 

126 if force: 

127 querystring_list.append("FORCE=True") 

128 if querystring_list: 

129 querystring = "?" + "&".join(querystring_list) 

130 http_code, resp = self._http.delete_cmd( 

131 "{}/{}{}".format(self._apiBase, ns["_id"], querystring) 

132 ) 

133 # TODO change to use a POST self._http.post_cmd('{}/{}/terminate{}'.format(_apiBase, ns['_id'], querystring), 

134 # postfields_dict=ns_config) 

135 # seting autoremove as True by default 

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

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

138 if http_code == 202: 

139 if wait and resp: 

140 resp = json.loads(resp) 

141 # For the 'delete' operation, '_id' is used 

142 self._wait(resp.get("_id"), wait, deleteFlag=True) 

143 else: 

144 print("Deletion in progress") 

145 elif http_code == 204: 

146 print("Deleted") 

147 else: 

148 msg = resp or "" 

149 # if resp: 

150 # try: 

151 # msg = json.loads(resp) 

152 # except ValueError: 

153 # msg = resp 

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

155 

156 def create( 

157 self, 

158 nsd_name, 

159 nsr_name, 

160 account, 

161 config=None, 

162 ssh_keys=None, 

163 description="default description", 

164 admin_status="ENABLED", 

165 wait=False, 

166 timeout=None, 

167 ): 

168 self._logger.debug("") 

169 self._client.get_token() 

170 nsd = self._client.nsd.get(nsd_name) 

171 

172 vim_account_id = {} 

173 wim_account_id = {} 

174 

175 def get_vim_account_id(vim_account): 

176 self._logger.debug("") 

177 if vim_account_id.get(vim_account): 

178 return vim_account_id[vim_account] 

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

180 if vim is None: 

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

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

183 return vim["_id"] 

184 

185 def get_wim_account_id(wim_account): 

186 self._logger.debug("") 

187 # wim_account can be False (boolean) to indicate not use wim account 

188 if not isinstance(wim_account, str): 

189 return wim_account 

190 if wim_account_id.get(wim_account): 

191 return wim_account_id[wim_account] 

192 wim = self._client.wim.get(wim_account) 

193 if wim is None: 

194 raise NotFound("cannot find wim account '{}'".format(wim_account)) 

195 wim_account_id[wim_account] = wim["_id"] 

196 return wim["_id"] 

197 

198 vim_id = get_vim_account_id(account) 

199 ns = {} 

200 ns["nsdId"] = nsd["_id"] 

201 ns["nsName"] = nsr_name 

202 ns["nsDescription"] = description 

203 ns["vimAccountId"] = vim_id 

204 # ns['userdata'] = {} 

205 # ns['userdata']['key1']='value1' 

206 # ns['userdata']['key2']='value2' 

207 

208 if ssh_keys is not None: 

209 ns["ssh_keys"] = [] 

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

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

212 ns["ssh_keys"].append(f.read()) 

213 if timeout: 

214 ns["timeout_ns_deploy"] = timeout 

215 if config: 

216 ns_config = yaml.safe_load(config) 

217 if "vim-network-name" in ns_config: 

218 ns_config["vld"] = ns_config.pop("vim-network-name") 

219 if "vld" in ns_config: 

220 if not isinstance(ns_config["vld"], list): 

221 raise ClientException( 

222 "Error at --config 'vld' must be a list of dictionaries" 

223 ) 

224 for vld in ns_config["vld"]: 

225 if not isinstance(vld, dict): 

226 raise ClientException( 

227 "Error at --config 'vld' must be a list of dictionaries" 

228 ) 

229 if vld.get("vim-network-name"): 

230 if isinstance(vld["vim-network-name"], dict): 

231 vim_network_name_dict = {} 

232 for vim_account, vim_net in vld["vim-network-name"].items(): 

233 vim_network_name_dict[ 

234 get_vim_account_id(vim_account) 

235 ] = vim_net 

236 vld["vim-network-name"] = vim_network_name_dict 

237 if "wim_account" in vld and vld["wim_account"] is not None: 

238 vld["wimAccountId"] = get_wim_account_id(vld.pop("wim_account")) 

239 if "vnf" in ns_config: 

240 for vnf in ns_config["vnf"]: 

241 if vnf.get("vim_account"): 

242 vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account")) 

243 

244 if "additionalParamsForNs" in ns_config: 

245 if not isinstance(ns_config["additionalParamsForNs"], dict): 

246 raise ClientException( 

247 "Error at --config 'additionalParamsForNs' must be a dictionary" 

248 ) 

249 if "additionalParamsForVnf" in ns_config: 

250 if not isinstance(ns_config["additionalParamsForVnf"], list): 

251 raise ClientException( 

252 "Error at --config 'additionalParamsForVnf' must be a list" 

253 ) 

254 for additional_param_vnf in ns_config["additionalParamsForVnf"]: 

255 if not isinstance(additional_param_vnf, dict): 

256 raise ClientException( 

257 "Error at --config 'additionalParamsForVnf' items must be dictionaries" 

258 ) 

259 if not additional_param_vnf.get("member-vnf-index"): 

260 raise ClientException( 

261 "Error at --config 'additionalParamsForVnf' items must contain " 

262 "'member-vnf-index'" 

263 ) 

264 if "wim_account" in ns_config: 

265 wim_account = ns_config.pop("wim_account") 

266 if wim_account is not None: 

267 ns["wimAccountId"] = get_wim_account_id(wim_account) 

268 # rest of parameters without any transformation or checking 

269 # "timeout_ns_deploy" 

270 # "placement-engine" 

271 ns.update(ns_config) 

272 

273 # print(yaml.safe_dump(ns)) 

274 try: 

275 self._apiResource = "/ns_instances_content" 

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

277 self._apiName, self._apiVersion, self._apiResource 

278 ) 

279 headers = self._client._headers 

280 headers["Content-Type"] = "application/yaml" 

281 self._http.set_http_header(headers) 

282 http_code, resp = self._http.post_cmd( 

283 endpoint=self._apiBase, postfields_dict=ns 

284 ) 

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

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

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

288 if resp: 

289 resp = json.loads(resp) 

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

291 raise ClientException( 

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

293 ) 

294 if wait: 

295 # Wait for status for NS instance creation 

296 self._wait(resp.get("nslcmop_id"), wait) 

297 print(resp["id"]) 

298 return resp["id"] 

299 # else: 

300 # msg = "" 

301 # if resp: 

302 # try: 

303 # msg = json.loads(resp) 

304 # except ValueError: 

305 # msg = resp 

306 # raise ClientException(msg) 

307 except ClientException as exc: 

308 message = "failed to create ns: {} nsd: {}\nerror:\n{}".format( 

309 nsr_name, nsd_name, str(exc) 

310 ) 

311 raise ClientException(message) 

312 

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

314 """Returns the list of operations of a NS""" 

315 self._logger.debug("") 

316 ns = self.get(name) 

317 try: 

318 self._apiResource = "/ns_lcm_op_occs" 

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

320 self._apiName, self._apiVersion, self._apiResource 

321 ) 

322 filter_string = "" 

323 if filter: 

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

325 http_code, resp = self._http.get2_cmd( 

326 "{}?nsInstanceId={}{}".format(self._apiBase, ns["_id"], filter_string) 

327 ) 

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

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

330 if http_code == 200: 

331 if resp: 

332 resp = json.loads(resp) 

333 return resp 

334 else: 

335 raise ClientException("unexpected response from server") 

336 else: 

337 msg = resp or "" 

338 # if resp: 

339 # try: 

340 # resp = json.loads(resp) 

341 # msg = resp['detail'] 

342 # except ValueError: 

343 # msg = resp 

344 raise ClientException(msg) 

345 except ClientException as exc: 

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

347 name, str(exc) 

348 ) 

349 raise ClientException(message) 

350 

351 def get_op(self, operationId): 

352 """Returns the status of an operation""" 

353 self._logger.debug("") 

354 self._client.get_token() 

355 try: 

356 self._apiResource = "/ns_lcm_op_occs" 

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

358 self._apiName, self._apiVersion, self._apiResource 

359 ) 

360 http_code, resp = self._http.get2_cmd( 

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

362 ) 

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

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

365 if http_code == 200: 

366 if resp: 

367 resp = json.loads(resp) 

368 return resp 

369 else: 

370 raise ClientException("unexpected response from server") 

371 else: 

372 msg = resp or "" 

373 # if resp: 

374 # try: 

375 # resp = json.loads(resp) 

376 # msg = resp['detail'] 

377 # except ValueError: 

378 # msg = resp 

379 raise ClientException(msg) 

380 except ClientException as exc: 

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

382 operationId, str(exc) 

383 ) 

384 raise ClientException(message) 

385 

386 def exec_op( 

387 self, 

388 name, 

389 op_name, 

390 op_data=None, 

391 wait=False, 

392 ): 

393 """Executes an operation on a NS""" 

394 self._logger.debug("") 

395 ns = self.get(name) 

396 try: 

397 ns = self.get(name) 

398 self._apiResource = "/ns_instances" 

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

400 self._apiName, self._apiVersion, self._apiResource 

401 ) 

402 endpoint = "{}/{}/{}".format(self._apiBase, ns["_id"], op_name) 

403 # print('OP_NAME: {}'.format(op_name)) 

404 # print('OP_DATA: {}'.format(json.dumps(op_data))) 

405 http_code, resp = self._http.post_cmd( 

406 endpoint=endpoint, postfields_dict=op_data 

407 ) 

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

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

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

411 if resp: 

412 resp = json.loads(resp) 

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

414 raise ClientException( 

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

416 ) 

417 if wait: 

418 # Wait for status for NS instance action 

419 # For the 'action' operation, 'id' is used 

420 self._wait(resp.get("id"), wait) 

421 return resp["id"] 

422 # else: 

423 # msg = "" 

424 # if resp: 

425 # try: 

426 # msg = json.loads(resp) 

427 # except ValueError: 

428 # msg = resp 

429 # raise ClientException(msg) 

430 except ClientException as exc: 

431 message = "failed to exec operation {}:\nerror:\n{}".format(name, str(exc)) 

432 raise ClientException(message) 

433 

434 def cancel_op(self, operation_id, cancel_mode, wait=False): 

435 """Cancels an LCM operation""" 

436 self._client.get_token() 

437 self._apiResource = "/ns_lcm_op_occs" 

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

439 self._apiName, self._apiVersion, self._apiResource 

440 ) 

441 endpoint = "{}/{}/cancel".format(self._apiBase, operation_id) 

442 op_data = {"cancelMode": cancel_mode} 

443 try: 

444 http_code, resp = self._http.post_cmd( 

445 endpoint=endpoint, postfields_dict=op_data 

446 ) 

447 if http_code == 202: 

448 if wait: 

449 self._wait(operation_id, wait, deleteFlag=True, entity="OPCANCEL") 

450 else: 

451 print("Cancellation in progress") 

452 else: 

453 msg = resp or "" 

454 raise ClientException(msg) 

455 except ClientException as exc: 

456 message = "failed to exec operation {}:\nerror:\n{}".format( 

457 operation_id, str(exc) 

458 ) 

459 raise ClientException(message) 

460 

461 def scale_vnf( 

462 self, 

463 ns_name, 

464 vnf_name, 

465 scaling_group, 

466 scale_in, 

467 scale_out, 

468 wait=False, 

469 timeout=None, 

470 ): 

471 """Scales a VNF by adding/removing VDUs""" 

472 self._logger.debug("") 

473 self._client.get_token() 

474 try: 

475 op_data = {} 

476 op_data["scaleType"] = "SCALE_VNF" 

477 op_data["scaleVnfData"] = {} 

478 if scale_in and not scale_out: 

479 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN" 

480 elif not scale_in and scale_out: 

481 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT" 

482 else: 

483 raise ClientException("you must set either 'scale_in' or 'scale_out'") 

484 op_data["scaleVnfData"]["scaleByStepData"] = { 

485 "member-vnf-index": vnf_name, 

486 "scaling-group-descriptor": scaling_group, 

487 } 

488 if timeout: 

489 op_data["timeout_ns_scale"] = timeout 

490 op_id = self.exec_op(ns_name, op_name="scale", op_data=op_data, wait=wait) 

491 print(str(op_id)) 

492 except ClientException as exc: 

493 message = "failed to scale vnf {} of ns {}:\nerror:\n{}".format( 

494 vnf_name, ns_name, str(exc) 

495 ) 

496 raise ClientException(message) 

497 

498 def update(self, ns_name, data, wait=False): 

499 """Update NS instance. 

500 

501 This function calls the NBI in order to perform an update operation 

502 on a Network Service instance. 

503 

504 Args: 

505 ns_name: (str) 

506 data: (dict) 

507 wait: (boolean) 

508 

509 Returns: 

510 None 

511 

512 """ 

513 self._logger.debug("") 

514 self._client.get_token() 

515 try: 

516 op_data = {"updateType": data.pop("updateType")} 

517 

518 # Check update parameters availability according to update type 

519 if op_data["updateType"] == "CHANGE_VNFPKG": 

520 if not ( 

521 data["config"]["changeVnfPackageData"][0].get("vnfInstanceId") 

522 and data["config"]["changeVnfPackageData"][0].get("vnfdId") 

523 ): 

524 raise ClientException("you must set both vnfInstanceId and vnfdId") 

525 

526 # Fill up op_data 

527 op_data["changeVnfPackageData"] = {} 

528 op_data["changeVnfPackageData"]["vnfInstanceId"] = data["config"][ 

529 "changeVnfPackageData" 

530 ][0].get("vnfInstanceId") 

531 

532 op_data["changeVnfPackageData"]["vnfdId"] = data["config"][ 

533 "changeVnfPackageData" 

534 ][0].get("vnfdId") 

535 

536 if data.get("timeout"): 

537 op_data["timeout_ns_update"] = data["timeout"] 

538 

539 op_id = self.exec_op(ns_name, op_name="update", op_data=op_data, wait=wait) 

540 print(str(op_id)) 

541 

542 except ClientException as exc: 

543 message = "failed to update ns {}:\nerror:\n{}".format(ns_name, str(exc)) 

544 raise ClientException(message) 

545 

546 def create_alarm(self, alarm): 

547 self._logger.debug("") 

548 self._client.get_token() 

549 data = {} 

550 data["create_alarm_request"] = {} 

551 data["create_alarm_request"]["alarm_create_request"] = alarm 

552 try: 

553 http_code, resp = self._http.post_cmd( 

554 endpoint="/test/message/alarm_request", postfields_dict=data 

555 ) 

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

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

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

559 # resp = json.loads(resp) 

560 print("Alarm created") 

561 # else: 

562 # msg = "" 

563 # if resp: 

564 # try: 

565 # msg = json.loads(resp) 

566 # except ValueError: 

567 # msg = resp 

568 # raise ClientException('error: code: {}, resp: {}'.format( 

569 # http_code, msg)) 

570 except ClientException as exc: 

571 message = "failed to create alarm: alarm {}\n{}".format(alarm, str(exc)) 

572 raise ClientException(message) 

573 

574 def delete_alarm(self, name): 

575 self._logger.debug("") 

576 self._client.get_token() 

577 data = {} 

578 data["delete_alarm_request"] = {} 

579 data["delete_alarm_request"]["alarm_delete_request"] = {} 

580 data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name 

581 try: 

582 http_code, resp = self._http.post_cmd( 

583 endpoint="/test/message/alarm_request", postfields_dict=data 

584 ) 

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

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

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

588 # resp = json.loads(resp) 

589 print("Alarm deleted") 

590 # else: 

591 # msg = "" 

592 # if resp: 

593 # try: 

594 # msg = json.loads(resp) 

595 # except ValueError: 

596 # msg = resp 

597 # raise ClientException('error: code: {}, resp: {}'.format( 

598 # http_code, msg)) 

599 except ClientException as exc: 

600 message = "failed to delete alarm: alarm {}\n{}".format(name, str(exc)) 

601 raise ClientException(message) 

602 

603 def get_alarm(self, project_name=None, ns_id=None, uuid=None): 

604 self._client.get_token() 

605 try: 

606 self._apiName = "/nsfm" 

607 self._apiResource = "/alarms" 

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

609 self._apiName, self._apiVersion, self._apiResource 

610 ) 

611 if uuid: 

612 # if request is for any uuid 

613 http_code, resp = self._http.get2_cmd( 

614 "{}/{}".format(self._apiBase, uuid) 

615 ) 

616 else: # if not uuid 

617 http_code, resp = self._http.get2_cmd( 

618 "{}/{}/{}/{}".format(self._apiBase, uuid, project_name, ns_id) 

619 ) 

620 if http_code == 200: 

621 if resp: 

622 resp = json.loads(resp) 

623 return resp 

624 else: 

625 raise ClientException("unexpected response from server") 

626 else: 

627 msg = resp or "" 

628 raise ClientException(msg) 

629 except ClientException as exc: 

630 message = "failed to get alarm :\nerror:\n{}".format(str(exc)) 

631 raise ClientException(message) 

632 

633 def update_alarm(self, uuid, threshold=None, is_enable=None, wait=None): 

634 self._client.get_token() 

635 try: 

636 op_data = {} 

637 op_data["uuid"] = uuid 

638 op_data["threshold"] = threshold 

639 op_data["is_enable"] = is_enable 

640 self._apiName = "/nsfm" 

641 self._apiResource = "/alarms" 

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

643 self._apiName, self._apiVersion, self._apiResource 

644 ) 

645 http_code, resp = self._http.patch_cmd( 

646 endpoint="{}".format(self._apiBase), postfields_dict=op_data 

647 ) 

648 if resp: 

649 resp = json.loads(resp) 

650 print(resp) 

651 return resp 

652 except ClientException as exc: 

653 message = "failed to update alarm :\nerror:\n{}".format(str(exc)) 

654 raise ClientException(message) 

655 

656 def export_metric(self, metric): 

657 self._logger.debug("") 

658 self._client.get_token() 

659 data = {} 

660 data["read_metric_data_request"] = metric 

661 try: 

662 http_code, resp = self._http.post_cmd( 

663 endpoint="/test/message/metric_request", postfields_dict=data 

664 ) 

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

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

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

668 # resp = json.loads(resp) 

669 return "Metric exported" 

670 # else: 

671 # msg = "" 

672 # if resp: 

673 # try: 

674 # msg = json.loads(resp) 

675 # except ValueError: 

676 # msg = resp 

677 # raise ClientException('error: code: {}, resp: {}'.format( 

678 # http_code, msg)) 

679 except ClientException as exc: 

680 message = "failed to export metric: metric {}\n{}".format(metric, str(exc)) 

681 raise ClientException(message) 

682 

683 def get_field(self, ns_name, field): 

684 self._logger.debug("") 

685 nsr = self.get(ns_name) 

686 print(yaml.safe_dump(nsr)) 

687 if nsr is None: 

688 raise NotFound("failed to retrieve ns {}".format(ns_name)) 

689 

690 if field in nsr: 

691 return nsr[field] 

692 

693 raise NotFound("failed to find {} in ns {}".format(field, ns_name)) 

694 

695 def heal( 

696 self, 

697 ns_name, 

698 heal_dict, 

699 wait=False, 

700 timeout=None, 

701 ): 

702 """Heals a NS""" 

703 self._logger.debug("") 

704 self._client.get_token() 

705 try: 

706 op_data = heal_dict 

707 if timeout: 

708 op_data["timeout_ns_heal"] = timeout 

709 op_id = self.exec_op(ns_name, op_name="heal", op_data=op_data, wait=wait) 

710 print(str(op_id)) 

711 except ClientException as exc: 

712 message = "failed to heal ns {}:\nerror:\n{}".format(ns_name, str(exc)) 

713 raise ClientException(message)