Coverage for manila/share/drivers/huawei/v3/connection.py: 95%

1120 statements  

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

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

2# All Rights Reserved. 

3# 

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

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

6# a copy of the License at 

7# 

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

9# 

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

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

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

13# License for the specific language governing permissions and limitations 

14# under the License. 

15 

16import os 

17import random 

18import string 

19import tempfile 

20import time 

21 

22from oslo_config import cfg 

23from oslo_log import log 

24import oslo_messaging as messaging 

25from oslo_serialization import jsonutils 

26from oslo_utils import excutils 

27from oslo_utils import strutils 

28from oslo_utils import units 

29 

30from manila.common import constants as common_constants 

31from manila.data import utils as data_utils 

32from manila import exception 

33from manila.i18n import _ 

34from manila import rpc 

35from manila.share.drivers.huawei import base as driver 

36from manila.share.drivers.huawei import constants 

37from manila.share.drivers.huawei import huawei_utils 

38from manila.share.drivers.huawei.v3 import helper 

39from manila.share.drivers.huawei.v3 import replication 

40from manila.share.drivers.huawei.v3 import rpcapi as v3_rpcapi 

41from manila.share.drivers.huawei.v3 import smartx 

42from manila.share import share_types 

43from manila.share import utils as share_utils 

44from manila import utils 

45 

46 

47CONF = cfg.CONF 

48 

49LOG = log.getLogger(__name__) 

50 

51 

52class V3StorageConnection(driver.HuaweiBase): 

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

54 

55 def __init__(self, configuration, **kwargs): 

56 super(V3StorageConnection, self).__init__(configuration) 

57 self.helper = helper.RestHelper(self.configuration) 

58 self.replica_mgr = replication.ReplicaPairManager(self.helper) 

59 self.rpc_client = v3_rpcapi.HuaweiV3API() 

60 self.private_storage = kwargs.get('private_storage') 

61 self.qos_support = False 

62 self.snapshot_support = False 

63 self.replication_support = False 

64 

65 def _setup_rpc_server(self, endpoints): 

66 host = "%s@%s" % (CONF.host, self.configuration.config_group) 

67 target = messaging.Target(topic=self.rpc_client.topic, server=host) 

68 self.rpc_server = rpc.get_server(target, endpoints) 

69 self.rpc_server.start() 

70 

71 def connect(self): 

72 """Try to connect to V3 server.""" 

73 self.helper.login() 

74 self._setup_rpc_server([self.replica_mgr]) 

75 self._setup_conf() 

76 

77 def _setup_conf(self): 

78 root = self.helper._read_xml() 

79 

80 snapshot_support = root.findtext('Storage/SnapshotSupport') 

81 if snapshot_support: 

82 self.snapshot_support = strutils.bool_from_string( 

83 snapshot_support, strict=True) 

84 

85 replication_support = root.findtext('Storage/ReplicationSupport') 

86 if replication_support: 

87 self.replication_support = strutils.bool_from_string( 

88 replication_support, strict=True) 

89 

90 def create_share(self, share, share_server=None): 

91 """Create a share.""" 

92 share_name = share['name'] 

93 share_proto = share['share_proto'] 

94 

95 pool_name = share_utils.extract_host(share['host'], level='pool') 

96 

97 if not pool_name: 

98 msg = _("Pool is not available in the share host field.") 

99 raise exception.InvalidHost(reason=msg) 

100 

101 result = self.helper._find_all_pool_info() 

102 poolinfo = self.helper._find_pool_info(pool_name, result) 

103 if not poolinfo: 

104 msg = (_("Can not find pool info by pool name: %s.") % pool_name) 

105 raise exception.InvalidHost(reason=msg) 

106 

107 fs_id = None 

108 # We sleep here to ensure the newly created filesystem can be read. 

109 wait_interval = self._get_wait_interval() 

110 timeout = self._get_timeout() 

111 

112 try: 

113 fs_id = self.allocate_container(share, poolinfo) 

114 fs = self.helper._get_fs_info_by_id(fs_id) 

115 end_time = time.time() + timeout 

116 

117 while not (self.check_fs_status(fs['HEALTHSTATUS'], 117 ↛ 120line 117 didn't jump to line 120 because the condition on line 117 was never true

118 fs['RUNNINGSTATUS']) 

119 or time.time() > end_time): 

120 time.sleep(wait_interval) 

121 fs = self.helper._get_fs_info_by_id(fs_id) 

122 

123 if not self.check_fs_status(fs['HEALTHSTATUS'], 123 ↛ 125line 123 didn't jump to line 125 because the condition on line 123 was never true

124 fs['RUNNINGSTATUS']): 

125 raise exception.InvalidShare( 

126 reason=(_('Invalid status of filesystem: ' 

127 'HEALTHSTATUS=%(health)s ' 

128 'RUNNINGSTATUS=%(running)s.') 

129 % {'health': fs['HEALTHSTATUS'], 

130 'running': fs['RUNNINGSTATUS']})) 

131 except Exception as err: 

132 if fs_id is not None: 132 ↛ 133line 132 didn't jump to line 133 because the condition on line 132 was never true

133 qos_id = self.helper.get_qosid_by_fsid(fs_id) 

134 if qos_id: 

135 self.remove_qos_fs(fs_id, qos_id) 

136 self.helper._delete_fs(fs_id) 

137 message = (_('Failed to create share %(name)s. ' 

138 'Reason: %(err)s.') 

139 % {'name': share_name, 

140 'err': err}) 

141 raise exception.InvalidShare(reason=message) 

142 

143 try: 

144 self.helper.create_share(share_name, fs_id, share_proto) 

145 except Exception as err: 

146 if fs_id is not None: 146 ↛ 151line 146 didn't jump to line 151 because the condition on line 146 was always true

147 qos_id = self.helper.get_qosid_by_fsid(fs_id) 

148 if qos_id: 148 ↛ 150line 148 didn't jump to line 150 because the condition on line 148 was always true

149 self.remove_qos_fs(fs_id, qos_id) 

150 self.helper._delete_fs(fs_id) 

151 raise exception.InvalidShare( 

152 reason=(_('Failed to create share %(name)s. Reason: %(err)s.') 

153 % {'name': share_name, 'err': err})) 

154 

155 ip = self._get_share_ip(share_server) 

156 location = self._get_location_path(share_name, share_proto, ip) 

157 return location 

158 

159 def _get_share_ip(self, share_server): 

160 """"Get share logical ip.""" 

161 if share_server: 

162 ip = share_server['backend_details'].get('ip') 

163 else: 

164 root = self.helper._read_xml() 

165 ip = root.findtext('Storage/LogicalPortIP').strip() 

166 

167 return ip 

168 

169 def extend_share(self, share, new_size, share_server): 

170 share_proto = share['share_proto'] 

171 share_name = share['name'] 

172 

173 # The unit is in sectors. 

174 size = int(new_size) * units.Mi * 2 

175 share_url_type = self.helper._get_share_url_type(share_proto) 

176 

177 share = self.helper._get_share_by_name(share_name, share_url_type) 

178 if not share: 

179 err_msg = (_("Can not get share ID by share %s.") 

180 % share_name) 

181 LOG.error(err_msg) 

182 raise exception.InvalidShareAccess(reason=err_msg) 

183 

184 fsid = share['FSID'] 

185 fs_info = self.helper._get_fs_info_by_id(fsid) 

186 

187 current_size = int(fs_info['CAPACITY']) / units.Mi / 2 

188 if current_size >= new_size: 

189 err_msg = (_("New size for extend must be bigger than " 

190 "current size on array. (current: %(size)s, " 

191 "new: %(new_size)s).") 

192 % {'size': current_size, 'new_size': new_size}) 

193 

194 LOG.error(err_msg) 

195 raise exception.InvalidInput(reason=err_msg) 

196 self.helper._change_share_size(fsid, size) 

197 

198 def shrink_share(self, share, new_size, share_server): 

199 """Shrinks size of existing share.""" 

200 share_proto = share['share_proto'] 

201 share_name = share['name'] 

202 

203 # The unit is in sectors. 

204 size = int(new_size) * units.Mi * 2 

205 share_url_type = self.helper._get_share_url_type(share_proto) 

206 

207 share = self.helper._get_share_by_name(share_name, share_url_type) 

208 if not share: 

209 err_msg = (_("Can not get share ID by share %s.") 

210 % share_name) 

211 LOG.error(err_msg) 

212 raise exception.InvalidShare(reason=err_msg) 

213 

214 fsid = share['FSID'] 

215 fs_info = self.helper._get_fs_info_by_id(fsid) 

216 if not fs_info: 216 ↛ 217line 216 didn't jump to line 217 because the condition on line 216 was never true

217 err_msg = (_("Can not get filesystem info by filesystem ID: %s.") 

218 % fsid) 

219 LOG.error(err_msg) 

220 raise exception.InvalidShare(reason=err_msg) 

221 

222 current_size = int(fs_info['CAPACITY']) / units.Mi / 2 

223 if current_size <= new_size: 

224 err_msg = (_("New size for shrink must be less than current " 

225 "size on array. (current: %(size)s, " 

226 "new: %(new_size)s).") 

227 % {'size': current_size, 'new_size': new_size}) 

228 LOG.error(err_msg) 

229 raise exception.InvalidShare(reason=err_msg) 

230 

231 if fs_info['ALLOCTYPE'] != constants.ALLOC_TYPE_THIN_FLAG: 

232 err_msg = (_("Share (%s) can not be shrunk. only 'Thin' shares " 

233 "support shrink.") 

234 % share_name) 

235 LOG.error(err_msg) 

236 raise exception.InvalidShare(reason=err_msg) 

237 

238 self.helper._change_share_size(fsid, size) 

239 

240 def check_fs_status(self, health_status, running_status): 

241 if (health_status == constants.STATUS_FS_HEALTH 

242 and running_status == constants.STATUS_FS_RUNNING): 

243 return True 

244 else: 

245 return False 

246 

247 def assert_filesystem(self, fsid): 

248 fs = self.helper._get_fs_info_by_id(fsid) 

249 if not self.check_fs_status(fs['HEALTHSTATUS'], 

250 fs['RUNNINGSTATUS']): 

