Coverage for manila/share/drivers/dell_emc/plugins/powermax/object_manager.py: 98%

1062 statements  

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

1# Copyright (c) 2016 Dell Inc. or its subsidiaries. 

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 copy 

17import re 

18 

19from lxml import builder 

20from lxml import etree as ET 

21from oslo_concurrency import processutils 

22from oslo_log import log 

23 

24from manila.common import constants as const 

25from manila import exception 

26from manila.i18n import _ 

27from manila.share.drivers.dell_emc.common.enas import connector 

28from manila.share.drivers.dell_emc.common.enas import constants 

29from manila.share.drivers.dell_emc.common.enas import utils as powermax_utils 

30from manila.share.drivers.dell_emc.common.enas import xml_api_parser as parser 

31from manila import utils 

32 

33LOG = log.getLogger(__name__) 

34 

35 

36@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

37 debug_only=True) 

38class StorageObjectManager(object): 

39 def __init__(self, configuration): 

40 self.context = {} 

41 

42 self.connectors = {} 

43 self.connectors['XML'] = connector.XMLAPIConnector(configuration) 

44 self.connectors['SSH'] = connector.SSHConnector(configuration) 

45 

46 elt_maker = builder.ElementMaker(nsmap={None: constants.XML_NAMESPACE}) 

47 xml_parser = parser.XMLAPIParser() 

48 

49 obj_types = StorageObject.__subclasses__() # pylint: disable=no-member 

50 for item in obj_types: 

51 key = item.__name__ 

52 self.context[key] = eval(key)(self.connectors, 

53 elt_maker, 

54 xml_parser, 

55 self) 

56 

57 def getStorageContext(self, type): 

58 if type in self.context: 

59 return self.context[type] 

60 else: 

61 message = (_("Invalid storage object type %s.") % type) 

62 LOG.error(message) 

63 raise exception.EMCPowerMaxXMLAPIError(err=message) 

64 

65 

66class StorageObject(object): 

67 def __init__(self, conn, elt_maker, xml_parser, manager): 

68 self.conn = conn 

69 self.elt_maker = elt_maker 

70 self.xml_parser = xml_parser 

71 self.manager = manager 

72 self.xml_retry = False 

73 self.ssh_retry_patterns = [ 

74 ( 

75 constants.SSH_DEFAULT_RETRY_PATTERN, 

76 exception.EMCPowerMaxLockRequiredException() 

77 ), 

78 ] 

79 

80 def _translate_response(self, response): 

81 """Translate different status to ok/error status.""" 

82 if (constants.STATUS_OK == response['maxSeverity'] or 

83 constants.STATUS_ERROR == response['maxSeverity']): 

84 return 

85 

86 old_Severity = response['maxSeverity'] 

87 if response['maxSeverity'] in (constants.STATUS_DEBUG, 

88 constants.STATUS_INFO): 

89 response['maxSeverity'] = constants.STATUS_OK 

90 

91 LOG.warning("Translated status from %(old)s to %(new)s. " 

92 "Message: %(info)s.", 

93 {'old': old_Severity, 

94 'new': response['maxSeverity'], 

95 'info': response}) 

96 

97 def _response_validation(self, response, error_code): 

98 """Validates whether a response includes a certain error code.""" 

99 msg_codes = self._get_problem_message_codes(response['problems']) 

100 

101 for code in msg_codes: 

102 if code == error_code: 

103 return True 

104 

105 return False 

106 

107 def _get_problem_message_codes(self, problems): 

108 message_codes = [] 

109 for problem in problems: 

110 if 'messageCode' in problem: 110 ↛ 109line 110 didn't jump to line 109 because the condition on line 110 was always true

111 message_codes.append(problem['messageCode']) 

112 

113 return message_codes 

114 

115 def _get_problem_messages(self, problems): 

116 messages = [] 

117 for problem in problems: 

118 if 'message' in problem: 118 ↛ 117line 118 didn't jump to line 117 because the condition on line 118 was always true

119 messages.append(problem['message']) 

120 

121 return messages 

122 

123 def _get_problem_diags(self, problems): 

124 diags = [] 

125 

126 for problem in problems: 

127 if 'Diagnostics' in problem: 127 ↛ 126line 127 didn't jump to line 126 because the condition on line 127 was always true

128 diags.append(problem['Diagnostics']) 

129 

130 return diags 

131 

132 def _build_query_package(self, body): 

133 return self.elt_maker.RequestPacket( 

134 self.elt_maker.Request( 

135 self.elt_maker.Query(body) 

136 ) 

137 ) 

138 

139 def _build_task_package(self, body): 

140 return self.elt_maker.RequestPacket( 

141 self.elt_maker.Request( 

142 self.elt_maker.StartTask(body, timeout='300') 

143 ) 

144 ) 

145 

146 @utils.retry(retry_param=exception.EMCPowerMaxLockRequiredException) 

147 def _send_request(self, req): 

148 req_xml = constants.XML_HEADER + ET.tostring(req).decode('utf-8') 

149 

150 rsp_xml = self.conn['XML'].request(str(req_xml)) 

151 

152 response = self.xml_parser.parse(rsp_xml) 

153 

154 self._translate_response(response) 

155 

156 if (response['maxSeverity'] != constants.STATUS_OK and 

157 self._response_validation(response, 

158 constants.MSG_CODE_RETRY)): 

159 raise exception.EMCPowerMaxLockRequiredException 

160 

161 return response 

162 

163 @utils.retry(retry_param=exception.EMCPowerMaxLockRequiredException) 

164 def _execute_cmd(self, cmd, retry_patterns=None, check_exit_code=False): 

165 """Execute NAS command via SSH. 

166 

167 :param retry_patterns: list of tuples,where each tuple contains a reg 

168 expression and an exception. 

169 :param check_exit_code: Boolean. Raise 

170 processutils.ProcessExecutionError if the command failed to 

171 execute and this parameter is set to True. 

172 """ 

173 if retry_patterns is None: 173 ↛ 176line 173 didn't jump to line 176 because the condition on line 173 was always true

174 retry_patterns = self.ssh_retry_patterns 

175 

176 try: 

177 out, err = self.conn['SSH'].run_ssh(cmd, check_exit_code) 

178 except processutils.ProcessExecutionError as e: 

179 for pattern in retry_patterns: 

180 if re.search(pattern[0], e.stdout): 

181 raise pattern[1] 

182 

183 raise 

184 

185 return out, err 

186 

187 def _copy_properties(self, source, target, property_map, deep_copy=True): 

188 for prop in property_map: 

189 if isinstance(prop, tuple): 

190 target_key, src_key = prop 

191 else: 

192 target_key = src_key = prop 

193 

194 if src_key in source: 

195 if deep_copy and isinstance(source[src_key], list): 

196 target[target_key] = copy.deepcopy(source[src_key]) 

197 else: 

198 target[target_key] = source[src_key] 

199 else: 

200 target[target_key] = None 

201 

202 def _get_mover_id(self, mover_name, is_vdm): 

203 if is_vdm: 

204 return self.get_context('VDM').get_id(mover_name) 

205 else: 

206 return self.get_context('Mover').get_id(mover_name, 

207 self.xml_retry) 

208 

209 def get_context(self, type): 

210 return self.manager.getStorageContext(type) 

211 

212 

213@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

214 debug_only=True) 

215class FileSystem(StorageObject): 

216 def __init__(self, conn, elt_maker, xml_parser, manager): 

217 super(FileSystem, self).__init__(conn, elt_maker, xml_parser, manager) 

218 self.filesystem_map = {} 

219 

220 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

221 def create(self, name, size, pool_name, mover_name, is_vdm=True): 

222 pool_id = self.get_context('StoragePool').get_id(pool_name) 

223 

224 mover_id = self._get_mover_id(mover_name, is_vdm) 

225 if is_vdm: 

226 mover = self.elt_maker.Vdm(vdm=mover_id) 

227 else: 

228 mover = self.elt_maker.Mover(mover=mover_id) 

229 

230 if self.xml_retry: 

231 self.xml_retry = False 

232 

233 request = self._build_task_package( 

234 self.elt_maker.NewFileSystem( 

235 mover, 

236 self.elt_maker.StoragePool( 

237 pool=pool_id, 

238 size=str(size), 

239 mayContainSlices='true' 

240 ), 

241 name=name 

242 ) 

243 ) 

244 

245 response = self._send_request(request) 

246 

247 if (self._response_validation(response, 

248 constants.MSG_INVALID_MOVER_ID) and 

249 not self.xml_retry): 

250 self.xml_retry = True 

251 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

252 elif self._response_validation( 

253 response, constants.MSG_FILESYSTEM_EXIST): 

254 LOG.warning("File system %s already exists. " 

255 "Skip the creation.", name) 

256 return 

257 elif constants.STATUS_OK != response['maxSeverity']: 

258 message = (_("Failed to create file system %(name)s. " 

259 "Reason: %(err)s.") % 

260 {'name': name, 'err': response['problems']}) 

261 LOG.error(message) 

262 raise exception.EMCPowerMaxXMLAPIError(err=message) 

263 

264 def get(self, name): 

265 if name not in self.filesystem_map: 

266 request = self._build_query_package( 

267 self.elt_maker.FileSystemQueryParams( 

268 self.elt_maker.AspectSelection( 

269 fileSystems='true', 

270 fileSystemCapacityInfos='true' 

271 ), 

272 self.elt_maker.Alias(name=name) 

273 ) 

274 ) 

275 

276 response = self._send_request(request) 

277 

278 if constants.STATUS_OK != response['maxSeverity']: 

279 if self._is_filesystem_nonexistent(response): 

280 return constants.STATUS_NOT_FOUND, response['problems'] 

281 else: 

282 return response['maxSeverity'], response['problems'] 

283 

284 if not response['objects']: 

285 return constants.STATUS_NOT_FOUND, response['problems'] 

286 

287 src = response['objects'][0] 

288 filesystem = {} 

