Coverage for manila/share/drivers/dell_emc/plugins/vnx/object_manager.py: 99%

1064 statements  

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

1# Copyright (c) 2015 EMC Corporation. 

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 enas_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@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

37 debug_only=True) 

38class StorageObjectManager(object): 

39 def __init__(self, configuration): 

40 self.context = dict() 

41 

42 self.connectors = dict() 

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.EMCVnxXMLAPIError(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.EMCVnxLockRequiredException() 

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

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.EMCVnxLockRequiredException 

160 

161 return response 

162 

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

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 property in property_map: 

189 if isinstance(property, tuple): 

190 target_key, src_key = property 

191 else: 

192 target_key = src_key = property 

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@enas_utils.decorate_all_methods(enas_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 = dict() 

219 

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

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.EMCVnxInvalidMoverID(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.EMCVnxXMLAPIError(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.EMCVnxXMLAPIError(err=message) 

317 

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

319 

320 request = self._build_task_package( 

321 self.elt_maker.DeleteFileSystem(fileSystem=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.EMCVnxXMLAPIError(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.EMCVnxXMLAPIError(err=message) 

343 

344 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.EMCVnxXMLAPIError(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=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.EMCVnxXMLAPIError(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.EMCVnxXMLAPIError(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: 

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

443 "to file system %(filesystem)s.", 

444 {'snap': snap_name, 

445 'filesystem': name}) 

446 

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

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

449 query_fs_cmd = [ 

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

451 '-info', name, 

452 ] 

453 out, err = self._execute_cmd(query_fs_cmd) 

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

455 m = re.search(re_ckpts, out) 

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

457 ckpts = m.group(1) 

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

459 umount_ckpt_cmd = [ 

460 'env', 'NAS_DB=/nas', 

461 '/nas/bin/server_umount', mover_name, 

462 '-perm', ckpt, 

463 ] 

464 self._execute_cmd(umount_ckpt_cmd) 

465 delete_ckpt_cmd = [ 

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

467 '-delete', ckpt, 

468 '-Force', 

469 ] 

470 self._execute_cmd(delete_ckpt_cmd) 

471 

472 rw_mount_cmd = [ 

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

474 '-option', 'rw', 

475 name, 

476 '/%s' % name, 

477 ] 

478 self._execute_cmd(rw_mount_cmd) 

479 

480 

481@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

482 debug_only=True) 

483class StoragePool(StorageObject): 

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

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

486 self.pool_map = dict() 

487 

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

489 if name not in self.pool_map or force: 

490 status, out = self.get_all() 

491 if constants.STATUS_OK != status: 

492 return status, out 

493 

494 if name not in self.pool_map: 

495 return constants.STATUS_NOT_FOUND, None 

496 

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

498 

499 def get_all(self): 

500 self.pool_map.clear() 

501 

502 request = self._build_query_package( 

503 self.elt_maker.StoragePoolQueryParams() 

504 ) 

505 

506 response = self._send_request(request) 

507 

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

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

510 

511 if not response['objects']: 

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

513 

514 for item in response['objects']: 

515 pool = {} 

516 property_map = ( 

517 'name', 

518 ('movers_id', 'movers'), 

519 ('total_size', 'autoSize'), 

520 ('used_size', 'usedSize'), 

521 'diskType', 

522 'dataServicePolicies', 

523 ('id', 'pool'), 

524 ) 

525 self._copy_properties(item, pool, property_map) 

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

527 

528 return constants.STATUS_OK, self.pool_map 

529 

530 def get_id(self, name): 

531 status, out = self.get(name) 

532 

533 if constants.STATUS_OK != status: 

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

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

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

537 LOG.error(message) 

538 raise exception.EMCVnxXMLAPIError(err=message) 

539 

540 return out['id'] 

541 

542 

543@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

544 debug_only=True) 

545class MountPoint(StorageObject): 

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

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

548 

549 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

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

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

552 

553 mover_id = self._get_mover_id(mover_name, is_vdm) 

554 

555 if self.xml_retry: 

556 self.xml_retry = False 

557 

558 request = self._build_task_package( 

559 self.elt_maker.NewMount( 

560 self.elt_maker.MoverOrVdm( 

561 mover=mover_id, 

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

563 ), 

564 fileSystem=fs_id, 

565 path=mount_path 

566 ) 

567 ) 

568 

569 response = self._send_request(request) 

570 

571 if (self._response_validation(response, 

572 constants.MSG_INVALID_MOVER_ID) and 

573 not self.xml_retry): 

574 self.xml_retry = True 

575 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

576 elif self._is_mount_point_already_existent(response): 

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

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

579 return 

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

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

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

583 {'mount': mount_path, 

584 'fs_name': fs_name, 

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

586 LOG.error(message) 

587 raise exception.EMCVnxXMLAPIError(err=message) 

588 

589 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

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

591 mover_id = self._get_mover_id(mover_name, is_vdm) 

592 

593 if self.xml_retry: 

594 self.xml_retry = False 

595 

596 request = self._build_query_package( 

597 self.elt_maker.MountQueryParams( 

598 self.elt_maker.MoverOrVdm( 

599 mover=mover_id, 

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

601 ) 

602 ) 

603 ) 

604 

605 response = self._send_request(request) 

606 

607 if (self._response_validation(response, 

608 constants.MSG_INVALID_MOVER_ID) and 

609 not self.xml_retry): 

610 self.xml_retry = True 

611 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

614 

615 if not response['objects']: 

616 return constants.STATUS_NOT_FOUND, None 

617 else: 

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

619 

620 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

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

622 mover_id = self._get_mover_id(mover_name, is_vdm) 

623 

624 if self.xml_retry: 

625 self.xml_retry = False 

626 

627 request = self._build_task_package( 

628 self.elt_maker.DeleteMount( 

629 mover=mover_id, 

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

631 path=mount_path 

632 ) 

633 ) 

634 

635 response = self._send_request(request) 

636 

637 if (self._response_validation(response, 

638 constants.MSG_INVALID_MOVER_ID) and 

639 not self.xml_retry): 

640 self.xml_retry = True 

641 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

642 elif self._is_mount_point_nonexistent(response): 

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

644 'not found.', 

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

646 

647 return 

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

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

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

651 {'mount': mount_path, 

652 'mover_name': mover_name, 

653 'err': response}) 

654 LOG.error(message) 

655 raise exception.EMCVnxXMLAPIError(err=message) 

656 

657 def _is_mount_point_nonexistent(self, response): 

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

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

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

661 

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

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

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

665 code == constants.MSG_INVALID_VDM_ID or 

666 code == constants.MSG_INVALID_MOVER_ID): 

667 return True 

668 

669 return False 

670 

671 def _is_mount_point_already_existent(self, response): 

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

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

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

675 

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

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

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

679 return True 

680 

681 return False 

682 

683 

684@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

685 debug_only=True) 

686class Mover(StorageObject): 

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

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

689 self.mover_map = dict() 

690 self.mover_ref_map = dict() 

691 

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

693 if name not in self.mover_ref_map or force: 

694 self.mover_ref_map.clear() 

695 

696 request = self._build_query_package( 

697 self.elt_maker.MoverQueryParams( 

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

699 ) 

700 ) 

701 

702 response = self._send_request(request) 

703 

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

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

706 

707 for item in response['objects']: 

708 mover = {} 

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

710 self._copy_properties(item, mover, property_map) 

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

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

713 

714 if (name not in self.mover_ref_map or 

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

716 return constants.STATUS_NOT_FOUND, None 

717 

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

719 

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

721 if name not in self.mover_map or force: 

722 if name in self.mover_ref_map and not force: 

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

724 else: 

725 mover_id = self.get_id(name, force) 

726 

727 if name in self.mover_map: 

728 self.mover_map.pop(name) 

729 

730 request = self._build_query_package( 

731 self.elt_maker.MoverQueryParams( 

732 self.elt_maker.AspectSelection( 

733 moverDeduplicationSettings='true', 

734 moverDnsDomains='true', 

735 moverInterfaces='true', 

736 moverNetworkDevices='true', 

737 moverNisDomains='true', 

738 moverRoutes='true', 

739 movers='true', 

740 moverStatuses='true' 

741 ), 

742 mover=mover_id 

743 ) 

744 ) 

745 

746 response = self._send_request(request) 

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

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

749 

750 if not response['objects']: 

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

752 

753 mover = {} 

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

755 property_map = ( 

756 'name', 

757 ('id', 'mover'), 

758 ('Status', 'maxSeverity'), 

759 'version', 

760 'uptime', 

761 'role', 

762 ('interfaces', 'MoverInterface'), 

763 ('devices', 'LogicalNetworkDevice'), 

764 ('dns_domain', 'MoverDnsDomain'), 

765 ) 

766 

767 self._copy_properties(src, mover, property_map) 

768 

769 internal_devices = [] 

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

771 for interface in mover['interfaces']: 

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

773 internal_devices.append(interface) 

774 

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

776 var not in internal_devices] 

777 

778 self.mover_map[name] = mover 

779 

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

781 

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

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

784 if constants.STATUS_OK != status: 

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

786 {'name': name}) 

787 LOG.error(message) 

788 raise exception.EMCVnxXMLAPIError(err=message) 

789 

790 return mover_ref['id'] 

791 

792 def _is_internal_device(self, device): 

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

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

795 return True 

796 return False 

797 

798 def get_interconnect_id(self, source, destination): 

799 header = [ 

800 'id', 

801 'name', 

802 'source_server', 

803 'destination_system', 

804 'destination_server', 

805 ] 

806 

807 conn_id = None 

808 

809 command_nas_cel = [ 

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

811 '-interconnect', '-l', 

812 ] 

813 out, err = self._execute_cmd(command_nas_cel) 

814 

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

816 for line in lines: 

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

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

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

820 else: 

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

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

823 conn_id = interconn[0] 

824 

825 return conn_id 

826 

827 def get_physical_devices(self, mover_name): 

828 

829 physical_network_devices = [] 

830 

831 cmd_sysconfig = [ 

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

833 '-pci' 

834 ] 

835 

836 out, err = self._execute_cmd(cmd_sysconfig) 

837 

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

839 r'.*\n' 

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

841 

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

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

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

845 

846 return physical_network_devices 

847 

848 

849@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

850 debug_only=True) 

851class VDM(StorageObject): 

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

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

854 self.vdm_map = dict() 

855 

856 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

857 def create(self, name, mover_name): 

858 mover_id = self._get_mover_id(mover_name, False) 

859 

860 if self.xml_retry: 

861 self.xml_retry = False 

862 

863 request = self._build_task_package( 

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

865 ) 

866 

867 response = self._send_request(request) 

868 

869 if (self._response_validation(response, 

870 constants.MSG_INVALID_MOVER_ID) and 

871 not self.xml_retry): 

872 self.xml_retry = True 

873 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

876 {'name': name}) 

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

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

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

880 {'name': name, 

881 'mover_name': mover_name, 

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

883 LOG.error(message) 

884 raise exception.EMCVnxXMLAPIError(err=message) 

885 

886 def get(self, name): 

887 if name not in self.vdm_map: 

888 request = self._build_query_package( 

889 self.elt_maker.VdmQueryParams() 

890 ) 

891 

892 response = self._send_request(request) 

893 

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

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

896 elif not response['objects']: 

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

898 

899 for item in response['objects']: 

900 vdm = {} 

901 property_map = ( 

902 'name', 

903 ('id', 'vdm'), 

904 'state', 

905 ('host_mover_id', 'mover'), 

906 ('interfaces', 'Interfaces'), 

907 ) 

908 self._copy_properties(item, vdm, property_map) 

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

910 

911 if name not in self.vdm_map: 

912 return constants.STATUS_NOT_FOUND, None 

913 

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

915 

916 def delete(self, name): 

917 status, out = self.get(name) 

918 if constants.STATUS_NOT_FOUND == status: 

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

920 name) 

921 return 

922 elif constants.STATUS_OK != status: 

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

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

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

926 LOG.error(message) 

927 raise exception.EMCVnxXMLAPIError(err=message) 

928 

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

930 

931 request = self._build_task_package( 

932 self.elt_maker.DeleteVdm(vdm=vdm_id) 

933 ) 

934 

935 response = self._send_request(request) 

936 

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

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

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

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

941 LOG.error(message) 

942 raise exception.EMCVnxXMLAPIError(err=message) 

943 

944 self.vdm_map.pop(name) 

945 

946 def get_id(self, name): 

947 status, vdm = self.get(name) 

948 if constants.STATUS_OK != status: 

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

950 {'name': name}) 

951 LOG.error(message) 

952 raise exception.EMCVnxXMLAPIError(err=message) 

953 

954 return vdm['id'] 

955 

956 def attach_nfs_interface(self, vdm_name, if_name): 

957 

958 command_attach_nfs_interface = [ 

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

960 '-vdm', vdm_name, 

961 '-attach', if_name, 

962 ] 

963 

964 self._execute_cmd(command_attach_nfs_interface) 

965 

966 def detach_nfs_interface(self, vdm_name, if_name): 

967 

968 command_detach_nfs_interface = [ 

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

970 '-vdm', vdm_name, 

971 '-detach', if_name, 

972 ] 

973 

974 try: 

975 self._execute_cmd(command_detach_nfs_interface, 

976 check_exit_code=True) 

977 except processutils.ProcessExecutionError: 

978 interfaces = self.get_interfaces(vdm_name) 

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

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

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

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

983 else: 

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

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

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

987 LOG.error(message) 

988 raise exception.EMCVnxXMLAPIError(err=message) 

989 

990 def get_interfaces(self, vdm_name): 

991 interfaces = { 

992 'cifs': [], 

993 'nfs': [], 

994 } 

995 

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

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

998 

999 command_get_interfaces = [ 

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

1001 '-i', 

1002 '-vdm', vdm_name, 

1003 ] 

1004 

1005 out, err = self._execute_cmd(command_get_interfaces) 

1006 

1007 m = re.search(re_pattern, out) 

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

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

1010 for i in if_list: 

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

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

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

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

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

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

1017 elif (m_if.group('type') in ('vdm', 'nfs') 

1018 and if_name != ''): 

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

1020 

1021 return interfaces 

1022 

1023 

1024@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

1025 debug_only=True) 

