Coverage for manila/share/drivers/huawei/v3/helper.py: 93%

891 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2014 Huawei Technologies Co., Ltd. 

2# All Rights Reserved. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); you may 

5# not use this file except in compliance with the License. You may obtain 

6# a copy of the License at 

7# 

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

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

13# License for the specific language governing permissions and limitations 

14# under the License. 

15 

16import base64 

17import copy 

18import requests 

19import time 

20 

21from defusedxml import ElementTree as ET 

22from oslo_log import log 

23from oslo_serialization import jsonutils 

24 

25from manila import exception 

26from manila.i18n import _ 

27from manila.share.drivers.huawei import constants 

28from manila import utils 

29 

30LOG = log.getLogger(__name__) 

31 

32 

33class RestHelper(object): 

34 """Helper class for Huawei OceanStor V3 storage system.""" 

35 

36 def __init__(self, configuration): 

37 self.configuration = configuration 

38 self.url = None 

39 self.session = None 

40 

41 # pylint: disable=no-member 

42 requests.packages.urllib3.disable_warnings( 

43 requests.packages.urllib3.exceptions.InsecureRequestWarning) 

44 requests.packages.urllib3.disable_warnings( 

45 requests.packages.urllib3.exceptions.InsecurePlatformWarning) 

46 # pylint: enable=no-member 

47 

48 def init_http_head(self): 

49 self.url = None 

50 self.session = requests.Session() 

51 self.session.headers.update({ 

52 "Connection": "keep-alive", 

53 "Content-Type": "application/json"}) 

54 self.session.verify = False 

55 

56 def do_call(self, url, data, method, calltimeout=constants.SOCKET_TIMEOUT): 

57 """Send requests to server. 

58 

59 Send HTTPS call, get response in JSON. 

60 Convert response into Python Object and return it. 

61 """ 

62 if self.url: 62 ↛ 63line 62 didn't jump to line 63 because the condition on line 62 was never true

63 url = self.url + url 

64 

65 LOG.debug('Request URL: %(url)s\n' 

66 'Call Method: %(method)s\n' 

67 'Request Data: %(data)s\n', 

68 {'url': url, 

69 'method': method, 

70 'data': data}) 

71 

72 kwargs = {'timeout': calltimeout} 

73 if data: 

74 kwargs['data'] = data 

75 

76 if method in ('POST', 'PUT', 'GET', 'DELETE'): 

77 func = getattr(self.session, method.lower()) 

78 else: 

79 msg = _("Request method %s is invalid.") % method 

80 LOG.error(msg) 

81 raise exception.ShareBackendException(msg=msg) 

82 

83 try: 

84 res = func(url, **kwargs) 

85 except Exception as err: 

86 LOG.error('\nBad response from server: %(url)s.' 

87 ' Error: %(err)s', {'url': url, 'err': err}) 

88 return {"error": {"code": constants.ERROR_CONNECT_TO_SERVER, 

89 "description": "Connect server error"}} 

90 

91 try: 

92 res.raise_for_status() 

93 except requests.HTTPError as exc: 

94 return {"error": {"code": exc.response.status_code, 

95 "description": str(exc)}} 

96 

97 result = res.json() 

98 LOG.debug('Response Data: %s', result) 

99 return result 

100 

101 def login(self): 

102 """Login huawei array.""" 

103 login_info = self._get_login_info() 

104 urlstr = login_info['RestURL'] 

105 url_list = urlstr.split(";") 

106 deviceid = None 

107 for item_url in url_list: 

108 url = item_url.strip('').strip('\n') + "xx/sessions" 

109 data = jsonutils.dumps({"username": login_info['UserName'], 

110 "password": login_info['UserPassword'], 

111 "scope": "0"}) 

112 self.init_http_head() 

113 result = self.do_call(url, data, 'POST', 

114 calltimeout=constants.LOGIN_SOCKET_TIMEOUT) 

115 

116 if ((result['error']['code'] != 0) 

117 or ("data" not in result) 

118 or (result['data']['deviceid'] is None)): 

119 LOG.error("Login to %s failed, try another.", item_url) 

120 continue 

121 

122 LOG.debug('Login success: %(url)s\n', {'url': item_url}) 

123 deviceid = result['data']['deviceid'] 

124 self.url = item_url + deviceid 

125 self.session.headers['iBaseToken'] = result['data']['iBaseToken'] 

126 break 

127 

128 if deviceid is None: 

129 err_msg = _("All url login fail.") 

130 LOG.error(err_msg) 

131 raise exception.InvalidShare(reason=err_msg) 

132 

133 return deviceid 

134 

135 @utils.synchronized('huawei_manila') 

136 def call(self, url, data, method): 

137 """Send requests to server. 

138 

139 If fail, try another RestURL. 

140 """ 

141 deviceid = None 

142 old_url = self.url 

143 result = self.do_call(url, data, method) 

144 error_code = result['error']['code'] 

145 if (error_code == constants.ERROR_CONNECT_TO_SERVER 

146 or error_code == constants.ERROR_UNAUTHORIZED_TO_SERVER): 

147 LOG.error("Can't open the recent url, re-login.") 

148 deviceid = self.login() 

149 

150 if deviceid is not None: 

151 LOG.debug('Replace URL: \n' 

152 'Old URL: %(old_url)s\n' 

153 'New URL: %(new_url)s\n', 

154 {'old_url': old_url, 

155 'new_url': self.url}) 

156 result = self.do_call(url, data, method) 

157 return result 

158 

159 def _create_filesystem(self, fs_param): 

160 """Create file system.""" 

161 url = "/filesystem" 

162 data = jsonutils.dumps(fs_param) 

163 result = self.call(url, data, 'POST') 

164 

165 msg = 'Create filesystem error.' 

166 self._assert_rest_result(result, msg) 

167 self._assert_data_in_result(result, msg) 

168 

169 return result['data']['ID'] 

170 

171 def _assert_rest_result(self, result, err_str): 

172 if result['error']['code'] != 0: 

173 err_msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str, 

174 'res': result}) 

175 LOG.error(err_msg) 

176 raise exception.InvalidShare(reason=err_msg) 

177 

178 def _assert_data_in_result(self, result, msg): 

179 if "data" not in result: 179 ↛ 180line 179 didn't jump to line 180 because the condition on line 179 was never true

180 err_msg = (_('%s "data" was not in result.') % msg) 

181 LOG.error(err_msg) 

182 raise exception.InvalidShare(reason=err_msg) 

183 

184 def _get_login_info(self): 

185 """Get login IP, username and password from config file.""" 

186 logininfo = {} 

187 filename = self.configuration.manila_huawei_conf_file 

188 tree = ET.parse(filename) 

189 root = tree.getroot() 

190 RestURL = root.findtext('Storage/RestURL') 

191 logininfo['RestURL'] = RestURL.strip() 

192 

193 # Prefix !$$$ means encoded already. 

194 prefix_name = '!$$$' 

195 need_encode = False 

196 for key in ['UserName', 'UserPassword']: 

197 node = root.find('Storage/%s' % key) 

198 if node.text.startswith(prefix_name): 

199 logininfo[key] = base64.b64decode( 

200 (node.text[4:]).encode("latin-1")).decode() 

201 else: 

202 logininfo[key] = node.text 

203 node.text = prefix_name + base64.b64encode( 

204 node.text.encode("latin-1")).decode() 

205 need_encode = True 

206 if need_encode: 

207 self._change_file_mode(filename) 

208 try: 

209 tree.write(filename, 'UTF-8') 