289 property_map = ( 

290 'name', 

291 ('pools_id', 'storagePools'), 

292 ('volume_id', 'volume'), 

293 ('size', 'volumeSize'), 

294 ('id', 'fileSystem'), 

295 'type', 

296 'dataServicePolicies', 

297 ) 

298 

299 self._copy_properties(src, filesystem, property_map) 

300 

301 self.filesystem_map[name] = filesystem 

302 

303 return constants.STATUS_OK, self.filesystem_map[name] 

304 

305 def delete(self, name): 

306 status, out = self.get(name) 

307 if constants.STATUS_NOT_FOUND == status: 

308 LOG.warning("File system %s not found. Skip the deletion.", 

309 name) 

310 return 

311 elif constants.STATUS_OK != status: 

312 message = (_("Failed to get file system by name %(name)s. " 

313 "Reason: %(err)s.") % 

314 {'name': name, 'err': out}) 

315 LOG.error(message) 

316 raise exception.EMCPowerMaxXMLAPIError(err=message) 

317 

318 enas_id = self.filesystem_map[name]['id'] 

319 

320 request = self._build_task_package( 

321 self.elt_maker.DeleteFileSystem(fileSystem=enas_id) 

322 ) 

323 

324 response = self._send_request(request) 

325 

326 if constants.STATUS_OK != response['maxSeverity']: 

327 message = (_("Failed to delete file system %(name)s. " 

328 "Reason: %(err)s.") % 

329 {'name': name, 'err': response['problems']}) 

330 LOG.error(message) 

331 raise exception.EMCPowerMaxXMLAPIError(err=message) 

332 

333 self.filesystem_map.pop(name) 

334 

335 def extend(self, name, pool_name, new_size): 

336 status, out = self.get(name) 

337 if constants.STATUS_OK != status: 

338 message = (_("Failed to get file system by name %(name)s. " 

339 "Reason: %(err)s.") % 

340 {'name': name, 'err': out}) 

341 LOG.error(message) 

342 raise exception.EMCPowerMaxXMLAPIError(err=message) 

343 

344 enas_id = out['id'] 

345 size = int(out['size']) 

346 if new_size < size: 

347 message = (_("Failed to extend file system %(name)s because new " 

348 "size %(new_size)d is smaller than old size " 

349 "%(size)d.") % 

350 {'name': name, 'new_size': new_size, 'size': size}) 

351 LOG.error(message) 

352 raise exception.EMCPowerMaxXMLAPIError(err=message) 

353 elif new_size == size: 

354 return 

355 

356 pool_id = self.get_context('StoragePool').get_id(pool_name) 

357 

358 request = self._build_task_package( 

359 self.elt_maker.ExtendFileSystem( 

360 self.elt_maker.StoragePool( 

361 pool=pool_id, 

362 size=str(new_size - size) 

363 ), 

364 fileSystem=enas_id, 

365 ) 

366 ) 

367 

368 response = self._send_request(request) 

369 

370 if constants.STATUS_OK != response['maxSeverity']: 

371 message = (_("Failed to extend file system %(name)s to new size " 

372 "%(new_size)d. Reason: %(err)s.") % 

373 {'name': name, 

374 'new_size': new_size, 

375 'err': response['problems']}) 

376 LOG.error(message) 

377 raise exception.EMCPowerMaxXMLAPIError(err=message) 

378 

379 def get_id(self, name): 

380 status, out = self.get(name) 

381 if constants.STATUS_OK != status: 

382 message = (_("Failed to get file system by name %(name)s. " 

383 "Reason: %(err)s.") % 

384 {'name': name, 'err': out}) 

385 LOG.error(message) 

386 raise exception.EMCPowerMaxXMLAPIError(err=message) 

387 

388 return self.filesystem_map[name]['id'] 

389 

390 def _is_filesystem_nonexistent(self, response): 

391 """Translate different status to ok/error status.""" 

392 msg_codes = self._get_problem_message_codes(response['problems']) 

393 diags = self._get_problem_diags(response['problems']) 

394 

395 for code, diagnose in zip(msg_codes, diags): 

396 if (code == constants.MSG_FILESYSTEM_NOT_FOUND and 

397 diagnose.find('File system not found.') != -1): 

398 return True 

399 

400 return False 

401 

402 def create_from_snapshot(self, name, snap_name, source_fs_name, pool_name, 

403 mover_name, connect_id): 

404 create_fs_cmd = [ 

405 'env', 'NAS_DB=/nas', '/nas/bin/nas_fs', 

406 '-name', name, 

407 '-type', 'uxfs', 

408 '-create', 

409 'samesize=' + source_fs_name, 

410 'pool=%s' % pool_name, 

411 'storage=SINGLE', 

412 'worm=off', 

413 '-thin', 'no', 

414 '-option', 'slice=y', 

415 ] 

416 

417 self._execute_cmd(create_fs_cmd) 

418 

419 ro_mount_cmd = [ 

420 'env', 'NAS_DB=/nas', '/nas/bin/server_mount', mover_name, 

421 '-option', 'ro', 

422 name, 

423 '/%s' % name, 

424 ] 

425 self._execute_cmd(ro_mount_cmd) 

426 

427 session_name = name + ':' + snap_name 

428 copy_ckpt_cmd = [ 

429 'env', 'NAS_DB=/nas', '/nas/bin/nas_copy', 

430 '-name', session_name[0:63], 

431 '-source', '-ckpt', snap_name, 

432 '-destination', '-fs', name, 

433 '-interconnect', 

434 'id=%s' % connect_id, 

435 '-overwrite_destination', 

436 '-full_copy', 

437 ] 

438 

439 try: 

440 self._execute_cmd(copy_ckpt_cmd, check_exit_code=True) 

441 except processutils.ProcessExecutionError as expt: 

442 LOG.error("Failed to copy content from snapshot %(snap)s to " 

443 "file system %(filesystem)s. Reason: %(err)s.", 

444 {'snap': snap_name, 

445 'filesystem': name, 

446 'err': expt}) 

447 

448 # When an error happens during nas_copy, we need to continue 

449 # deleting the checkpoint of the target file system if it exists. 

450 query_fs_cmd = [ 

451 'env', 'NAS_DB=/nas', '/nas/bin/nas_fs', 

452 '-info', name, 

453 ] 

454 out, err = self._execute_cmd(query_fs_cmd) 

455 re_ckpts = r'ckpts\s*=\s*(.*)\s*' 

456 m = re.search(re_ckpts, out) 

457 if m is not None: 457 ↛ 473line 457 didn't jump to line 473 because the condition on line 457 was always true

458 ckpts = m.group(1) 

459 for ckpt in re.split(',', ckpts): 

460 umount_ckpt_cmd = [ 

461 'env', 'NAS_DB=/nas', 

462 '/nas/bin/server_umount', mover_name, 

463 '-perm', ckpt, 

464 ] 

465 self._execute_cmd(umount_ckpt_cmd) 

466 delete_ckpt_cmd = [ 

467 'env', 'NAS_DB=/nas', '/nas/bin/nas_fs', 

468 '-delete', ckpt, 

469 '-Force', 

470 ] 

471 self._execute_cmd(delete_ckpt_cmd) 

472 

473 rw_mount_cmd = [ 

474 'env', 'NAS_DB=/nas', '/nas/bin/server_mount', mover_name, 

475 '-option', 'rw', 

476 name, 

477 '/%s' % name, 

478 ] 

479 self._execute_cmd(rw_mount_cmd) 

480 

481 

482@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

483 debug_only=True) 

484class StoragePool(StorageObject): 

485 def __init__(self, conn, elt_maker, xml_parser, manager): 

486 super(StoragePool, self).__init__(conn, elt_maker, xml_parser, manager) 

487 self.pool_map = {} 

488 

489 def get(self, name, force=False): 

490 if name not in self.pool_map or force: 

491 status, out = self.get_all() 

492 if constants.STATUS_OK != status: 

493 return status, out 

494 

495 if name not in self.pool_map: 

496 return constants.STATUS_NOT_FOUND, None 

497 

498 return constants.STATUS_OK, self.pool_map[name] 

499 

500 def get_all(self): 

501 self.pool_map.clear() 

502 

503 request = self._build_query_package( 

504 self.elt_maker.StoragePoolQueryParams() 

505 ) 

506 

507 response = self._send_request(request) 

508 

509 if constants.STATUS_OK != response['maxSeverity']: 

510 return response['maxSeverity'], response['problems'] 

511 

512 if not response['objects']: 

513 return constants.STATUS_NOT_FOUND, response['problems'] 

514 

515 for item in response['objects']: 

516 pool = {} 

517 property_map = ( 

518 'name', 

519 ('movers_id', 'movers'), 

520 ('total_size', 'autoSize'), 

521 ('used_size', 'usedSize'), 

522 'diskType', 

523 'dataServicePolicies', 

524 ('id', 'pool'), 

525 ) 

526 self._copy_properties(item, pool, property_map) 

527 self.pool_map[item['name']] = pool 

528 

529 return constants.STATUS_OK, self.pool_map 

530 

531 def get_id(self, name): 

532 status, out = self.get(name) 

533 

534 if constants.STATUS_OK != status: 

535 message = (_("Failed to get storage pool by name %(name)s. " 

536 "Reason: %(err)s.") % 

537 {'name': name, 'err': out}) 

538 LOG.error(message) 

539 raise exception.EMCPowerMaxXMLAPIError(err=message) 

540 

541 return out['id'] 

542 

543 

544@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

545 debug_only=True) 

546class MountPoint(StorageObject): 

547 def __init__(self, conn, elt_maker, xml_parser, manager): 

548 super(MountPoint, self).__init__(conn, elt_maker, xml_parser, manager) 

549 

550 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

551 def create(self, mount_path, fs_name, mover_name, is_vdm=True): 

552 fs_id = self.get_context('FileSystem').get_id(fs_name) 

553 

554 mover_id = self._get_mover_id(mover_name, is_vdm) 

555 

556 if self.xml_retry: 