251 err_msg = (_('Invalid status of filesystem: ' 

252 'HEALTHSTATUS=%(health)s ' 

253 'RUNNINGSTATUS=%(running)s.') 

254 % {'health': fs['HEALTHSTATUS'], 

255 'running': fs['RUNNINGSTATUS']}) 

256 raise exception.StorageResourceException(err_msg) 

257 

258 def create_snapshot(self, snapshot, share_server=None): 

259 """Create a snapshot.""" 

260 snap_name = snapshot['id'] 

261 share_proto = snapshot['share']['share_proto'] 

262 

263 share_url_type = self.helper._get_share_url_type(share_proto) 

264 share = self.helper._get_share_by_name(snapshot['share_name'], 

265 share_url_type) 

266 

267 if not share: 

268 err_msg = _('Can not create snapshot,' 

269 ' because share id is not provided.') 

270 LOG.error(err_msg) 

271 raise exception.InvalidInput(reason=err_msg) 

272 

273 sharefsid = share['FSID'] 

274 snapshot_name = "share_snapshot_" + snap_name 

275 snap_id = self.helper._create_snapshot(sharefsid, 

276 snapshot_name) 

277 LOG.info('Creating snapshot id %s.', snap_id) 

278 return snapshot_name.replace("-", "_") 

279 

280 def delete_snapshot(self, snapshot, share_server=None): 

281 """Delete a snapshot.""" 

282 LOG.debug("Delete a snapshot.") 

283 snap_name = snapshot['id'] 

284 

285 sharefsid = self.helper.get_fsid_by_name(snapshot['share_name']) 

286 

287 if sharefsid is None: 287 ↛ 288line 287 didn't jump to line 288 because the condition on line 287 was never true

288 LOG.warning('Delete snapshot share id %s fs has been ' 

289 'deleted.', snap_name) 

290 return 

291 

292 snapshot_id = self.helper._get_snapshot_id(sharefsid, snap_name) 

293 snapshot_info = self.helper._get_snapshot_by_id(snapshot_id) 

294 snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_info) 

295 

296 if snapshot_flag: 

297 self.helper._delete_snapshot(snapshot_id) 

298 else: 

299 LOG.warning("Can not find snapshot %s on array.", snap_name) 

300 

301 def update_share_stats(self, stats_dict): 

302 """Retrieve status info from share group.""" 

303 root = self.helper._read_xml() 

304 all_pool_info = self.helper._find_all_pool_info() 

305 stats_dict["pools"] = [] 

306 

307 pool_name_list = root.findtext('Filesystem/StoragePool') 

308 pool_name_list = pool_name_list.split(";") 

309 for pool_name in pool_name_list: 

310 pool_name = pool_name.strip().strip('\n') 

311 capacity = self._get_capacity(pool_name, all_pool_info) 

312 disk_type = self._get_disk_type(pool_name, all_pool_info) 

313 

314 if capacity: 

315 pool = dict( 

316 pool_name=pool_name, 

317 total_capacity_gb=capacity['TOTALCAPACITY'], 

318 free_capacity_gb=capacity['CAPACITY'], 

319 provisioned_capacity_gb=( 

320 capacity['PROVISIONEDCAPACITYGB']), 

321 max_over_subscription_ratio=( 

322 self.configuration.safe_get( 

323 'max_over_subscription_ratio')), 

324 allocated_capacity_gb=capacity['CONSUMEDCAPACITY'], 

325 qos=self._get_qos_capability(), 

326 reserved_percentage=0, 

327 reserved_snapshot_percentage=0, 

328 reserved_share_extend_percentage=0, 

329 thin_provisioning=[True, False], 

330 dedupe=[True, False], 

331 compression=[True, False], 

332 huawei_smartcache=[True, False], 

333 huawei_smartpartition=[True, False], 

334 huawei_sectorsize=[True, False], 

335 ) 

336 

337 if disk_type: 

338 pool['huawei_disk_type'] = disk_type 

339 

340 stats_dict["pools"].append(pool) 

341 

342 if not stats_dict["pools"]: 

343 err_msg = _("The StoragePool is None.") 

344 LOG.error(err_msg) 

345 raise exception.InvalidInput(reason=err_msg) 

346 

347 def _get_qos_capability(self): 

348 version = self.helper.find_array_version() 

349 if version.upper() >= constants.MIN_ARRAY_VERSION_FOR_QOS: 349 ↛ 352line 349 didn't jump to line 352 because the condition on line 349 was always true

350 self.qos_support = True 

351 else: 

352 self.qos_support = False 

353 return self.qos_support 

354 

355 def delete_share(self, share, share_server=None): 

356 """Delete share.""" 

357 share_name = share['name'] 

358 share_url_type = self.helper._get_share_url_type(share['share_proto']) 

359 share = self.helper._get_share_by_name(share_name, share_url_type) 

360 

361 if not share: 

362 LOG.warning('The share was not found. Share name:%s', 

363 share_name) 

364 fsid = self.helper.get_fsid_by_name(share_name) 

365 if fsid: 365 ↛ 368line 365 didn't jump to line 368 because the condition on line 365 was always true

366 self.helper._delete_fs(fsid) 

367 return 

368 LOG.warning('The filesystem was not found.') 

369 return 

370 

371 share_id = share['ID'] 

372 share_fs_id = share['FSID'] 

373 

374 if share_id: 374 ↛ 377line 374 didn't jump to line 377 because the condition on line 374 was always true

375 self.helper._delete_share_by_id(share_id, share_url_type) 

376 

377 if share_fs_id: 377 ↛ 384line 377 didn't jump to line 384 because the condition on line 377 was always true

378 if self.qos_support: 

379 qos_id = self.helper.get_qosid_by_fsid(share_fs_id) 

380 if qos_id: 380 ↛ 382line 380 didn't jump to line 382 because the condition on line 380 was always true

381 self.remove_qos_fs(share_fs_id, qos_id) 

382 self.helper._delete_fs(share_fs_id) 

383 

384 return share 

385 

386 def create_share_from_snapshot(self, share, snapshot, 

387 share_server=None, parent_share=None): 

388 """Create a share from snapshot.""" 

389 share_fs_id = self.helper.get_fsid_by_name(snapshot['share_name']) 

390 if not share_fs_id: 

391 err_msg = (_("The source filesystem of snapshot %s " 

392 "does not exist.") 

393 % snapshot['snapshot_id']) 

394 LOG.error(err_msg) 

395 raise exception.StorageResourceNotFound( 

396 name=snapshot['share_name']) 

397 

398 snapshot_id = self.helper._get_snapshot_id(share_fs_id, snapshot['id']) 

399 snapshot_info = self.helper._get_snapshot_by_id(snapshot_id) 

400 snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_info) 

401 if not snapshot_flag: 

402 err_msg = (_("Cannot find snapshot %s on array.") 

403 % snapshot['snapshot_id']) 

404 LOG.error(err_msg) 

405 raise exception.ShareSnapshotNotFound( 

406 snapshot_id=snapshot['snapshot_id']) 

407 

408 self.assert_filesystem(share_fs_id) 

409 

410 old_share_name = self.helper.get_share_name_by_id( 

411 snapshot['share_id']) 

412 old_share_proto = self._get_share_proto(old_share_name) 

413 if not old_share_proto: 

414 err_msg = (_("Cannot find source share %(share)s of " 

415 "snapshot %(snapshot)s on array.") 

416 % {'share': snapshot['share_id'], 

417 'snapshot': snapshot['snapshot_id']}) 

418 LOG.error(err_msg) 

419 raise exception.ShareResourceNotFound( 

420 share_id=snapshot['share_id']) 

421 

422 new_share_path = self.create_share(share) 

423 new_share = { 

424 "share_proto": share['share_proto'], 

425 "size": share['size'], 

426 "name": share['name'], 

427 "mount_path": new_share_path.replace("\\", "/"), 

428 "mount_src": 

429 tempfile.mkdtemp(prefix=constants.TMP_PATH_DST_PREFIX), 

430 "id": snapshot['share_id'], 

431 } 

432 

433 old_share_path = self._get_location_path(old_share_name, 

434 old_share_proto) 

435 old_share = { 

436 "share_proto": old_share_proto, 

437 "name": old_share_name, 

438 "mount_path": old_share_path.replace("\\", "/"), 

439 "mount_src": 

440 tempfile.mkdtemp(prefix=constants.TMP_PATH_SRC_PREFIX), 

441 "snapshot_name": ("share_snapshot_" + 

442 snapshot['id'].replace("-", "_")), 

443 "id": snapshot['share_id'], 

444 } 

445 

446 try: 

447 self.copy_data_from_parent_share(old_share, new_share) 

448 except Exception: 

449 with excutils.save_and_reraise_exception(): 

450 self.delete_share(new_share) 

451 finally: 

452 for item in (new_share, old_share): 

453 try: 

454 os.rmdir(item['mount_src']) 

455 except Exception as err: 

456 LOG.warning('Failed to remove temp file. File path:' 

457 '%(file_path)s. Reason: %(err)s.', 

458 {'file_path': item['mount_src'], 

459 'err': err}) 

460 

461 return new_share_path 

462 

463 def copy_data_from_parent_share(self, old_share, new_share): 

464 old_access = self.get_access(old_share) 

465 old_access_id = self._get_access_id(old_share, old_access) 

466 if not old_access_id: 

467 try: 

468 self.allow_access(old_share, old_access) 

469 except exception.ManilaException as err: 

470 with excutils.save_and_reraise_exception(): 

471 LOG.error('Failed to add access to share %(name)s. ' 

472 'Reason: %(err)s.', 

473 {'name': old_share['name'], 

474 'err': err}) 

475 

476 new_access = self.get_access(new_share) 

477 try: 

478 try: 

479 self.mount_share_to_host(old_share, old_access) 

480 except exception.ShareMountException as err: 

481 with excutils.save_and_reraise_exception(): 

482 LOG.error('Failed to mount old share %(name)s. ' 

483 'Reason: %(err)s.', 

484 {'name': old_share['name'], 

485 'err': err}) 

486 

487 try: 

488 self.allow_access(new_share, new_access) 

489 self.mount_share_to_host(new_share, new_access) 

490 except Exception as err: 

491 with excutils.save_and_reraise_exception(): 

492 self.umount_share_from_host(old_share) 