210 except Exception as err: 

211 err_msg = (_('File write error %s.') % err) 

212 LOG.error(err_msg) 

213 raise exception.InvalidShare(reason=err_msg) 

214 

215 return logininfo 

216 

217 def _change_file_mode(self, filepath): 

218 try: 

219 utils.execute('chmod', '666', filepath, run_as_root=True) 

220 

221 except Exception as err: 

222 LOG.error('Bad response from change file: %s.', err) 

223 raise 

224 

225 def create_share(self, share_name, fs_id, share_proto): 

226 """Create a share.""" 

227 share_url_type = self._get_share_url_type(share_proto) 

228 share_path = self._get_share_path(share_name) 

229 

230 filepath = {} 

231 if share_proto == 'NFS': 

232 filepath = { 

233 "DESCRIPTION": "", 

234 "FSID": fs_id, 

235 "SHAREPATH": share_path, 

236 } 

237 elif share_proto == 'CIFS': 237 ↛ 249line 237 didn't jump to line 249 because the condition on line 237 was always true

238 filepath = { 

239 "SHAREPATH": share_path, 

240 "DESCRIPTION": "", 

241 "ABEENABLE": "false", 

242 "ENABLENOTIFY": "true", 

243 "ENABLEOPLOCK": "true", 

244 "NAME": share_name.replace("-", "_"), 

245 "FSID": fs_id, 

246 "TENANCYID": "0", 

247 } 

248 else: 

249 raise exception.InvalidShare( 

250 reason=(_('Invalid NAS protocol supplied: %s.') 

251 % share_proto)) 

252 

253 url = "/" + share_url_type 

254 data = jsonutils.dumps(filepath) 

255 

256 result = self.call(url, data, "POST") 

257 

258 msg = 'Create share error.' 

259 self._assert_rest_result(result, msg) 

260 self._assert_data_in_result(result, msg) 

261 

262 return result['data']['ID'] 

263 

264 def _delete_share_by_id(self, share_id, share_url_type): 

265 """Delete share by share id.""" 

266 url = "/" + share_url_type + "/" + share_id 

267 

268 result = self.call(url, None, "DELETE") 

269 self._assert_rest_result(result, 'Delete share error.') 

270 

271 def _delete_fs(self, fs_id): 

272 """Delete file system.""" 

273 # Get available file system 

274 url = "/filesystem/" + fs_id 

275 

276 result = self.call(url, None, "DELETE") 

277 self._assert_rest_result(result, 'Delete file system error.') 

278 

279 def _get_cifs_service_status(self): 

280 url = "/CIFSSERVICE" 

281 result = self.call(url, None, "GET") 

282 

283 msg = 'Get CIFS service status error.' 

284 self._assert_rest_result(result, msg) 

285 self._assert_data_in_result(result, msg) 

286 

287 return result['data']['RUNNINGSTATUS'] 

288 

289 def _get_nfs_service_status(self): 

290 url = "/NFSSERVICE" 

291 result = self.call(url, None, "GET") 

292 

293 msg = 'Get NFS service status error.' 

294 self._assert_rest_result(result, msg) 

295 self._assert_data_in_result(result, msg) 

296 

297 service = {} 

298 

299 service['RUNNINGSTATUS'] = result['data']['RUNNINGSTATUS'] 

300 service['SUPPORTV3'] = result['data']['SUPPORTV3'] 

301 service['SUPPORTV4'] = result['data']['SUPPORTV4'] 

302 return service 

303 

304 def _start_nfs_service_status(self): 

305 url = "/NFSSERVICE" 

306 nfsserviceinfo = { 

307 "NFSV4DOMAIN": "localdomain", 

308 "RUNNINGSTATUS": "2", 

309 "SUPPORTV3": 'true', 

310 "SUPPORTV4": 'true', 

311 "TYPE": "16452", 

312 } 

313 

314 data = jsonutils.dumps(nfsserviceinfo) 

315 result = self.call(url, data, "PUT") 

316 

317 self._assert_rest_result(result, 'Start NFS service error.') 

318 

319 def _start_cifs_service_status(self): 

320 url = "/CIFSSERVICE" 

321 cifsserviceinfo = { 

322 "ENABLENOTIFY": "true", 

323 "ENABLEOPLOCK": "true", 

324 "ENABLEOPLOCKLEASE": "false", 

325 "GUESTENABLE": "false", 

326 "OPLOCKTIMEOUT": "35", 

327 "RUNNINGSTATUS": "2", 

328 "SECURITYMODEL": "3", 

329 "SIGNINGENABLE": "false", 

330 "SIGNINGREQUIRED": "false", 

331 "TYPE": "16453", 

332 } 

333 

334 data = jsonutils.dumps(cifsserviceinfo) 

335 result = self.call(url, data, "PUT") 

336 

337 self._assert_rest_result(result, 'Start CIFS service error.') 

338 

339 def _find_pool_info(self, pool_name, result): 

340 if pool_name is None: 340 ↛ 341line 340 didn't jump to line 341 because the condition on line 340 was never true

341 return 

342 

343 poolinfo = {} 

344 pool_name = pool_name.strip() 

345 for item in result.get('data', []): 

346 if pool_name == item['NAME'] and '2' == item['USAGETYPE']: 

347 poolinfo['name'] = pool_name 

348 poolinfo['ID'] = item['ID'] 

349 poolinfo['CAPACITY'] = item['USERFREECAPACITY'] 

350 poolinfo['TOTALCAPACITY'] = item['USERTOTALCAPACITY'] 

351 poolinfo['CONSUMEDCAPACITY'] = item['USERCONSUMEDCAPACITY'] 

352 poolinfo['TIER0CAPACITY'] = item['TIER0CAPACITY'] 

353 poolinfo['TIER1CAPACITY'] = item['TIER1CAPACITY'] 

354 poolinfo['TIER2CAPACITY'] = item['TIER2CAPACITY'] 

355 break 

356 

357 return poolinfo 

358 

359 def _find_all_pool_info(self): 

360 url = "/storagepool" 

361 result = self.call(url, None, "GET") 

362 

363 msg = "Query resource pool error." 

364 self._assert_rest_result(result, msg) 

365 self._assert_data_in_result(result, msg) 

366 

367 return result 

368 

369 def _read_xml(self): 

370 """Open xml file and parse the content.""" 

371 filename = self.configuration.manila_huawei_conf_file 

372 try: 

373 tree = ET.parse(filename) 

374 root = tree.getroot() 

375 except Exception as err: 

376 message = (_('Read Huawei config file(%(filename)s)' 

377 ' for Manila error: %(err)s') 

378 % {'filename': filename, 

379 'err': err}) 

380 LOG.error(message) 

381 raise exception.InvalidInput(reason=message) 

382 return root 

383 

384 def _remove_access_from_share(self, access_id, share_proto): 

385 access_type = self._get_share_client_type(share_proto) 

386 url = "/" + access_type + "/" + access_id 

387 result = self.call(url, None, "DELETE") 

388 self._assert_rest_result(result, 'delete access from share error!') 

389 

390 def _get_access_count(self, share_id, share_client_type): 

391 url_subfix = ("/" + share_client_type + "/count?" 

392 + "filter=PARENTID::" + share_id) 

393 url = url_subfix 

394 result = self.call(url, None, "GET") 

395 

396 msg = "Get access count by share error!" 

397 self._assert_rest_result(result, msg) 

398 self._assert_data_in_result(result, msg) 

399 

400 return int(result['data']['COUNT']) 

401 