1026class Snapshot(StorageObject): 

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

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

1029 self.snap_map = dict() 

1030 

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

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

1033 

1034 if ckpt_size: 

1035 elt_pool = self.elt_maker.StoragePool( 

1036 pool=pool_id, 

1037 size=str(ckpt_size) 

1038 ) 

1039 else: 

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

1041 

1042 new_ckpt = self.elt_maker.NewCheckpoint( 

1043 self.elt_maker.SpaceAllocationMethod( 

1044 elt_pool 

1045 ), 

1046 checkpointOf=fs_id, 

1047 name=name 

1048 ) 

1049 

1050 request = self._build_task_package(new_ckpt) 

1051 

1052 response = self._send_request(request) 

1053 

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

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

1056 "Skip the creation.", 

1057 {'name': name}) 

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

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

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

1061 {'name': name, 

1062 'fs_name': fs_name, 

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

1064 LOG.error(message) 

1065 raise exception.EMCVnxXMLAPIError(err=message) 

1066 

1067 def get(self, name): 

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

1069 request = self._build_query_package( 

1070 self.elt_maker.CheckpointQueryParams( 

1071 self.elt_maker.Alias(name=name) 

1072 ) 

1073 ) 

1074 

1075 response = self._send_request(request) 

1076 

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

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

1079 

1080 if not response['objects']: 

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

1082 

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

1084 snap = {} 

1085 property_map = ( 

1086 'name', 

1087 ('id', 'checkpoint'), 

1088 'checkpointOf', 

1089 'state', 

1090 ) 

1091 self._copy_properties(src, snap, property_map) 

1092 

1093 self.snap_map[name] = snap 

1094 

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

1096 

1097 def delete(self, name): 

1098 status, out = self.get(name) 

1099 if constants.STATUS_NOT_FOUND == status: 

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

1101 name) 