493 LOG.error('Failed to mount new share %(name)s. ' 

494 'Reason: %(err)s.', 

495 {'name': new_share['name'], 

496 'err': err}) 

497 

498 copied = self.copy_snapshot_data(old_share, new_share) 

499 

500 for item in (new_share, old_share): 

501 try: 

502 self.umount_share_from_host(item) 

503 except exception.ShareUmountException as err: 

504 LOG.warning('Failed to unmount share %(name)s. ' 

505 'Reason: %(err)s.', 

506 {'name': item['name'], 

507 'err': err}) 

508 

509 self.deny_access(new_share, new_access) 

510 

511 if copied: 

512 LOG.debug("Created share from snapshot successfully, " 

513 "new_share: %s, old_share: %s.", 

514 new_share, old_share) 

515 else: 

516 message = (_('Failed to copy data from share %(old_share)s ' 

517 'to share %(new_share)s.') 

518 % {'old_share': old_share['name'], 

519 'new_share': new_share['name']}) 

520 raise exception.ShareCopyDataException(reason=message) 

521 finally: 

522 if not old_access_id: 

523 self.deny_access(old_share, old_access) 

524 

525 def get_access(self, share): 

526 share_proto = share['share_proto'] 

527 access = {} 

528 root = self.helper._read_xml() 

529 

530 if share_proto == 'NFS': 

531 access['access_to'] = root.findtext('Filesystem/NFSClient/IP') 

532 access['access_level'] = common_constants.ACCESS_LEVEL_RW 

533 access['access_type'] = 'ip' 

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

535 access['access_to'] = root.findtext( 

536 'Filesystem/CIFSClient/UserName') 

537 access['access_password'] = root.findtext( 

538 'Filesystem/CIFSClient/UserPassword') 

539 access['access_level'] = common_constants.ACCESS_LEVEL_RW 

540 access['access_type'] = 'user' 

541 

542 LOG.debug("Get access for share: %s, access_type: %s, access_to: %s, " 

543 "access_level: %s", share['name'], access['access_type'], 

544 access['access_to'], access['access_level']) 

545 return access 

546 

547 def _get_access_id(self, share, access): 

548 """Get access id of the share.""" 

549 access_id = None 

550 share_name = share['name'] 

551 share_proto = share['share_proto'] 

552 share_url_type = self.helper._get_share_url_type(share_proto) 

553 access_to = access['access_to'] 

554 share = self.helper._get_share_by_name(share_name, share_url_type) 

555 access_id = self.helper._get_access_from_share(share['ID'], access_to, 

556 share_proto) 

557 if access_id is None: 557 ↛ 561line 557 didn't jump to line 561 because the condition on line 557 was always true

558 LOG.debug('Cannot get access ID from share. ' 

559 'share_name: %s', share_name) 

560 

561 return access_id 

562 

563 def copy_snapshot_data(self, old_share, new_share): 

564 src_path = '/'.join((old_share['mount_src'], '.snapshot', 

565 old_share['snapshot_name'])) 

566 dst_path = new_share['mount_src'] 

567 copy_finish = False 

568 LOG.debug("Copy data from src_path: %s to dst_path: %s.", 

569 src_path, dst_path) 

570 try: 

571 ignore_list = '' 

572 copy = data_utils.Copy(src_path, dst_path, ignore_list) 

573 copy.run() 

574 if copy.get_progress()['total_progress'] == 100: 574 ↛ 579line 574 didn't jump to line 579 because the condition on line 574 was always true

575 copy_finish = True 

576 except Exception as err: 

577 LOG.error("Failed to copy data, reason: %s.", err) 

578 

579 return copy_finish 

580 

581 def umount_share_from_host(self, share): 

582 try: 

583 utils.execute('umount', share['mount_path'], 

584 run_as_root=True) 

585 except Exception as err: 

586 message = (_("Failed to unmount share %(share)s. " 

587 "Reason: %(reason)s.") 

588 % {'share': share['name'], 

589 'reason': str(err)}) 

590 raise exception.ShareUmountException(reason=message) 

591 

592 def mount_share_to_host(self, share, access): 

593 LOG.debug("Mounting share: %s to host, mount_src: %s", 

594 share['name'], share['mount_src']) 

595 try: 

596 if share['share_proto'] == 'NFS': 

597 utils.execute('mount', '-t', 'nfs', 

598 share['mount_path'], share['mount_src'], 

599 run_as_root=True) 

600 

601 LOG.debug("Execute mount. mount_src: %s", 

602 share['mount_src']) 

603 

604 elif share['share_proto'] == 'CIFS': 604 ↛ exitline 604 didn't return from function 'mount_share_to_host' because the condition on line 604 was always true

605 user = ('username=' + access['access_to'] + ',' + 

606 'password=' + access['access_password']) 

607 utils.execute('mount', '-t', 'cifs', 

608 share['mount_path'], share['mount_src'], 

609 '-o', user, run_as_root=True) 

610 except Exception as err: 

611 message = (_('Bad response from mount share: %(share)s. ' 

612 'Reason: %(reason)s.') 

613 % {'share': share['name'], 

614 'reason': str(err)}) 

615 raise exception.ShareMountException(reason=message) 

616 

617 def get_network_allocations_number(self): 

618 """Get number of network interfaces to be created.""" 

619 if self.configuration.driver_handles_share_servers: 

620 return constants.IP_ALLOCATIONS_DHSS_TRUE 

621 else: 

622 return constants.IP_ALLOCATIONS_DHSS_FALSE 

623 

624 def _get_capacity(self, pool_name, result): 

625 """Get free capacity and total capacity of the pools.""" 

626 poolinfo = self.helper._find_pool_info(pool_name, result) 

627 

628 if poolinfo: 

629 total = float(poolinfo['TOTALCAPACITY']) / units.Mi / 2 

630 free = float(poolinfo['CAPACITY']) / units.Mi / 2 

631 consumed = float(poolinfo['CONSUMEDCAPACITY']) / units.Mi / 2 

632 poolinfo['TOTALCAPACITY'] = total 

633 poolinfo['CAPACITY'] = free 

634 poolinfo['CONSUMEDCAPACITY'] = consumed 

635 poolinfo['PROVISIONEDCAPACITYGB'] = round( 

636 float(total) - float(free), 2) 

637 

638 return poolinfo 

639 

640 def _get_disk_type(self, pool_name, result): 

641 """Get disk type of the pool.""" 

642 pool_info = self.helper._find_pool_info(pool_name, result) 

643 if not pool_info: 

644 return None 

645 

646 pool_disk = [] 

647 for i, x in enumerate(['ssd', 'sas', 'nl_sas']): 

648 if pool_info['TIER%dCAPACITY' % i] != '0': 

649 pool_disk.append(x) 

650 

651 if len(pool_disk) > 1: 

652 pool_disk = ['mix'] 

653 

654 return pool_disk[0] if pool_disk else None 

655 

656 def _init_filesys_para(self, share, poolinfo, extra_specs): 

657 """Init basic filesystem parameters.""" 

658 name = share['name'] 

659 size = int(share['size']) * units.Mi * 2 

660 fileparam = { 

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

662 "DESCRIPTION": "", 

663 "ALLOCTYPE": extra_specs['LUNType'], 

664 "CAPACITY": size, 

665 "PARENTID": poolinfo['ID'], 

666 "INITIALALLOCCAPACITY": units.Ki * 20, 

667 "PARENTTYPE": 216, 

668 "SNAPSHOTRESERVEPER": 20, 

669 "INITIALDISTRIBUTEPOLICY": 0, 

670 "ISSHOWSNAPDIR": True, 

671 "RECYCLESWITCH": 0, 

672 "RECYCLEHOLDTIME": 15, 

673 "RECYCLETHRESHOLD": 0, 

674 "RECYCLEAUTOCLEANSWITCH": 0, 

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

676 "ENABLECOMPRESSION": extra_specs['compression'], 

677 } 

678 

679 if fileparam['ALLOCTYPE'] == constants.ALLOC_TYPE_THICK_FLAG: 

680 if (extra_specs['dedupe'] or 

681 extra_specs['compression']): 

682 err_msg = _( 

683 'The filesystem type is "Thick",' 

684 ' so dedupe or compression cannot be set.') 

685 LOG.error(err_msg) 

686 raise exception.InvalidInput(reason=err_msg) 

687 if extra_specs['sectorsize']: 

688 fileparam['SECTORSIZE'] = extra_specs['sectorsize'] * units.Ki 

689 

690 return fileparam 

691 

692 def deny_access(self, share, access, share_server=None): 

693 """Deny access to share.""" 

694 share_proto = share['share_proto'] 

695 share_name = share['name'] 

696 share_url_type = self.helper._get_share_url_type(share_proto) 

697 access_type = access['access_type'] 

698 if share_proto == 'NFS' and access_type not in ('ip', 'user'): 

699 LOG.warning('Only IP or USER access types are allowed for ' 

700 'NFS shares.') 

701 return 

702 elif share_proto == 'CIFS' and access_type != 'user': 

703 LOG.warning('Only USER access type is allowed for' 

704 ' CIFS shares.') 

705 return 

706 

707 access_to = access['access_to'] 

708 # Huawei array uses * to represent IP addresses of all clients 

709 if (share_proto == 'NFS' and access_type == 'ip' and 

710 access_to == '0.0.0.0/0'): 

711 access_to = '*' 

712 share = self.helper._get_share_by_name(share_name, share_url_type) 

713 if not share: 

714 LOG.warning('Can not get share %s.', share_name) 

715 return 

716 

717 access_id = self.helper._get_access_from_share(share['ID'], access_to, 

718 share_proto) 

719 if not access_id: 

720 LOG.warning('Can not get access id from share. ' 

721 'share_name: %s', share_name) 

722 return 

723 

724 self.helper._remove_access_from_share(access_id, share_proto) 

725 

726 def allow_access(self, share, access, share_server=None): 

727 """Allow access to the share.""" 

728 share_proto = share['share_proto'] 

729 share_name = share['name'] 

730 share_url_type = self.helper._get_share_url_type(share_proto) 

731 access_type = access['access_type'] 

732 access_level = access['access_level'] 

733 access_to = access['access_to'] 

734 

735 if access_level not in common_constants.ACCESS_LEVELS: 