402 def _get_all_access_from_share(self, share_id, share_proto): 

403 """Return a list of all the access IDs of the share""" 

404 share_client_type = self._get_share_client_type(share_proto) 

405 count = self._get_access_count(share_id, share_client_type) 

406 

407 access_ids = [] 

408 range_begin = 0 

409 while count > 0: 

410 access_range = self._get_access_from_share_range(share_id, 

411 range_begin, 

412 share_client_type) 

413 for item in access_range: 

414 access_ids.append(item['ID']) 

415 range_begin += 100 

416 count -= 100 

417 

418 return access_ids 

419 

420 def _get_access_from_share(self, share_id, access_to, share_proto): 

421 """Segments to find access for a period of 100.""" 

422 share_client_type = self._get_share_client_type(share_proto) 

423 count = self._get_access_count(share_id, share_client_type) 

424 

425 access_id = None 

426 range_begin = 0 

427 while count > 0: 

428 if access_id: 428 ↛ 429line 428 didn't jump to line 429 because the condition on line 428 was never true

429 break 

430 access_range = self._get_access_from_share_range(share_id, 

431 range_begin, 

432 share_client_type) 

433 for item in access_range: 

434 if item['NAME'] in (access_to, '@' + access_to): 

435 access_id = item['ID'] 

436 

437 range_begin += 100 

438 count -= 100 

439 

440 return access_id 

441 

442 def _get_access_from_share_range(self, share_id, 

443 range_begin, 

444 share_client_type): 

445 range_end = range_begin + 100 

446 url = ("/" + share_client_type + "?filter=PARENTID::" 

447 + share_id + "&range=[" + str(range_begin) 

448 + "-" + str(range_end) + "]") 

449 result = self.call(url, None, "GET") 

450 self._assert_rest_result(result, 'Get access id by share error!') 

451 return result.get('data', []) 

452 

453 def _get_level_by_access_id(self, access_id, share_proto): 

454 share_client_type = self._get_share_client_type(share_proto) 

455 url = "/" + share_client_type + "/" + access_id 

456 result = self.call(url, None, "GET") 

457 self._assert_rest_result(result, 'Get access information error!') 

458 access_info = result.get('data', []) 

459 access_level = access_info.get('ACCESSVAL') 

460 if not access_level: 

461 access_level = access_info.get('PERMISSION') 

462 return access_level 

463 

464 def _change_access_rest(self, access_id, 

465 share_proto, access_level): 

466 """Change access level of the share.""" 

467 if share_proto == 'NFS': 

468 self._change_nfs_access_rest(access_id, access_level) 

469 elif share_proto == 'CIFS': 469 ↛ 472line 469 didn't jump to line 472 because the condition on line 469 was always true

470 self._change_cifs_access_rest(access_id, access_level) 

471 else: 

472 raise exception.InvalidInput( 

473 reason=(_('Invalid NAS protocol supplied: %s.') 

474 % share_proto)) 

475 

476 def _change_nfs_access_rest(self, access_id, access_level): 

477 url = "/NFS_SHARE_AUTH_CLIENT/" + access_id 

478 access = { 

479 "ACCESSVAL": access_level, 

480 "SYNC": "0", 

481 "ALLSQUASH": "1", 

482 "ROOTSQUASH": "0", 

483 } 

484 data = jsonutils.dumps(access) 

485 result = self.call(url, data, "PUT") 

486 

487 msg = 'Change access error.' 

488 self._assert_rest_result(result, msg) 

489 

490 def _change_cifs_access_rest(self, access_id, access_level): 

491 url = "/CIFS_SHARE_AUTH_CLIENT/" + access_id 

492 access = { 

493 "PERMISSION": access_level, 

494 } 

495 data = jsonutils.dumps(access) 

496 result = self.call(url, data, "PUT") 

497 

498 msg = 'Change access error.' 

499 self._assert_rest_result(result, msg) 

500 

501 def _allow_access_rest(self, share_id, access_to, 

502 share_proto, access_level): 

503 """Allow access to the share.""" 

504 if share_proto == 'NFS': 

505 self._allow_nfs_access_rest(share_id, access_to, access_level) 

506 elif share_proto == 'CIFS': 506 ↛ 509line 506 didn't jump to line 509 because the condition on line 506 was always true

507 self._allow_cifs_access_rest(share_id, access_to, access_level) 

508 else: 

509 raise exception.InvalidInput( 

510 reason=(_('Invalid NAS protocol supplied: %s.') 

511 % share_proto)) 

512 

513 def _allow_nfs_access_rest(self, share_id, access_to, access_level): 

514 url = "/NFS_SHARE_AUTH_CLIENT" 

515 access = { 

516 "TYPE": "16409", 

517 "NAME": access_to, 

518 "PARENTID": share_id, 

519 "ACCESSVAL": access_level, 

520 "SYNC": "0", 

521 "ALLSQUASH": "1", 

522 "ROOTSQUASH": "0", 

523 } 

524 data = jsonutils.dumps(access) 

525 result = self.call(url, data, "POST") 

526 

527 msg = 'Allow access error.' 

528 self._assert_rest_result(result, msg) 

529 

530 def _allow_cifs_access_rest(self, share_id, access_to, access_level): 

531 url = "/CIFS_SHARE_AUTH_CLIENT" 

532 domain_type = { 

533 'local': '2', 

534 'ad': '0' 

535 } 

536 error_msg = 'Allow access error.' 

537 access_info = ('Access info (access_to: %(access_to)s, ' 

538 'access_level: %(access_level)s, share_id: %(id)s)' 

539 % {'access_to': access_to, 

540 'access_level': access_level, 

541 'id': share_id}) 

542 

543 def send_rest(access_to, domain_type): 

544 access = { 

545 "NAME": access_to, 

546 "PARENTID": share_id, 

547 "PERMISSION": access_level, 

548 "DOMAINTYPE": domain_type, 

549 } 

550 data = jsonutils.dumps(access) 

551 result = self.call(url, data, "POST") 

552 error_code = result['error']['code'] 

553 if error_code == 0: 

554 return True 

555 elif error_code != constants.ERROR_USER_OR_GROUP_NOT_EXIST: 555 ↛ 556line 555 didn't jump to line 556 because the condition on line 555 was never true

556 self._assert_rest_result(result, error_msg) 

557 return False 

558 

559 if '\\' not in access_to: 

560 # First, try to add user access. 

561 LOG.debug('Try to add user access. %s.', access_info) 

562 if send_rest(access_to, domain_type['local']): 

563 return 

564 # Second, if add user access failed, 

565 # try to add group access. 

566 LOG.debug('Failed with add user access, ' 

567 'try to add group access. %s.', access_info) 

568 # Group name starts with @. 

569 if send_rest('@' + access_to, domain_type['local']): 569 ↛ 583line 569 didn't jump to line 583 because the condition on line 569 was always true

570 return 

571 else: 

572 LOG.debug('Try to add domain user access. %s.', access_info) 

573 if send_rest(access_to, domain_type['ad']): 

574 return 

575 # If add domain user access failed, 

576 # try to add domain group access. 

577 LOG.debug('Failed with add domain user access, ' 

578 'try to add domain group access. %s.', access_info) 

579 # Group name starts with @. 

580 if send_rest('@' + access_to, domain_type['ad']): 580 ↛ 583line 580 didn't jump to line 583 because the condition on line 580 was always true

581 return 

582 

583 raise exception.InvalidShare(reason=error_msg) 

584 

585 def _get_share_client_type(self, share_proto): 

586 share_client_type = None 