557 self.xml_retry = False 

558 

559 request = self._build_task_package( 

560 self.elt_maker.NewMount( 

561 self.elt_maker.MoverOrVdm( 

562 mover=mover_id, 

563 moverIdIsVdm='true' if is_vdm else 'false', 

564 ), 

565 fileSystem=fs_id, 

566 path=mount_path 

567 ) 

568 ) 

569 

570 response = self._send_request(request) 

571 

572 if (self._response_validation(response, 

573 constants.MSG_INVALID_MOVER_ID) and 

574 not self.xml_retry): 

575 self.xml_retry = True 

576 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

577 elif self._is_mount_point_already_existent(response): 

578 LOG.warning("Mount Point %(mount)s already exists. " 

579 "Skip the creation.", {'mount': mount_path}) 

580 return 

581 elif constants.STATUS_OK != response['maxSeverity']: 

582 message = (_('Failed to create Mount Point %(mount)s for ' 

583 'file system %(fs_name)s. Reason: %(err)s.') % 

584 {'mount': mount_path, 

585 'fs_name': fs_name, 

586 'err': response['problems']}) 

587 LOG.error(message) 

588 raise exception.EMCPowerMaxXMLAPIError(err=message) 

589 

590 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

591 def get(self, mover_name, is_vdm=True): 

592 mover_id = self._get_mover_id(mover_name, is_vdm) 

593 

594 if self.xml_retry: 

595 self.xml_retry = False 

596 

597 request = self._build_query_package( 

598 self.elt_maker.MountQueryParams( 

599 self.elt_maker.MoverOrVdm( 

600 mover=mover_id, 

601 moverIdIsVdm='true' if is_vdm else 'false' 

602 ) 

603 ) 

604 ) 

605 

606 response = self._send_request(request) 

607 

608 if (self._response_validation(response, 

609 constants.MSG_INVALID_MOVER_ID) and 

610 not self.xml_retry): 

611 self.xml_retry = True 

612 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

613 elif constants.STATUS_OK != response['maxSeverity']: 

614 return response['maxSeverity'], response['objects'] 

615 

616 if not response['objects']: 

617 return constants.STATUS_NOT_FOUND, None 

618 else: 

619 return constants.STATUS_OK, response['objects'] 

620 

621 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

622 def delete(self, mount_path, mover_name, is_vdm=True): 

623 mover_id = self._get_mover_id(mover_name, is_vdm) 

624 

625 if self.xml_retry: 

626 self.xml_retry = False 

627 

628 request = self._build_task_package( 

629 self.elt_maker.DeleteMount( 

630 mover=mover_id, 

631 moverIdIsVdm='true' if is_vdm else 'false', 

632 path=mount_path 

633 ) 

634 ) 

635 

636 response = self._send_request(request) 

637 

638 if (self._response_validation(response, 

639 constants.MSG_INVALID_MOVER_ID) and 

640 not self.xml_retry): 

641 self.xml_retry = True 

642 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

643 elif self._is_mount_point_nonexistent(response): 

644 LOG.warning('Mount point %(mount)s on mover %(mover_name)s ' 

645 'not found.', 

646 {'mount': mount_path, 'mover_name': mover_name}) 

647 

648 return 

649 elif constants.STATUS_OK != response['maxSeverity']: 

650 message = (_('Failed to delete mount point %(mount)s on mover ' 

651 '%(mover_name)s. Reason: %(err)s.') % 

652 {'mount': mount_path, 

653 'mover_name': mover_name, 

654 'err': response}) 

655 LOG.error(message) 

656 raise exception.EMCPowerMaxXMLAPIError(err=message) 

657 

658 def _is_mount_point_nonexistent(self, response): 

659 """Translate different status to ok/error status.""" 

660 msg_codes = self._get_problem_message_codes(response['problems']) 

661 message = self._get_problem_messages(response['problems']) 

662 

663 for code, msg in zip(msg_codes, message): 

664 if ((code == constants.MSG_GENERAL_ERROR and msg.find( 664 ↛ 663line 664 didn't jump to line 663 because the condition on line 664 was always true

665 'No such path or invalid operation') != -1) or 

666 code == constants.MSG_INVALID_VDM_ID or 

667 code == constants.MSG_INVALID_MOVER_ID): 

668 return True 

669 

670 return False 

671 

672 def _is_mount_point_already_existent(self, response): 

673 """Translate different status to ok/error status.""" 

674 msg_codes = self._get_problem_message_codes(response['problems']) 

675 message = self._get_problem_messages(response['problems']) 

676 

677 for code, msg in zip(msg_codes, message): 

678 if ((code == constants.MSG_GENERAL_ERROR and msg.find( 678 ↛ 677line 678 didn't jump to line 677 because the condition on line 678 was always true

679 'Mount already exists') != -1)): 

680 return True 

681 

682 return False 

683 

684 

685@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

686 debug_only=True) 

687class Mover(StorageObject): 

688 def __init__(self, conn, elt_maker, xml_parser, manager): 

689 super(Mover, self).__init__(conn, elt_maker, xml_parser, manager) 

690 self.mover_map = {} 

691 self.mover_ref_map = {} 

692 

693 def get_ref(self, name, force=False): 

694 if name not in self.mover_ref_map or force: 

695 self.mover_ref_map.clear() 

696 

697 request = self._build_query_package( 

698 self.elt_maker.MoverQueryParams( 

699 self.elt_maker.AspectSelection(movers='true') 

700 ) 

701 ) 

702 

703 response = self._send_request(request) 

704 

705 if constants.STATUS_ERROR == response['maxSeverity']: 

706 return response['maxSeverity'], response['problems'] 

707 

708 for item in response['objects']: 

709 mover = {} 

710 property_map = ('name', ('id', 'mover')) 

711 self._copy_properties(item, mover, property_map) 

712 if mover: 712 ↛ 708line 712 didn't jump to line 708 because the condition on line 712 was always true

713 self.mover_ref_map[mover['name']] = mover 

714 

715 if (name not in self.mover_ref_map or 

716 self.mover_ref_map[name]['id'] == ''): 

717 return constants.STATUS_NOT_FOUND, None 

718 

719 return constants.STATUS_OK, self.mover_ref_map[name] 

720 

721 def get(self, name, force=False): 

722 if name not in self.mover_map or force: 

723 if name in self.mover_ref_map and not force: 

724 mover_id = self.mover_ref_map[name]['id'] 

725 else: 

726 mover_id = self.get_id(name, force) 

727 

728 if name in self.mover_map: 

729 self.mover_map.pop(name) 

730 

731 request = self._build_query_package( 

732 self.elt_maker.MoverQueryParams( 

733 self.elt_maker.AspectSelection( 

734 moverDeduplicationSettings='true', 

735 moverDnsDomains='true', 

736 moverInterfaces='true', 

737 moverNetworkDevices='true', 

738 moverNisDomains='true', 

739 moverRoutes='true', 

740 movers='true', 

741 moverStatuses='true' 

742 ), 

743 mover=mover_id 

744 ) 

745 ) 

746 

747 response = self._send_request(request) 

748 if constants.STATUS_ERROR == response['maxSeverity']: 

749 return response['maxSeverity'], response['problems'] 

750 

751 if not response['objects']: 

752 return constants.STATUS_NOT_FOUND, response['problems'] 

753 

754 mover = {} 

755 src = response['objects'][0] 

756 property_map = ( 

757 'name', 

758 ('id', 'mover'), 

759 ('Status', 'maxSeverity'), 

760 'version', 

761 'uptime', 

762 'role', 

763 ('interfaces', 'MoverInterface'), 

764 ('devices', 'LogicalNetworkDevice'), 

765 ('dns_domain', 'MoverDnsDomain'), 

766 ) 

767 

768 self._copy_properties(src, mover, property_map) 

769 

770 internal_devices = [] 

771 if mover['interfaces']: 771 ↛ 779line 771 didn't jump to line 779 because the condition on line 771 was always true

772 for interface in mover['interfaces']: 

773 if self._is_internal_device(interface['device']): 

774 internal_devices.append(interface) 

775 

776 mover['interfaces'] = [var for var in mover['interfaces'] if 

777 var not in internal_devices] 

778 

779 self.mover_map[name] = mover 

780 

781 return constants.STATUS_OK, self.mover_map[name] 

782 

783 def get_id(self, name, force=False): 

784 status, mover_ref = self.get_ref(name, force) 

785 if constants.STATUS_OK != status: 

786 message = (_("Failed to get mover by name %(name)s.") % 

787 {'name': name}) 

788 LOG.error(message) 

789 raise exception.EMCPowerMaxXMLAPIError(err=message) 

790 

791 return mover_ref['id'] 

792 

793 def _is_internal_device(self, device): 

794 for device_type in ('mge', 'fxg', 'tks', 'fsn'): 

795 if device.find(device_type) == 0: 

796 return True 

797 return False 

798 

799 def get_interconnect_id(self, source, destination): 

800 header = [ 

801 'id', 

802 'name', 

803 'source_server', 

804 'destination_system', 

805 'destination_server', 

806 ] 

807 

808 conn_id = None 

809 

810 command_nas_cel = [ 

811 'env', 'NAS_DB=/nas', '/nas/bin/nas_cel', 

812 '-interconnect', '-l', 

813 ] 

814 out, err = self._execute_cmd(command_nas_cel) 

815 

816 lines = out.strip().split('\n') 

817 for line in lines: 

818 if line.strip().split() == header: 

819 LOG.info('Found the header of the command ' 

820 '/nas/bin/nas_cel -interconnect -l.') 

821 else: 

822 interconn = line.strip().split() 

823 if interconn[2] == source and interconn[4] == destination: 823 ↛ 817line 823 didn't jump to line 817 because the condition on line 823 was always true

824 conn_id = interconn[0] 

825 

826 return conn_id 

827 

828 def get_physical_devices(self, mover_name): 

829 

830 physical_network_devices = [] 

831 