1102 return 

1103 elif constants.STATUS_OK != status: 

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

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

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

1107 LOG.error(message) 

1108 raise exception.EMCVnxXMLAPIError(err=message) 

1109 

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

1111 

1112 request = self._build_task_package( 

1113 self.elt_maker.DeleteCheckpoint(checkpoint=chpt_id) 

1114 ) 

1115 

1116 response = self._send_request(request) 

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

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

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

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

1121 LOG.error(message) 

1122 raise exception.EMCVnxXMLAPIError(err=message) 

1123 

1124 self.snap_map.pop(name) 

1125 

1126 def get_id(self, name): 

1127 status, out = self.get(name) 

1128 

1129 if constants.STATUS_OK != status: 

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

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

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

1133 LOG.error(message) 

1134 raise exception.EMCVnxXMLAPIError(err=message) 

1135 

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

1137 

1138 

1139@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

1140 debug_only=True) 

1141class MoverInterface(StorageObject): 

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

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

1144 manager) 

1145 

1146 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

1147 def create(self, interface): 

1148 # Maximum of 32 characters for mover interface name 

1149 name = interface['name'] 

1150 if len(name) > 32: 

1151 name = name[0:31] 

1152 

1153 device_name = interface['device_name'] 

1154 ip_addr = interface['ip'] 