736 raise exception.InvalidShareAccess( 

737 reason=(_('Unsupported level of access was provided - %s') % 

738 access_level)) 

739 

740 if share_proto == 'NFS': 

741 if access_type == 'user': 

742 # Use 'user' as 'netgroup' for NFS. 

743 # A group name starts with @. 

744 access_to = '@' + access_to 

745 elif access_type != 'ip': 

746 message = _('Only IP or USER access types ' 

747 'are allowed for NFS shares.') 

748 raise exception.InvalidShareAccess(reason=message) 

749 if access_level == common_constants.ACCESS_LEVEL_RW: 

750 access_level = constants.ACCESS_NFS_RW 

751 else: 

752 access_level = constants.ACCESS_NFS_RO 

753 # Huawei array uses * to represent IP addresses of all clients 

754 if access_to == '0.0.0.0/0': 

755 access_to = '*' 

756 

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

758 if access_type == 'user': 

759 if access_level == common_constants.ACCESS_LEVEL_RW: 

760 access_level = constants.ACCESS_CIFS_FULLCONTROL 

761 else: 

762 access_level = constants.ACCESS_CIFS_RO 

763 else: 

764 message = _('Only USER access type is allowed' 

765 ' for CIFS shares.') 

766 raise exception.InvalidShareAccess(reason=message) 

767 

768 share_stor = self.helper._get_share_by_name(share_name, 

769 share_url_type) 

770 if not share_stor: 

771 err_msg = (_("Share %s does not exist on the backend.") 

772 % share_name) 

773 LOG.error(err_msg) 

774 raise exception.ShareResourceNotFound(share_id=share['id']) 

775 

776 share_id = share_stor['ID'] 

777 

778 # Check if access already exists 

779 access_id = self.helper._get_access_from_share(share_id, 

780 access_to, 

781 share_proto) 

782 if access_id: 

783 # Check if the access level equal 

784 level_exist = self.helper._get_level_by_access_id(access_id, 

785 share_proto) 

786 if level_exist != access_level: 786 ↛ exitline 786 didn't return from function 'allow_access' because the condition on line 786 was always true

787 # Change the access level 

788 self.helper._change_access_rest(access_id, 

789 share_proto, access_level) 

790 else: 

791 # Add this access to share 

792 self.helper._allow_access_rest(share_id, access_to, 

793 share_proto, access_level) 

794 

795 def clear_access(self, share, share_server=None): 

796 """Remove all access rules of the share""" 

797 share_proto = share['share_proto'] 

798 share_name = share['name'] 

799 share_url_type = self.helper._get_share_url_type(share_proto) 

800 share_stor = self.helper._get_share_by_name(share_name, share_url_type) 

801 if not share_stor: 

802 LOG.warning('Cannot get share %s.', share_name) 

803 return 

804 share_id = share_stor['ID'] 

805 all_accesses = self.helper._get_all_access_from_share(share_id, 

806 share_proto) 

807 for access_id in all_accesses: 

808 self.helper._remove_access_from_share(access_id, 

809 share_proto) 

810 

811 def update_access(self, share, access_rules, add_rules, 

812 delete_rules, update_rules, share_server=None): 

813 """Update access rules list.""" 

814 if not (add_rules or delete_rules): 

815 self.clear_access(share, share_server) 

816 for access in access_rules: 

817 self.allow_access(share, access, share_server) 

818 else: 

819 for access in delete_rules: 

820 self.deny_access(share, access, share_server) 

821 for access in add_rules: 

822 self.allow_access(share, access, share_server) 

823 

824 def get_pool(self, share): 

825 pool_name = share_utils.extract_host(share['host'], level='pool') 

826 if pool_name: 826 ↛ 827line 826 didn't jump to line 827 because the condition on line 826 was never true

827 return pool_name 

828 share_name = share['name'] 

829 share_url_type = self.helper._get_share_url_type(share['share_proto']) 

830 share = self.helper._get_share_by_name(share_name, share_url_type) 

831 

832 pool_name = None 

833 if share: 

834 pool = self.helper._get_fs_info_by_id(share['FSID']) 

835 pool_name = pool['POOLNAME'] 

836 

837 return pool_name 

838 

839 def allocate_container(self, share, poolinfo): 

840 """Creates filesystem associated to share by name.""" 

841 opts = huawei_utils.get_share_extra_specs_params( 

842 share['share_type_id']) 

843 

844 if opts is None: 844 ↛ 845line 844 didn't jump to line 845 because the condition on line 844 was never true

845 opts = constants.OPTS_CAPABILITIES 

846 smart = smartx.SmartX(self.helper) 

847 smartx_opts, qos = smart.get_smartx_extra_specs_opts(opts) 

848 

849 fileParam = self._init_filesys_para(share, poolinfo, smartx_opts) 

850 fsid = self.helper._create_filesystem(fileParam) 

851 

852 try: 

853 if qos: 

854 smart_qos = smartx.SmartQos(self.helper) 

855 smart_qos.create_qos(qos, fsid) 

856 

857 smartpartition = smartx.SmartPartition(self.helper) 

858 smartpartition.add(opts, fsid) 

859 

860 smartcache = smartx.SmartCache(self.helper) 

861 smartcache.add(opts, fsid) 

862 except Exception as err: 

863 if fsid is not None: 863 ↛ 868line 863 didn't jump to line 868 because the condition on line 863 was always true

864 qos_id = self.helper.get_qosid_by_fsid(fsid) 

865 if qos_id: 865 ↛ 867line 865 didn't jump to line 867 because the condition on line 865 was always true

866 self.remove_qos_fs(fsid, qos_id) 

867 self.helper._delete_fs(fsid) 

868 message = (_('Failed to add smartx. Reason: %(err)s.') 

869 % {'err': err}) 

870 raise exception.InvalidShare(reason=message) 

871 return fsid 

872 

873 def manage_existing(self, share, driver_options): 

874 """Manage existing share.""" 

875 

876 share_proto = share['share_proto'] 

877 share_name = share['name'] 

878 old_export_location = share['export_locations'][0]['path'] 

879 pool_name = share_utils.extract_host(share['host'], level='pool') 

880 share_url_type = self.helper._get_share_url_type(share_proto) 

881 old_share_name = self.helper._get_share_name_by_export_location( 

882 old_export_location, share_proto) 

883 

884 share_storage = self.helper._get_share_by_name(old_share_name, 

885 share_url_type) 

886 if not share_storage: 

887 err_msg = (_("Can not get share ID by share %s.") 

888 % old_export_location) 

889 LOG.error(err_msg) 

890 raise exception.InvalidShare(reason=err_msg) 

891 

892 fs_id = share_storage['FSID'] 

893 fs = self.helper._get_fs_info_by_id(fs_id) 

894 if not self.check_fs_status(fs['HEALTHSTATUS'], 

895 fs['RUNNINGSTATUS']): 

896 raise exception.InvalidShare( 

897 reason=(_('Invalid status of filesystem: ' 

898 'HEALTHSTATUS=%(health)s ' 

899 'RUNNINGSTATUS=%(running)s.') 

900 % {'health': fs['HEALTHSTATUS'], 

901 'running': fs['RUNNINGSTATUS']})) 

902 

903 if pool_name and pool_name != fs['POOLNAME']: 

904 raise exception.InvalidHost( 

905 reason=(_('The current pool(%(fs_pool)s) of filesystem ' 

906 'does not match the input pool(%(host_pool)s).') 

907 % {'fs_pool': fs['POOLNAME'], 

908 'host_pool': pool_name})) 

909 

910 result = self.helper._find_all_pool_info() 

911 poolinfo = self.helper._find_pool_info(pool_name, result) 

912 

913 opts = huawei_utils.get_share_extra_specs_params( 

914 share['share_type_id']) 

915 specs = share_types.get_share_type_extra_specs(share['share_type_id']) 

916 if ('capabilities:thin_provisioning' not in specs.keys() 

917 and 'thin_provisioning' not in specs.keys()): 

918 if fs['ALLOCTYPE'] == constants.ALLOC_TYPE_THIN_FLAG: 

919 opts['thin_provisioning'] = constants.THIN_PROVISIONING 

920 else: 

921 opts['thin_provisioning'] = constants.THICK_PROVISIONING 

922 

923 change_opts = self.check_retype_change_opts(opts, poolinfo, fs) 

924 LOG.info('Retyping share (%(share)s), changed options are : ' 

925 '(%(change_opts)s).', 

926 {'share': old_share_name, 'change_opts': change_opts}) 

927 try: 

928 self.retype_share(change_opts, fs_id) 

929 except Exception as err: 

930 message = (_("Retype share error. Share: %(share)s. " 

931 "Reason: %(reason)s.") 

932 % {'share': old_share_name, 

933 'reason': err}) 

934 raise exception.InvalidShare(reason=message) 

935 

936 share_size = int(fs['CAPACITY']) / units.Mi / 2 

937 self.helper._change_fs_name(fs_id, share_name) 

938 location = self._get_location_path(share_name, share_proto) 

939 return (share_size, [location]) 

940 

941 def _check_snapshot_valid_for_manage(self, snapshot_info): 

942 snapshot_name = snapshot_info['data']['NAME'] 

943 

944 # Check whether the snapshot is normal. 

945 if (snapshot_info['data']['HEALTHSTATUS'] 

946 != constants.STATUS_FSSNAPSHOT_HEALTH): 

947 msg = (_("Can't import snapshot %(snapshot)s to Manila. " 

948 "Snapshot status is not normal, snapshot status: " 

949 "%(status)s.") 

950 % {'snapshot': snapshot_name, 

951 'status': snapshot_info['data']['HEALTHSTATUS']}) 

952 raise exception.ManageInvalidShareSnapshot( 

953 reason=msg) 

954 

955 def manage_existing_snapshot(self, snapshot, driver_options): 

956 """Manage existing snapshot.""" 

957 

958 share_proto = snapshot['share']['share_proto'] 

959 share_url_type = self.helper._get_share_url_type(share_proto) 

960 share_storage = self.helper._get_share_by_name(snapshot['share_name'], 

961 share_url_type) 

962 if not share_storage: 

963 err_msg = (_("Failed to import snapshot %(snapshot)s to Manila. " 

964 "Snapshot source share %(share)s doesn't exist " 

965 "on array.") 

966 % {'snapshot': snapshot['provider_location'], 

967 'share': snapshot['share_name']}) 