587 if share_proto == 'NFS': 

588 share_client_type = "NFS_SHARE_AUTH_CLIENT" 

589 elif share_proto == 'CIFS': 

590 share_client_type = "CIFS_SHARE_AUTH_CLIENT" 

591 else: 

592 raise exception.InvalidInput( 

593 reason=(_('Invalid NAS protocol supplied: %s.') 

594 % share_proto)) 

595 

596 return share_client_type 

597 

598 def _check_snapshot_id_exist(self, snapshot_info): 

599 """Check the snapshot id exists.""" 

600 

601 if snapshot_info['error']['code'] == constants.MSG_SNAPSHOT_NOT_FOUND: 

602 return False 

603 elif snapshot_info['error']['code'] == 0: 

604 return True 

605 else: 

606 err_str = "Check the snapshot id exists error!" 

607 err_msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str, 

608 'res': snapshot_info}) 

609 raise exception.InvalidShareSnapshot(reason=err_msg) 

610 

611 def _get_snapshot_by_id(self, snap_id): 

612 """Get snapshot by id""" 

613 url = "/FSSNAPSHOT/" + snap_id 

614 

615 result = self.call(url, None, "GET") 

616 return result 

617 

618 def _delete_snapshot(self, snap_id): 

619 """Deletes snapshot.""" 

620 url = "/FSSNAPSHOT/%s" % snap_id 

621 data = jsonutils.dumps({"TYPE": "48", "ID": snap_id}) 

622 result = self.call(url, data, "DELETE") 

623 self._assert_rest_result(result, 'Delete snapshot error.') 

624 

625 def _create_snapshot(self, sharefsid, snapshot_name): 

626 """Create a snapshot.""" 

627 filepath = { 

628 "PARENTTYPE": "40", 

629 "TYPE": "48", 

630 "PARENTID": sharefsid, 

631 "NAME": snapshot_name.replace("-", "_"), 

632 "DESCRIPTION": "", 

633 } 

634 

635 url = "/FSSNAPSHOT" 

636 data = jsonutils.dumps(filepath) 

637 

638 result = self.call(url, data, "POST") 

639 

640 msg = 'Create a snapshot error.' 

641 self._assert_rest_result(result, msg) 

642 self._assert_data_in_result(result, msg) 

643 

644 return result['data']['ID'] 

645 

646 def _get_share_by_name(self, share_name, share_url_type): 

647 """Segments to find share for a period of 100.""" 

648 count = self._get_share_count(share_url_type) 

649 

650 share = {} 

651 range_begin = 0 

652 while True: 

653 if count < 0 or share: 

654 break 

655 share = self._get_share_by_name_range(share_name, 

656 range_begin, 

657 share_url_type) 

658 range_begin += 100 

659 count -= 100 

660 

661 return share 

662 

663 def _get_share_count(self, share_url_type): 

664 """Get share count.""" 

665 url = "/" + share_url_type + "/count" 

666 result = self.call(url, None, "GET") 

667 self._assert_rest_result(result, 'Get share count error!') 

668 

669 return int(result['data']['COUNT']) 

670 

671 def _get_share_by_name_range(self, share_name, 

672 range_begin, share_url_type): 

673 """Get share by share name.""" 

674 range_end = range_begin + 100 

675 url = ("/" + share_url_type + "?range=[" 

676 + str(range_begin) + "-" 

677 + str(range_end) + "]") 

678 result = self.call(url, None, "GET") 

679 self._assert_rest_result(result, 'Get share by name error!') 

680 

681 share_path = self._get_share_path(share_name) 

682 

683 share = {} 

684 for item in result.get('data', []): 

685 if share_path == item['SHAREPATH']: 

686 share['ID'] = item['ID'] 

687 share['FSID'] = item['FSID'] 

688 break 

689 

690 return share 

691 

692 def _get_share_url_type(self, share_proto): 

693 share_url_type = None 

694 if share_proto == 'NFS': 

695 share_url_type = "NFSHARE" 

696 elif share_proto == 'CIFS': 

697 share_url_type = "CIFSHARE" 

698 else: 

699 raise exception.InvalidInput( 

700 reason=(_('Invalid NAS protocol supplied: %s.') 

701 % share_proto)) 

702 

703 return share_url_type 

704 

705 def get_fsid_by_name(self, share_name): 

706 share_name = share_name.replace("-", "_") 

707 url = "/FILESYSTEM?filter=NAME::%s&range=[0-8191]" % share_name 

708 result = self.call(url, None, "GET") 

709 self._assert_rest_result(result, 'Get filesystem by name error!') 

710 

711 for item in result.get('data', []): 

712 if share_name == item['NAME']: 

713 return item['ID'] 

714 

715 def _get_fs_info_by_id(self, fsid): 

716 url = "/filesystem/%s" % fsid 

717 result = self.call(url, None, "GET") 

718 

719 msg = "Get filesystem info by id error!" 

720 self._assert_rest_result(result, msg) 

721 self._assert_data_in_result(result, msg) 

722 

723 fs = {} 

724 fs['HEALTHSTATUS'] = result['data']['HEALTHSTATUS'] 

725 fs['RUNNINGSTATUS'] = result['data']['RUNNINGSTATUS'] 

726 fs['CAPACITY'] = result['data']['CAPACITY'] 

727 fs['ALLOCTYPE'] = result['data']['ALLOCTYPE'] 

728 fs['POOLNAME'] = result['data']['PARENTNAME'] 

729 fs['COMPRESSION'] = result['data']['ENABLECOMPRESSION'] 

730 fs['DEDUP'] = result['data']['ENABLEDEDUP'] 

731 fs['SMARTPARTITIONID'] = result['data']['CACHEPARTITIONID'] 

732 fs['SMARTCACHEID'] = result['data']['SMARTCACHEPARTITIONID'] 

733 return fs 

734 

735 def _get_share_path(self, share_name): 

736 share_path = "/" + share_name.replace("-", "_") + "/" 

737 return share_path 

738 

739 def get_share_name_by_id(self, share_id): 

740 share_name = "share_" + share_id 

741 return share_name 

742 

743 def _get_share_name_by_export_location(self, export_location, share_proto): 

744 export_location_split = None 

745 share_name = None 

746 share_ip = None 

747 if export_location: 

748 if share_proto == 'NFS': 

749 export_location_split = export_location.split(':/') 

750 if len(export_location_split) == 2: 

751 share_name = export_location_split[1] 

752 share_ip = export_location_split[0] 

753 elif share_proto == 'CIFS': 753 ↛ 761line 753 didn't jump to line 761 because the condition on line 753 was always true

754 export_location_split = export_location.split('\\') 

755 if (len(export_location_split) == 4 and 

756 export_location_split[0] == "" and 

757 export_location_split[1] == ""): 

758 share_ip = export_location_split[2] 

759 share_name = export_location_split[3] 

760 

761 if share_name is None: 

762 raise exception.InvalidInput( 

763 reason=(_('No share with export location %s could be found.') 

764 % export_location)) 

765 

766 root = self._read_xml() 

767 target_ip = root.findtext('Storage/LogicalPortIP') 

768 

769 if target_ip: 

770 if share_ip != target_ip.strip(): 

771 raise exception.InvalidInput( 

772 reason=(_('The share IP %s is not configured.') 

773 % share_ip)) 

774 else: 

775 raise exception.InvalidInput( 

776 reason=(_('The config parameter LogicalPortIP is not set.'))) 

777 

778 return share_name 

779 

780 def _get_snapshot_id(self, fs_id, snap_name): 