832 cmd_sysconfig = [ 

833 'env', 'NAS_DB=/nas', '/nas/bin/server_sysconfig', mover_name, 

834 '-pci' 

835 ] 

836 

837 out, err = self._execute_cmd(cmd_sysconfig) 

838 

839 re_pattern = (r'0:\s*(?P<name>\S+)\s*IRQ:\s*(?P<irq>\d+)\n' 

840 r'.*\n' 

841 r'\s*Link:\s*(?P<link>[A-Za-z]+)') 

842 

843 for device in re.finditer(re_pattern, out): 

844 if 'Up' in device.group('link'): 

845 physical_network_devices.append(device.group('name')) 

846 

847 return physical_network_devices 

848 

849 

850@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

851 debug_only=True) 

852class VDM(StorageObject): 

853 def __init__(self, conn, elt_maker, xml_parser, manager): 

854 super(VDM, self).__init__(conn, elt_maker, xml_parser, manager) 

855 self.vdm_map = {} 

856 

857 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

858 def create(self, name, mover_name): 

859 mover_id = self._get_mover_id(mover_name, False) 

860 

861 if self.xml_retry: 

862 self.xml_retry = False 

863 

864 request = self._build_task_package( 

865 self.elt_maker.NewVdm(mover=mover_id, name=name) 

866 ) 

867 

868 response = self._send_request(request) 

869 

870 if (self._response_validation(response, 

871 constants.MSG_INVALID_MOVER_ID) and 

872 not self.xml_retry): 

873 self.xml_retry = True 

874 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

875 elif self._response_validation(response, constants.MSG_VDM_EXIST): 

876 LOG.warning("VDM %(name)s already exists. Skip the creation.", 

877 {'name': name}) 

878 elif constants.STATUS_OK != response['maxSeverity']: 

879 message = (_("Failed to create VDM %(name)s on mover " 

880 "%(mover_name)s. Reason: %(err)s.") % 

881 {'name': name, 

882 'mover_name': mover_name, 

883 'err': response['problems']}) 

884 LOG.error(message) 

885 raise exception.EMCPowerMaxXMLAPIError(err=message) 

886 

887 def get(self, name): 

888 if name not in self.vdm_map: 

889 request = self._build_query_package( 

890 self.elt_maker.VdmQueryParams() 

891 ) 

892 

893 response = self._send_request(request) 

894 

895 if constants.STATUS_OK != response['maxSeverity']: 

896 return response['maxSeverity'], response['problems'] 

897 elif not response['objects']: 

898 return constants.STATUS_NOT_FOUND, response['problems'] 

899 

900 for item in response['objects']: 

901 vdm = {} 

902 property_map = ( 

903 'name', 

904 ('id', 'vdm'), 

905 'state', 

906 ('host_mover_id', 'mover'), 

907 ('interfaces', 'Interfaces'), 

908 ) 

909 self._copy_properties(item, vdm, property_map) 

910 self.vdm_map[item['name']] = vdm 

911 

912 if name not in self.vdm_map: 

913 return constants.STATUS_NOT_FOUND, None 

914 

915 return constants.STATUS_OK, self.vdm_map[name] 

916 

917 def delete(self, name): 

918 status, out = self.get(name) 

919 if constants.STATUS_NOT_FOUND == status: 

920 LOG.warning("VDM %s not found. Skip the deletion.", 

921 name) 

922 return 

923 elif constants.STATUS_OK != status: 

924 message = (_("Failed to get VDM by name %(name)s. " 

925 "Reason: %(err)s.") % 

926 {'name': name, 'err': out}) 

927 LOG.error(message) 

928 raise exception.EMCPowerMaxXMLAPIError(err=message) 

929 

930 vdm_id = self.vdm_map[name]['id'] 

931 

932 request = self._build_task_package( 

933 self.elt_maker.DeleteVdm(vdm=vdm_id) 

934 ) 

935 

936 response = self._send_request(request) 

937 

938 if constants.STATUS_OK != response['maxSeverity']: 

939 message = (_("Failed to delete VDM %(name)s. " 

940 "Reason: %(err)s.") % 

941 {'name': name, 'err': response['problems']}) 

942 LOG.error(message) 

943 raise exception.EMCPowerMaxXMLAPIError(err=message) 

944 

945 self.vdm_map.pop(name) 

946 

947 def get_id(self, name): 

948 status, vdm = self.get(name) 

949 if constants.STATUS_OK != status: 

950 message = (_("Failed to get VDM by name %(name)s.") % 

951 {'name': name}) 

952 LOG.error(message) 

953 raise exception.EMCPowerMaxXMLAPIError(err=message) 

954 

955 return vdm['id'] 

956 

957 def attach_nfs_interface(self, vdm_name, if_name): 

958 

959 command_attach_nfs_interface = [ 

960 'env', 'NAS_DB=/nas', '/nas/bin/nas_server', 

961 '-vdm', vdm_name, 

962 '-attach', if_name, 

963 ] 

964 

965 self._execute_cmd(command_attach_nfs_interface) 

966 

967 def detach_nfs_interface(self, vdm_name, if_name): 

968 

969 command_detach_nfs_interface = [ 

970 'env', 'NAS_DB=/nas', '/nas/bin/nas_server', 

971 '-vdm', vdm_name, 

972 '-detach', if_name, 

973 ] 

974 

975 try: 

976 self._execute_cmd(command_detach_nfs_interface, 

977 check_exit_code=True) 

978 except processutils.ProcessExecutionError: 

979 interfaces = self.get_interfaces(vdm_name) 

980 if if_name not in interfaces['nfs']: 

981 LOG.debug("Failed to detach interface %(interface)s " 

982 "from mover %(mover_name)s.", 

983 {'interface': if_name, 'mover_name': vdm_name}) 

984 else: 

985 message = (_("Failed to detach interface %(interface)s " 

986 "from mover %(mover_name)s.") % 

987 {'interface': if_name, 'mover_name': vdm_name}) 

988 LOG.exception(message) 

989 raise exception.EMCPowerMaxXMLAPIError(err=message) 

990 

991 def get_interfaces(self, vdm_name): 

992 interfaces = { 

993 'cifs': [], 

994 'nfs': [], 

995 } 

996 

997 re_pattern = (r'Interfaces to services mapping:' 

998 r'\s*(?P<interfaces>(\s*interface=.*)*)') 

999 

1000 command_get_interfaces = [ 

1001 'env', 'NAS_DB=/nas', '/nas/bin/nas_server', 

1002 '-i', 

1003 '-vdm', vdm_name, 

1004 ] 

1005 

1006 out, err = self._execute_cmd(command_get_interfaces) 

1007 

1008 m = re.search(re_pattern, out) 

1009 if m: 1009 ↛ 1022line 1009 didn't jump to line 1022 because the condition on line 1009 was always true

1010 if_list = m.group('interfaces').split('\n') 

1011 for i in if_list: 

1012 m_if = re.search(r'\s*interface=(?P<if>.*)\s*:' 

1013 r'\s*(?P<type>.*)\s*', i) 

1014 if m_if: 1014 ↛ 1011line 1014 didn't jump to line 1011 because the condition on line 1014 was always true

1015 if_name = m_if.group('if').strip() 

1016 if 'cifs' == m_if.group('type') and if_name != '': 

1017 interfaces['cifs'].append(if_name) 

1018 elif (m_if.group('type') in ('vdm', 'nfs') 1018 ↛ 1011line 1018 didn't jump to line 1011 because the condition on line 1018 was always true

1019 and if_name != ''): 

1020 interfaces['nfs'].append(if_name) 

1021 

1022 return interfaces 

1023 

1024 

1025@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

1026 debug_only=True) 

1027class Snapshot(StorageObject): 

1028 def __init__(self, conn, elt_maker, xml_parser, manager): 

1029 super(Snapshot, self).__init__(conn, elt_maker, xml_parser, manager) 

1030 self.snap_map = {} 

1031 

1032 def create(self, name, fs_name, pool_id, ckpt_size=None): 

1033 fs_id = self.get_context('FileSystem').get_id(fs_name) 

1034 

1035 if ckpt_size: 

1036 elt_pool = self.elt_maker.StoragePool( 

1037 pool=pool_id, 

1038 size=str(ckpt_size) 

1039 ) 

1040 else: 

1041 elt_pool = self.elt_maker.StoragePool(pool=pool_id) 

1042 

1043 new_ckpt = self.elt_maker.NewCheckpoint( 

1044 self.elt_maker.SpaceAllocationMethod( 

1045 elt_pool 

1046 ), 

1047 checkpointOf=fs_id, 

1048 name=name 

1049 ) 

1050 

1051 request = self._build_task_package(new_ckpt) 

1052 

1053 response = self._send_request(request) 

1054 

1055 if self._response_validation(response, constants.MSG_SNAP_EXIST): 

1056 LOG.warning("Snapshot %(name)s already exists. " 

1057 "Skip the creation.", 

1058 {'name': name}) 

1059 elif constants.STATUS_OK != response['maxSeverity']: 

1060 message = (_("Failed to create snapshot %(name)s on " 

1061 "filesystem %(fs_name)s. Reason: %(err)s.") % 

1062 {'name': name, 

1063 'fs_name': fs_name, 

1064 'err': response['problems']}) 

1065 LOG.error(message) 

1066 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1067 

1068 def get(self, name): 

1069 if name not in self.snap_map: 1069 ↛ 1096line 1069 didn't jump to line 1096 because the condition on line 1069 was always true

1070 request = self._build_query_package( 

1071 self.elt_maker.CheckpointQueryParams( 

1072 self.elt_maker.Alias(name=name) 

1073 ) 

1074 ) 

1075 

1076 response = self._send_request(request) 

1077 

1078 if constants.STATUS_OK != response['maxSeverity']: 

1079 return response['maxSeverity'], response['problems'] 

1080 

1081 if not response['objects']: 

1082 return constants.STATUS_NOT_FOUND, response['problems'] 

1083 

1084 src = response['objects'][0] 