968 raise exception.InvalidShare(reason=err_msg) 

969 sharefsid = share_storage['FSID'] 

970 

971 provider_location = snapshot.get('provider_location') 

972 snapshot_id = sharefsid + "@" + provider_location 

973 snapshot_info = self.helper._get_snapshot_by_id(snapshot_id) 

974 snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_info) 

975 if not snapshot_flag: 

976 err_msg = (_("Cannot find snapshot %s on array.") 

977 % snapshot['provider_location']) 

978 raise exception.ManageInvalidShareSnapshot(reason=err_msg) 

979 else: 

980 self._check_snapshot_valid_for_manage(snapshot_info) 

981 snapshot_name = ("share_snapshot_" 

982 + snapshot['id'].replace("-", "_")) 

983 self.helper._rename_share_snapshot(snapshot_id, snapshot_name) 

984 return snapshot_name 

985 

986 def check_retype_change_opts(self, opts, poolinfo, fs): 

987 change_opts = { 

988 "partitionid": None, 

989 "cacheid": None, 

990 "dedupe&compression": None, 

991 } 

992 

993 # SmartPartition 

994 old_partition_id = fs['SMARTPARTITIONID'] 

995 old_partition_name = None 

996 new_partition_id = None 

997 new_partition_name = None 

998 if strutils.bool_from_string(opts['huawei_smartpartition']): 

999 if not opts['partitionname']: 

1000 raise exception.InvalidInput( 

1001 reason=_('Partition name is None, please set ' 

1002 'huawei_smartpartition:partitionname in key.')) 

1003 new_partition_name = opts['partitionname'] 

1004 new_partition_id = self.helper._get_partition_id_by_name( 

1005 new_partition_name) 

1006 if new_partition_id is None: 

1007 raise exception.InvalidInput( 

1008 reason=(_("Can't find partition name on the array, " 

1009 "partition name is: %(name)s.") 

1010 % {"name": new_partition_name})) 

1011 

1012 if old_partition_id != new_partition_id: 1012 ↛ 1023line 1012 didn't jump to line 1023 because the condition on line 1012 was always true

1013 if old_partition_id: 

1014 partition_info = self.helper.get_partition_info_by_id( 

1015 old_partition_id) 

1016 old_partition_name = partition_info['NAME'] 

1017 change_opts["partitionid"] = ([old_partition_id, 

1018 old_partition_name], 

1019 [new_partition_id, 

1020 new_partition_name]) 

1021 

1022 # SmartCache 

1023 old_cache_id = fs['SMARTCACHEID'] 

1024 old_cache_name = None 

1025 new_cache_id = None 

1026 new_cache_name = None 

1027 if strutils.bool_from_string(opts['huawei_smartcache']): 

1028 if not opts['cachename']: 1028 ↛ 1029line 1028 didn't jump to line 1029 because the condition on line 1028 was never true

1029 raise exception.InvalidInput( 

1030 reason=_('Cache name is None, please set ' 

1031 'huawei_smartcache:cachename in key.')) 

1032 new_cache_name = opts['cachename'] 

1033 new_cache_id = self.helper._get_cache_id_by_name( 

1034 new_cache_name) 

1035 if new_cache_id is None: 

1036 raise exception.InvalidInput( 

1037 reason=(_("Can't find cache name on the array, " 

1038 "cache name is: %(name)s.") 

1039 % {"name": new_cache_name})) 

1040 

1041 if old_cache_id != new_cache_id: 1041 ↛ 1050line 1041 didn't jump to line 1050 because the condition on line 1041 was always true

1042 if old_cache_id: 

1043 cache_info = self.helper.get_cache_info_by_id( 

1044 old_cache_id) 

1045 old_cache_name = cache_info['NAME'] 

1046 change_opts["cacheid"] = ([old_cache_id, old_cache_name], 

1047 [new_cache_id, new_cache_name]) 

1048 

1049 # SmartDedupe&SmartCompression 

1050 smartx_opts = constants.OPTS_CAPABILITIES 

1051 if opts is not None: 1051 ↛ 1055line 1051 didn't jump to line 1055 because the condition on line 1051 was always true

1052 smart = smartx.SmartX(self.helper) 

1053 smartx_opts, qos = smart.get_smartx_extra_specs_opts(opts) 

1054 

1055 old_compression = fs['COMPRESSION'] 

1056 new_compression = smartx_opts['compression'] 

1057 old_dedupe = fs['DEDUP'] 

1058 new_dedupe = smartx_opts['dedupe'] 

1059 

1060 if fs['ALLOCTYPE'] == constants.ALLOC_TYPE_THIN_FLAG: 

1061 fs['ALLOCTYPE'] = constants.ALLOC_TYPE_THIN 

1062 else: 

1063 fs['ALLOCTYPE'] = constants.ALLOC_TYPE_THICK 

1064 

1065 if strutils.bool_from_string(opts['thin_provisioning']): 

1066 opts['thin_provisioning'] = constants.ALLOC_TYPE_THIN 

1067 else: 

1068 opts['thin_provisioning'] = constants.ALLOC_TYPE_THICK 

1069 

1070 if fs['ALLOCTYPE'] != opts['thin_provisioning']: 1070 ↛ 1071line 1070 didn't jump to line 1071 because the condition on line 1070 was never true

1071 msg = (_("Manage existing share " 

1072 "fs type and new_share_type mismatch. " 

1073 "fs type is: %(fs_type)s, " 

1074 "new_share_type is: %(new_share_type)s") 

1075 % {"fs_type": fs['ALLOCTYPE'], 

1076 "new_share_type": opts['thin_provisioning']}) 

1077 raise exception.InvalidHost(reason=msg) 

1078 else: 

1079 if fs['ALLOCTYPE'] == constants.ALLOC_TYPE_THICK: 

1080 if new_compression or new_dedupe: 

1081 raise exception.InvalidInput( 

1082 reason=_("Dedupe or compression cannot be set for " 

1083 "thick filesystem.")) 

1084 else: 

1085 if (old_dedupe != new_dedupe 1085 ↛ 1091line 1085 didn't jump to line 1091 because the condition on line 1085 was always true

1086 or old_compression != new_compression): 

1087 change_opts["dedupe&compression"] = ([old_dedupe, 

1088 old_compression], 

1089 [new_dedupe, 

1090 new_compression]) 

1091 return change_opts 

1092 

1093 def retype_share(self, change_opts, fs_id): 

1094 if change_opts.get('partitionid'): 1094 ↛ 1114line 1094 didn't jump to line 1114 because the condition on line 1094 was always true

1095 old, new = change_opts['partitionid'] 

1096 old_id = old[0] 

1097 old_name = old[1] 

1098 new_id = new[0] 

1099 new_name = new[1] 

1100 

1101 if old_id: 

1102 self.helper._remove_fs_from_partition(fs_id, old_id) 

1103 if new_id: 

1104 self.helper._add_fs_to_partition(fs_id, new_id) 

1105 msg = (_("Retype FS(id: %(fs_id)s) smartpartition from " 

1106 "(name: %(old_name)s, id: %(old_id)s) to " 

1107 "(name: %(new_name)s, id: %(new_id)s) " 

1108 "performed successfully.") 

1109 % {"fs_id": fs_id, 

1110 "old_id": old_id, "old_name": old_name, 

1111 "new_id": new_id, "new_name": new_name}) 

1112 LOG.info(msg) 

1113 

1114 if change_opts.get('cacheid'): 1114 ↛ 1133line 1114 didn't jump to line 1133 because the condition on line 1114 was always true

1115 old, new = change_opts['cacheid'] 

1116 old_id = old[0] 

1117 old_name = old[1] 

1118 new_id = new[0] 

1119 new_name = new[1] 

1120 if old_id: 

1121 self.helper._remove_fs_from_cache(fs_id, old_id) 

1122 if new_id: 

1123 self.helper._add_fs_to_cache(fs_id, new_id) 

1124 msg = (_("Retype FS(id: %(fs_id)s) smartcache from " 

1125 "(name: %(old_name)s, id: %(old_id)s) to " 

1126 "(name: %(new_name)s, id: %(new_id)s) " 

1127 "performed successfully.") 

1128 % {"fs_id": fs_id, 

1129 "old_id": old_id, "old_name": old_name, 

1130 "new_id": new_id, "new_name": new_name}) 

1131 LOG.info(msg) 

1132 

1133 if change_opts.get('dedupe&compression'): 

1134 old, new = change_opts['dedupe&compression'] 

1135 old_dedupe = old[0] 

1136 old_compression = old[1] 

1137 new_dedupe = new[0] 

1138 new_compression = new[1] 

1139 if ((old_dedupe != new_dedupe) 1139 ↛ exitline 1139 didn't return from function 'retype_share' because the condition on line 1139 was always true

1140 or (old_compression != new_compression)): 

1141 

1142 new_smartx_opts = {"dedupe": new_dedupe, 

1143 "compression": new_compression} 

1144 

1145 self.helper._change_extra_specs(fs_id, new_smartx_opts) 

1146 msg = (_("Retype FS(id: %(fs_id)s) dedupe from %(old_dedupe)s " 

1147 "to %(new_dedupe)s performed successfully, " 

1148 "compression from " 

1149 "%(old_compression)s to %(new_compression)s " 

1150 "performed successfully.") 

1151 % {"fs_id": fs_id, 

1152 "old_dedupe": old_dedupe, 

1153 "new_dedupe": new_dedupe, 

1154 "old_compression": old_compression, 

1155 "new_compression": new_compression}) 

1156 LOG.info(msg) 

1157 

1158 def remove_qos_fs(self, fs_id, qos_id): 

1159 fs_list = self.helper.get_fs_list_in_qos(qos_id) 

1160 fs_count = len(fs_list) 

1161 if fs_count <= 1: 1161 ↛ 1165line 1161 didn't jump to line 1165 because the condition on line 1161 was always true

1162 qos = smartx.SmartQos(self.helper) 

1163 qos.delete_qos(qos_id) 

1164 else: 

1165 self.helper.remove_fs_from_qos(fs_id, 

1166 fs_list, 

1167 qos_id) 

1168 

1169 def _get_location_path(self, share_name, share_proto, ip=None): 

1170 location = None 