781 snapshot_id = (fs_id + "@" + "share_snapshot_" 

782 + snap_name.replace("-", "_")) 

783 return snapshot_id 

784 

785 def _change_share_size(self, fsid, new_size): 

786 url = "/filesystem/%s" % fsid 

787 

788 capacityinfo = { 

789 "CAPACITY": new_size, 

790 } 

791 

792 data = jsonutils.dumps(capacityinfo) 

793 result = self.call(url, data, "PUT") 

794 

795 msg = "Change a share size error!" 

796 self._assert_rest_result(result, msg) 

797 self._assert_data_in_result(result, msg) 

798 

799 def _change_fs_name(self, fsid, name): 

800 url = "/filesystem/%s" % fsid 

801 fs_param = { 

802 "NAME": name.replace("-", "_"), 

803 } 

804 data = jsonutils.dumps(fs_param) 

805 result = self.call(url, data, "PUT") 

806 

807 msg = _("Change filesystem name error.") 

808 self._assert_rest_result(result, msg) 

809 

810 def _change_extra_specs(self, fsid, extra_specs): 

811 url = "/filesystem/%s" % fsid 

812 fs_param = { 

813 "ENABLEDEDUP": extra_specs['dedupe'], 

814 "ENABLECOMPRESSION": extra_specs['compression'] 

815 } 

816 data = jsonutils.dumps(fs_param) 

817 result = self.call(url, data, "PUT") 

818 

819 msg = _("Change extra_specs error.") 

820 self._assert_rest_result(result, msg) 

821 

822 def _get_partition_id_by_name(self, name): 

823 url = "/cachepartition" 

824 result = self.call(url, None, "GET") 

825 self._assert_rest_result(result, _('Get partition by name error.')) 

826 

827 if "data" in result: 827 ↛ 831line 827 didn't jump to line 831 because the condition on line 827 was always true

828 for item in result['data']: 

829 if name == item['NAME']: 

830 return item['ID'] 

831 return None 

832 

833 def get_partition_info_by_id(self, partitionid): 

834 url = '/cachepartition/' + partitionid 

835 result = self.call(url, None, "GET") 

836 self._assert_rest_result(result, 

837 _('Get partition by partition id error.')) 

838 

839 return result['data'] 

840 

841 def _add_fs_to_partition(self, fs_id, partition_id): 

842 url = "/filesystem/associate/cachepartition" 

843 data = jsonutils.dumps({"ID": partition_id, 

844 "ASSOCIATEOBJTYPE": 40, 

845 "ASSOCIATEOBJID": fs_id, 

846 "TYPE": 268}) 

847 result = self.call(url, data, "POST") 

848 

849 self._assert_rest_result(result, 

850 _('Add filesystem to partition error.')) 

851 

852 def _remove_fs_from_partition(self, fs_id, partition_id): 

853 url = "/smartPartition/removeFs" 

854 data = jsonutils.dumps({"ID": partition_id, 

855 "ASSOCIATEOBJTYPE": 40, 

856 "ASSOCIATEOBJID": fs_id, 

857 "TYPE": 268}) 

858 result = self.call(url, data, "PUT") 

859 

860 self._assert_rest_result(result, 

861 _('Remove filesystem from partition error.')) 

862 

863 def _rename_share_snapshot(self, snapshot_id, new_name): 

864 url = "/FSSNAPSHOT/" + snapshot_id 

865 data = jsonutils.dumps({"NAME": new_name}) 

866 result = self.call(url, data, "PUT") 

867 msg = _('Rename share snapshot on array error.') 

868 self._assert_rest_result(result, msg) 

869 self._assert_data_in_result(result, msg) 

870 

871 def _get_cache_id_by_name(self, name): 

872 url = "/SMARTCACHEPARTITION" 

873 result = self.call(url, None, "GET") 

874 self._assert_rest_result(result, _('Get cache by name error.')) 

875 

876 if "data" in result: 876 ↛ 880line 876 didn't jump to line 880 because the condition on line 876 was always true

877 for item in result['data']: 

878 if name == item['NAME']: 

879 return item['ID'] 

880 return None 

881 

882 def get_cache_info_by_id(self, cacheid): 

883 url = "/SMARTCACHEPARTITION/" + cacheid 

884 data = jsonutils.dumps({"TYPE": "273", 

885 "ID": cacheid}) 

886 

887 result = self.call(url, data, "GET") 

888 self._assert_rest_result( 

889 result, _('Get smartcache by cache id error.')) 

890 

891 return result['data'] 

892 

893 def _add_fs_to_cache(self, fs_id, cache_id): 

894 url = "/SMARTCACHEPARTITION/CREATE_ASSOCIATE" 

895 data = jsonutils.dumps({"ID": cache_id, 

896 "ASSOCIATEOBJTYPE": 40, 

897 "ASSOCIATEOBJID": fs_id, 

898 "TYPE": 273}) 

899 result = self.call(url, data, "PUT") 

900 

901 self._assert_rest_result(result, _('Add filesystem to cache error.')) 

902 

903 def get_qos(self): 

904 url = "/ioclass" 

905 result = self.call(url, None, "GET") 

906 self._assert_rest_result(result, _('Get QoS information error.')) 

907 return result 

908 

909 def find_available_qos(self, qos): 

910 """"Find available QoS on the array.""" 

911 qos_id = None 

912 fs_list = [] 

913 temp_qos = copy.deepcopy(qos) 

914 result = self.get_qos() 

915 

916 if 'data' in result: 916 ↛ 937line 916 didn't jump to line 937 because the condition on line 916 was always true

917 if 'LATENCY' not in temp_qos: 

918 temp_qos['LATENCY'] = '0' 

919 for item in result['data']: 

920 for key in constants.OPTS_QOS_VALUE: 

921 if temp_qos.get(key.upper()) != item.get(key.upper()): 921 ↛ 922line 921 didn't jump to line 922 because the condition on line 921 was never true

922 break 

923 else: 

924 fs_num = len(item['FSLIST'].split(",")) 

925 # We use this QoS only if the filesystems in it is less 

926 # than 64, else we cannot add filesystem to this QoS 

927 # any more. 

928 if (item['RUNNINGSTATUS'] == constants.STATUS_QOS_ACTIVE 

929 and fs_num < constants.MAX_FS_NUM_IN_QOS 

930 and item['NAME'].startswith( 

931 constants.QOS_NAME_PREFIX) 

932 and item['LUNLIST'] == '[""]'): 

933 qos_id = item['ID'] 

934 fs_list = item['FSLIST'] 

935 break 

936 

937 return (qos_id, fs_list) 

938 

939 def add_share_to_qos(self, qos_id, fs_id, fs_list): 

940 """Add filesystem to QoS.""" 

941 url = "/ioclass/" + qos_id 

942 new_fs_list = [] 

943 fs_list_string = fs_list[1:-1] 

944 for fs_string in fs_list_string.split(","): 

945 tmp_fs_id = fs_string[1:-1] 

946 if '' != tmp_fs_id and tmp_fs_id != fs_id: 946 ↛ 944line 946 didn't jump to line 944 because the condition on line 946 was always true

947 new_fs_list.append(tmp_fs_id) 

948 

949 new_fs_list.append(fs_id) 

950 

951 data = jsonutils.dumps({"FSLIST": new_fs_list, 

952 "TYPE": 230, 

953 "ID": qos_id}) 

954 result = self.call(url, data, "PUT") 

955 msg = _('Associate filesystem to Qos error.') 

956 self._assert_rest_result(result, msg) 

957 