1155 mover_name = interface['mover_name'] 

1156 net_mask = interface['net_mask'] 

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

1158 

1159 mover_id = self._get_mover_id(mover_name, False) 

1160 

1161 params = dict(device=device_name, 

1162 ipAddress=str(ip_addr), 

1163 mover=mover_id, 

1164 name=name, 

1165 netMask=net_mask, 

1166 vlanid=str(vlan_id)) 

1167 

1168 if interface.get('ip_version') == 6: 

1169 params['ipVersion'] = 'IPv6' 

1170 

1171 if self.xml_retry: 

1172 self.xml_retry = False 

1173 

1174 request = self._build_task_package( 

1175 self.elt_maker.NewMoverInterface(**params) 

1176 ) 

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.EMCVnxInvalidMoverID(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 return 

1190 elif self._response_validation( 

1191 response, constants.MSG_INTERFACE_EXIST): 

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

1193 "Skip the creation.", ip_addr) 

1194 return 

1195 elif self._response_validation( 

1196 response, constants.MSG_INTERFACE_INVALID_VLAN_ID): 

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

1198 # vlan id, VNX will leave an interface with vlan id 0 in the 

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

1200 try: 

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

1202 except exception.EMCVnxXMLAPIError: 

1203 pass 

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

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

1206 LOG.error(message) 

1207 raise exception.EMCVnxXMLAPIError(err=message) 

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

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

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

1211 {'interface': interface, 

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

1213 LOG.error(message) 

1214 raise exception.EMCVnxXMLAPIError(err=message) 

1215 

1216 def get(self, name, mover_name): 

1217 # Maximum of 32 characters for mover interface name 

1218 if len(name) > 32: 

1219 name = name[0:31] 

1220 

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

1222 mover_name, True) 

1223 if constants.STATUS_OK == status: 

1224 for interface in mover['interfaces']: 

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

1226 return constants.STATUS_OK, interface 

1227 

1228 return constants.STATUS_NOT_FOUND, None 

1229 

1230 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

1231 def delete(self, ip_addr, mover_name): 

1232 mover_id = self._get_mover_id(mover_name, False) 

1233 

1234 if self.xml_retry: 

1235 self.xml_retry = False 

1236 

1237 request = self._build_task_package( 

1238 self.elt_maker.DeleteMoverInterface( 

1239 ipAddress=str(ip_addr), 

1240 mover=mover_id 

1241 ) 

1242 ) 

1243 

1244 response = self._send_request(request) 

1245 

1246 if (self._response_validation(response, 

1247 constants.MSG_INVALID_MOVER_ID) and 

1248 not self.xml_retry): 

1249 self.xml_retry = True 

1250 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

1251 elif self._response_validation( 

1252 response, constants.MSG_INTERFACE_NON_EXISTENT): 

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

1254 "Skip the deletion.", ip_addr) 

1255 return 

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

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

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

1259 {'ip': ip_addr, 

1260 'mover': mover_name, 

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

1262 LOG.error(message) 

1263 raise exception.EMCVnxXMLAPIError(err=message) 

1264 

1265 

1266@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

1267 debug_only=True) 

1268class DNSDomain(StorageObject): 

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

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

1271 

1272 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

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

1274 mover_id = self._get_mover_id(mover_name, False) 

1275 