1171 if ip is None: 

1172 root = self.helper._read_xml() 

1173 ip = root.findtext('Storage/LogicalPortIP').strip() 

1174 if share_proto == 'NFS': 

1175 location = '%s:/%s' % (ip, share_name.replace("-", "_")) 

1176 elif share_proto == 'CIFS': 

1177 location = '\\\\%s\\%s' % (ip, share_name.replace("-", "_")) 

1178 else: 

1179 raise exception.InvalidShareAccess( 

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

1181 % share_proto)) 

1182 

1183 return location 

1184 

1185 def _get_share_proto(self, share_name): 

1186 share_proto = None 

1187 for proto in ('NFS', 'CIFS'): 1187 ↛ 1193line 1187 didn't jump to line 1193 because the loop on line 1187 didn't complete

1188 share_url_type = self.helper._get_share_url_type(proto) 

1189 share = self.helper._get_share_by_name(share_name, share_url_type) 

1190 if share: 1190 ↛ 1187line 1190 didn't jump to line 1187 because the condition on line 1190 was always true

1191 share_proto = proto 

1192 break 

1193 return share_proto 

1194 

1195 def _get_wait_interval(self): 

1196 """Get wait interval from huawei conf file.""" 

1197 root = self.helper._read_xml() 

1198 wait_interval = root.findtext('Filesystem/WaitInterval') 

1199 if wait_interval: 

1200 return int(wait_interval) 

1201 else: 

1202 LOG.info( 

1203 "Wait interval is not configured in huawei " 

1204 "conf file. Use default: %(default_wait_interval)d.", 

1205 {"default_wait_interval": constants.DEFAULT_WAIT_INTERVAL}) 

1206 return constants.DEFAULT_WAIT_INTERVAL 

1207 

1208 def _get_timeout(self): 

1209 """Get timeout from huawei conf file.""" 

1210 root = self.helper._read_xml() 

1211 timeout = root.findtext('Filesystem/Timeout') 

1212 if timeout: 

1213 return int(timeout) 

1214 else: 

1215 LOG.info( 

1216 "Timeout is not configured in huawei conf file. " 

1217 "Use default: %(default_timeout)d.", 

1218 {"default_timeout": constants.DEFAULT_TIMEOUT}) 

1219 return constants.DEFAULT_TIMEOUT 

1220 

1221 def check_conf_file(self): 

1222 """Check the config file, make sure the essential items are set.""" 

1223 root = self.helper._read_xml() 

1224 resturl = root.findtext('Storage/RestURL') 

1225 username = root.findtext('Storage/UserName') 

1226 pwd = root.findtext('Storage/UserPassword') 

1227 product = root.findtext('Storage/Product') 

1228 pool_node = root.findtext('Filesystem/StoragePool') 

1229 logical_port_ip = root.findtext('Storage/LogicalPortIP') 

1230 

1231 if product != "V3": 

1232 err_msg = (_( 

1233 'check_conf_file: Config file invalid. ' 

1234 'Product must be set to V3.')) 

1235 LOG.error(err_msg) 

1236 raise exception.InvalidInput(err_msg) 

1237 

1238 if not (resturl and username and pwd): 

1239 err_msg = (_( 

1240 'check_conf_file: Config file invalid. RestURL,' 

1241 ' UserName and UserPassword must be set.')) 

1242 LOG.error(err_msg) 

1243 raise exception.InvalidInput(err_msg) 

1244 

1245 if not pool_node: 

1246 err_msg = (_( 

1247 'check_conf_file: Config file invalid. ' 

1248 'StoragePool must be set.')) 

1249 LOG.error(err_msg) 

1250 raise exception.InvalidInput(err_msg) 

1251 

1252 if not (self.configuration.driver_handles_share_servers 

1253 or logical_port_ip): 

1254 err_msg = (_( 

1255 'check_conf_file: Config file invalid. LogicalPortIP ' 

1256 'must be set when driver_handles_share_servers is False.')) 

1257 LOG.error(err_msg) 

1258 raise exception.InvalidInput(reason=err_msg) 

1259 

1260 if self.snapshot_support and self.replication_support: 

1261 err_msg = _('Config file invalid. SnapshotSupport and ' 

1262 'ReplicationSupport can not both be set to True.') 

1263 LOG.error(err_msg) 

1264 raise exception.BadConfigurationException(reason=err_msg) 

1265 

1266 def check_service(self): 

1267 running_status = self.helper._get_cifs_service_status() 

1268 if running_status != constants.STATUS_SERVICE_RUNNING: 

1269 self.helper._start_cifs_service_status() 

1270 

1271 service = self.helper._get_nfs_service_status() 

1272 if ((service['RUNNINGSTATUS'] != constants.STATUS_SERVICE_RUNNING) or 

1273 (service['SUPPORTV3'] == 'false') or 

1274 (service['SUPPORTV4'] == 'false')): 

1275 self.helper._start_nfs_service_status() 

1276 

1277 def setup_server(self, network_info, metadata=None): 

1278 """Set up share server with given network parameters.""" 

1279 self._check_network_type_validate(network_info['network_type']) 

1280 

1281 vlan_tag = network_info['segmentation_id'] or 0 

1282 ip = network_info['network_allocations'][0]['ip_address'] 

1283 subnet = utils.cidr_to_netmask(network_info['cidr']) 

1284 if not utils.is_valid_ip_address(ip, '4'): 

1285 err_msg = (_( 

1286 "IP (%s) is invalid. Only IPv4 addresses are supported.") % ip) 

1287 LOG.error(err_msg) 

1288 raise exception.InvalidInput(reason=err_msg) 

1289 

1290 ad_created = False 

1291 ldap_created = False 

1292 try: 

1293 if network_info.get('security_services'): 

1294 active_directory, ldap = self._get_valid_security_service( 

1295 network_info.get('security_services')) 

1296 

1297 # Configure AD or LDAP Domain. 

1298 if active_directory: 

1299 self._configure_AD_domain(active_directory) 

1300 ad_created = True 

1301 if ldap: 

1302 self._configure_LDAP_domain(ldap) 

1303 ldap_created = True 

1304 

1305 # Create vlan and logical_port. 

1306 vlan_id, logical_port_id = ( 

1307 self._create_vlan_and_logical_port(vlan_tag, ip, subnet)) 

1308 except exception.ManilaException: 

1309 if ad_created: 

1310 dns_ip_list = [] 

1311 user = active_directory['user'] 

1312 password = active_directory['password'] 

1313 self.helper.set_DNS_ip_address(dns_ip_list) 

1314 self.helper.delete_AD_config(user, password) 

1315 self._check_AD_expected_status(constants.STATUS_EXIT_DOMAIN) 

1316 if ldap_created: 

1317 self.helper.delete_LDAP_config() 

1318 raise 

1319 

1320 return { 

1321 'share_server_name': network_info['server_id'], 

1322 'share_server_id': network_info['server_id'], 

1323 'vlan_id': vlan_id, 

1324 'logical_port_id': logical_port_id, 

1325 'ip': ip, 

1326 'subnet': subnet, 

1327 'vlan_tag': vlan_tag, 

1328 'ad_created': ad_created, 

1329 'ldap_created': ldap_created, 

1330 } 

1331 

1332 def _check_network_type_validate(self, network_type): 

1333 if network_type not in ('flat', 'vlan', None): 

1334 err_msg = (_( 

1335 'Invalid network type. Network type must be flat or vlan.')) 

1336 raise exception.NetworkBadConfigurationException(reason=err_msg) 

1337 

1338 def _get_valid_security_service(self, security_services): 

1339 """Validate security services and return AD/LDAP config.""" 

1340 service_number = len(security_services) 

1341 err_msg = _("Unsupported security services. " 

1342 "Only AD and LDAP are supported.") 

1343 if service_number > 2: 

1344 LOG.error(err_msg) 

1345 raise exception.InvalidInput(reason=err_msg) 

1346 

1347 active_directory = None 

1348 ldap = None 

1349 for ss in security_services: 

1350 if ss['type'] == 'active_directory': 

1351 active_directory = ss 

1352 elif ss['type'] == 'ldap': 

1353 ldap = ss 

1354 else: 

1355 LOG.error(err_msg) 

1356 raise exception.InvalidInput(reason=err_msg) 

1357 

1358 return active_directory, ldap 

1359 

1360 def _configure_AD_domain(self, active_directory): 

1361 dns_ip = active_directory['dns_ip'] 

1362 user = active_directory['user'] 

1363 password = active_directory['password'] 

1364 domain = active_directory['domain'] 

1365 if not (dns_ip and user and password and domain): 

1366 raise exception.InvalidInput( 

1367 reason=_("dns_ip or user or password or domain " 

1368 "in security_services is None.")) 

1369 

1370 # Check DNS server exists or not. 

1371 ip_address = self.helper.get_DNS_ip_address() 

1372 if ip_address and ip_address[0]: 

1373 err_msg = (_("DNS server (%s) has already been configured.") 

1374 % ip_address[0]) 

1375 LOG.error(err_msg) 

1376 raise exception.InvalidInput(reason=err_msg) 

1377 

1378 # Check AD config exists or not. 

1379 ad_exists, AD_domain = self.helper.get_AD_domain_name() 

1380 if ad_exists: 

1381 err_msg = (_("AD domain (%s) has already been configured.") 

1382 % AD_domain) 

1383 LOG.error(err_msg) 

1384 raise exception.InvalidInput(reason=err_msg) 

1385 

1386 # Set DNS server ip. 

1387 dns_ip_list = dns_ip.split(",") 

1388 DNS_config = self.helper.set_DNS_ip_address(dns_ip_list) 

1389 

1390 # Set AD config. 

1391 digits = string.digits 

1392 random_id = ''.join([random.choice(digits) for i in range(9)]) 

1393 system_name = constants.SYSTEM_NAME_PREFIX + random_id 

1394 

1395 try: 

1396 self.helper.add_AD_config(user, password, domain, system_name) 

1397 self._check_AD_expected_status(constants.STATUS_JOIN_DOMAIN) 

1398 except exception.ManilaException as err: 

1399 if DNS_config: 1399 ↛ 1402line 1399 didn't jump to line 1402 because the condition on line 1399 was always true

1400 dns_ip_list = [] 