1085 snap = {} 

1086 property_map = ( 

1087 'name', 

1088 ('id', 'checkpoint'), 

1089 'checkpointOf', 

1090 'state', 

1091 ) 

1092 self._copy_properties(src, snap, property_map) 

1093 

1094 self.snap_map[name] = snap 

1095 

1096 return constants.STATUS_OK, self.snap_map[name] 

1097 

1098 def delete(self, name): 

1099 status, out = self.get(name) 

1100 if constants.STATUS_NOT_FOUND == status: 

1101 LOG.warning("Snapshot %s not found. Skip the deletion.", 

1102 name) 

1103 return 

1104 elif constants.STATUS_OK != status: 

1105 message = (_("Failed to get snapshot by name %(name)s. " 

1106 "Reason: %(err)s.") % 

1107 {'name': name, 'err': out}) 

1108 LOG.error(message) 

1109 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1110 

1111 chpt_id = self.snap_map[name]['id'] 

1112 

1113 request = self._build_task_package( 

1114 self.elt_maker.DeleteCheckpoint(checkpoint=chpt_id) 

1115 ) 

1116 

1117 response = self._send_request(request) 

1118 if constants.STATUS_OK != response['maxSeverity']: 

1119 message = (_("Failed to delete snapshot %(name)s. " 

1120 "Reason: %(err)s.") % 

1121 {'name': name, 'err': response['problems']}) 

1122 LOG.error(message) 

1123 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1124 

1125 self.snap_map.pop(name) 

1126 

1127 def get_id(self, name): 

1128 status, out = self.get(name) 

1129 

1130 if constants.STATUS_OK != status: 

1131 message = (_("Failed to get snapshot by %(name)s. " 

1132 "Reason: %(err)s.") % 

1133 {'name': name, 'err': out}) 

1134 LOG.error(message) 

1135 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1136 

1137 return self.snap_map[name]['id'] 

1138 

1139 

1140@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

1141 debug_only=True) 

1142class MoverInterface(StorageObject): 

1143 def __init__(self, conn, elt_maker, xml_parser, manager): 

1144 super(MoverInterface, self).__init__(conn, elt_maker, xml_parser, 

1145 manager) 

1146 

1147 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1148 def create(self, interface): 

1149 # Maximum of 32 characters for mover interface name 

1150 name = interface['name'] 

1151 if len(name) > 32: 

1152 name = name[0:31] 

1153 

1154 device_name = interface['device_name'] 

1155 ip_addr = interface['ip'] 

1156 mover_name = interface['mover_name'] 

1157 net_mask = interface['net_mask'] 

1158 vlan_id = interface['vlan_id'] if interface['vlan_id'] else -1 

1159 

1160 mover_id = self._get_mover_id(mover_name, False) 

1161 

1162 params = dict(device=device_name, 

1163 ipAddress=str(ip_addr), 

1164 mover=mover_id, 

1165 name=name, 

1166 netMask=net_mask, 

1167 vlanid=str(vlan_id)) 

1168 

1169 if interface.get('ip_version') == 6: 1169 ↛ 1170line 1169 didn't jump to line 1170 because the condition on line 1169 was never true

1170 params['ipVersion'] = 'IPv6' 

1171 

1172 if self.xml_retry: 

1173 self.xml_retry = False 

1174 

1175 request = self._build_task_package( 

1176 self.elt_maker.NewMoverInterface(**params)) 

1177 

1178 response = self._send_request(request) 

1179 

1180 if (self._response_validation(response, 

1181 constants.MSG_INVALID_MOVER_ID) and 

1182 not self.xml_retry): 

1183 self.xml_retry = True 

1184 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1185 elif self._response_validation( 

1186 response, constants.MSG_INTERFACE_NAME_EXIST): 

1187 LOG.warning("Mover interface name %s already exists. " 

1188 "Skip the creation.", name) 

1189 elif self._response_validation( 

1190 response, constants.MSG_INTERFACE_EXIST): 

1191 LOG.warning("Mover interface IP %s already exists. " 

1192 "Skip the creation.", ip_addr) 

1193 elif self._response_validation( 

1194 response, constants.MSG_INTERFACE_INVALID_VLAN_ID): 

1195 # When fail to create a mover interface with the specified 

1196 # vlan id, PowerMax will leave an interface with vlan id 0 in the 

1197 # backend. So we should explicitly remove the interface. 

1198 try: 

1199 self.delete(str(ip_addr), mover_name) 

1200 except exception.EMCPowerMaxXMLAPIError: 

1201 pass 

1202 message = (_("Invalid vlan id %s. Other interfaces on this " 

1203 "subnet are in a different vlan.") % vlan_id) 

1204 LOG.error(message) 

1205 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1206 elif constants.STATUS_OK != response['maxSeverity']: 

1207 message = (_("Failed to create mover interface %(interface)s. " 

1208 "Reason: %(err)s.") % 

1209 {'interface': interface, 

1210 'err': response['problems']}) 

1211 LOG.error(message) 

1212 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1213 

1214 def get(self, name, mover_name): 

1215 # Maximum of 32 characters for mover interface name 

1216 if len(name) > 32: 

1217 name = name[0:31] 

1218 

1219 status, mover = self.manager.getStorageContext('Mover').get( 

1220 mover_name, True) 

1221 if constants.STATUS_OK == status: 

1222 for interface in mover['interfaces']: 

1223 if name == interface['name']: 

1224 return constants.STATUS_OK, interface 

1225 

1226 return constants.STATUS_NOT_FOUND, None 

1227 

1228 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1229 def delete(self, ip_addr, mover_name): 

1230 mover_id = self._get_mover_id(mover_name, False) 

1231 

1232 if self.xml_retry: 

1233 self.xml_retry = False 

1234 

1235 request = self._build_task_package( 

1236 self.elt_maker.DeleteMoverInterface( 

1237 ipAddress=str(ip_addr), 

1238 mover=mover_id 

1239 ) 

1240 ) 

1241 

1242 response = self._send_request(request) 

1243 

1244 if (self._response_validation(response, 

1245 constants.MSG_INVALID_MOVER_ID) and 

1246 not self.xml_retry): 

1247 self.xml_retry = True 

1248 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1249 elif self._response_validation( 

1250 response, constants.MSG_INTERFACE_NON_EXISTENT): 

1251 LOG.warning("Mover interface %s not found. " 

1252 "Skip the deletion.", ip_addr) 

1253 return 

1254 elif constants.STATUS_OK != response['maxSeverity']: 

1255 message = (_("Failed to delete mover interface %(ip)s on mover " 

1256 "%(mover)s. Reason: %(err)s.") % 

1257 {'ip': ip_addr, 

1258 'mover': mover_name, 

1259 'err': response['problems']}) 

1260 LOG.error(message) 

1261 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1262 

1263 

1264@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

1265 debug_only=True) 

1266class DNSDomain(StorageObject): 

1267 def __init__(self, conn, elt_maker, xml_parser, manager): 

1268 super(DNSDomain, self).__init__(conn, elt_maker, xml_parser, manager) 

1269 

1270 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1271 def create(self, mover_name, name, servers, protocol='udp'): 

1272 mover_id = self._get_mover_id(mover_name, False) 

1273 

1274 if self.xml_retry: 

1275 self.xml_retry = False 

1276 

1277 request = self._build_task_package( 

1278 self.elt_maker.NewMoverDnsDomain( 

1279 mover=mover_id, 

1280 name=name, 

1281 servers=servers, 

1282 protocol=protocol 

1283 ) 

1284 ) 

1285 

1286 response = self._send_request(request) 

1287 

1288 if (self._response_validation(response, 

1289 constants.MSG_INVALID_MOVER_ID) and 

1290 not self.xml_retry): 

1291 self.xml_retry = True 

1292 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1293 elif constants.STATUS_OK != response['maxSeverity']: 

1294 message = (_("Failed to create DNS domain %(name)s. " 

1295 "Reason: %(err)s.") % 

1296 {'name': name, 'err': response['problems']}) 

1297 LOG.error(message) 

1298 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1299 

1300 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1301 def delete(self, mover_name, name): 

1302 mover_id = self._get_mover_id(mover_name, False) 

1303 

1304 if self.xml_retry: 

1305 self.xml_retry = False 

1306 

1307 request = self._build_task_package( 

1308 self.elt_maker.DeleteMoverDnsDomain( 

1309 mover=mover_id, 

1310 name=name 

1311 ) 

1312 ) 

1313 

1314 response = self._send_request(request) 

1315 if (self._response_validation(response, 

1316 constants.MSG_INVALID_MOVER_ID) and 

1317 not self.xml_retry): 

1318 self.xml_retry = True 

1319 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1320 elif constants.STATUS_OK != response['maxSeverity']: 

1321 LOG.warning("Failed to delete DNS domain %(name)s. " 

1322 "Reason: %(err)s.", 

1323 {'name': name, 'err': response['problems']}) 

1324 

1325 

1326@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

1327 debug_only=True) 

1328class CIFSServer(StorageObject): 

1329 def __init__(self, conn, elt_maker, xml_parser, manager): 

1330 super(CIFSServer, self).__init__(conn, elt_maker, xml_parser, manager) 

1331 self.cifs_server_map = {} 

1332 

1333 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1334 def create(self, server_args): 

1335 compName = server_args['name'] 

1336 # Maximum of 14 characters for netBIOS name 

1337 name = server_args['name'][-14:] 

1338 # Maximum of 12 characters for alias name 

1339 alias_name = server_args['name'][-12:] 

1340 interfaces = server_args['interface_ip'] 

1341 domain_name = server_args['domain_name'] 

1342 user_name = server_args['user_name'] 

1343 password = server_args['password'] 

1344 mover_name = server_args['mover_name'] 

1345 is_vdm = server_args['is_vdm'] 

1346 

1347 mover_id = self._get_mover_id(mover_name, is_vdm) 

1348 

1349 if self.xml_retry: 

1350 self.xml_retry = False 