958 def create_qos_policy(self, qos, fs_id): 

959 # Get local time. 

960 localtime = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())) 

961 # Package QoS name. 

962 qos_name = constants.QOS_NAME_PREFIX + fs_id + '_' + localtime 

963 

964 mergedata = { 

965 "TYPE": "230", 

966 "NAME": qos_name, 

967 "FSLIST": ["%s" % fs_id], 

968 "CLASSTYPE": "1", 

969 "SCHEDULEPOLICY": "2", 

970 "SCHEDULESTARTTIME": "1410969600", 

971 "STARTTIME": "08:00", 

972 "DURATION": "86400", 

973 "CYCLESET": "[1,2,3,4,5,6,0]", 

974 } 

975 mergedata.update(qos) 

976 data = jsonutils.dumps(mergedata) 

977 url = "/ioclass" 

978 

979 result = self.call(url, data, 'POST') 

980 self._assert_rest_result(result, _('Create QoS policy error.')) 

981 

982 return result['data']['ID'] 

983 

984 def activate_deactivate_qos(self, qos_id, enablestatus): 

985 """Activate or deactivate QoS. 

986 

987 enablestatus: true (activate) 

988 enablestatus: false (deactivate) 

989 """ 

990 url = "/ioclass/active/" + qos_id 

991 data = jsonutils.dumps({ 

992 "TYPE": 230, 

993 "ID": qos_id, 

994 "ENABLESTATUS": enablestatus}) 

995 result = self.call(url, data, "PUT") 

996 self._assert_rest_result( 

997 result, _('Activate or deactivate QoS error.')) 

998 

999 def change_fs_priority_high(self, fs_id): 

1000 """Change fs priority to high.""" 

1001 url = "/filesystem/" + fs_id 

1002 data = jsonutils.dumps({"IOPRIORITY": "3"}) 

1003 

1004 result = self.call(url, data, "PUT") 

1005 self._assert_rest_result( 

1006 result, _('Change filesystem priority error.')) 

1007 

1008 def delete_qos_policy(self, qos_id): 

1009 """Delete a QoS policy.""" 

1010 url = "/ioclass/" + qos_id 

1011 data = jsonutils.dumps({"TYPE": "230", 

1012 "ID": qos_id}) 

1013 

1014 result = self.call(url, data, 'DELETE') 

1015 self._assert_rest_result(result, _('Delete QoS policy error.')) 

1016 

1017 def get_qosid_by_fsid(self, fs_id): 

1018 """Get QoS id by fs id.""" 

1019 url = "/filesystem/" + fs_id 

1020 result = self.call(url, None, "GET") 

1021 self._assert_rest_result( 

1022 result, _('Get QoS id by filesystem id error.')) 

1023 

1024 return result['data'].get('IOCLASSID') 

1025 

1026 def get_fs_list_in_qos(self, qos_id): 

1027 """Get the filesystem list in QoS.""" 

1028 qos_info = self.get_qos_info(qos_id) 

1029 

1030 fs_list = [] 

1031 fs_string = qos_info['FSLIST'][1:-1] 

1032 

1033 for fs in fs_string.split(","): 

1034 fs_id = fs[1:-1] 

1035 fs_list.append(fs_id) 

1036 

1037 return fs_list 

1038 

1039 def get_qos_info(self, qos_id): 

1040 """Get QoS information.""" 

1041 url = "/ioclass/" + qos_id 

1042 result = self.call(url, None, "GET") 

1043 self._assert_rest_result(result, _('Get QoS information error.')) 

1044 

1045 return result['data'] 

1046 

1047 def remove_fs_from_qos(self, fs_id, fs_list, qos_id): 

1048 """Remove filesystem from QoS.""" 

1049 fs_list = [i for i in fs_list if i != fs_id] 

1050 url = "/ioclass/" + qos_id 

1051 data = jsonutils.dumps({"FSLIST": fs_list, 

1052 "TYPE": 230, 

1053 "ID": qos_id}) 

1054 result = self.call(url, data, "PUT") 

1055 

1056 msg = _('Remove filesystem from QoS error.') 

1057 self._assert_rest_result(result, msg) 

1058 

1059 def _remove_fs_from_cache(self, fs_id, cache_id): 

1060 url = "/SMARTCACHEPARTITION/REMOVE_ASSOCIATE" 

1061 data = jsonutils.dumps({"ID": cache_id, 

1062 "ASSOCIATEOBJTYPE": 40, 

1063 "ASSOCIATEOBJID": fs_id, 

1064 "TYPE": 273}) 

1065 result = self.call(url, data, "PUT") 

1066 

1067 self._assert_rest_result(result, 

1068 _('Remove filesystem from cache error.')) 

1069 

1070 def get_all_eth_port(self): 

1071 url = "/ETH_PORT" 

1072 result = self.call(url, None, 'GET') 

1073 self._assert_rest_result(result, _('Get all eth port error.')) 

1074 

1075 all_eth = {} 

1076 if "data" in result: 1076 ↛ 1079line 1076 didn't jump to line 1079 because the condition on line 1076 was always true

1077 all_eth = result['data'] 

1078 

1079 return all_eth 

1080 

1081 def get_eth_port_by_id(self, port_id): 

1082 url = "/ETH_PORT/" + port_id 

1083 result = self.call(url, None, 'GET') 

1084 self._assert_rest_result(result, _('Get eth port by id error.')) 

1085 

1086 if "data" in result: 1086 ↛ 1089line 1086 didn't jump to line 1089 because the condition on line 1086 was always true

1087 return result['data'] 

1088 

1089 return None 

1090 

1091 def get_all_bond_port(self): 

1092 url = "/BOND_PORT" 

1093 result = self.call(url, None, 'GET') 

1094 self._assert_rest_result(result, _('Get all bond port error.')) 

1095 

1096 all_bond = {} 

1097 if "data" in result: 1097 ↛ 1100line 1097 didn't jump to line 1100 because the condition on line 1097 was always true

1098 all_bond = result['data'] 

1099 

1100 return all_bond 

1101 

1102 def get_port_id(self, port_name, port_type): 

1103 if port_type == constants.PORT_TYPE_ETH: 

1104 all_eth = self.get_all_eth_port() 

1105 for item in all_eth: 1105 ↛ 1114line 1105 didn't jump to line 1114 because the loop on line 1105 didn't complete

1106 if port_name == item['LOCATION']: 1106 ↛ 1105line 1106 didn't jump to line 1105 because the condition on line 1106 was always true

1107 return item['ID'] 

1108 elif port_type == constants.PORT_TYPE_BOND: 

1109 all_bond = self.get_all_bond_port() 

1110 for item in all_bond: 1110 ↛ 1114line 1110 didn't jump to line 1114 because the loop on line 1110 didn't complete

1111 if port_name == item['NAME']: 1111 ↛ 1110line 1111 didn't jump to line 1110 because the condition on line 1111 was always true

1112 return item['ID'] 

1113 

1114 return None 

1115 

1116 def get_all_vlan(self): 

1117 url = "/vlan" 

1118 result = self.call(url, None, 'GET') 

1119 self._assert_rest_result(result, _('Get all vlan error.')) 

1120 

1121 all_vlan = {} 

1122 if "data" in result: 

1123 all_vlan = result['data'] 

1124 

1125 return all_vlan 

1126 

1127 def get_vlan(self, port_id, vlan_tag): 

1128 url = "/vlan" 

1129 result = self.call(url, None, 'GET') 

1130 self._assert_rest_result(result, _('Get vlan error.')) 