1276 if self.xml_retry: 

1277 self.xml_retry = False 

1278 

1279 request = self._build_task_package( 

1280 self.elt_maker.NewMoverDnsDomain( 

1281 mover=mover_id, 

1282 name=name, 

1283 servers=servers, 

1284 protocol=protocol 

1285 ) 

1286 ) 

1287 

1288 response = self._send_request(request) 

1289 

1290 if (self._response_validation(response, 

1291 constants.MSG_INVALID_MOVER_ID) and 

1292 not self.xml_retry): 

1293 self.xml_retry = True 

1294 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

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

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

1299 LOG.error(message) 

1300 raise exception.EMCVnxXMLAPIError(err=message) 

1301 

1302 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

1303 def delete(self, mover_name, name): 

1304 mover_id = self._get_mover_id(mover_name, False) 

1305 

1306 if self.xml_retry: 

1307 self.xml_retry = False 

1308 

1309 request = self._build_task_package( 

1310 self.elt_maker.DeleteMoverDnsDomain( 

1311 mover=mover_id, 

1312 name=name 

1313 ) 

1314 ) 

1315 

1316 response = self._send_request(request) 

1317 if (self._response_validation(response, 

1318 constants.MSG_INVALID_MOVER_ID) and 

1319 not self.xml_retry): 

1320 self.xml_retry = True 

1321 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

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

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

1326 

1327 

1328@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

1329 debug_only=True) 

1330class CIFSServer(StorageObject): 

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

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

1333 self.cifs_server_map = dict() 

1334 

1335 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

1336 def create(self, server_args): 

1337 compName = server_args['name'] 

1338 # Maximum of 14 characters for netBIOS name 

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

1340 # Maximum of 12 characters for alias name 

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

1342 interfaces = server_args['interface_ip'] 

1343 domain_name = server_args['domain_name'] 

1344 user_name = server_args['user_name'] 

1345 password = server_args['password'] 

1346 mover_name = server_args['mover_name'] 

1347 is_vdm = server_args['is_vdm'] 

1348 

1349 mover_id = self._get_mover_id(mover_name, is_vdm) 

1350 

1351 if self.xml_retry: 

1352 self.xml_retry = False 

1353 

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

1355 

1356 request = self._build_task_package( 

1357 self.elt_maker.NewW2KCifsServer( 

1358 self.elt_maker.MoverOrVdm( 

1359 mover=mover_id, 

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

1361 ), 

1362 self.elt_maker.Aliases(*alias_name_list), 

1363 self.elt_maker.JoinDomain(userName=user_name, 

1364 password=password), 

1365 compName=compName, 

1366 domain=domain_name, 

1367 interfaces=interfaces, 

1368 name=name 

1369 ) 

1370 ) 

1371 

1372 response = self._send_request(request) 

1373 

1374 if (self._response_validation(response, 

1375 constants.MSG_INVALID_MOVER_ID) and 

1376 not self.xml_retry): 

1377 self.xml_retry = True 

1378 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

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

1382 return 

1383 else: 

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

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