1351 

1352 alias_name_list = [self.elt_maker.li(alias_name)] 

1353 

1354 request = self._build_task_package( 

1355 self.elt_maker.NewW2KCifsServer( 

1356 self.elt_maker.MoverOrVdm( 

1357 mover=mover_id, 

1358 moverIdIsVdm='true' if server_args['is_vdm'] else 'false' 

1359 ), 

1360 self.elt_maker.Aliases(*alias_name_list), 

1361 self.elt_maker.JoinDomain(userName=user_name, 

1362 password=password), 

1363 compName=compName, 

1364 domain=domain_name, 

1365 interfaces=interfaces, 

1366 name=name 

1367 ) 

1368 ) 

1369 

1370 response = self._send_request(request) 

1371 

1372 if (self._response_validation(response, 

1373 constants.MSG_INVALID_MOVER_ID) and 

1374 not self.xml_retry): 

1375 self.xml_retry = True 

1376 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1377 if constants.STATUS_OK != response['maxSeverity']: 

1378 status, out = self.get(compName, mover_name, is_vdm) 

1379 if constants.STATUS_OK == status and out['domainJoined'] == 'true': 

1380 return 

1381 else: 

1382 message = (_("Failed to create CIFS server %(name)s. " 

1383 "Reason: %(err)s.") % 

1384 {'name': name, 

1385 'err': response['problems']}) 

1386 LOG.error(message) 

1387 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1388 

1389 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1390 def get_all(self, mover_name, is_vdm=True): 

1391 mover_id = self._get_mover_id(mover_name, is_vdm) 

1392 

1393 if self.xml_retry: 

1394 self.xml_retry = False 

1395 

1396 request = self._build_query_package( 

1397 self.elt_maker.CifsServerQueryParams( 

1398 self.elt_maker.MoverOrVdm( 

1399 mover=mover_id, 

1400 moverIdIsVdm='true' if is_vdm else 'false' 

1401 ) 

1402 ) 

1403 ) 

1404 

1405 response = self._send_request(request) 

1406 if (self._response_validation(response, 

1407 constants.MSG_INVALID_MOVER_ID) and 

1408 not self.xml_retry): 

1409 self.xml_retry = True 

1410 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1411 elif constants.STATUS_OK != response['maxSeverity']: 

1412 return response['maxSeverity'], response['objects'] 

1413 

1414 if mover_name in self.cifs_server_map: 

1415 self.cifs_server_map.pop(mover_name) 

1416 

1417 self.cifs_server_map[mover_name] = {} 

1418 

1419 for item in response['objects']: 

1420 self.cifs_server_map[mover_name][item['compName'].lower()] = item 

1421 

1422 return constants.STATUS_OK, self.cifs_server_map[mover_name] 

1423 

1424 def get(self, name, mover_name, is_vdm=True, force=False): 

1425 # name is compName 

1426 name = name.lower() 

1427 

1428 if (mover_name in self.cifs_server_map and 

1429 name in self.cifs_server_map[mover_name]) and not force: 

1430 return constants.STATUS_OK, self.cifs_server_map[mover_name][name] 

1431 

1432 self.get_all(mover_name, is_vdm) 

1433 

1434 if mover_name in self.cifs_server_map: 

1435 for compName, server in self.cifs_server_map[mover_name].items(): 

1436 if name == compName: 1436 ↛ 1435line 1436 didn't jump to line 1435 because the condition on line 1436 was always true

1437 return constants.STATUS_OK, server 

1438 

1439 return constants.STATUS_NOT_FOUND, None 

1440 

1441 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1442 def modify(self, server_args): 

1443 """Make CIFS server join or un-join the domain. 

1444 

1445 :param server_args: Dictionary for CIFS server modification 

1446 name: CIFS server name instead of compName 

1447 join_domain: True for joining the domain, false for un-joining 

1448 user_name: User name under which the domain is joined 

1449 password: Password associated with the user name 

1450 mover_name: mover or VDM name 

1451 is_vdm: Boolean to indicate mover or VDM 

1452 :raises exception.EMCPowerMaxXMLAPIError: if modification fails. 

1453 """ 

1454 name = server_args['name'] 

1455 join_domain = server_args['join_domain'] 

1456 user_name = server_args['user_name'] 

1457 password = server_args['password'] 

1458 mover_name = server_args['mover_name'] 

1459 

1460 if 'is_vdm' in server_args.keys(): 

1461 is_vdm = server_args['is_vdm'] 

1462 else: 

1463 is_vdm = True 

1464 

1465 mover_id = self._get_mover_id(mover_name, is_vdm) 

1466 

1467 if self.xml_retry: 

1468 self.xml_retry = False 

1469 

1470 request = self._build_task_package( 

1471 self.elt_maker.ModifyW2KCifsServer( 

1472 self.elt_maker.DomainSetting( 

1473 joinDomain='true' if join_domain else 'false', 

1474 password=password, 

1475 userName=user_name, 

1476 ), 

1477 mover=mover_id, 

1478 moverIdIsVdm='true' if is_vdm else 'false', 

1479 name=name 

1480 ) 

1481 ) 

1482 

1483 response = self._send_request(request) 

1484 

1485 if (self._response_validation(response, 

1486 constants.MSG_INVALID_MOVER_ID) and 

1487 not self.xml_retry): 

1488 self.xml_retry = True 

1489 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1490 elif self._ignore_modification_error(response, join_domain): 

1491 return 

1492 elif constants.STATUS_OK != response['maxSeverity']: 

1493 message = (_("Failed to modify CIFS server %(name)s. " 

1494 "Reason: %(err)s.") % 

1495 {'name': name, 

1496 'err': response['problems']}) 

1497 LOG.error(message) 

1498 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1499 

1500 def _ignore_modification_error(self, response, join_domain): 

1501 if self._response_validation(response, constants.MSG_JOIN_DOMAIN): 

1502 return join_domain 

1503 elif self._response_validation(response, constants.MSG_UNJOIN_DOMAIN): 

1504 return not join_domain 

1505 

1506 return False 

1507 

1508 def delete(self, computer_name, mover_name, is_vdm=True): 

1509 try: 

1510 status, out = self.get( 

1511 computer_name.lower(), mover_name, is_vdm, self.xml_retry) 

1512 if constants.STATUS_NOT_FOUND == status: 

1513 LOG.warning("CIFS server %(name)s on mover %(mover_name)s " 

1514 "not found. Skip the deletion.", 

1515 {'name': computer_name, 'mover_name': mover_name}) 

1516 return 

1517 except exception.EMCPowerMaxXMLAPIError: 

1518 LOG.warning("CIFS server %(name)s on mover %(mover_name)s " 

1519 "not found. Skip the deletion.", 

1520 {'name': computer_name, 'mover_name': mover_name}) 

1521 return 

1522 

1523 server_name = out['name'] 

1524 

1525 mover_id = self._get_mover_id(mover_name, is_vdm) 

1526 

1527 request = self._build_task_package( 

1528 self.elt_maker.DeleteCifsServer( 

1529 mover=mover_id, 

1530 moverIdIsVdm='true' if is_vdm else 'false', 

1531 name=server_name 

1532 ) 

1533 ) 

1534 

1535 response = self._send_request(request) 

1536 

1537 if constants.STATUS_OK != response['maxSeverity']: 

1538 message = (_("Failed to delete CIFS server %(name)s. " 

1539 "Reason: %(err)s.") % 

1540 {'name': computer_name, 'err': response['problems']}) 

1541 LOG.error(message) 

1542 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1543 

1544 self.cifs_server_map[mover_name].pop(computer_name) 

1545 

1546 

1547@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

1548 debug_only=True) 

1549class CIFSShare(StorageObject): 

1550 def __init__(self, conn, elt_maker, xml_parser, manager): 

1551 super(CIFSShare, self).__init__(conn, elt_maker, xml_parser, manager) 

1552 self.cifs_share_map = {} 

1553 

1554 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1555 def create(self, name, server_name, mover_name, is_vdm=True): 

1556 mover_id = self._get_mover_id(mover_name, is_vdm) 

1557 

1558 if self.xml_retry: 

1559 self.xml_retry = False 

1560 

1561 share_path = '/' + name 

1562 

1563 request = self._build_task_package( 

1564 self.elt_maker.NewCifsShare( 

1565 self.elt_maker.MoverOrVdm( 

1566 mover=mover_id, 

1567 moverIdIsVdm='true' if is_vdm else 'false' 

1568 ), 

1569 self.elt_maker.CifsServers(self.elt_maker.li(server_name)), 

1570 name=name, 

1571 path=share_path 

1572 ) 

1573 ) 

1574 

1575 response = self._send_request(request) 

1576 

1577 if (self._response_validation(response, 

1578 constants.MSG_INVALID_MOVER_ID) and 

1579 not self.xml_retry): 

1580 self.xml_retry = True 

1581 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1582 elif constants.STATUS_OK != response['maxSeverity']: 

1583 message = (_("Failed to create file share %(name)s. " 

1584 "Reason: %(err)s.") % 

1585 {'name': name, 'err': response['problems']}) 

1586 LOG.error(message) 

1587 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1588 

1589 def get(self, name): 

1590 if name not in self.cifs_share_map: 

1591 request = self._build_query_package( 

1592 self.elt_maker.CifsShareQueryParams(name=name) 

1593 ) 

1594 

1595 response = self._send_request(request) 

1596 

1597 if constants.STATUS_OK != response['maxSeverity']: 

1598 return response['maxSeverity'], response['problems'] 

1599 

1600 if not response['objects']: 

1601 return constants.STATUS_NOT_FOUND, None 

1602 

1603 self.cifs_share_map[name] = response['objects'][0] 

1604 

1605 return constants.STATUS_OK, self.cifs_share_map[name] 

1606 

1607 @utils.retry(retry_param=exception.EMCPowerMaxInvalidMoverID) 

1608 def delete(self, name, mover_name, is_vdm=True): 

1609 status, out = self.get(name) 