1131 

1132 vlan_tag = str(vlan_tag) 

1133 if "data" in result: 

1134 for item in result['data']: 1134 ↛ 1138line 1134 didn't jump to line 1138 because the loop on line 1134 didn't complete

1135 if port_id == item['PORTID'] and vlan_tag == item['TAG']: 1135 ↛ 1134line 1135 didn't jump to line 1134 because the condition on line 1135 was always true

1136 return True, item['ID'] 

1137 

1138 return False, None 

1139 

1140 def create_vlan(self, port_id, port_type, vlan_tag): 

1141 url = "/vlan" 

1142 data = jsonutils.dumps({"PORTID": port_id, 

1143 "PORTTYPE": port_type, 

1144 "TAG": str(vlan_tag), 

1145 "TYPE": "280"}) 

1146 result = self.call(url, data, "POST") 

1147 self._assert_rest_result(result, _('Create vlan error.')) 

1148 

1149 return result['data']['ID'] 

1150 

1151 def check_vlan_exists_by_id(self, vlan_id): 

1152 all_vlan = self.get_all_vlan() 

1153 return any(vlan['ID'] == vlan_id for vlan in all_vlan) 

1154 

1155 def delete_vlan(self, vlan_id): 

1156 url = "/vlan/" + vlan_id 

1157 result = self.call(url, None, 'DELETE') 

1158 if result['error']['code'] == constants.ERROR_LOGICAL_PORT_EXIST: 1158 ↛ 1163line 1158 didn't jump to line 1163 because the condition on line 1158 was always true

1159 LOG.warning('Cannot delete vlan because there is ' 

1160 'a logical port on vlan.') 

1161 return 

1162 

1163 self._assert_rest_result(result, _('Delete vlan error.')) 

1164 

1165 def get_logical_port(self, home_port_id, ip, subnet): 

1166 url = "/LIF" 

1167 result = self.call(url, None, 'GET') 

1168 self._assert_rest_result(result, _('Get logical port error.')) 

1169 

1170 if "data" not in result: 

1171 return False, None 

1172 

1173 for item in result['data']: 1173 ↛ 1181line 1173 didn't jump to line 1181 because the loop on line 1173 didn't complete

1174 if (home_port_id == item['HOMEPORTID'] 1174 ↛ 1173line 1174 didn't jump to line 1173 because the condition on line 1174 was always true

1175 and ip == item['IPV4ADDR'] 

1176 and subnet == item['IPV4MASK']): 

1177 if item['OPERATIONALSTATUS'] != 'true': 1177 ↛ 1179line 1177 didn't jump to line 1179 because the condition on line 1177 was always true

1178 self._activate_logical_port(item['ID']) 

1179 return True, item['ID'] 

1180 

1181 return False, None 

1182 

1183 def _activate_logical_port(self, logical_port_id): 

1184 url = "/LIF/" + logical_port_id 

1185 data = jsonutils.dumps({"OPERATIONALSTATUS": "true"}) 

1186 result = self.call(url, data, 'PUT') 

1187 self._assert_rest_result(result, _('Activate logical port error.')) 

1188 

1189 def create_logical_port(self, home_port_id, home_port_type, ip, subnet): 

1190 url = "/LIF" 

1191 info = { 

1192 "ADDRESSFAMILY": 0, 

1193 "CANFAILOVER": "true", 

1194 "HOMEPORTID": home_port_id, 

1195 "HOMEPORTTYPE": home_port_type, 

1196 "IPV4ADDR": ip, 

1197 "IPV4GATEWAY": "", 

1198 "IPV4MASK": subnet, 

1199 "NAME": ip, 

1200 "OPERATIONALSTATUS": "true", 

1201 "ROLE": 2, 

1202 "SUPPORTPROTOCOL": 3, 

1203 "TYPE": "279", 

1204 } 

1205 

1206 data = jsonutils.dumps(info) 

1207 result = self.call(url, data, 'POST') 

1208 self._assert_rest_result(result, _('Create logical port error.')) 

1209 

1210 return result['data']['ID'] 

1211 

1212 def check_logical_port_exists_by_id(self, logical_port_id): 

1213 all_logical_port = self.get_all_logical_port() 

1214 return any(port['ID'] == logical_port_id for port in all_logical_port) 

1215 

1216 def get_all_logical_port(self): 

1217 url = "/LIF" 

1218 result = self.call(url, None, 'GET') 

1219 self._assert_rest_result(result, _('Get all logical port error.')) 

1220 

1221 all_logical_port = {} 

1222 if "data" in result: 1222 ↛ 1225line 1222 didn't jump to line 1225 because the condition on line 1222 was always true

1223 all_logical_port = result['data'] 

1224 

1225 return all_logical_port 

1226 

1227 def delete_logical_port(self, logical_port_id): 

1228 url = "/LIF/" + logical_port_id 

1229 result = self.call(url, None, 'DELETE') 

1230 self._assert_rest_result(result, _('Delete logical port error.')) 

1231 

1232 def set_DNS_ip_address(self, dns_ip_list): 

1233 if len(dns_ip_list) > 3: 

1234 message = _('Most three ips can be set to DNS.') 

1235 LOG.error(message) 

1236 raise exception.InvalidInput(reason=message) 

1237 

1238 url = "/DNS_Server" 

1239 dns_info = { 

1240 "ADDRESS": jsonutils.dumps(dns_ip_list), 

1241 "TYPE": "260", 

1242 } 

1243 data = jsonutils.dumps(dns_info) 

1244 result = self.call(url, data, 'PUT') 

1245 self._assert_rest_result(result, _('Set DNS ip address error.')) 

1246 

1247 if "data" in result: 1247 ↛ 1248line 1247 didn't jump to line 1248 because the condition on line 1247 was never true

1248 return result['data'] 

1249 

1250 return None 

1251 

1252 def get_DNS_ip_address(self): 

1253 url = "/DNS_Server" 

1254 result = self.call(url, None, 'GET') 

1255 self._assert_rest_result(result, _('Get DNS ip address error.')) 

1256 

1257 ip_address = {} 

1258 if "data" in result: 1258 ↛ 1261line 1258 didn't jump to line 1261 because the condition on line 1258 was always true

1259 ip_address = jsonutils.loads(result['data']['ADDRESS']) 

1260 

1261 return ip_address 

1262 

1263 def add_AD_config(self, user, password, domain, system_name): 

1264 url = "/AD_CONFIG" 

1265 info = { 

1266 "ADMINNAME": user, 

1267 "ADMINPWD": password, 

1268 "DOMAINSTATUS": 1, 

1269 "FULLDOMAINNAME": domain, 

1270 "OU": "", 

1271 "SYSTEMNAME": system_name, 

1272 "TYPE": "16414", 

1273 } 

1274 data = jsonutils.dumps(info) 

1275 result = self.call(url, data, 'PUT') 

1276 self._assert_rest_result(result, _('Add AD config error.')) 

1277 

1278 def delete_AD_config(self, user, password): 

1279 url = "/AD_CONFIG" 

1280 info = { 

1281 "ADMINNAME": user, 

1282 "ADMINPWD": password, 

1283 "DOMAINSTATUS": 0, 

1284 "TYPE": "16414", 

1285 } 

1286 data = jsonutils.dumps(info) 

1287 result = self.call(url, data, 'PUT') 

1288 self._assert_rest_result(result, _('Delete AD config error.')) 

1289 

1290 def get_AD_config(self): 

1291 url = "/AD_CONFIG" 

1292 result = self.call(url, None, 'GET') 