1401 self.helper.set_DNS_ip_address(dns_ip_list) 

1402 raise exception.InvalidShare( 

1403 reason=(_('Failed to add AD config. ' 

1404 'Reason: %s.') % err)) 

1405 

1406 def _check_AD_expected_status(self, expected_status): 

1407 wait_interval = self._get_wait_interval() 

1408 timeout = self._get_timeout() 

1409 retries = timeout / wait_interval 

1410 interval = wait_interval 

1411 backoff_rate = 1 

1412 

1413 @utils.retry(retry_param=exception.InvalidShare, 

1414 interval=interval, 

1415 retries=retries, 

1416 backoff_rate=backoff_rate) 

1417 def _check_AD_status(): 

1418 ad = self.helper.get_AD_config() 

1419 if ad['DOMAINSTATUS'] != expected_status: 

1420 raise exception.InvalidShare( 

1421 reason=(_('AD domain (%s) status is not expected.') 

1422 % ad['FULLDOMAINNAME'])) 

1423 

1424 _check_AD_status() 

1425 

1426 def _configure_LDAP_domain(self, ldap): 

1427 server = ldap['server'] 

1428 domain = ldap['domain'] 

1429 if not server or not domain: 

1430 raise exception.InvalidInput(reason=_("Server or domain is None.")) 

1431 

1432 # Check LDAP config exists or not. 

1433 ldap_exists, LDAP_domain = self.helper.get_LDAP_domain_server() 

1434 if ldap_exists: 

1435 err_msg = (_("LDAP domain (%s) has already been configured.") 

1436 % LDAP_domain) 

1437 LOG.error(err_msg) 

1438 raise exception.InvalidInput(reason=err_msg) 

1439 

1440 # Set LDAP config. 

1441 server_number = len(server.split(',')) 

1442 if server_number == 1: 

1443 server = server + ",," 

1444 elif server_number == 2: 

1445 server = server + "," 

1446 elif server_number > 3: 

1447 raise exception.InvalidInput( 

1448 reason=_("Cannot support more than three LDAP servers.")) 

1449 

1450 self.helper.add_LDAP_config(server, domain) 

1451 

1452 def _create_vlan_and_logical_port(self, vlan_tag, ip, subnet): 

1453 optimal_port, port_type = self._get_optimal_port(vlan_tag) 

1454 port_id = self.helper.get_port_id(optimal_port, port_type) 

1455 home_port_id = port_id 

1456 home_port_type = port_type 

1457 vlan_id = 0 

1458 vlan_exists = True 

1459 

1460 if port_type is None or port_id is None: 

1461 err_msg = _("No appropriate port found to create logical port.") 

1462 LOG.error(err_msg) 

1463 raise exception.InvalidInput(reason=err_msg) 

1464 if vlan_tag: 

1465 vlan_exists, vlan_id = self.helper.get_vlan(port_id, vlan_tag) 

1466 if not vlan_exists: 

1467 # Create vlan. 

1468 vlan_id = self.helper.create_vlan( 

1469 port_id, port_type, vlan_tag) 

1470 home_port_id = vlan_id 

1471 home_port_type = constants.PORT_TYPE_VLAN 

1472 

1473 logical_port_exists, logical_port_id = ( 

1474 self.helper.get_logical_port(home_port_id, ip, subnet)) 

1475 if not logical_port_exists: 

1476 try: 

1477 # Create logical port. 

1478 logical_port_id = ( 

1479 self.helper.create_logical_port( 

1480 home_port_id, home_port_type, ip, subnet)) 

1481 except exception.ManilaException as err: 

1482 if not vlan_exists: 1482 ↛ 1484line 1482 didn't jump to line 1484 because the condition on line 1482 was always true

1483 self.helper.delete_vlan(vlan_id) 

1484 raise exception.InvalidShare( 

1485 reason=(_('Failed to create logical port. ' 

1486 'Reason: %s.') % err)) 

1487 

1488 return vlan_id, logical_port_id 

1489 

1490 def _get_optimal_port(self, vlan_tag): 

1491 """Get an optimal physical port or bond port.""" 

1492 root = self.helper._read_xml() 

1493 port_info = [] 

1494 port_list = root.findtext('Storage/Port') 

1495 if port_list: 

1496 port_list = port_list.split(";") 

1497 for port in port_list: 

1498 port = port.strip().strip('\n') 

1499 if port: 1499 ↛ 1497line 1499 didn't jump to line 1497 because the condition on line 1499 was always true

1500 port_info.append(port) 

1501 

1502 eth_port, bond_port = self._get_online_port(port_info) 

1503 if vlan_tag: 

1504 optimal_port, port_type = ( 

1505 self._get_least_port(eth_port, bond_port, 

1506 sort_type=constants.SORT_BY_VLAN)) 

1507 else: 

1508 optimal_port, port_type = ( 

1509 self._get_least_port(eth_port, bond_port, 

1510 sort_type=constants.SORT_BY_LOGICAL)) 

1511 

1512 if not optimal_port: 

1513 err_msg = (_("Cannot find optimal port. port_info: %s.") 

1514 % port_info) 

1515 LOG.error(err_msg) 

1516 raise exception.InvalidInput(reason=err_msg) 

1517 

1518 return optimal_port, port_type 

1519 

1520 def _get_online_port(self, all_port_list): 

1521 eth_port = self.helper.get_all_eth_port() 

1522 bond_port = self.helper.get_all_bond_port() 

1523 

1524 eth_status = constants.STATUS_ETH_RUNNING 

1525 online_eth_port = [] 

1526 for eth in eth_port: 

1527 if (eth_status == eth['RUNNINGSTATUS'] 

1528 and not eth['IPV4ADDR'] and not eth['BONDNAME']): 

1529 online_eth_port.append(eth['LOCATION']) 

1530 

1531 online_bond_port = [] 

1532 for bond in bond_port: 

1533 if eth_status == bond['RUNNINGSTATUS']: 1533 ↛ 1532line 1533 didn't jump to line 1532 because the condition on line 1533 was always true

1534 port_id = jsonutils.loads(bond['PORTIDLIST']) 

1535 bond_eth_port = self.helper.get_eth_port_by_id(port_id[0]) 

1536 if bond_eth_port and not bond_eth_port['IPV4ADDR']: 1536 ↛ 1532line 1536 didn't jump to line 1532 because the condition on line 1536 was always true

1537 online_bond_port.append(bond['NAME']) 

1538 

1539 filtered_eth_port = [] 

1540 filtered_bond_port = [] 

1541 if len(all_port_list) == 0: 

1542 filtered_eth_port = online_eth_port 

1543 filtered_bond_port = online_bond_port 

1544 else: 

1545 all_port_list = list(set(all_port_list)) 

1546 for port in all_port_list: 

1547 is_eth_port = False 

1548 for eth in online_eth_port: 

1549 if port == eth: 

1550 filtered_eth_port.append(port) 

1551 is_eth_port = True 

1552 break 

1553 if is_eth_port: 

1554 continue 

1555 for bond in online_bond_port: 1555 ↛ 1546line 1555 didn't jump to line 1546 because the loop on line 1555 didn't complete

1556 if port == bond: 1556 ↛ 1555line 1556 didn't jump to line 1555 because the condition on line 1556 was always true

1557 filtered_bond_port.append(port) 

1558 break 

1559 

1560 return filtered_eth_port, filtered_bond_port 

1561 

1562 def _get_least_port(self, eth_port, bond_port, sort_type): 

1563 sorted_eth = [] 

1564 sorted_bond = [] 

1565 

1566 if sort_type == constants.SORT_BY_VLAN: 

1567 _get_sorted_least_port = self._get_sorted_least_port_by_vlan 

1568 else: 

1569 _get_sorted_least_port = self._get_sorted_least_port_by_logical 

1570 

1571 if eth_port: 

1572 sorted_eth = _get_sorted_least_port(eth_port) 

1573 if bond_port: 

1574 sorted_bond = _get_sorted_least_port(bond_port) 

1575 

1576 if sorted_eth and sorted_bond: 

1577 if sorted_eth[1] >= sorted_bond[1]: 

1578 return sorted_bond[0], constants.PORT_TYPE_BOND 

1579 else: 

1580 return sorted_eth[0], constants.PORT_TYPE_ETH 

1581 elif sorted_eth: 

1582 return sorted_eth[0], constants.PORT_TYPE_ETH 

1583 elif sorted_bond: 

1584 return sorted_bond[0], constants.PORT_TYPE_BOND 

1585 else: 

1586 return None, None 

1587 

1588 def _get_sorted_least_port_by_vlan(self, port_list): 

1589 if not port_list: 1589 ↛ 1590line 1589 didn't jump to line 1590 because the condition on line 1589 was never true

1590 return None 

1591 

1592 vlan_list = self.helper.get_all_vlan() 

1593 count = {} 

1594 for item in port_list: 

1595 count[item] = 0 

1596 

1597 for item in port_list: 

1598 for vlan in vlan_list: 

1599 pos = vlan['NAME'].rfind('.') 

1600 if vlan['NAME'][:pos] == item: 

1601 count[item] += 1 

1602 

1603 sort_port = sorted(count.items(), key=lambda count: count[1]) 

1604 

1605 return sort_port[0] 

1606 

1607 def _get_sorted_least_port_by_logical(self, port_list): 

1608 if not port_list: 1608 ↛ 1609line 1608 didn't jump to line 1609 because the condition on line 1608 was never true

1609 return None 

1610 

1611 logical_list = self.helper.get_all_logical_port() 

1612 count = {} 

1613 for item in port_list: 

1614 count[item] = 0 

1615 for logical in logical_list: 

1616 if logical['HOMEPORTTYPE'] == constants.PORT_TYPE_VLAN: 

1617 pos = logical['HOMEPORTNAME'].rfind('.') 

1618 if logical['HOMEPORTNAME'][:pos] == item: 

1619 count[item] += 1 

1620 else: 

1621 if logical['HOMEPORTNAME'] == item: 

1622 count[item] += 1 

1623 

1624 sort_port = sorted(count.items(), key=lambda count: count[1]) 

1625 

1626 return sort_port[0] 

1627 

1628 def teardown_server(self, server_details, security_services=None): 

1629 if not server_details: 

1630 LOG.debug('Server details are empty.') 

1631 return 

1632 