1610 if constants.STATUS_NOT_FOUND == status: 

1611 LOG.warning("CIFS share %s not found. Skip the deletion.", 

1612 name) 

1613 return 

1614 elif constants.STATUS_OK != status: 

1615 message = (_("Failed to get CIFS share by name %(name)s. " 

1616 "Reason: %(err)s.") % 

1617 {'name': name, 'err': out}) 

1618 LOG.error(message) 

1619 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1620 

1621 mover_id = self._get_mover_id(mover_name, is_vdm) 

1622 

1623 if self.xml_retry: 

1624 self.xml_retry = False 

1625 

1626 netbios_names = self.cifs_share_map[name]['CifsServers'] 

1627 

1628 request = self._build_task_package( 

1629 self.elt_maker.DeleteCifsShare( 

1630 self.elt_maker.CifsServers(*map(lambda a: self.elt_maker.li(a), 

1631 netbios_names)), 

1632 mover=mover_id, 

1633 moverIdIsVdm='true' if is_vdm else 'false', 

1634 name=name 

1635 ) 

1636 ) 

1637 

1638 response = self._send_request(request) 

1639 

1640 if (self._response_validation(response, 

1641 constants.MSG_INVALID_MOVER_ID) and 

1642 not self.xml_retry): 

1643 self.xml_retry = True 

1644 raise exception.EMCPowerMaxInvalidMoverID(id=mover_id) 

1645 elif constants.STATUS_OK != response['maxSeverity']: 

1646 message = (_("Failed to delete file system %(name)s. " 

1647 "Reason: %(err)s.") % 

1648 {'name': name, 'err': response['problems']}) 

1649 LOG.error(message) 

1650 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1651 

1652 self.cifs_share_map.pop(name) 

1653 

1654 def disable_share_access(self, share_name, mover_name): 

1655 cmd_str = 'sharesd %s set noaccess' % share_name 

1656 disable_access = [ 

1657 'env', 'NAS_DB=/nas', '/nas/bin/.server_config', mover_name, 

1658 '-v', "%s" % cmd_str, 

1659 ] 

1660 

1661 try: 

1662 self._execute_cmd(disable_access, check_exit_code=True) 

1663 except processutils.ProcessExecutionError: 

1664 message = (_('Failed to disable the access to CIFS share ' 

1665 '%(name)s.') % 

1666 {'name': share_name}) 

1667 LOG.exception(message) 

1668 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1669 

1670 def allow_share_access(self, mover_name, share_name, user_name, domain, 

1671 access=constants.CIFS_ACL_FULLCONTROL): 

1672 account = user_name + "@" + domain 

1673 allow_str = ('sharesd %(share_name)s grant %(account)s=%(access)s' 

1674 % {'share_name': share_name, 

1675 'account': account, 

1676 'access': access}) 

1677 

1678 allow_access = [ 

1679 'env', 'NAS_DB=/nas', '/nas/bin/.server_config', mover_name, 

1680 '-v', "%s" % allow_str, 

1681 ] 

1682 

1683 try: 

1684 self._execute_cmd(allow_access, check_exit_code=True) 

1685 except processutils.ProcessExecutionError as expt: 

1686 dup_msg = re.compile(r'ACE for %(domain)s\\%(user)s unchanged' % 

1687 {'domain': domain, 'user': user_name}, re.I) 

1688 if re.search(dup_msg, expt.stdout): 

1689 LOG.warning("Duplicate access control entry, " 

1690 "skipping allow...") 

1691 else: 

1692 message = (_('Failed to allow the access %(access)s to ' 

1693 'CIFS share %(name)s. Reason: %(err)s.') % 

1694 {'access': access, 'name': share_name, 'err': expt}) 

1695 LOG.error(message) 

1696 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1697 

1698 def deny_share_access(self, mover_name, share_name, user_name, domain, 

1699 access=constants.CIFS_ACL_FULLCONTROL): 

1700 account = user_name + "@" + domain 

1701 revoke_str = ('sharesd %(share_name)s revoke %(account)s=%(access)s' 

1702 % {'share_name': share_name, 

1703 'account': account, 

1704 'access': access}) 

1705 

1706 allow_access = [ 

1707 'env', 'NAS_DB=/nas', '/nas/bin/.server_config', mover_name, 

1708 '-v', "%s" % revoke_str, 

1709 ] 

1710 try: 

1711 self._execute_cmd(allow_access, check_exit_code=True) 

1712 except processutils.ProcessExecutionError as expt: 

1713 not_found_msg = re.compile( 

1714 r'No ACE found for %(domain)s\\%(user)s' 

1715 % {'domain': domain, 'user': user_name}, re.I) 

1716 user_err_msg = re.compile( 

1717 r'Cannot get mapping for %(domain)s\\%(user)s' 

1718 % {'domain': domain, 'user': user_name}, re.I) 

1719 

1720 if re.search(not_found_msg, expt.stdout): 

1721 LOG.warning("No access control entry found, " 

1722 "skipping deny...") 

1723 elif re.search(user_err_msg, expt.stdout): 

1724 LOG.warning("User not found on domain, skipping deny...") 

1725 else: 

1726 message = (_('Failed to deny the access %(access)s to ' 

1727 'CIFS share %(name)s. Reason: %(err)s.') % 

1728 {'access': access, 'name': share_name, 'err': expt}) 

1729 LOG.exception(message) 

1730 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1731 

1732 def get_share_access(self, mover_name, share_name): 

1733 get_str = 'sharesd %s dump' % share_name 

1734 get_access = [ 

1735 'env', 'NAS_DB=/nas', '/nas/bin/.server_config', mover_name, 

1736 '-v', "%s" % get_str, 

1737 ] 

1738 

1739 try: 

1740 out, err = self._execute_cmd(get_access, check_exit_code=True) 

1741 except processutils.ProcessExecutionError: 

1742 msg = _('Failed to get access list of CIFS share %s.') % share_name 

1743 LOG.exception(msg) 

1744 raise exception.EMCPowerMaxXMLAPIError(err=msg) 

1745 

1746 ret = {} 

1747 name_pattern = re.compile(r"Unix user '(.+?)'") 

1748 access_pattern = re.compile(r"ALLOWED:(.+?):") 

1749 

1750 name = None 

1751 for line in out.splitlines(): 

1752 if name is None: 

1753 names = name_pattern.findall(line) 

1754 if names: 

1755 name = names[0].lower() 

1756 else: 

1757 accesses = access_pattern.findall(line) 

1758 if accesses: 

1759 ret[name] = accesses[0].lower() 

1760 name = None 

1761 return ret 

1762 

1763 def clear_share_access(self, mover_name, share_name, domain, 

1764 white_list_users): 

1765 existing_users = self.get_share_access(mover_name, share_name) 

1766 white_list_users_set = set(user.lower() for user in white_list_users) 

1767 users_to_remove = set(existing_users.keys()) - white_list_users_set 

1768 for user in users_to_remove: 

1769 self.deny_share_access(mover_name, share_name, user, domain, 

1770 existing_users[user]) 

1771 return users_to_remove 

1772 

1773 

1774@powermax_utils.decorate_all_methods(powermax_utils.log_enter_exit, 

1775 debug_only=True) 

1776class NFSShare(StorageObject): 

1777 def __init__(self, conn, elt_maker, xml_parser, manager): 

1778 super(NFSShare, self).__init__(conn, elt_maker, xml_parser, manager) 

1779 self.nfs_share_map = {} 

1780 

1781 def create(self, name, mover_name): 

1782 share_path = '/' + name 

1783 create_nfs_share_cmd = [ 

1784 'env', 'NAS_DB=/nas', '/nas/bin/server_export', mover_name, 

1785 '-option', 'access=-0.0.0.0/0.0.0.0', 

1786 share_path, 

1787 ] 

1788 

1789 try: 

1790 self._execute_cmd(create_nfs_share_cmd, check_exit_code=True) 

1791 except processutils.ProcessExecutionError as expt: 

1792 message = (_('Failed to create NFS share %(name)s on mover ' 

1793 '%(mover_name)s. Reason: %(err)s.') % 

1794 {'name': name, 'mover_name': mover_name, 'err': expt}) 

1795 LOG.exception(message) 

1796 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1797 

1798 def delete(self, name, mover_name): 

1799 path = '/' + name 

1800 

1801 status, out = self.get(name, mover_name) 

1802 if constants.STATUS_NOT_FOUND == status: 

1803 LOG.warning("NFS share %s not found. Skip the deletion.", 

1804 path) 

1805 return 

1806 

1807 delete_nfs_share_cmd = [ 

1808 'env', 'NAS_DB=/nas', '/nas/bin/server_export', mover_name, 

1809 '-unexport', 

1810 '-perm', 

1811 path, 

1812 ] 

1813 

1814 try: 

1815 self._execute_cmd(delete_nfs_share_cmd, check_exit_code=True) 

1816 except processutils.ProcessExecutionError as expt: 

1817 message = (_('Failed to delete NFS share %(name)s on ' 

1818 '%(mover_name)s. Reason: %(err)s.') % 

1819 {'name': name, 'mover_name': mover_name, 'err': expt}) 

1820 LOG.exception(message) 

1821 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1822 

1823 self.nfs_share_map.pop(name) 

1824 

1825 def get(self, name, mover_name, force=False, check_exit_code=False): 

1826 if name in self.nfs_share_map and not force: 

1827 return constants.STATUS_OK, self.nfs_share_map[name] 

1828 

1829 path = '/' + name 

1830 

1831 nfs_share = { 

1832 "mover_name": '', 

1833 "path": '', 

1834 'AccessHosts': [], 

1835 'RwHosts': [], 

1836 'RoHosts': [], 

1837 'RootHosts': [], 

1838 'readOnly': '', 

1839 } 

1840 

1841 nfs_query_cmd = [ 

1842 'env', 'NAS_DB=/nas', '/nas/bin/server_export', mover_name, 

1843 '-P', 'nfs', 

1844 '-list', path, 

1845 ] 