1386 {'name': name, 

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

1388 LOG.error(message) 

1389 raise exception.EMCVnxXMLAPIError(err=message) 

1390 

1391 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

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

1393 mover_id = self._get_mover_id(mover_name, is_vdm) 

1394 

1395 if self.xml_retry: 

1396 self.xml_retry = False 

1397 

1398 request = self._build_query_package( 

1399 self.elt_maker.CifsServerQueryParams( 

1400 self.elt_maker.MoverOrVdm( 

1401 mover=mover_id, 

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

1403 ) 

1404 ) 

1405 ) 

1406 

1407 response = self._send_request(request) 

1408 if (self._response_validation(response, 

1409 constants.MSG_INVALID_MOVER_ID) and 

1410 not self.xml_retry): 

1411 self.xml_retry = True 

1412 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

1415 

1416 if mover_name in self.cifs_server_map: 

1417 self.cifs_server_map.pop(mover_name) 

1418 

1419 self.cifs_server_map[mover_name] = dict() 

1420 

1421 for item in response['objects']: 

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

1423 

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

1425 

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

1427 # name is compName 

1428 name = name.lower() 

1429 

1430 if (mover_name in self.cifs_server_map and 

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

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

1433 

1434 self.get_all(mover_name, is_vdm) 

1435 

1436 if mover_name in self.cifs_server_map: 

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

1438 if name == compName: 

1439 return constants.STATUS_OK, server 

1440 

1441 return constants.STATUS_NOT_FOUND, None 

1442 

1443 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

1444 def modify(self, server_args): 

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

1446 

1447 :param server_args: Dictionary for CIFS server modification 

1448 name: CIFS server name instead of compName 

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

1450 user_name: User name under which the domain is joined 

1451 password: Password associated with the user name 

1452 mover_name: mover or VDM name 

1453 is_vdm: Boolean to indicate mover or VDM 

1454 :raises exception.EMCVnxXMLAPIError: if modification fails. 

1455 """ 

1456 name = server_args['name'] 

1457 join_domain = server_args['join_domain'] 

1458 user_name = server_args['user_name'] 

1459 password = server_args['password'] 

1460 mover_name = server_args['mover_name'] 

1461 

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

1463 is_vdm = server_args['is_vdm'] 

1464 else: 

1465 is_vdm = True 

1466 

1467 mover_id = self._get_mover_id(mover_name, is_vdm) 

1468 

1469 if self.xml_retry: 

1470 self.xml_retry = False 

1471 

1472 request = self._build_task_package( 

1473 self.elt_maker.ModifyW2KCifsServer( 

1474 self.elt_maker.DomainSetting( 

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

1476 password=password, 

1477 userName=user_name, 

1478 ), 

1479 mover=mover_id, 

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

1481 name=name 

1482 ) 

1483 ) 

1484 

1485 response = self._send_request(request) 

1486 

1487 if (self._response_validation(response, 

1488 constants.MSG_INVALID_MOVER_ID) and 

1489 not self.xml_retry): 

1490 self.xml_retry = True 

1491 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

1492 elif self._ignore_modification_error(response, join_domain): 

1493 return 

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

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

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

1497 {'name': name, 

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

1499 LOG.error(message) 

1500 raise exception.EMCVnxXMLAPIError(err=message) 

1501 

1502 def _ignore_modification_error(self, response, join_domain): 

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

1504 return join_domain 

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

1506 return not join_domain 

1507 

1508 return False 

1509 

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

1511 try: 

1512 status, out = self.get( 

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

1514 if constants.STATUS_NOT_FOUND == status: 

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

1516 "not found. Skip the deletion.", 

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

1518 return 

1519 except exception.EMCVnxXMLAPIError: 

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

1521 "not found. Skip the deletion.", 

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

1523 return 

1524 

1525 server_name = out['name'] 

1526 

1527 mover_id = self._get_mover_id(mover_name, is_vdm) 

1528 

1529 request = self._build_task_package( 

1530 self.elt_maker.DeleteCifsServer( 

1531 mover=mover_id, 

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

1533 name=server_name 

1534 ) 

1535 ) 

1536 

1537 response = self._send_request(request) 

1538 

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

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

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

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

1543 LOG.error(message) 

1544 raise exception.EMCVnxXMLAPIError(err=message) 

1545 

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

1547 

1548 

1549@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

1550 debug_only=True) 

1551class CIFSShare(StorageObject): 

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

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

1554 self.cifs_share_map = dict() 

1555 

1556 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

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

1558 mover_id = self._get_mover_id(mover_name, is_vdm) 

1559 

1560 if self.xml_retry: 

1561 self.xml_retry = False 

1562 

1563 share_path = '/' + name 

1564 

1565 request = self._build_task_package( 

1566 self.elt_maker.NewCifsShare( 

1567 self.elt_maker.MoverOrVdm( 

1568 mover=mover_id, 

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

1570 ), 

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

1572 name=name, 

1573 path=share_path 

1574 ) 

1575 ) 

1576 

1577 response = self._send_request(request) 

1578 

1579 if (self._response_validation(response, 

1580 constants.MSG_INVALID_MOVER_ID) and 

1581 not self.xml_retry): 

1582 self.xml_retry = True 

1583 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

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

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

1588 LOG.error(message) 

1589 raise exception.EMCVnxXMLAPIError(err=message) 

1590 

1591 def get(self, name): 

1592 if name not in self.cifs_share_map: 

1593 request = self._build_query_package( 

1594 self.elt_maker.CifsShareQueryParams(name=name) 

1595 ) 

1596 

1597 response = self._send_request(request) 

1598 

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

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

1601 

1602 if not response['objects']: 

1603 return constants.STATUS_NOT_FOUND, None 

1604 

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

1606 

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

1608 

1609 @utils.retry(retry_param=exception.EMCVnxInvalidMoverID) 

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

1611 status, out = self.get(name) 

1612 if constants.STATUS_NOT_FOUND == status: 

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

1614 name) 

1615 return 

1616 elif constants.STATUS_OK != status: 

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

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

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

1620 LOG.error(message) 

1621 raise exception.EMCVnxXMLAPIError(err=message) 

1622 

1623 mover_id = self._get_mover_id(mover_name, is_vdm) 

1624 

1625 if self.xml_retry: 

1626 self.xml_retry = False 

1627 

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

1629 

1630 request = self._build_task_package( 

1631 self.elt_maker.DeleteCifsShare( 

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

1633 netbios_names)), 

1634 mover=mover_id, 

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

1636 name=name 

1637 ) 

1638 ) 

1639 

1640 response = self._send_request(request) 

1641 

1642 if (self._response_validation(response, 

1643 constants.MSG_INVALID_MOVER_ID) and 

1644 not self.xml_retry): 

1645 self.xml_retry = True 

1646 raise exception.EMCVnxInvalidMoverID(id=mover_id) 

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

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

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

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

1651 LOG.error(message) 

1652 raise exception.EMCVnxXMLAPIError(err=message) 

1653 

1654 self.cifs_share_map.pop(name) 

1655 

1656 def disable_share_access(self, share_name, mover_name): 

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

1658 disable_access = [ 

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

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

1661 ] 

1662 

1663 try: 

1664 self._execute_cmd(disable_access, check_exit_code=True) 

1665 except processutils.ProcessExecutionError as expt: 

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

1667 '%(name)s. Reason: %(err)s.') % 

1668 {'name': share_name, 'err': expt}) 

1669 LOG.error(message) 

1670 raise exception.EMCVnxXMLAPIError(err=message) 

1671 

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

1673 access=constants.CIFS_ACL_FULLCONTROL): 

1674 account = user_name + "@" + domain 

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

1676 % {'share_name': share_name, 

1677 'account': account, 

1678 'access': access}) 

1679 

1680 allow_access = [ 

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

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

1683 ] 

1684 

1685 try: 

1686 self._execute_cmd(allow_access, check_exit_code=True) 

1687 except processutils.ProcessExecutionError as expt: 

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

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

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

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

1692 "skipping allow...") 

1693 else: 

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

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

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

1697 LOG.error(message) 

1698 raise exception.EMCVnxXMLAPIError(err=message) 

1699 

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

1701 access=constants.CIFS_ACL_FULLCONTROL): 

1702 account = user_name + "@" + domain 

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

1704 % {'share_name': share_name, 

1705 'account': account, 

1706 'access': access}) 

1707 

1708 allow_access = [ 

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

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

1711 ] 

1712 try: 

1713 self._execute_cmd(allow_access, check_exit_code=True) 

1714 except processutils.ProcessExecutionError as expt: 

1715 not_found_msg = re.compile( 

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

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

1718 user_err_msg = re.compile( 

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

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

1721 

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

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

1724 "skipping deny...") 

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

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

1727 else: 

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

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

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

1731 LOG.error(message) 

1732 raise exception.EMCVnxXMLAPIError(err=message) 

1733 

1734 def get_share_access(self, mover_name, share_name): 

1735 get_str = 'sharesd %s dump' % share_name 

1736 get_access = [ 

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

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

1739 ] 

1740 

1741 try: 

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

1743 except processutils.ProcessExecutionError: 

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

1745 LOG.exception(msg) 

1746 raise exception.EMCVnxXMLAPIError(err=msg) 

1747 

1748 ret = {} 

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

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

1751 

1752 name = None 

1753 for line in out.splitlines(): 

1754 if name is None: 

1755 names = name_pattern.findall(line) 

1756 if names: 

1757 name = names[0].lower() 

1758 else: 

1759 accesses = access_pattern.findall(line) 

1760 if accesses: 

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

1762 name = None 

1763 return ret 

1764 

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

1766 white_list_users): 

1767 existing_users = self.get_share_access(mover_name, share_name) 

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

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

1770 for user in users_to_remove: 

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

1772 existing_users[user]) 

1773 return users_to_remove 

1774 

1775 

1776@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

1777 debug_only=True) 

1778class NFSShare(StorageObject): 

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

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

1781 self.nfs_share_map = {} 

1782 

1783 def create(self, name, mover_name): 

1784 share_path = '/' + name 

1785 create_nfs_share_cmd = [ 

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

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

1788 share_path, 

1789 ] 

1790 

1791 try: 

1792 self._execute_cmd(create_nfs_share_cmd, check_exit_code=True) 

1793 except processutils.ProcessExecutionError as expt: 

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

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

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

1797 LOG.error(message) 

1798 raise exception.EMCVnxXMLAPIError(err=message) 

1799 

1800 def delete(self, name, mover_name): 

1801 path = '/' + name 

1802 

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

1804 if constants.STATUS_NOT_FOUND == status: 

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

1806 path) 

1807 return 

1808 

1809 delete_nfs_share_cmd = [ 

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

1811 '-unexport', 

1812 '-perm', 

1813 path, 

1814 ] 

1815 

1816 try: 

1817 self._execute_cmd(delete_nfs_share_cmd, check_exit_code=True) 

1818 except processutils.ProcessExecutionError as expt: 

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

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

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

1822 LOG.error(message) 

1823 raise exception.EMCVnxXMLAPIError(err=message) 

1824 

1825 self.nfs_share_map.pop(name) 

1826 

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

1828 if name in self.nfs_share_map and not force: 

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

1830 

1831 path = '/' + name 

1832 

1833 nfs_share = { 

1834 "mover_name": '', 

1835 "path": '', 

1836 'AccessHosts': [], 

1837 'RwHosts': [], 

1838 'RoHosts': [], 

1839 'RootHosts': [], 

1840 'readOnly': '', 

1841 } 

1842 

1843 nfs_query_cmd = [ 

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

1845 '-P', 'nfs', 

1846 '-list', path, 

1847 ] 

1848 

1849 try: 

1850 out, err = self._execute_cmd(nfs_query_cmd, 

1851 check_exit_code=check_exit_code) 

1852 except processutils.ProcessExecutionError as expt: 

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

1854 {'mover_name': mover_name}) 

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

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

1857 return constants.STATUS_NOT_FOUND, None 

1858 else: 

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

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

1861 {'name': name, 

1862 'mover_name': mover_name, 

1863 'err': expt}) 

1864 LOG.error(message) 

1865 raise exception.EMCVnxXMLAPIError(err=message) 

1866 

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

1868 m = re.search(re_exports, out) 

1869 if m is not None: 

1870 nfs_share['path'] = path 

1871 nfs_share['mover_name'] = mover_name 

1872 export = m.group(1) 

1873 fields = export.split(" ") 

1874 for field in fields: 

1875 field = field.strip() 

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

1877 nfs_share['RwHosts'] = enas_utils.parse_ipaddr(field[3:]) 

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

1879 nfs_share['AccessHosts'] = enas_utils.parse_ipaddr( 

1880 field[7:]) 

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

1882 nfs_share['RootHosts'] = enas_utils.parse_ipaddr(field[5:]) 

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

1884 nfs_share['RoHosts'] = enas_utils.parse_ipaddr(field[3:]) 

1885 

1886 self.nfs_share_map[name] = nfs_share 

1887 else: 

1888 return constants.STATUS_NOT_FOUND, None 

1889 

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

1891 

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

1893 access_level=const.ACCESS_LEVEL_RW): 

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

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

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

1897 if constants.STATUS_NOT_FOUND == status: 

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

1899 LOG.error(message) 

1900 raise exception.EMCVnxXMLAPIError(err=message) 

1901 

1902 changed = False 

1903 rwhosts = share['RwHosts'] 

1904 rohosts = share['RoHosts'] 

1905 

1906 host_ip = enas_utils.convert_ipv6_format_if_needed(host_ip) 

1907 

1908 if access_level == const.ACCESS_LEVEL_RW: 

1909 if host_ip not in rwhosts: 

1910 rwhosts.append(host_ip) 

1911 changed = True 

1912 if host_ip in rohosts: 

1913 rohosts.remove(host_ip) 

1914 changed = True 

1915 if access_level == const.ACCESS_LEVEL_RO: 

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

1917 rohosts.append(host_ip) 

1918 changed = True 

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

1920 rwhosts.remove(host_ip) 

1921 changed = True 

1922 

1923 roothosts = share['RootHosts'] 

1924 if host_ip not in roothosts: 

1925 roothosts.append(host_ip) 

1926 changed = True 

1927 accesshosts = share['AccessHosts'] 

1928 if host_ip not in accesshosts: 

1929 accesshosts.append(host_ip) 

1930 changed = True 

1931 

1932 if not changed: 

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

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

1935 else: 

1936 path = '/' + share_name 

1937 self._set_share_access(path, 

1938 mover_name, 

1939 rwhosts, 

1940 rohosts, 

1941 roothosts, 

1942 accesshosts) 

1943 

1944 # Update self.nfs_share_map 

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

1946 check_exit_code=True) 

1947 

1948 do_allow_access(share_name, host_ip, mover_name, access_level) 

1949 

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

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

1952 def do_deny_access(share_name, host_ip, mover_name): 

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

1954 if constants.STATUS_OK != status: 

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

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

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

1958 LOG.error(message) 

1959 raise exception.EMCVnxXMLAPIError(err=message) 

1960 

1961 changed = False 

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

1963 if host_ip in rwhosts: 

1964 rwhosts.remove(host_ip) 

1965 changed = True 

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

1967 if host_ip in roothosts: 

1968 roothosts.remove(host_ip) 

1969 changed = True 

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

1971 if host_ip in accesshosts: 

1972 accesshosts.remove(host_ip) 

1973 changed = True 

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

1975 if host_ip in rohosts: 

1976 rohosts.remove(host_ip) 

1977 changed = True 

1978 if not changed: 

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

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

1981 else: 

1982 path = '/' + share_name 

1983 self._set_share_access(path, 

1984 mover_name, 

1985 rwhosts, 

1986 rohosts, 

1987 roothosts, 

1988 accesshosts) 

1989 

1990 # Update self.nfs_share_map 

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

1992 check_exit_code=True) 

1993 

1994 do_deny_access(share_name, host_ip, mover_name) 

1995 

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

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

1998 def do_clear_access(share_name, mover_name, white_list_hosts): 

1999 def hosts_to_remove(orig_list): 

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

2001 ret = set() 

2002 else: 

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

2004 return ret 

2005 

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

2007 if constants.STATUS_OK != status: 

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

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

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

2011 raise exception.EMCVnxXMLAPIError(err=message) 

2012 

2013 self._set_share_access('/' + share_name, 

2014 mover_name, 

2015 hosts_to_remove(share['RwHosts']), 

2016 hosts_to_remove(share['RoHosts']), 

2017 hosts_to_remove(share['RootHosts']), 

2018 hosts_to_remove(share['AccessHosts'])) 

2019 

2020 # Update self.nfs_share_map 

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

2022 check_exit_code=True) 

2023 

2024 do_clear_access(share_name, mover_name, white_list_hosts) 

2025 

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

2027 root_hosts, access_hosts): 

2028 

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

2030 access_hosts = set() 

2031 try: 

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

2033 except (ValueError, KeyError): 

2034 pass 

2035 

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

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

2038 

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

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

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

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

2043 if ro_hosts: 

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

2045 

2046 set_nfs_share_access_cmd = [ 

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

2048 '-ignore', 

2049 '-option', access_str, 

2050 path, 

2051 ] 

2052 

2053 try: 

2054 self._execute_cmd(set_nfs_share_access_cmd, check_exit_code=True) 

2055 except processutils.ProcessExecutionError as expt: 

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

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

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

2059 'mover_name': mover_name, 

2060 'err': expt}) 

2061 LOG.error(message) 

2062 raise exception.EMCVnxXMLAPIError(err=message)