1633 logical_port_id = server_details.get('logical_port_id') 

1634 vlan_id = server_details.get('vlan_id') 

1635 ad_created = server_details.get('ad_created') 

1636 ldap_created = server_details.get('ldap_created') 

1637 

1638 # Delete logical_port. 

1639 if logical_port_id: 1639 ↛ 1646line 1639 didn't jump to line 1646 because the condition on line 1639 was always true

1640 logical_port_exists = ( 

1641 self.helper.check_logical_port_exists_by_id(logical_port_id)) 

1642 if logical_port_exists: 

1643 self.helper.delete_logical_port(logical_port_id) 

1644 

1645 # Delete vlan. 

1646 if vlan_id and vlan_id != '0': 1646 ↛ 1651line 1646 didn't jump to line 1651 because the condition on line 1646 was always true

1647 vlan_exists = self.helper.check_vlan_exists_by_id(vlan_id) 

1648 if vlan_exists: 

1649 self.helper.delete_vlan(vlan_id) 

1650 

1651 if security_services: 

1652 active_directory, ldap = ( 

1653 self._get_valid_security_service(security_services)) 

1654 

1655 if ad_created and ad_created == '1' and active_directory: 1655 ↛ 1674line 1655 didn't jump to line 1674 because the condition on line 1655 was always true

1656 dns_ip = active_directory['dns_ip'] 

1657 user = active_directory['user'] 

1658 password = active_directory['password'] 

1659 domain = active_directory['domain'] 

1660 

1661 # Check DNS server exists or not. 

1662 ip_address = self.helper.get_DNS_ip_address() 

1663 if ip_address and ip_address[0] == dns_ip: 

1664 dns_ip_list = [] 

1665 self.helper.set_DNS_ip_address(dns_ip_list) 

1666 

1667 # Check AD config exists or not. 

1668 ad_exists, AD_domain = self.helper.get_AD_domain_name() 

1669 if ad_exists and AD_domain == domain: 

1670 self.helper.delete_AD_config(user, password) 

1671 self._check_AD_expected_status( 

1672 constants.STATUS_EXIT_DOMAIN) 

1673 

1674 if ldap_created and ldap_created == '1' and ldap: 1674 ↛ exitline 1674 didn't return from function 'teardown_server' because the condition on line 1674 was always true

1675 server = ldap['server'] 

1676 domain = ldap['domain'] 

1677 

1678 # Check LDAP config exists or not. 

1679 ldap_exists, LDAP_domain = ( 

1680 self.helper.get_LDAP_domain_server()) 

1681 if ldap_exists: 

1682 LDAP_config = self.helper.get_LDAP_config() 

1683 if (LDAP_config['LDAPSERVER'] == server 1683 ↛ exitline 1683 didn't return from function 'teardown_server' because the condition on line 1683 was always true

1684 and LDAP_config['BASEDN'] == domain): 

1685 self.helper.delete_LDAP_config() 

1686 

1687 def ensure_share(self, share, share_server=None): 

1688 """Ensure that share is exported.""" 

1689 share_proto = share['share_proto'] 

1690 share_name = share['name'] 

1691 share_id = share['id'] 

1692 share_url_type = self.helper._get_share_url_type(share_proto) 

1693 

1694 share_storage = self.helper._get_share_by_name(share_name, 

1695 share_url_type) 

1696 if not share_storage: 

1697 raise exception.ShareResourceNotFound(share_id=share_id) 

1698 

1699 fs_id = share_storage['FSID'] 

1700 self.assert_filesystem(fs_id) 

1701 

1702 ip = self._get_share_ip(share_server) 

1703 location = self._get_location_path(share_name, share_proto, ip) 

1704 return [location] 

1705 

1706 def create_replica(self, context, replica_list, new_replica, 

1707 access_rules, replica_snapshots, share_server=None): 

1708 """Create a new share, and create a remote replication pair.""" 

1709 

1710 active_replica = share_utils.get_active_replica(replica_list) 

1711 

1712 if (self.private_storage.get(active_replica['share_id'], 

1713 'replica_pair_id')): 

1714 # for huawei array, only one replication can be created for 

1715 # each active replica, so if a replica pair id is recorded for 

1716 # this share, it means active replica already has a replication, 

1717 # can not create anymore. 

1718 msg = _('Cannot create more than one replica for share %s.') 

1719 LOG.error(msg, active_replica['share_id']) 

1720 raise exception.ReplicationException( 

1721 reason=msg % active_replica['share_id']) 

1722 

1723 # Create a new share 

1724 new_share_name = new_replica['name'] 

1725 location = self.create_share(new_replica, share_server) 

1726 

1727 # create a replication pair. 

1728 # replication pair only can be created by master node, 

1729 # so here is a remote call to trigger master node to 

1730 # start the creating progress. 

1731 try: 

1732 replica_pair_id = self.rpc_client.create_replica_pair( 

1733 context, 

1734 active_replica['host'], 

1735 local_share_info=active_replica, 

1736 remote_device_wwn=self.helper.get_array_wwn(), 

1737 remote_fs_id=self.helper.get_fsid_by_name(new_share_name) 

1738 ) 

1739 except Exception: 

1740 LOG.exception('Failed to create a replication pair ' 

1741 'with host %s.', 

1742 active_replica['host']) 

1743 raise 

1744 

1745 self.private_storage.update(new_replica['share_id'], 

1746 {'replica_pair_id': replica_pair_id}) 

1747 

1748 # Get the state of the new created replica 

1749 replica_state = self.replica_mgr.get_replica_state(replica_pair_id) 

1750 replica_ref = { 

1751 'export_locations': [location], 

1752 'replica_state': replica_state, 

1753 'access_rules_status': common_constants.STATUS_ACTIVE, 

1754 } 

1755 

1756 return replica_ref 

1757 

1758 def update_replica_state(self, context, replica_list, replica, 

1759 access_rules, replica_snapshots, 

1760 share_server=None): 

1761 replica_pair_id = self.private_storage.get(replica['share_id'], 

1762 'replica_pair_id') 

1763 if replica_pair_id is None: 

1764 msg = ("No replication pair ID recorded for share %s.") 

1765 LOG.error(msg, replica['share_id']) 

1766 return common_constants.STATUS_ERROR 

1767 

1768 self.replica_mgr.update_replication_pair_state(replica_pair_id) 

1769 return self.replica_mgr.get_replica_state(replica_pair_id) 

1770 

1771 def promote_replica(self, context, replica_list, replica, access_rules, 

1772 share_server=None, quiesce_wait_time=None): 

1773 replica_pair_id = self.private_storage.get(replica['share_id'], 

1774 'replica_pair_id') 

1775 if replica_pair_id is None: 

1776 msg = _("No replication pair ID recorded for share %s.") 

1777 LOG.error(msg, replica['share_id']) 

1778 raise exception.ReplicationException( 

1779 reason=msg % replica['share_id']) 

1780 

1781 try: 

1782 self.replica_mgr.switch_over(replica_pair_id) 

1783 except Exception: 

1784 LOG.exception('Failed to promote replica %s.', 

1785 replica['id']) 

1786 raise 

1787 

1788 updated_new_active_access = True 

1789 cleared_old_active_access = True 

1790 

1791 try: 

1792 self.update_access(replica, access_rules, 

1793 [], [], [], share_server) 

1794 except Exception: 

1795 LOG.warning('Failed to set access rules to ' 

1796 'new active replica %s.', 

1797 replica['id']) 

1798 updated_new_active_access = False 

1799 

1800 old_active_replica = share_utils.get_active_replica(replica_list) 

1801 

1802 try: 

1803 self.clear_access(old_active_replica, share_server) 

1804 except Exception: 

1805 LOG.warning("Failed to clear access rules from " 

1806 "old active replica %s.", 

1807 old_active_replica['id']) 

1808 cleared_old_active_access = False 

1809 

1810 new_active_update = { 

1811 'id': replica['id'], 

1812 'replica_state': common_constants.REPLICA_STATE_ACTIVE, 

1813 } 

1814 new_active_update['access_rules_status'] = ( 

1815 common_constants.STATUS_ACTIVE 

1816 if updated_new_active_access 

1817 else common_constants.SHARE_INSTANCE_RULES_SYNCING) 

1818 

1819 # get replica state for new secondary after switch over 

1820 replica_state = self.replica_mgr.get_replica_state(replica_pair_id) 

1821 

1822 old_active_update = { 

1823 'id': old_active_replica['id'], 

1824 'replica_state': replica_state, 

1825 } 

1826 old_active_update['access_rules_status'] = ( 

1827 common_constants.SHARE_INSTANCE_RULES_SYNCING 

1828 if cleared_old_active_access 

1829 else common_constants.STATUS_ACTIVE) 

1830 

1831 return [new_active_update, old_active_update] 

1832 

1833 def delete_replica(self, context, replica_list, replica_snapshots, 

1834 replica, share_server=None): 

1835 replica_pair_id = self.private_storage.get(replica['share_id'], 

1836 'replica_pair_id') 

1837 if replica_pair_id is None: 

1838 msg = ("No replication pair ID recorded for share %(share)s. " 

1839 "Continue to delete replica %(replica)s.") 

1840 LOG.warning(msg, {'share': replica['share_id'], 

1841 'replica': replica['id']}) 

1842 else: 

1843 self.replica_mgr.delete_replication_pair(replica_pair_id) 

1844 self.private_storage.delete(replica['share_id']) 

1845 

1846 try: 

1847 self.delete_share(replica, share_server) 

1848 except Exception: 

1849 LOG.exception('Failed to delete replica %s.', 

1850 replica['id']) 

1851 raise 

1852 

1853 def revert_to_snapshot(self, context, snapshot, share_access_rules, 

1854 snapshot_access_rules, share_server): 

1855 fs_id = self.helper.get_fsid_by_name(snapshot['share_name']) 

1856 if not fs_id: 

1857 msg = _("The source filesystem of snapshot %s " 

1858 "not exist.") % snapshot['id'] 

1859 LOG.error(msg) 

1860 raise exception.ShareResourceNotFound( 

1861 share_id=snapshot['share_id']) 

1862 

1863 snapshot_id = self.helper._get_snapshot_id(fs_id, snapshot['id']) 

1864 self.helper.rollback_snapshot(snapshot_id)