1846 

1847 try: 

1848 out, err = self._execute_cmd(nfs_query_cmd, 

1849 check_exit_code=check_exit_code) 

1850 except processutils.ProcessExecutionError as expt: 

1851 dup_msg = (r'%(mover_name)s : No such file or directory' % 

1852 {'mover_name': mover_name}) 

1853 if re.search(dup_msg, expt.stdout): 

1854 LOG.warning("NFS share %s not found.", name) 

1855 return constants.STATUS_NOT_FOUND, None 

1856 else: 

1857 message = (_('Failed to list NFS share %(name)s on ' 

1858 '%(mover_name)s. Reason: %(err)s.') % 

1859 {'name': name, 

1860 'mover_name': mover_name, 

1861 'err': expt}) 

1862 LOG.exception(message) 

1863 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1864 

1865 re_exports = r'%s\s*:\s*\nexport\s*(.*)\n' % mover_name 

1866 m = re.search(re_exports, out) 

1867 if m is not None: 

1868 nfs_share['path'] = path 

1869 nfs_share['mover_name'] = mover_name 

1870 export = m.group(1) 

1871 fields = export.split(" ") 

1872 for field in fields: 

1873 field = field.strip() 

1874 if field.startswith('rw='): 

1875 nfs_share['RwHosts'] = powermax_utils.parse_ipaddr( 

1876 field[3:]) 

1877 elif field.startswith('access='): 

1878 nfs_share['AccessHosts'] = powermax_utils.parse_ipaddr( 

1879 field[7:]) 

1880 elif field.startswith('root='): 

1881 nfs_share['RootHosts'] = powermax_utils.parse_ipaddr( 

1882 field[5:]) 

1883 elif field.startswith('ro='): 

1884 nfs_share['RoHosts'] = powermax_utils.parse_ipaddr( 

1885 field[3:]) 

1886 

1887 self.nfs_share_map[name] = nfs_share 

1888 else: 

1889 return constants.STATUS_NOT_FOUND, None 

1890 

1891 return constants.STATUS_OK, self.nfs_share_map[name] 

1892 

1893 def allow_share_access(self, share_name, host_ip, mover_name, 

1894 access_level=const.ACCESS_LEVEL_RW): 

1895 @utils.synchronized('emc-shareaccess-' + share_name) 

1896 def do_allow_access(share_name, host_ip, mover_name, access_level): 

1897 status, share = self.get(share_name, mover_name) 

1898 if constants.STATUS_NOT_FOUND == status: 

1899 message = (_('NFS share %s not found.') % share_name) 

1900 LOG.error(message) 

1901 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1902 

1903 changed = False 

1904 rwhosts = share['RwHosts'] 

1905 rohosts = share['RoHosts'] 

1906 

1907 host_ip = powermax_utils.convert_ipv6_format_if_needed(host_ip) 

1908 

1909 if access_level == const.ACCESS_LEVEL_RW: 

1910 if host_ip not in rwhosts: 

1911 rwhosts.append(host_ip) 

1912 changed = True 

1913 if host_ip in rohosts: 

1914 rohosts.remove(host_ip) 

1915 changed = True 

1916 if access_level == const.ACCESS_LEVEL_RO: 

1917 if host_ip not in rohosts: 1917 ↛ 1920line 1917 didn't jump to line 1920 because the condition on line 1917 was always true

1918 rohosts.append(host_ip) 

1919 changed = True 

1920 if host_ip in rwhosts: 1920 ↛ 1924line 1920 didn't jump to line 1924 because the condition on line 1920 was always true

1921 rwhosts.remove(host_ip) 

1922 changed = True 

1923 

1924 roothosts = share['RootHosts'] 

1925 if host_ip not in roothosts: 

1926 roothosts.append(host_ip) 

1927 changed = True 

1928 accesshosts = share['AccessHosts'] 

1929 if host_ip not in accesshosts: 

1930 accesshosts.append(host_ip) 

1931 changed = True 

1932 

1933 if not changed: 

1934 LOG.debug("%(host)s is already in access list of share " 

1935 "%(name)s.", {'host': host_ip, 'name': share_name}) 

1936 else: 

1937 path = '/' + share_name 

1938 self._set_share_access(path, 

1939 mover_name, 

1940 rwhosts, 

1941 rohosts, 

1942 roothosts, 

1943 accesshosts) 

1944 

1945 # Update self.nfs_share_map 

1946 self.get(share_name, mover_name, force=True, 

1947 check_exit_code=True) 

1948 

1949 do_allow_access(share_name, host_ip, mover_name, access_level) 

1950 

1951 def deny_share_access(self, share_name, host_ip, mover_name): 

1952 

1953 @utils.synchronized('emc-shareaccess-' + share_name) 

1954 def do_deny_access(share_name, host_ip, mover_name): 

1955 status, share = self.get(share_name, mover_name) 

1956 if constants.STATUS_OK != status: 

1957 message = (_('Query nfs share %(path)s failed. ' 

1958 'Reason %(err)s.') % 

1959 {'path': share_name, 'err': share}) 

1960 LOG.error(message) 

1961 raise exception.EMCPowerMaxXMLAPIError(err=message) 

1962 

1963 changed = False 

1964 rwhosts = set(share['RwHosts']) 

1965 if host_ip in rwhosts: 

1966 rwhosts.remove(host_ip) 

1967 changed = True 

1968 roothosts = set(share['RootHosts']) 

1969 if host_ip in roothosts: 

1970 roothosts.remove(host_ip) 

1971 changed = True 

1972 accesshosts = set(share['AccessHosts']) 

1973 if host_ip in accesshosts: 

1974 accesshosts.remove(host_ip) 

1975 changed = True 

1976 rohosts = set(share['RoHosts']) 

1977 if host_ip in rohosts: 

1978 rohosts.remove(host_ip) 

1979 changed = True 

1980 if not changed: 

1981 LOG.debug("%(host)s is already in access list of share " 

1982 "%(name)s.", {'host': host_ip, 'name': share_name}) 

1983 else: 

1984 path = '/' + share_name 

1985 self._set_share_access(path, 

1986 mover_name, 

1987 rwhosts, 

1988 rohosts, 

1989 roothosts, 

1990 accesshosts) 

1991 

1992 # Update self.nfs_share_map 

1993 self.get(share_name, mover_name, force=True, 

1994 check_exit_code=True) 

1995 

1996 do_deny_access(share_name, host_ip, mover_name) 

1997 

1998 def clear_share_access(self, share_name, mover_name, white_list_hosts): 

1999 @utils.synchronized('emc-shareaccess-' + share_name) 

2000 def do_clear_access(share_name, mover_name, white_list_hosts): 

2001 def hosts_to_remove(orig_list): 

2002 if white_list_hosts is None: 2002 ↛ 2003line 2002 didn't jump to line 2003 because the condition on line 2002 was never true

2003 ret = set() 

2004 else: 

2005 ret = set(white_list_hosts).intersection(set(orig_list)) 

2006 return ret 

2007 

2008 status, share = self.get(share_name, mover_name) 

2009 if constants.STATUS_OK != status: 

2010 message = (_('Query nfs share %(path)s failed. ' 

2011 'Reason %(err)s.') % 

2012 {'path': share_name, 'err': status}) 

2013 raise exception.EMCPowerMaxXMLAPIError(err=message) 

2014 

2015 self._set_share_access('/' + share_name, 

2016 mover_name, 

2017 hosts_to_remove(share['RwHosts']), 

2018 hosts_to_remove(share['RoHosts']), 

2019 hosts_to_remove(share['RootHosts']), 

2020 hosts_to_remove(share['AccessHosts'])) 

2021 

2022 # Update self.nfs_share_map 

2023 self.get(share_name, mover_name, force=True, 

2024 check_exit_code=True) 

2025 

2026 do_clear_access(share_name, mover_name, white_list_hosts) 

2027 

2028 def _set_share_access(self, path, mover_name, rw_hosts, ro_hosts, 

2029 root_hosts, access_hosts): 

2030 

2031 if access_hosts is None: 2031 ↛ 2032line 2031 didn't jump to line 2032 because the condition on line 2031 was never true

2032 access_hosts = set() 

2033 try: 

2034 access_hosts.remove('-0.0.0.0/0.0.0.0') 

2035 except (ValueError, KeyError): 

2036 pass 

2037 

2038 access_str = ('access=%(access)s' % {'access': ':'.join( 

2039 list(access_hosts) + ['-0.0.0.0/0.0.0.0'])}) 

2040 

2041 if root_hosts: 2041 ↛ 2043line 2041 didn't jump to line 2043 because the condition on line 2041 was always true

2042 access_str += (',root=%(root)s' % {'root': ':'.join(root_hosts)}) 

2043 if rw_hosts: 2043 ↛ 2045line 2043 didn't jump to line 2045 because the condition on line 2043 was always true

2044 access_str += ',rw=%(rw)s' % {'rw': ':'.join(rw_hosts)} 

2045 if ro_hosts: 2045 ↛ 2047line 2045 didn't jump to line 2047 because the condition on line 2045 was always true

2046 access_str += ',ro=%(ro)s' % {'ro': ':'.join(ro_hosts)} 

2047 set_nfs_share_access_cmd = [ 

2048 'env', 'NAS_DB=/nas', '/nas/bin/server_export', mover_name, 

2049 '-ignore', 

2050 '-option', access_str, 

2051 path, 

2052 ] 

2053 

2054 try: 

2055 self._execute_cmd(set_nfs_share_access_cmd, check_exit_code=True) 

2056 except processutils.ProcessExecutionError as expt: 

2057 message = (_('Failed to set NFS share %(name)s access on ' 

2058 '%(mover_name)s. Reason: %(err)s.') % 

2059 {'name': path[1:], 

2060 'mover_name': mover_name, 

2061 'err': expt}) 

2062 LOG.exception(message) 

2063 raise exception.EMCPowerMaxXMLAPIError(err=message)