1293 self._assert_rest_result(result, _('Get AD config error.')) 

1294 

1295 if "data" in result: 1295 ↛ 1298line 1295 didn't jump to line 1298 because the condition on line 1295 was always true

1296 return result['data'] 

1297 

1298 return None 

1299 

1300 def get_AD_domain_name(self): 

1301 result = self.get_AD_config() 

1302 if result and result['DOMAINSTATUS'] == '1': 

1303 return True, result['FULLDOMAINNAME'] 

1304 

1305 return False, None 

1306 

1307 def add_LDAP_config(self, server, domain): 

1308 url = "/LDAP_CONFIG" 

1309 info = { 

1310 "BASEDN": domain, 

1311 "LDAPSERVER": server, 

1312 "PORTNUM": 389, 

1313 "TRANSFERTYPE": "1", 

1314 "TYPE": "16413", 

1315 "USERNAME": "", 

1316 } 

1317 data = jsonutils.dumps(info) 

1318 result = self.call(url, data, 'PUT') 

1319 self._assert_rest_result(result, _('Add LDAP config error.')) 

1320 

1321 def delete_LDAP_config(self): 

1322 url = "/LDAP_CONFIG" 

1323 result = self.call(url, None, 'DELETE') 

1324 self._assert_rest_result(result, _('Delete LDAP config error.')) 

1325 

1326 def get_LDAP_config(self): 

1327 url = "/LDAP_CONFIG" 

1328 result = self.call(url, None, 'GET') 

1329 self._assert_rest_result(result, _('Get LDAP config error.')) 

1330 

1331 if "data" in result: 1331 ↛ 1334line 1331 didn't jump to line 1334 because the condition on line 1331 was always true

1332 return result['data'] 

1333 

1334 return None 

1335 

1336 def get_LDAP_domain_server(self): 

1337 result = self.get_LDAP_config() 

1338 if result and result['LDAPSERVER']: 

1339 return True, result['LDAPSERVER'] 

1340 

1341 return False, None 

1342 

1343 def _get_array_info(self): 

1344 url = "/system/" 

1345 result = self.call(url, None, "GET") 

1346 msg = _('Get array info error.') 

1347 self._assert_rest_result(result, msg) 

1348 self._assert_data_in_result(result, msg) 

1349 return result.get('data') 

1350 

1351 def find_array_version(self): 

1352 info = self._get_array_info() 

1353 return info.get('PRODUCTVERSION') 

1354 

1355 def get_array_wwn(self): 

1356 info = self._get_array_info() 

1357 return info.get('wwn') 

1358 

1359 def _get_all_remote_devices(self): 

1360 url = "/remote_device" 

1361 result = self.call(url, None, "GET") 

1362 self._assert_rest_result(result, _('Get all remote devices error.')) 

1363 return result.get('data', []) 

1364 

1365 def get_remote_device_by_wwn(self, wwn): 

1366 devices = self._get_all_remote_devices() 

1367 for device in devices: 1367 ↛ 1370line 1367 didn't jump to line 1370 because the loop on line 1367 didn't complete

1368 if device.get('WWN') == wwn: 1368 ↛ 1367line 1368 didn't jump to line 1367 because the condition on line 1368 was always true

1369 return device 

1370 return {} 

1371 

1372 def create_replication_pair(self, pair_params): 

1373 url = "/REPLICATIONPAIR" 

1374 data = jsonutils.dumps(pair_params) 

1375 result = self.call(url, data, "POST") 

1376 

1377 msg = _('Failed to create replication pair for ' 

1378 '(LOCALRESID: %(lres)s, REMOTEDEVICEID: %(rdev)s, ' 

1379 'REMOTERESID: %(rres)s).') % { 

1380 'lres': pair_params['LOCALRESID'], 

1381 'rdev': pair_params['REMOTEDEVICEID'], 

1382 'rres': pair_params['REMOTERESID']} 

1383 self._assert_rest_result(result, msg) 

1384 self._assert_data_in_result(result, msg) 

1385 return result['data'] 

1386 

1387 def split_replication_pair(self, pair_id): 

1388 url = '/REPLICATIONPAIR/split' 

1389 data = jsonutils.dumps({"ID": pair_id, "TYPE": "263"}) 

1390 result = self.call(url, data, "PUT") 

1391 

1392 msg = _('Failed to split replication pair %s.') % pair_id 

1393 self._assert_rest_result(result, msg) 

1394 

1395 def switch_replication_pair(self, pair_id): 

1396 url = '/REPLICATIONPAIR/switch' 

1397 data = jsonutils.dumps({"ID": pair_id, "TYPE": "263"}) 

1398 result = self.call(url, data, "PUT") 

1399 

1400 msg = _('Failed to switch replication pair %s.') % pair_id 

1401 self._assert_rest_result(result, msg) 

1402 

1403 def delete_replication_pair(self, pair_id): 

1404 url = "/REPLICATIONPAIR/" + pair_id 

1405 data = None 

1406 result = self.call(url, data, "DELETE") 

1407 

1408 if (result['error']['code'] == 

1409 constants.ERROR_REPLICATION_PAIR_NOT_EXIST): 

1410 LOG.warning('Replication pair %s was not found.', 

1411 pair_id) 

1412 return 

1413 

1414 msg = _('Failed to delete replication pair %s.') % pair_id 

1415 self._assert_rest_result(result, msg) 

1416 

1417 def sync_replication_pair(self, pair_id): 

1418 url = "/REPLICATIONPAIR/sync" 

1419 data = jsonutils.dumps({"ID": pair_id, "TYPE": "263"}) 

1420 result = self.call(url, data, "PUT") 

1421 

1422 msg = _('Failed to sync replication pair %s.') % pair_id 

1423 self._assert_rest_result(result, msg) 

1424 

1425 def cancel_pair_secondary_write_lock(self, pair_id): 

1426 url = "/REPLICATIONPAIR/CANCEL_SECODARY_WRITE_LOCK" 

1427 data = jsonutils.dumps({"ID": pair_id, "TYPE": "263"}) 

1428 result = self.call(url, data, "PUT") 

1429 

1430 msg = _('Failed to cancel replication pair %s ' 

1431 'secondary write lock.') % pair_id 

1432 self._assert_rest_result(result, msg) 

1433 

1434 def set_pair_secondary_write_lock(self, pair_id): 

1435 url = "/REPLICATIONPAIR/SET_SECODARY_WRITE_LOCK" 

1436 data = jsonutils.dumps({"ID": pair_id, "TYPE": "263"}) 

1437 result = self.call(url, data, "PUT") 

1438 

1439 msg = _('Failed to set replication pair %s ' 

1440 'secondary write lock.') % pair_id 

1441 self._assert_rest_result(result, msg) 

1442 

1443 def get_replication_pair_by_id(self, pair_id): 

1444 url = "/REPLICATIONPAIR/" + pair_id 

1445 result = self.call(url, None, "GET") 

1446 

1447 msg = _('Failed to get replication pair %s.') % pair_id 

1448 self._assert_rest_result(result, msg) 

1449 self._assert_data_in_result(result, msg) 

1450 return result.get('data') 

1451 

1452 def rollback_snapshot(self, snap_id): 

1453 url = "/FSSNAPSHOT/ROLLBACK_FSSNAPSHOT" 

1454 data = jsonutils.dumps({"ID": snap_id}) 

1455 result = self.call(url, data, "PUT") 

1456 

1457 msg = _('Failed to rollback snapshot %s.') % snap_id 

1458 self._assert_rest_result(result, msg)