Coverage for manila/share/manager.py: 86%

2924 statements  

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

1# Copyright (c) 2014 NetApp Inc. 

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"""NAS share manager managers creating shares and access rights. 

16 

17**Related Flags** 

18 

19:share_driver: Used by :class:`ShareManager`. 

20""" 

21 

22import copy 

23import datetime 

24import functools 

25import hashlib 

26import json 

27from operator import xor 

28 

29from keystoneauth1 import loading as ks_loading 

30from oslo_config import cfg 

31from oslo_log import log 

32from oslo_serialization import jsonutils 

33from oslo_service import periodic_task 

34from oslo_utils import excutils 

35from oslo_utils import importutils 

36from oslo_utils import timeutils 

37 

38from manila.common import constants 

39from manila import context 

40from manila import coordination 

41from manila.data import rpcapi as data_rpcapi 

42from manila import exception 

43from manila.i18n import _ 

44from manila.keymgr import barbican as barbican_api 

45from manila import manager 

46from manila.message import api as message_api 

47from manila.message import message_field 

48from manila import quota 

49from manila.share import access 

50from manila.share import api 

51from manila.share import configuration 

52from manila.share import drivers_private_data 

53from manila.share import migration 

54from manila.share import rpcapi as share_rpcapi 

55from manila.share import share_types 

56from manila.share import snapshot_access 

57from manila.share import utils as share_utils 

58from manila.transfer import api as transfer_api 

59from manila import utils 

60 

61profiler = importutils.try_import('osprofiler.profiler') 

62 

63LOG = log.getLogger(__name__) 

64 

65share_manager_opts = [ 

66 cfg.StrOpt('share_driver', 

67 default='manila.share.drivers.generic.GenericShareDriver', 

68 help='Driver to use for share creation.'), 

69 cfg.ListOpt('hook_drivers', 

70 default=[], 

71 help='Driver(s) to perform some additional actions before and ' 

72 'after share driver actions and on a periodic basis. ' 

73 'Default is [].'), 

74 cfg.BoolOpt('delete_share_server_with_last_share', 

75 default=False, 

76 help='Whether share servers will ' 

77 'be deleted on deletion of the last share.'), 

78 cfg.BoolOpt('unmanage_remove_access_rules', 

79 default=False, 

80 help='If set to True, then manila will deny access and remove ' 

81 'all access rules on share unmanage.' 

82 'If set to False - nothing will be changed.'), 

83 cfg.BoolOpt('automatic_share_server_cleanup', 

84 default=True, 

85 help='If set to True, then Manila will delete all share ' 

86 'servers which were unused more than specified time .' 

87 'If set to False - automatic deletion of share servers ' 

88 'will be disabled.'), 

89 cfg.IntOpt('unused_share_server_cleanup_interval', 

90 default=10, 

91 help='Unallocated share servers reclamation time interval ' 

92 '(minutes). Minimum value is 10 minutes, maximum is 720 ' 

93 'minutes. The reclamation function is run every ' 

94 '10 minutes and delete share servers which were unused ' 

95 'more than unused_share_server_cleanup_interval option ' 

96 'defines. This value reflects the shortest time Manila ' 

97 'will wait for a share server to go unutilized before ' 

98 'deleting it.', 

99 min=10, 

100 max=720), 

101 cfg.IntOpt('replica_state_update_interval', 

102 default=300, 

103 help='This value, specified in seconds, determines how often ' 

104 'the share manager will poll for the health ' 

105 '(replica_state) of each replica instance.'), 

106 cfg.IntOpt('migration_driver_continue_update_interval', 

107 default=60, 

108 help='This value, specified in seconds, determines how often ' 

109 'the share manager will poll the driver to perform the ' 

110 'next step of migration in the storage backend, for a ' 

111 'migrating share.'), 

112 cfg.IntOpt('server_migration_driver_continue_update_interval', 

113 default=900, 

114 help='This value, specified in seconds, determines how often ' 

115 'the share manager will poll the driver to perform the ' 

116 'next step of migration in the storage backend, for a ' 

117 'migrating share server.'), 

118 cfg.BoolOpt('server_migration_extend_neutron_network', 

119 default=False, 

120 help='If set to True, neutron network are extended to ' 

121 'destination host during share server migration. This ' 

122 'option should only be enabled if using ' 

123 'NeutronNetworkPlugin or its derivatives and when ' 

124 'multiple bindings of Manila ports are supported by ' 

125 'Neutron ML2 plugin.'), 

126 cfg.IntOpt('share_usage_size_update_interval', 

127 default=300, 

128 help='This value, specified in seconds, determines how often ' 

129 'the share manager will poll the driver to update the ' 

130 'share usage size in the storage backend, for shares in ' 

131 'that backend.'), 

132 cfg.BoolOpt('enable_gathering_share_usage_size', 

133 default=False, 

134 help='If set to True, share usage size will be polled for in ' 

135 'the interval specified with ' 

136 '"share_usage_size_update_interval". Usage data can be ' 

137 'consumed by telemetry integration. If telemetry is not ' 

138 'configured, this option must be set to False. ' 

139 'If set to False - gathering share usage size will be' 

140 ' disabled.'), 

141 cfg.BoolOpt('share_service_inithost_offload', 

142 default=False, 

143 help='Offload pending share ensure during ' 

144 'share service startup'), 

145 cfg.IntOpt('check_for_expired_shares_in_recycle_bin_interval', 

146 default=3600, 

147 help='This value, specified in seconds, determines how often ' 

148 'the share manager will check for expired shares and ' 

149 'delete them from the Recycle bin.'), 

150 cfg.IntOpt('check_for_expired_transfers', 

151 default=300, 

152 help='This value, specified in seconds, determines how often ' 

153 'the share manager will check for expired transfers and ' 

154 'destroy them and roll back share state.'), 

155 cfg.IntOpt('driver_backup_continue_update_interval', 

156 default=60, 

157 help='This value, specified in seconds, determines how often ' 

158 'the share manager will poll to perform the next steps ' 

159 'of backup such as fetch the progress of backup.'), 

160 cfg.IntOpt('driver_restore_continue_update_interval', 

161 default=60, 

162 help='This value, specified in seconds, determines how often ' 

163 'the share manager will poll to perform the next steps ' 

164 'of restore such as fetch the progress of restore.'), 

165 cfg.IntOpt('periodic_deferred_delete_interval', 

166 default=300, 

167 help='This value, specified in seconds, determines how often ' 

168 'the share manager will try to delete the share and share ' 

169 'snapshots in backend driver.'), 

170] 

171 

172 

173ks_opts = [ 

174 cfg.StrOpt('auth_url', 

175 help='Keystone authentication URL.'), 

176] 

177 

178CONF = cfg.CONF 

179CONF.register_opts(share_manager_opts) 

180CONF.import_opt('periodic_hooks_interval', 'manila.share.hook') 

181CONF.import_opt('periodic_interval', 'manila.service') 

182 

183KEYSTONE_AUTHTOKEN_GROUP = 'keystone_authtoken' 

184CONF.register_opts(ks_opts, KEYSTONE_AUTHTOKEN_GROUP) 

185ks_loading.register_auth_conf_options(CONF, KEYSTONE_AUTHTOKEN_GROUP) 

186keystone_url = getattr(CONF.keystone_authtoken, 'auth_url') 

187 

188# Drivers that need to change module paths or class names can add their 

189# old/new path here to maintain backward compatibility. 

190MAPPING = { 

191 'manila.share.drivers.netapp.cluster_mode.NetAppClusteredShareDriver': 

192 'manila.share.drivers.netapp.common.NetAppDriver', 

193 'manila.share.drivers.hp.hp_3par_driver.HP3ParShareDriver': 

194 'manila.share.drivers.hpe.hpe_3par_driver.HPE3ParShareDriver', 

195 'manila.share.drivers.hitachi.hds_hnas.HDSHNASDriver': 

196 'manila.share.drivers.hitachi.hnas.driver.HitachiHNASDriver', 

197 'manila.share.drivers.glusterfs_native.GlusterfsNativeShareDriver': 

198 'manila.share.drivers.glusterfs.glusterfs_native.' 

199 'GlusterfsNativeShareDriver', 

200 'manila.share.drivers.emc.driver.EMCShareDriver': 

201 'manila.share.drivers.dell_emc.driver.EMCShareDriver', 

202 'manila.share.drivers.cephfs.cephfs_native.CephFSNativeDriver': 

203 'manila.share.drivers.cephfs.driver.CephFSDriver', 

204} 

205 

206QUOTAS = quota.QUOTAS 

207 

208 

209def locked_share_replica_operation(operation): 

210 """Lock decorator for share replica operations. 

211 

212 Takes a named lock prior to executing the operation. The lock is named with 

213 the id of the share to which the replica belongs. 

214 

215 Intended use: 

216 If a replica operation uses this decorator, it will block actions on 

217 all share replicas of the share until the named lock is free. This is 

218 used to protect concurrent operations on replicas of the same share e.g. 

219 promote ReplicaA while deleting ReplicaB, both belonging to the same share. 

220 """ 

221 

222 def wrapped(*args, **kwargs): 

223 share_id = kwargs.get('share_id') 

224 

225 @coordination.synchronized( 

226 'locked-share-replica-operation-for-share-%s' % share_id) 

227 def locked_replica_operation(*_args, **_kwargs): 

228 return operation(*_args, **_kwargs) 

229 return locked_replica_operation(*args, **kwargs) 

230 

231 return wrapped 

232 

233 

234def locked_share_network_operation(operation): 

235 """Lock decorator for share network operations. 

236 

237 Takes a named lock prior to executing the operation. The lock is named with 

238 the id of the share network. 

239 """ 

240 

241 def wrapped(*args, **kwargs): 

242 share_network_id = kwargs.get('share_network_id') 

243 

244 @coordination.synchronized( 

245 'locked-share-network-operation-%s' % share_network_id) 

246 def locked_network_operation(*_args, **_kwargs): 

247 return operation(*_args, **_kwargs) 

248 return locked_network_operation(*args, **kwargs) 

249 

250 return wrapped 

251 

252 

253def add_hooks(f): 

254 """Hook decorator to perform action before and after a share method call 

255 

256 The hook decorator can perform actions before some share driver methods 

257 calls and after a call with results of driver call and preceding hook call. 

258 """ 

259 @functools.wraps(f) 

260 def wrapped(self, *args, **kwargs): 

261 if not self.hooks: 

262 return f(self, *args, **kwargs) 

263 

264 pre_hook_results = [] 

265 for hook in self.hooks: 

266 pre_hook_results.append( 

267 hook.execute_pre_hook( 

268 func_name=f.__name__, 

269 *args, **kwargs)) 

270 

271 wrapped_func_results = f(self, *args, **kwargs) 

272 

273 for i, hook in enumerate(self.hooks): 

274 hook.execute_post_hook( 

275 func_name=f.__name__, 

276 driver_action_results=wrapped_func_results, 

277 pre_hook_data=pre_hook_results[i], 

278 *args, **kwargs) 

279 

280 return wrapped_func_results 

281 

282 return wrapped 

283 

284 

285class ShareManager(manager.SchedulerDependentManager): 

286 """Manages NAS storages.""" 

287 

288 RPC_API_VERSION = '1.30' 

289 

290 def __init__(self, share_driver=None, service_name=None, *args, **kwargs): 

291 """Load the driver from args, or from flags.""" 

292 self.configuration = configuration.Configuration( 

293 share_manager_opts, 

294 config_group=service_name) 

295 super(ShareManager, self).__init__(service_name='share', 

296 *args, **kwargs) 

297 

298 if not share_driver: 298 ↛ 300line 298 didn't jump to line 300 because the condition on line 298 was always true

299 share_driver = self.configuration.share_driver 

300 if share_driver in MAPPING: 300 ↛ 301line 300 didn't jump to line 301 because the condition on line 300 was never true

301 msg_args = {'old': share_driver, 'new': MAPPING[share_driver]} 

302 LOG.warning("Driver path %(old)s is deprecated, update your " 

303 "configuration to the new path %(new)s", 

304 msg_args) 

305 share_driver = MAPPING[share_driver] 

306 

307 ctxt = context.get_admin_context() 

308 private_storage = drivers_private_data.DriverPrivateData( 

309 context=ctxt, backend_host=self.host, 

310 config_group=self.configuration.config_group 

311 ) 

312 self.driver = importutils.import_object( 

313 share_driver, private_storage=private_storage, 

314 configuration=self.configuration, 

315 ) 

316 

317 backend_availability_zone = self.driver.configuration.safe_get( 

318 'backend_availability_zone') 

319 self.availability_zone = ( 

320 backend_availability_zone or CONF.storage_availability_zone 

321 ) 

322 

323 self.access_helper = access.ShareInstanceAccess(self.db, self.driver) 

324 self.snapshot_access_helper = ( 

325 snapshot_access.ShareSnapshotInstanceAccess(self.db, self.driver)) 

326 self.migration_wait_access_rules_timeout = ( 

327 CONF.migration_wait_access_rules_timeout) 

328 

329 self.message_api = message_api.API() 

330 self.share_api = api.API() 

331 self.transfer_api = transfer_api.API() 

332 if CONF.profiler.enabled and profiler is not None: 332 ↛ 333line 332 didn't jump to line 333 because the condition on line 332 was never true

333 self.driver = profiler.trace_cls("driver")(self.driver) 

334 self.hooks = [] 

335 self._init_hook_drivers() 

336 self.service_id = None 

337 

338 def _init_hook_drivers(self): 

339 # Try to initialize hook driver(s). 

340 hook_drivers = self.configuration.safe_get("hook_drivers") 

341 for hook_driver in hook_drivers: 

342 self.hooks.append( 

343 importutils.import_object( 

344 hook_driver, 

345 configuration=self.configuration, 

346 host=self.host, 

347 ) 

348 ) 

349 

350 def _ensure_share_instance_has_pool(self, ctxt, share_instance): 

351 pool = share_utils.extract_host(share_instance['host'], 'pool') 

352 if pool is None: 

353 # No pool name encoded in host, so this is a legacy 

354 # share created before pool is introduced, ask 

355 # driver to provide pool info if it has such 

356 # knowledge and update the DB. 

357 try: 

358 pool = self.driver.get_pool(share_instance) 

359 except Exception: 

360 LOG.exception("Failed to fetch pool name for share: " 

361 "%(share)s.", 

362 {'share': share_instance['id']}) 

363 return 

364 

365 if pool: 

366 new_host = share_utils.append_host( 

367 share_instance['host'], pool) 

368 self.db.share_instance_update( 

369 ctxt, share_instance['id'], {'host': new_host}) 

370 

371 return pool 

372 

373 @add_hooks 

374 def init_host(self, service_id=None): 

375 """Initialization for a standalone service.""" 

376 

377 self.service_id = service_id 

378 ctxt = context.get_admin_context() 

379 driver_host_pair = "{}@{}".format( 

380 self.driver.__class__.__name__, 

381 self.host) 

382 

383 # we want to retry to setup the driver. In case of a multi-backend 

384 # scenario, working backends are usable and the non-working ones (where 

385 # do_setup() or check_for_setup_error() fail) retry. 

386 @utils.retry(interval=2, backoff_rate=2, 

387 infinite=True, backoff_sleep_max=600) 

388 def _driver_setup(): 

389 self.driver.initialized = False 

390 LOG.debug("Start initialization of driver: '%s'", driver_host_pair) 

391 try: 

392 self.driver.do_setup(ctxt) 

393 self.driver.check_for_setup_error() 

394 except Exception: 

395 LOG.exception("Error encountered during initialization of " 

396 "driver %s", driver_host_pair) 

397 raise 

398 else: 

399 self.driver.initialized = True 

400 

401 _driver_setup() 

402 if (self.driver.driver_handles_share_servers and 402 ↛ 407line 402 didn't jump to line 407 because the condition on line 402 was always true

403 hasattr(self.driver, 'service_instance_manager')): 

404 (self.driver.service_instance_manager.network_helper. 

405 setup_connectivity_with_service_instances()) 

406 

407 self.ensure_driver_resources(ctxt) 

408 

409 self.publish_service_capabilities(ctxt) 

410 LOG.info("Finished initialization of driver: '%(driver)s" 

411 "@%(host)s'", 

412 {"driver": self.driver.__class__.__name__, 

413 "host": self.host}) 

414 

415 def is_service_ready(self): 

416 """Return if Manager is ready to accept requests. 

417 

418 This is to inform Service class that in case of manila driver 

419 initialization failure the manager is actually down and not ready to 

420 accept any requests. 

421 

422 """ 

423 return self.driver.initialized 

424 

425 def ensure_driver_resources(self, ctxt, skip_backend_info_check=False): 

426 update_instances_status = CONF.update_shares_status_on_ensure 

427 old_backend_info = self.db.backend_info_get(ctxt, self.host) 

428 old_backend_info_hash = (old_backend_info.get('info_hash') 

429 if old_backend_info is not None else None) 

430 new_backend_info = None 

431 new_backend_info_hash = None 

432 backend_info_implemented = True 

433 update_share_instances = [] 

434 if not skip_backend_info_check: 434 ↛ 462line 434 didn't jump to line 462 because the condition on line 434 was always true

435 try: 

436 new_backend_info = self.driver.get_backend_info(ctxt) 

437 except Exception as e: 

438 if not isinstance(e, NotImplementedError): 

439 LOG.exception( 

440 "The backend %(host)s could not get backend info.", 

441 {'host': self.host}) 

442 raise 

443 else: 

444 backend_info_implemented = False 

445 LOG.debug( 

446 ("The backend %(host)s does not support get backend" 

447 " info method."), 

448 {'host': self.host}) 

449 

450 if new_backend_info: 

451 new_backend_info_hash = hashlib.sha1( 

452 str(sorted(new_backend_info.items())).encode( 

453 'utf-8')).hexdigest() 

454 if ((old_backend_info_hash == new_backend_info_hash and 

455 backend_info_implemented) and not skip_backend_info_check): 

456 LOG.debug( 

457 ("Ensure shares is being skipped because the %(host)s's " 

458 "old backend info is the same as its new backend info."), 

459 {'host': self.host}) 

460 return 

461 

462 share_instances = self.db.share_instance_get_all_by_host( 

463 ctxt, self.host) 

464 LOG.debug("Re-exporting %s shares", len(share_instances)) 

465 

466 for share_instance in share_instances: 

467 share_ref = self.db.share_get(ctxt, share_instance['share_id']) 

468 

469 if share_ref.is_busy: 

470 LOG.info( 

471 "Share instance %(id)s: skipping export, " 

472 "because it is busy with an active task: %(task)s.", 

473 {'id': share_instance['id'], 

474 'task': share_ref['task_state']}, 

475 ) 

476 continue 

477 

478 # If the share's status is 'ensuring', we must allow re-doing the 

479 # ensuring operation otherwise it will be stuck 

480 if (share_instance['status'] not in 

481 [constants.STATUS_AVAILABLE, constants.STATUS_ENSURING]): 

482 LOG.info( 

483 "Share instance %(id)s: skipping export, " 

484 "because it has '%(status)s' status.", 

485 {'id': share_instance['id'], 

486 'status': share_instance['status']}, 

487 ) 

488 continue 

489 

490 self._ensure_share_instance_has_pool(ctxt, share_instance) 

491 share_instance = self.db.share_instance_get( 

492 ctxt, share_instance['id'], with_share_data=True) 

493 share_instance_dict = self._get_share_instance_dict( 

494 ctxt, share_instance) 

495 update_share_instances.append(share_instance_dict) 

496 

497 do_service_status_update = False 

498 if update_share_instances: 

499 # No reason to update the shares status if nothing will be done. 

500 do_service_status_update = True 

501 service = self.db.service_get_by_args( 

502 ctxt, self.host, 'manila-share') 

503 self.db.service_update(ctxt, service['id'], {'ensuring': True}) 

504 if update_instances_status: 504 ↛ 510line 504 didn't jump to line 510 because the condition on line 504 was always true

505 for instance in update_share_instances: 

506 self.db.share_instance_update( 

507 ctxt, instance['id'], 

508 {'status': constants.STATUS_ENSURING} 

509 ) 

510 try: 

511 update_share_instances = self.driver.ensure_shares( 

512 ctxt, update_share_instances) or {} 

513 except Exception as e: 

514 if not isinstance(e, NotImplementedError): 

515 LOG.exception("Caught exception trying ensure " 

516 "share instances.") 

517 else: 

518 for share_instance in update_share_instances: 

519 if CONF.share_service_inithost_offload: 519 ↛ 520line 519 didn't jump to line 520 because the condition on line 519 was never true

520 self._add_to_threadpool(self._ensure_share, 

521 ctxt, share_instance) 

522 else: 

523 self._ensure_share(ctxt, share_instance) 

524 # At this point, we assume that the ensuring operation is 

525 # complete or everything is okay, even though it might be 

526 # running on different threads. 

527 LOG.debug( 

528 "Shares' export locations were ensured individually, " 

529 "so triggering the ensure shares operation is " 

530 "complete.") 

531 self.db.service_update( 

532 ctxt, service['id'], {'ensuring': False}) 

533 

534 if new_backend_info: 

535 self.db.backend_info_update( 

536 ctxt, self.host, new_backend_info_hash) 

537 

538 shares_with_metadata_already_updated = set() 

539 for share_instance in share_instances: 

540 if share_instance['id'] not in update_share_instances: 

541 continue 

542 share_instance_update_dict = ( 

543 update_share_instances[share_instance['id']] 

544 ) 

545 backend_provided_status = share_instance_update_dict.get('status') 

546 if backend_provided_status: 

547 self.db.share_instance_update( 

548 ctxt, share_instance['id'], 

549 {'status': backend_provided_status, 

550 'host': share_instance['host']} 

551 ) 

552 metadata_updates = share_instance_update_dict.get('metadata') 

553 if metadata_updates: 

554 share_id = share_instance['share_id'] 

555 # NOTE(carloss): Multiple instances might exist, and in such 

556 # cases, a single share metadata update would be enough. 

557 if share_id not in shares_with_metadata_already_updated: 557 ↛ 562line 557 didn't jump to line 562 because the condition on line 557 was always true

558 self.db.share_metadata_update( 

559 ctxt, share_id, metadata_updates, False) 

560 shares_with_metadata_already_updated.add(share_id) 

561 

562 update_export_locations = ( 

563 share_instance_update_dict.get('export_locations') 

564 ) 

565 if update_export_locations: 

566 self.db.export_locations_update( 

567 ctxt, share_instance['id'], update_export_locations) 

568 

569 share_server = self._get_share_server(ctxt, share_instance) 

570 driver_has_to_reapply_access_rules = ( 

571 share_instance_update_dict.get('reapply_access_rules') is True 

572 ) 

573 share_instance_has_pending_rules = ( 

574 share_instance['access_rules_status'] != 

575 constants.STATUS_ACTIVE 

576 ) 

577 if (driver_has_to_reapply_access_rules or 

578 share_instance_has_pending_rules): 

579 try: 

580 # Cast any existing 'applying' rules to 'new' 

581 self.access_helper.reset_rules_to_queueing_states( 

582 ctxt, share_instance['id'], 

583 reset_active=driver_has_to_reapply_access_rules) 

584 self.access_helper.update_access_rules( 

585 ctxt, share_instance['id'], share_server=share_server) 

586 except Exception: 

587 LOG.exception( 

588 ("Unexpected error occurred while updating access " 

589 "rules for share instance %(s_id)s."), 

590 {'s_id': share_instance['id']}, 

591 ) 

592 

593 snapshot_instances = ( 

594 self.db.share_snapshot_instance_get_all_with_filters( 

595 ctxt, {'share_instance_ids': share_instance['id']}, 

596 with_share_data=True)) 

597 

598 for snap_instance in snapshot_instances: 598 ↛ 600line 598 didn't jump to line 600 because the loop on line 598 never started

599 

600 rules = ( 

601 self.db. 

602 share_snapshot_access_get_all_for_snapshot_instance( 

603 ctxt, snap_instance['id'])) 

604 

605 # NOTE(ganso): We don't invoke update_access for snapshots if 

606 # we don't have invalid rules or pending updates 

607 if any(r['state'] in (constants.ACCESS_STATE_DENYING, 

608 constants.ACCESS_STATE_QUEUED_TO_DENY, 

609 constants.ACCESS_STATE_APPLYING, 

610 constants.ACCESS_STATE_QUEUED_TO_APPLY) 

611 for r in rules): 

612 try: 

613 self.snapshot_access_helper.update_access_rules( 

614 ctxt, snap_instance['id'], share_server) 

615 except Exception: 

616 LOG.exception( 

617 "Unexpected error occurred while updating " 

618 "access rules for snapshot instance %s.", 

619 snap_instance['id']) 

620 if not backend_provided_status and update_instances_status: 

621 self.db.share_instance_update( 

622 ctxt, share_instance['id'], 

623 {'status': constants.STATUS_AVAILABLE} 

624 ) 

625 if do_service_status_update: 

626 self.db.service_update(ctxt, service['id'], {'ensuring': False}) 

627 

628 def _ensure_share(self, ctxt, share_instance): 

629 export_locations = None 

630 try: 

631 export_locations = self.driver.ensure_share( 

632 ctxt, share_instance, 

633 share_server=share_instance['share_server']) 

634 except Exception: 

635 LOG.exception("Caught exception trying ensure " 

636 "share '%(s_id)s'.", 

637 {'s_id': share_instance['id']}) 

638 if export_locations: 

639 self.db.export_locations_update( 

640 ctxt, share_instance['id'], export_locations) 

641 

642 # NOTE(carloss): we can't determine if the share is actually alright, 

643 # but we expect that after the export location is updated in the 

644 # database, everything is okay. 

645 self.db.share_instance_update( 

646 ctxt, share_instance['id'], 

647 {'status': constants.STATUS_AVAILABLE} 

648 ) 

649 

650 def _check_share_server_backend_limits( 

651 self, context, available_share_servers, share_instance=None): 

652 max_shares_limit = self.driver.max_shares_per_share_server 

653 max_server_size = self.driver.max_share_server_size 

654 

655 if max_server_size == max_shares_limit == -1: 

656 return available_share_servers 

657 

658 for ss in available_share_servers[:]: 

659 share_instances = self.db.share_instance_get_all_by_share_server( 

660 context, ss['id'], with_share_data=True) 

661 if not share_instances: 661 ↛ 662line 661 didn't jump to line 662 because the condition on line 661 was never true

662 continue 

663 share_instance_ids = [si['id'] for si in share_instances] 

664 share_snapshot_instances = ( 

665 self.db.share_snapshot_instance_get_all_with_filters( 

666 context, {"share_instance_ids": share_instance_ids}, 

667 with_share_data=True)) 

668 

669 server_instances_size_sum = 0 

670 num_instances = 0 

671 

672 server_instances_size_sum += sum( 

673 instance['size'] for instance in share_instances) 

674 server_instances_size_sum += sum( 

675 instance['size'] for instance in share_snapshot_instances) 

676 num_instances += len(share_instances) 

677 

678 # NOTE(carloss): If a share instance was not provided, means that 

679 # a share group is being requested and there aren't shares to 

680 # be added to to the sum yet. 

681 if share_instance: 

682 server_instances_size_sum += share_instance['size'] 

683 num_instances += 1 

684 

685 achieved_gigabytes_limit = ( 

686 max_server_size != -1 and ( 

687 server_instances_size_sum > max_server_size)) 

688 

689 achieved_instances_limit = num_instances > max_shares_limit > -1 

690 

691 providing_server_for_share_migration = ( 

692 share_instance and share_instance['status'] == 

693 constants.STATUS_MIGRATING_TO) 

694 

695 src_server_id_equals_current_iteration = False 

696 

697 if providing_server_for_share_migration: 

698 share = self.db.share_get(context, share_instance['share_id']) 

699 src_instance_id, dest_instance_id = ( 

700 self.share_api.get_migrating_instances(share)) 

701 src_instance = self.db.share_instance_get( 

702 context, src_instance_id) 

703 src_server_id_equals_current_iteration = ( 

704 src_instance['share_server_id'] == ss['id']) 

705 

706 if (not src_server_id_equals_current_iteration and ( 

707 achieved_gigabytes_limit or achieved_instances_limit)): 

708 available_share_servers.remove(ss) 

709 

710 return available_share_servers 

711 

712 def _provide_share_server_for_share(self, context, share_network_id, 

713 share_instance, snapshot=None, 

714 share_group=None, 

715 create_on_backend=True): 

716 """Gets or creates share_server and updates share with its id. 

717 

718 Active share_server can be deleted if there are no dependent shares 

719 on it. 

720 So we need avoid possibility to delete share_server in time gap 

721 between reaching active state for share_server and setting up 

722 share_server_id for share. It is possible, for example, with first 

723 share creation, which starts share_server creation. 

724 For this purpose used shared lock between this method and the one 

725 with deletion of share_server. 

726 

727 :param context: Current context 

728 :param share_network_id: Share network where existing share server 

729 should be found or created. If 

730 share_network_id is None method use 

731 share_network_id from provided snapshot. 

732 :param share_instance: Share Instance model 

733 :param snapshot: Optional -- Snapshot model 

734 :param create_on_backend: Boolean. If True, driver will be asked to 

735 create the share server if no share server 

736 is available. 

737 

738 :returns: dict, dict -- first value is share_server, that 

739 has been chosen for share schedule. Second value is 

740 share updated with share_server_id. 

741 """ 

742 if not (share_network_id or snapshot): 

743 msg = _("'share_network_id' parameter or 'snapshot'" 

744 " should be provided. ") 

745 raise ValueError(msg) 

746 

747 def error(msg, *args): 

748 LOG.error(msg, *args) 

749 self.db.share_instance_update(context, share_instance['id'], 

750 {'status': constants.STATUS_ERROR}) 

751 parent_share_server = None 

752 parent_share_same_dest = False 

753 if snapshot: 

754 parent_share_server_id = ( 

755 snapshot['share']['instance']['share_server_id']) 

756 try: 

757 parent_share_server = self.db.share_server_get( 

758 context, parent_share_server_id) 

759 except exception.ShareServerNotFound: 

760 with excutils.save_and_reraise_exception(): 

761 error("Parent share server %s does not exist.", 

762 parent_share_server_id) 

763 

764 if parent_share_server['status'] != constants.STATUS_ACTIVE: 

765 error_params = { 

766 'id': parent_share_server_id, 

767 'status': parent_share_server['status'], 

768 } 

769 msg = _("Parent share server %(id)s has invalid status " 

770 "'%(status)s'.") 

771 error(msg, error_params) 

772 raise exception.InvalidShareServer(reason=msg % error_params) 

773 parent_share_same_dest = (snapshot['share']['instance']['host'] 

774 == share_instance['host']) 

775 share_network_subnets = None 

776 if share_network_id: 

777 share_network_subnets = ( 

778 self.db.share_network_subnets_get_all_by_availability_zone_id( 

779 context, share_network_id, 

780 availability_zone_id=share_instance.get( 

781 'availability_zone_id'))) 

782 if not share_network_subnets: 

783 raise exception.ShareNetworkSubnetNotFound( 

784 share_network_subnet_id=None) 

785 elif parent_share_server: 785 ↛ 793line 785 didn't jump to line 793 because the condition on line 785 was always true

786 share_network_subnets = ( 

787 parent_share_server['share_network_subnets']) 

788 

789 # NOTE(felipe_rodrigues): it can retrieve the available share 

790 # servers using one single subnet_id from the availability zone 

791 # subnets, because if the share server has one, it will have 

792 # all subnets on that availability zone. 

793 share_network_subnet_id = share_network_subnets[0]['id'] 

794 

795 def get_available_share_servers(): 

796 if parent_share_server and parent_share_same_dest: 

797 return [parent_share_server] 

798 else: 

799 return ( 

800 self.db 

801 .share_server_get_all_by_host_and_share_subnet_valid( 

802 context, self.host, share_network_subnet_id) 

803 ) 

804 

805 @utils.synchronized("share_manager_%s" % share_network_subnet_id, 

806 external=True) 

807 def _wrapped_provide_share_server_for_share(): 

808 try: 

809 available_share_servers = get_available_share_servers() 

810 except exception.ShareServerNotFound: 

811 available_share_servers = None 

812 

813 # creating from snapshot in the same host must reuse the server, 

814 # so it ignores the server limits. 

815 if available_share_servers and not parent_share_same_dest: 

816 available_share_servers = ( 

817 self._check_share_server_backend_limits( 

818 context, available_share_servers, 

819 share_instance=share_instance)) 

820 

821 encryption_key_ref = share_instance.get('encryption_key_ref') 

822 

823 compatible_share_server = None 

824 if available_share_servers: 

825 try: 

826 compatible_share_server = ( 

827 self.driver.choose_share_server_compatible_with_share( 

828 context, available_share_servers, share_instance, 

829 snapshot=snapshot.instance if snapshot else None, 

830 share_group=share_group, 

831 encryption_key_ref=encryption_key_ref, 

832 ) 

833 ) 

834 except Exception as e: 

835 with excutils.save_and_reraise_exception(): 

836 error("Cannot choose compatible share server: %s", 

837 e) 

838 

839 share_server_should_be_encrypted = ( 

840 (encryption_key_ref and self.driver.encryption_support) and 

841 ("share_server" in self.driver.encryption_support)) 

842 

843 app_cred = None 

844 if not compatible_share_server: 

845 if share_server_should_be_encrypted: 

846 # Create secret_ref ACL for Barbican User 

847 try: 

848 barbican_api.create_secret_access(context, 

849 encryption_key_ref) 

850 LOG.debug('Created Barbican ACL for encryption key ' 

851 'reference %s.', encryption_key_ref) 

852 except Exception as e: 

853 self._delete_encryption_keys_quota(context) 

854 with excutils.save_and_reraise_exception(): 

855 error("Cannot create ACL for Barbican user %s", e) 

856 

857 # Create application credentials for barbican user 

858 try: 

859 app_cred = ( 

860 barbican_api.create_application_credentials( 

861 context, encryption_key_ref).to_dict()) 

862 LOG.debug("Created app cred id %s", app_cred['id']) 

863 except Exception as e: 

864 self._delete_encryption_keys_quota(context) 

865 barbican_api.delete_secret_access(context, 

866 encryption_key_ref) 

867 with excutils.save_and_reraise_exception(): 

868 error("Cannot create application credential: " 

869 "%s", e) 

870 

871 compatible_share_server = self.db.share_server_create( 

872 context, 

873 { 

874 'host': self.host, 

875 'share_network_subnets': share_network_subnets, 

876 'status': constants.STATUS_CREATING, 

877 'security_service_update_support': ( 

878 self.driver.security_service_update_support), 

879 'network_allocation_update_support': ( 

880 self.driver.network_allocation_update_support), 

881 'share_replicas_migration_support': ( 

882 self.driver.share_replicas_migration_support), 

883 'encryption_key_ref': ( 

884 encryption_key_ref if 

885 share_server_should_be_encrypted else None), 

886 'application_credential_id': ( 

887 app_cred['id'] if app_cred else None), 

888 } 

889 ) 

890 else: 

891 if share_server_should_be_encrypted: 891 ↛ 893line 891 didn't jump to line 893 because the condition on line 891 was never true

892 # Get application credentials for barbican user 

893 try: 

894 app_cred = barbican_api.get_application_credentials( 

895 context, 

896 compatible_share_server.get( 

897 'application_credential_id')).to_dict() 

898 LOG.debug('Got app cred id %s', app_cred['id']) 

899 except Exception as e: 

900 with excutils.save_and_reraise_exception(): 

901 error("Cannot get application credential: %s", e) 

902 

903 msg = ("Using share_server %(share_server)s for share instance" 

904 " %(share_instance_id)s") 

905 LOG.debug(msg, { 

906 'share_server': compatible_share_server['id'], 

907 'share_instance_id': share_instance['id'] 

908 }) 

909 

910 share_instance_ref = self.db.share_instance_update( 

911 context, 

912 share_instance['id'], 

913 {'share_server_id': compatible_share_server['id']}, 

914 with_share_data=True 

915 ) 

916 

917 if create_on_backend: 917 ↛ 929line 917 didn't jump to line 929 because the condition on line 917 was always true

918 metadata = self._build_server_metadata( 

919 context, 

920 share_instance['host'], 

921 share_instance['share_type_id'], 

922 app_cred=app_cred, 

923 encryption_key_ref=encryption_key_ref 

924 ) 

925 compatible_share_server = ( 

926 self._create_share_server_in_backend( 

927 context, compatible_share_server, metadata)) 

928 

929 return compatible_share_server, share_instance_ref 

930 

931 return _wrapped_provide_share_server_for_share() 

932 

933 def _build_server_metadata(self, context, host, share_type_id, 

934 app_cred=None, 

935 encryption_key_ref=None): 

936 

937 encryption_key_href = None 

938 if encryption_key_ref: 

939 encryption_key_href = barbican_api.get_secret_href( 

940 context, encryption_key_ref) 

941 LOG.debug("Generated encryption_key_href %s for backend share " 

942 "server.", encryption_key_href) 

943 

944 metadata = { 

945 'request_host': host, 

946 'share_type_id': share_type_id, 

947 'encryption_key_ref': encryption_key_href, 

948 'keystone_url': keystone_url, 

949 } 

950 if app_cred: 

951 metadata.update({ 

952 'application_credential_id': app_cred.get('id'), 

953 'application_credential_secret': encryption_key_ref, 

954 }) 

955 return metadata 

956 

957 def _provide_share_server_for_migration(self, context, 

958 source_share_server, 

959 new_share_network_id, 

960 availability_zone_id, 

961 destination_host, 

962 create_on_backend=True, 

963 server_metadata=None): 

964 """Gets or creates share_server for a migration procedure. 

965 

966 Active share_server can be deleted if there are no dependent shares 

967 on it. 

968 So we need avoid possibility to delete share_server in time gap 

969 between reaching active state for share_server and setting up 

970 share_server_id for share. It is possible, for example, with first 

971 share creation, which starts share_server creation. 

972 For this purpose used shared lock between this method and the one 

973 with deletion of share_server. 

974 

975 :param context: Current context 

976 :param source_share_server: Share server model that will be migrated. 

977 :param new_share_network_id: Share network where existing share server 

978 should be found or created. 

979 :param availability_zone_id: Id of the availability zone where the 

980 new share server will be placed. 

981 :param destination_host: The destination host where the new share 

982 server will be created or retrieved. 

983 :param create_on_backend: Boolean. If True, driver will be asked to 

984 create the share server if no share server is available. 

985 :param server_metadata: dict. Holds some important information that 

986 can help drivers whether to create a new share server or not. 

987 :returns: Share server that has been chosen for share server 

988 migration. 

989 """ 

990 

991 share_network_subnets = ( 

992 self.db.share_network_subnets_get_all_by_availability_zone_id( 

993 context, new_share_network_id, 

994 availability_zone_id=availability_zone_id)) 

995 if not share_network_subnets: 

996 raise exception.ShareNetworkSubnetNotFound( 

997 share_network_subnet_id=None) 

998 

999 server_metadata = {} if not server_metadata else server_metadata 

1000 

1001 @utils.synchronized( 

1002 "share_manager_%s" % share_network_subnets[0]['id'], external=True) 

1003 def _wrapped_provide_share_server_for_migration(): 

1004 destination_share_server = self.db.share_server_create( 

1005 context, 

1006 { 

1007 'host': self.host, 

1008 'share_network_subnets': share_network_subnets, 

1009 'status': constants.STATUS_CREATING, 

1010 'security_service_update_support': ( 

1011 self.driver.security_service_update_support), 

1012 'network_allocation_update_support': ( 

1013 self.driver.network_allocation_update_support), 

1014 'share_replicas_migration_support': ( 

1015 self.driver.share_replicas_migration_support), 

1016 } 

1017 ) 

1018 

1019 msg = ("Using share_server %(share_server)s as destination for " 

1020 "migration.") 

1021 LOG.debug(msg, { 

1022 'share_server': destination_share_server['id'], 

1023 }) 

1024 

1025 if create_on_backend: 1025 ↛ 1039line 1025 didn't jump to line 1039 because the condition on line 1025 was always true

1026 # NOTE(carloss): adding some information about the request, so 

1027 # backends that support share server migration and need to know 

1028 # if the request share server is from a share server migration 

1029 # request can use this metadata to take actions. 

1030 server_metadata['migration_destination'] = True 

1031 server_metadata['request_host'] = destination_host 

1032 server_metadata['source_share_server'] = ( 

1033 source_share_server) 

1034 destination_share_server = ( 

1035 self._create_share_server_in_backend( 

1036 context, destination_share_server, 

1037 metadata=server_metadata)) 

1038 

1039 return destination_share_server 

1040 

1041 return _wrapped_provide_share_server_for_migration() 

1042 

1043 def _create_share_server_in_backend(self, context, share_server, 

1044 metadata): 

1045 """Perform setup_server on backend 

1046 

1047 :param metadata: A dictionary, to be passed to driver's setup_server() 

1048 """ 

1049 

1050 if share_server['status'] == constants.STATUS_CREATING: 

1051 # Create share server on backend with data from db. 

1052 share_server = self._setup_server(context, share_server, metadata) 

1053 LOG.info("Share server created successfully.") 

1054 else: 

1055 LOG.info("Using preexisting share server: " 

1056 "'%(share_server_id)s'", 

1057 {'share_server_id': share_server['id']}) 

1058 return share_server 

1059 

1060 def create_share_server( 

1061 self, context, share_server_id, share_instance_id): 

1062 """Invoked to create a share server in this backend. 

1063 

1064 This method is invoked to create the share server defined in the model 

1065 obtained by the supplied id. 

1066 

1067 :param context: The 'context.RequestContext' object for the request. 

1068 :param share_server_id: The id of the server to be created. 

1069 :param share_instance_id: The id of the share instance 

1070 """ 

1071 share_server = self.db.share_server_get(context, share_server_id) 

1072 share = self.db.share_instance_get( 

1073 context, share_instance_id, with_share_data=True) 

1074 metadata = self._build_server_metadata(context, share['host'], 

1075 share['share_type_id']) 

1076 

1077 self._create_share_server_in_backend(context, share_server, metadata) 

1078 

1079 def provide_share_server(self, context, share_instance_id, 

1080 share_network_id, snapshot_id=None): 

1081 """Invoked to provide a compatible share server. 

1082 

1083 This method is invoked to find a compatible share server among the 

1084 existing ones or create a share server database instance with the share 

1085 server properties that will be used to create the share server later. 

1086 

1087 :param context: The 'context.RequestContext' object for the request. 

1088 :param share_instance_id: The id of the share instance whose model 

1089 attributes will be used to provide the share server. 

1090 :param share_network_id: The id of the share network the share server 

1091 to be provided has to be related to. 

1092 :param snapshot_id: The id of the snapshot to be used to obtain the 

1093 share server if applicable. 

1094 :return: The id of the share server that is being provided. 

1095 """ 

1096 share_instance = self.db.share_instance_get(context, share_instance_id, 

1097 with_share_data=True) 

1098 snapshot_ref = None 

1099 if snapshot_id: 1099 ↛ 1102line 1099 didn't jump to line 1102 because the condition on line 1099 was always true

1100 snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) 

1101 

1102 share_group_ref = None 

1103 if share_instance.get('share_group_id'): 1103 ↛ 1107line 1103 didn't jump to line 1107 because the condition on line 1103 was always true

1104 share_group_ref = self.db.share_group_get( 

1105 context, share_instance['share_group_id']) 

1106 

1107 share_server, share_instance = self._provide_share_server_for_share( 

1108 context, share_network_id, share_instance, snapshot_ref, 

1109 share_group_ref, create_on_backend=False) 

1110 

1111 return share_server['id'] 

1112 

1113 def _provide_share_server_for_share_group(self, context, 

1114 share_network_id, 

1115 share_network_subnets, 

1116 share_group_ref, 

1117 share_group_snapshot=None): 

1118 """Gets or creates share_server and updates share group with its id. 

1119 

1120 Active share_server can be deleted if there are no shares or share 

1121 groups dependent on it. 

1122 

1123 So we need avoid possibility to delete share_server in time gap 

1124 between reaching active state for share_server and setting up 

1125 share_server_id for share group. It is possible, for example, with 

1126 first share group creation, which starts share_server creation. 

1127 For this purpose used shared lock between this method and the one 

1128 with deletion of share_server. 

1129 

1130 :param context: Current context 

1131 :param share_network_id: Share network where existing share server 

1132 should be found or created. 

1133 :param share_network_subnets: Share network subnets where existing 

1134 share server should be found or created. 

1135 :param share_group_ref: Share Group model 

1136 :param share_group_snapshot: Optional -- ShareGroupSnapshot model. If 

1137 supplied, driver will use it to choose 

1138 the appropriate share server. 

1139 

1140 :returns: dict, dict -- first value is share_server, that 

1141 has been chosen for share group schedule. 

1142 Second value is share group updated with 

1143 share_server_id. 

1144 """ 

1145 if not share_network_id: 

1146 msg = _("'share_network_id' parameter should be provided. ") 

1147 raise exception.InvalidInput(reason=msg) 

1148 

1149 def error(msg, *args): 

1150 LOG.error(msg, *args) 

1151 self.db.share_group_update( 

1152 context, share_group_ref['id'], 

1153 {'status': constants.STATUS_ERROR}) 

1154 

1155 @utils.synchronized("share_manager_%s" % share_network_id, 

1156 external=True) 

1157 def _wrapped_provide_share_server_for_share_group(): 

1158 # NOTE(felipe_rodrigues): it can retrieve the available share 

1159 # servers using one single subnet_id from the availability zone 

1160 # subnets, because if the share server has one, it will have 

1161 # all subnets on that availability zone. 

1162 share_network_subnet_id = share_network_subnets[0]['id'] 

1163 try: 

1164 available_share_servers = ( 

1165 self.db 

1166 .share_server_get_all_by_host_and_share_subnet_valid( 

1167 context, self.host, share_network_subnet_id)) 

1168 except exception.ShareServerNotFound: 

1169 available_share_servers = None 

1170 

1171 compatible_share_server = None 

1172 

1173 if available_share_servers: 1173 ↛ 1178line 1173 didn't jump to line 1178 because the condition on line 1173 was always true

1174 available_share_servers = ( 

1175 self._check_share_server_backend_limits( 

1176 context, available_share_servers)) 

1177 

1178 choose_share_server = ( 

1179 self.driver.choose_share_server_compatible_with_share_group) 

1180 

1181 if available_share_servers: 1181 ↛ 1192line 1181 didn't jump to line 1192 because the condition on line 1181 was always true

1182 try: 

1183 compatible_share_server = choose_share_server( 

1184 context, available_share_servers, share_group_ref, 

1185 share_group_snapshot=share_group_snapshot, 

1186 ) 

1187 except Exception as e: 

1188 with excutils.save_and_reraise_exception(): 

1189 error("Cannot choose compatible share-server: %s", 

1190 e) 

1191 

1192 if not compatible_share_server: 

1193 compatible_share_server = self.db.share_server_create( 

1194 context, 

1195 { 

1196 'host': self.host, 

1197 'share_network_subnets': share_network_subnets, 

1198 'status': constants.STATUS_CREATING, 

1199 'security_service_update_support': ( 

1200 self.driver.security_service_update_support), 

1201 'network_allocation_update_support': ( 

1202 self.driver.network_allocation_update_support), 

1203 'share_replicas_migration_support': ( 

1204 self.driver.share_replicas_migration_support), 

1205 } 

1206 ) 

1207 

1208 msg = ("Using share_server %(share_server)s for share " 

1209 "group %(group_id)s") 

1210 LOG.debug(msg, { 

1211 'share_server': compatible_share_server['id'], 

1212 'group_id': share_group_ref['id'] 

1213 }) 

1214 

1215 updated_share_group = self.db.share_group_update( 

1216 context, 

1217 share_group_ref['id'], 

1218 {'share_server_id': compatible_share_server['id']}, 

1219 ) 

1220 

1221 if compatible_share_server['status'] == constants.STATUS_CREATING: 

1222 # Create share server on backend with data from db. 

1223 metadata = self._build_server_metadata( 

1224 context, 

1225 share_group_ref['host'], 

1226 share_group_ref['share_types'][0]['share_type_id']) 

1227 compatible_share_server = self._setup_server( 

1228 context, compatible_share_server, metadata) 

1229 LOG.info("Share server created successfully.") 

1230 else: 

1231 LOG.info("Used preexisting share server " 

1232 "'%(share_server_id)s'", 

1233 {'share_server_id': compatible_share_server['id']}) 

1234 return compatible_share_server, updated_share_group 

1235 

1236 return _wrapped_provide_share_server_for_share_group() 

1237 

1238 def _get_share_server(self, context, share_instance): 

1239 if share_instance['share_server_id']: 

1240 return self.db.share_server_get( 

1241 context, share_instance['share_server_id']) 

1242 else: 

1243 return None 

1244 

1245 @utils.require_driver_initialized 

1246 def connection_get_info(self, context, share_instance_id): 

1247 share_instance = self.db.share_instance_get( 

1248 context, share_instance_id, with_share_data=True) 

1249 

1250 share_server = None 

1251 if share_instance.get('share_server_id'): 1251 ↛ 1255line 1251 didn't jump to line 1255 because the condition on line 1251 was always true

1252 share_server = self.db.share_server_get( 

1253 context, share_instance['share_server_id']) 

1254 

1255 return self.driver.connection_get_info(context, share_instance, 

1256 share_server) 

1257 

1258 def _migration_start_driver( 

1259 self, context, share_ref, src_share_instance, dest_host, writable, 

1260 preserve_metadata, nondisruptive, preserve_snapshots, 

1261 new_share_network_id, new_az_id, new_share_type_id): 

1262 

1263 share_server = self._get_share_server(context, src_share_instance) 

1264 

1265 request_spec, dest_share_instance = ( 

1266 self.share_api.create_share_instance_and_get_request_spec( 

1267 context, share_ref, new_az_id, None, dest_host, 

1268 new_share_network_id, new_share_type_id)) 

1269 

1270 self.db.share_instance_update( 

1271 context, dest_share_instance['id'], 

1272 {'status': constants.STATUS_MIGRATING_TO}) 

1273 

1274 # refresh and obtain proxified properties 

1275 dest_share_instance = self.db.share_instance_get( 

1276 context, dest_share_instance['id'], with_share_data=True) 

1277 

1278 helper = migration.ShareMigrationHelper( 

1279 context, self.db, self.access_helper) 

1280 

1281 try: 

1282 if dest_share_instance['share_network_id']: 

1283 # NOTE(carloss): For a nondisruptive migration request, we must 

1284 # not change the share server, otherwise the share's export 

1285 # location will change, disconnecting the user. Disruptive 

1286 # migration requests the share server from the driver. 

1287 if nondisruptive: 

1288 dest_share_server = self._get_share_server_dict( 

1289 context, share_server) 

1290 dest_share_instance = self.db.share_instance_update( 

1291 context, 

1292 dest_share_instance['id'], 

1293 {'share_server_id': dest_share_server['id']}, 

1294 with_share_data=True 

1295 ) 

1296 else: 

1297 rpcapi = share_rpcapi.ShareAPI() 

1298 

1299 # NOTE(ganso): Obtaining the share_server_id asynchronously 

1300 # so we can wait for it to be ready. 

1301 dest_share_server_id = rpcapi.provide_share_server( 

1302 context, dest_share_instance, 

1303 dest_share_instance['share_network_id']) 

1304 

1305 rpcapi.create_share_server( 

1306 context, dest_share_instance, dest_share_server_id) 

1307 

1308 dest_share_server = helper.wait_for_share_server( 

1309 dest_share_server_id) 

1310 

1311 else: 

1312 dest_share_server = None 

1313 

1314 compatibility = self.driver.migration_check_compatibility( 

1315 context, src_share_instance, dest_share_instance, 

1316 share_server, dest_share_server) 

1317 

1318 if not compatibility.get('compatible'): 

1319 msg = _("Destination host %(host)s is not compatible with " 

1320 "share %(share)s's source backend for driver-assisted " 

1321 "migration.") % { 

1322 'host': dest_host, 

1323 'share': share_ref['id'], 

1324 } 

1325 raise exception.ShareMigrationFailed(reason=msg) 

1326 

1327 if (not compatibility.get('nondisruptive') and 1327 ↛ 1329line 1327 didn't jump to line 1329 because the condition on line 1327 was never true

1328 nondisruptive): 

1329 msg = _("Driver cannot perform a non-disruptive migration of " 

1330 "share %s.") % share_ref['id'] 

1331 raise exception.ShareMigrationFailed(reason=msg) 

1332 

1333 if (not compatibility.get('preserve_metadata') and 

1334 preserve_metadata): 

1335 msg = _("Driver cannot perform migration of share %s while " 

1336 "preserving all metadata.") % share_ref['id'] 

1337 raise exception.ShareMigrationFailed(reason=msg) 

1338 

1339 if not compatibility.get('writable') and writable: 

1340 msg = _("Driver cannot perform migration of share %s while " 

1341 "remaining writable.") % share_ref['id'] 

1342 raise exception.ShareMigrationFailed(reason=msg) 

1343 

1344 if (not compatibility.get('preserve_snapshots') and 

1345 preserve_snapshots): 

1346 msg = _("Driver cannot perform migration of share %s while " 

1347 "preserving snapshots.") % share_ref['id'] 

1348 raise exception.ShareMigrationFailed(reason=msg) 

1349 

1350 snapshot_mapping = {} 

1351 src_snap_instances = [] 

1352 src_snapshots = self.db.share_snapshot_get_all_for_share( 

1353 context, share_ref['id']) 

1354 

1355 if compatibility.get('preserve_snapshots'): 

1356 

1357 # Make sure all snapshots are 'available' 

1358 if any(x['status'] != constants.STATUS_AVAILABLE 

1359 for x in src_snapshots): 

1360 msg = _( 

1361 "All snapshots must have '%(status)s' status to be " 

1362 "migrated by the driver along with share " 

1363 "%(share)s.") % { 

1364 'share': share_ref['id'], 

1365 'status': constants.STATUS_AVAILABLE 

1366 } 

1367 raise exception.ShareMigrationFailed(reason=msg) 

1368 

1369 src_snap_instances = [x.instance for x in src_snapshots] 

1370 

1371 dest_snap_instance_data = { 

1372 'status': constants.STATUS_MIGRATING_TO, 

1373 'progress': '0%', 

1374 'share_instance_id': dest_share_instance['id'], 

1375 } 

1376 

1377 for snap_instance in src_snap_instances: 

1378 snapshot_mapping[snap_instance['id']] = ( 

1379 self.db.share_snapshot_instance_create( 

1380 context, snap_instance['snapshot_id'], 

1381 dest_snap_instance_data)) 

1382 self.db.share_snapshot_instance_update( 

1383 context, snap_instance['id'], 

1384 {'status': constants.STATUS_MIGRATING}) 

1385 

1386 else: 

1387 if src_snapshots: 

1388 msg = _("Driver does not support preserving snapshots, " 

1389 "driver-assisted migration cannot proceed while " 

1390 "share %s has snapshots.") % share_ref['id'] 

1391 raise exception.ShareMigrationFailed(reason=msg) 

1392 

1393 if not compatibility.get('writable'): 

1394 self._cast_access_rules_to_readonly( 

1395 context, src_share_instance, share_server) 

1396 

1397 LOG.debug("Initiating driver migration for share %s.", 

1398 share_ref['id']) 

1399 

1400 self.db.share_update( 

1401 context, share_ref['id'], 

1402 {'task_state': ( 

1403 constants.TASK_STATE_MIGRATION_DRIVER_STARTING)}) 

1404 

1405 self.driver.migration_start( 

1406 context, src_share_instance, dest_share_instance, 

1407 src_snap_instances, snapshot_mapping, share_server, 

1408 dest_share_server) 

1409 

1410 self.db.share_update( 

1411 context, share_ref['id'], 

1412 {'task_state': ( 

1413 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS)}) 

1414 

1415 except Exception: 

1416 # NOTE(ganso): Cleaning up error'ed destination share instance from 

1417 # database. It is assumed that driver cleans up leftovers in 

1418 # backend when migration fails. 

1419 share_types.revert_allocated_share_type_quotas_during_migration( 

1420 context, src_share_instance, 

1421 src_share_instance['share_type_id']) 

1422 self._migration_delete_instance(context, dest_share_instance['id']) 

1423 self._restore_migrating_snapshots_status( 

1424 context, src_share_instance['id']) 

1425 

1426 # NOTE(ganso): Read only access rules and share instance status 

1427 # will be restored in migration_start's except block. 

1428 

1429 # NOTE(ganso): For now source share instance should remain in 

1430 # migrating status for host-assisted migration. 

1431 msg = _("Driver-assisted migration of share %s " 

1432 "failed.") % share_ref['id'] 

1433 LOG.exception(msg) 

1434 raise exception.ShareMigrationFailed(reason=msg) 

1435 

1436 return True 

1437 

1438 def _cast_access_rules_to_readonly(self, context, src_share_instance, 

1439 share_server, dest_host=None): 

1440 self._cast_access_rules_to_readonly_for_server( 

1441 context, [src_share_instance], share_server, dest_host=dest_host) 

1442 

1443 def _cast_access_rules_to_readonly_for_server( 

1444 self, context, src_share_instances, share_server, dest_host=None): 

1445 for src_share_instance in src_share_instances: 

1446 self.db.share_instance_update( 

1447 context, src_share_instance['id'], 

1448 {'cast_rules_to_readonly': True}) 

1449 

1450 # Set all 'applying' or 'active' rules to 'queued_to_apply'. Since 

1451 # the share instance has its cast_rules_to_readonly attribute set 

1452 # to True, existing rules will be cast to read/only. 

1453 acceptable_past_states = (constants.ACCESS_STATE_APPLYING, 

1454 constants.ACCESS_STATE_ACTIVE) 

1455 new_state = constants.ACCESS_STATE_QUEUED_TO_APPLY 

1456 conditionally_change = {k: new_state 

1457 for k in acceptable_past_states} 

1458 self.access_helper.get_and_update_share_instance_access_rules( 

1459 context, share_instance_id=src_share_instance['id'], 

1460 conditionally_change=conditionally_change) 

1461 

1462 src_share_instance_ids = [x.id for x in src_share_instances] 

1463 share_server_id = share_server['id'] if share_server else None 

1464 if dest_host: 1464 ↛ 1465line 1464 didn't jump to line 1465 because the condition on line 1464 was never true

1465 rpcapi = share_rpcapi.ShareAPI() 

1466 rpcapi.update_access_for_instances(context, 

1467 dest_host, 

1468 src_share_instance_ids, 

1469 share_server_id) 

1470 else: 

1471 self.update_access_for_instances(context, src_share_instance_ids, 

1472 share_server_id=share_server_id) 

1473 

1474 for src_share_instance in src_share_instances: 

1475 utils.wait_for_access_update( 

1476 context, self.db, src_share_instance, 

1477 self.migration_wait_access_rules_timeout) 

1478 

1479 def _reset_read_only_access_rules( 

1480 self, context, share_instance_id, supress_errors=True, 

1481 helper=None): 

1482 instance = self.db.share_instance_get(context, share_instance_id, 

1483 with_share_data=True) 

1484 share_server = self._get_share_server(context, instance) 

1485 self._reset_read_only_access_rules_for_server( 

1486 context, [instance], share_server, supress_errors, helper) 

1487 

1488 def _reset_read_only_access_rules_for_server( 

1489 self, context, share_instances, share_server, 

1490 supress_errors=True, helper=None, dest_host=None): 

1491 if helper is None: 1491 ↛ 1495line 1491 didn't jump to line 1495 because the condition on line 1491 was always true

1492 helper = migration.ShareMigrationHelper( 

1493 context, self.db, self.access_helper) 

1494 

1495 instances_to_update = [] 

1496 for share_instance in share_instances: 

1497 instance = self.db.share_instance_get(context, 

1498 share_instance['id'], 

1499 with_share_data=True) 

1500 if instance['cast_rules_to_readonly']: 1500 ↛ 1496line 1500 didn't jump to line 1496 because the condition on line 1500 was always true

1501 update = {'cast_rules_to_readonly': False} 

1502 instances_to_update.append(share_instance) 

1503 

1504 self.db.share_instance_update( 

1505 context, share_instance['id'], update) 

1506 

1507 if instances_to_update: 1507 ↛ exitline 1507 didn't return from function '_reset_read_only_access_rules_for_server' because the condition on line 1507 was always true

1508 if supress_errors: 

1509 helper.cleanup_access_rules(instances_to_update, 

1510 share_server, 

1511 dest_host) 

1512 else: 

1513 helper.revert_access_rules(instances_to_update, 

1514 share_server, 

1515 dest_host) 

1516 

1517 @periodic_task.periodic_task( 

1518 spacing=CONF.migration_driver_continue_update_interval) 

1519 @utils.require_driver_initialized 

1520 def migration_driver_continue(self, context): 

1521 """Invokes driver to continue migration of shares.""" 

1522 

1523 instances = self.db.share_instance_get_all_by_host( 

1524 context, self.host, with_share_data=True) 

1525 

1526 for instance in instances: 

1527 

1528 if instance['status'] != constants.STATUS_MIGRATING: 

1529 continue 

1530 

1531 share = self.db.share_get(context, instance['share_id']) 

1532 

1533 if share['task_state'] == ( 1533 ↛ 1526line 1533 didn't jump to line 1526 because the condition on line 1533 was always true

1534 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS): 

1535 

1536 src_share_instance_id, dest_share_instance_id = ( 

1537 self.share_api.get_migrating_instances(share)) 

1538 

1539 src_share_instance = instance 

1540 

1541 dest_share_instance = self.db.share_instance_get( 

1542 context, dest_share_instance_id, with_share_data=True) 

1543 

1544 src_share_server = self._get_share_server( 

1545 context, src_share_instance) 

1546 

1547 dest_share_server = self._get_share_server( 

1548 context, dest_share_instance) 

1549 

1550 src_snap_instances, snapshot_mappings = ( 

1551 self._get_migrating_snapshots(context, src_share_instance, 

1552 dest_share_instance)) 

1553 

1554 try: 

1555 

1556 finished = self.driver.migration_continue( 

1557 context, src_share_instance, dest_share_instance, 

1558 src_snap_instances, snapshot_mappings, 

1559 src_share_server, dest_share_server) 

1560 

1561 if finished: 

1562 self.db.share_update( 

1563 context, instance['share_id'], 

1564 {'task_state': 

1565 (constants. 

1566 TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)}) 

1567 

1568 LOG.info("Share Migration for share %s completed " 

1569 "first phase successfully.", 

1570 share['id']) 

1571 else: 

1572 share = self.db.share_get( 

1573 context, instance['share_id']) 

1574 

1575 if (share['task_state'] == 1575 ↛ 1526line 1575 didn't jump to line 1526 because the condition on line 1575 was always true

1576 constants.TASK_STATE_MIGRATION_CANCELLED): 

1577 LOG.warning( 

1578 "Share Migration for share %s was cancelled.", 

1579 share['id']) 

1580 

1581 except Exception: 

1582 

1583 (share_types. 

1584 revert_allocated_share_type_quotas_during_migration( 

1585 context, src_share_instance, 

1586 dest_share_instance['share_type_id'])) 

1587 # NOTE(ganso): Cleaning up error'ed destination share 

1588 # instance from database. It is assumed that driver cleans 

1589 # up leftovers in backend when migration fails. 

1590 self._migration_delete_instance( 

1591 context, dest_share_instance['id']) 

1592 self._restore_migrating_snapshots_status( 

1593 context, src_share_instance['id']) 

1594 self._reset_read_only_access_rules( 

1595 context, src_share_instance_id) 

1596 self.db.share_instance_update( 

1597 context, src_share_instance_id, 

1598 {'status': constants.STATUS_AVAILABLE}) 

1599 

1600 self.db.share_update( 

1601 context, instance['share_id'], 

1602 {'task_state': constants.TASK_STATE_MIGRATION_ERROR}) 

1603 msg = _("Driver-assisted migration of share %s " 

1604 "failed.") % share['id'] 

1605 LOG.exception(msg) 

1606 

1607 def _get_migrating_snapshots( 

1608 self, context, src_share_instance, dest_share_instance): 

1609 

1610 dest_snap_instances = ( 

1611 self.db.share_snapshot_instance_get_all_with_filters( 

1612 context, 

1613 {'share_instance_ids': [dest_share_instance['id']]})) 

1614 

1615 snapshot_mappings = {} 

1616 src_snap_instances = [] 

1617 if len(dest_snap_instances) > 0: 

1618 src_snap_instances = ( 

1619 self.db.share_snapshot_instance_get_all_with_filters( 

1620 context, 

1621 {'share_instance_ids': [src_share_instance['id']]})) 

1622 for snap in src_snap_instances: 

1623 dest_snap_instance = next( 

1624 x for x in dest_snap_instances 

1625 if snap['snapshot_id'] == x['snapshot_id']) 

1626 snapshot_mappings[snap['id']] = dest_snap_instance 

1627 

1628 return src_snap_instances, snapshot_mappings 

1629 

1630 def _restore_migrating_snapshots_status( 

1631 self, context, src_share_instance_id, 

1632 errored_dest_instance_id=None): 

1633 filters = {'share_instance_ids': [src_share_instance_id]} 

1634 status = constants.STATUS_AVAILABLE 

1635 if errored_dest_instance_id: 

1636 filters['share_instance_ids'].append(errored_dest_instance_id) 

1637 status = constants.STATUS_ERROR 

1638 snap_instances = ( 

1639 self.db.share_snapshot_instance_get_all_with_filters( 

1640 context, filters) 

1641 ) 

1642 for instance in snap_instances: 

1643 if instance['status'] == constants.STATUS_MIGRATING: 

1644 self.db.share_snapshot_instance_update( 

1645 context, instance['id'], {'status': status}) 

1646 elif (errored_dest_instance_id and 1646 ↛ 1642line 1646 didn't jump to line 1642 because the condition on line 1646 was always true

1647 instance['status'] == constants.STATUS_MIGRATING_TO): 

1648 self.db.share_snapshot_instance_update( 

1649 context, instance['id'], {'status': status}) 

1650 

1651 @utils.require_driver_initialized 

1652 def migration_start( 

1653 self, context, share_id, dest_host, force_host_assisted_migration, 

1654 preserve_metadata, writable, nondisruptive, preserve_snapshots, 

1655 new_share_network_id=None, new_share_type_id=None): 

1656 """Migrates a share from current host to another host.""" 

1657 LOG.debug("Entered migration_start method for share %s.", share_id) 

1658 

1659 self.db.share_update( 

1660 context, share_id, 

1661 {'task_state': constants.TASK_STATE_MIGRATION_IN_PROGRESS}) 

1662 

1663 share_ref = self.db.share_get(context, share_id) 

1664 share_instance = self._get_share_instance(context, share_ref) 

1665 success = False 

1666 

1667 host_value = share_utils.extract_host(dest_host) 

1668 service = self.db.service_get_by_args( 

1669 context, host_value, 'manila-share') 

1670 new_az_id = service['availability_zone_id'] 

1671 

1672 if not force_host_assisted_migration: 

1673 

1674 try: 

1675 success = self._migration_start_driver( 

1676 context, share_ref, share_instance, dest_host, writable, 

1677 preserve_metadata, nondisruptive, preserve_snapshots, 

1678 new_share_network_id, new_az_id, new_share_type_id) 

1679 

1680 except Exception as e: 

1681 if not isinstance(e, NotImplementedError): 1681 ↛ 1686line 1681 didn't jump to line 1686 because the condition on line 1681 was always true

1682 LOG.exception( 

1683 ("The driver could not migrate the share %(shr)s"), 

1684 {'shr': share_id}) 

1685 

1686 try: 

1687 

1688 if not success: 

1689 if (writable or preserve_metadata or nondisruptive or 

1690 preserve_snapshots): 

1691 msg = _("Migration for share %s could not be " 

1692 "performed because host-assisted migration is not " 

1693 "allowed when share must remain writable, " 

1694 "preserve snapshots and/or file metadata or be " 

1695 "performed nondisruptively.") % share_id 

1696 

1697 raise exception.ShareMigrationFailed(reason=msg) 

1698 

1699 # We only handle shares without snapshots for now 

1700 snaps = self.db.share_snapshot_get_all_for_share( 

1701 context, share_id) 

1702 if snaps: 

1703 msg = _("Share %s must not have snapshots in order to " 

1704 "perform a host-assisted migration.") % share_id 

1705 raise exception.ShareMigrationFailed(reason=msg) 

1706 

1707 LOG.debug("Starting host-assisted migration " 

1708 "for share %s.", share_id) 

1709 

1710 self.db.share_update( 

1711 context, share_id, 

1712 {'task_state': constants.TASK_STATE_MIGRATION_IN_PROGRESS}) 

1713 

1714 self._migration_start_host_assisted( 

1715 context, share_ref, share_instance, dest_host, 

1716 new_share_network_id, new_az_id, new_share_type_id) 

1717 

1718 except Exception: 

1719 msg = _("Host-assisted migration failed for share %s.") % share_id 

1720 LOG.exception(msg) 

1721 self.db.share_update( 

1722 context, share_id, 

1723 {'task_state': constants.TASK_STATE_MIGRATION_ERROR}) 

1724 self._reset_read_only_access_rules( 

1725 context, share_instance['id']) 

1726 self.db.share_instance_update( 

1727 context, share_instance['id'], 

1728 {'status': constants.STATUS_AVAILABLE}) 

1729 

1730 raise exception.ShareMigrationFailed(reason=msg) 

1731 

1732 def _migration_start_host_assisted( 

1733 self, context, share, src_share_instance, dest_host, 

1734 new_share_network_id, new_az_id, new_share_type_id): 

1735 

1736 rpcapi = share_rpcapi.ShareAPI() 

1737 

1738 helper = migration.ShareMigrationHelper( 

1739 context, self.db, self.access_helper) 

1740 

1741 share_server = self._get_share_server(context.elevated(), 

1742 src_share_instance) 

1743 

1744 self._cast_access_rules_to_readonly( 

1745 context, src_share_instance, share_server) 

1746 

1747 try: 

1748 dest_share_instance = helper.create_instance_and_wait( 

1749 share, dest_host, new_share_network_id, new_az_id, 

1750 new_share_type_id) 

1751 

1752 self.db.share_instance_update( 

1753 context, dest_share_instance['id'], 

1754 {'status': constants.STATUS_MIGRATING_TO}) 

1755 

1756 except Exception: 

1757 msg = _("Failed to create instance on destination " 

1758 "backend during migration of share %s.") % share['id'] 

1759 LOG.exception(msg) 

1760 raise exception.ShareMigrationFailed(reason=msg) 

1761 

1762 ignore_list = self.driver.configuration.safe_get( 

1763 'migration_ignore_files') 

1764 

1765 data_rpc = data_rpcapi.DataAPI() 

1766 

1767 try: 

1768 src_connection_info = self.driver.connection_get_info( 

1769 context, src_share_instance, share_server) 

1770 

1771 dest_connection_info = rpcapi.connection_get_info( 

1772 context, dest_share_instance) 

1773 

1774 LOG.debug("Time to start copying in migration" 

1775 " for share %s.", share['id']) 

1776 

1777 data_rpc.migration_start( 

1778 context, share['id'], ignore_list, src_share_instance['id'], 

1779 dest_share_instance['id'], src_connection_info, 

1780 dest_connection_info) 

1781 

1782 except Exception: 

1783 msg = _("Failed to obtain migration info from backends or" 

1784 " invoking Data Service for migration of " 

1785 "share %s.") % share['id'] 

1786 LOG.exception(msg) 

1787 helper.cleanup_new_instance(dest_share_instance) 

1788 raise exception.ShareMigrationFailed(reason=msg) 

1789 

1790 def _migration_complete_driver( 

1791 self, context, share_ref, src_share_instance, dest_share_instance): 

1792 

1793 share_server = self._get_share_server(context, src_share_instance) 

1794 dest_share_server = self._get_share_server( 

1795 context, dest_share_instance) 

1796 

1797 self.db.share_update( 

1798 context, share_ref['id'], 

1799 {'task_state': constants.TASK_STATE_MIGRATION_COMPLETING}) 

1800 

1801 src_snap_instances, snapshot_mappings = ( 

1802 self._get_migrating_snapshots(context, src_share_instance, 

1803 dest_share_instance)) 

1804 

1805 data_updates = self.driver.migration_complete( 

1806 context, src_share_instance, dest_share_instance, 

1807 src_snap_instances, snapshot_mappings, share_server, 

1808 dest_share_server) or {} 

1809 

1810 if data_updates.get('export_locations'): 1810 ↛ 1815line 1810 didn't jump to line 1815 because the condition on line 1810 was always true

1811 self.db.export_locations_update( 

1812 context, dest_share_instance['id'], 

1813 data_updates['export_locations']) 

1814 

1815 snapshot_updates = data_updates.get('snapshot_updates') or {} 

1816 

1817 dest_extra_specs = self._get_extra_specs_from_share_type( 

1818 context, dest_share_instance['share_type_id']) 

1819 

1820 for src_snap_ins, dest_snap_ins in snapshot_mappings.items(): 

1821 model_update = snapshot_updates.get(dest_snap_ins['id']) or {} 

1822 snapshot_export_locations = model_update.pop( 

1823 'export_locations', []) 

1824 

1825 model_update['status'] = constants.STATUS_AVAILABLE 

1826 model_update['progress'] = '100%' 

1827 self.db.share_snapshot_instance_update( 

1828 context, dest_snap_ins['id'], model_update) 

1829 

1830 if dest_extra_specs['mount_snapshot_support']: 

1831 

1832 for el in snapshot_export_locations: 

1833 values = { 

1834 'share_snapshot_instance_id': dest_snap_ins['id'], 

1835 'path': el['path'], 

1836 'is_admin_only': el['is_admin_only'], 

1837 } 

1838 self.db.share_snapshot_instance_export_location_create( 

1839 context, values) 

1840 

1841 helper = migration.ShareMigrationHelper( 

1842 context, self.db, self.access_helper) 

1843 

1844 helper.apply_new_access_rules(dest_share_instance, share_ref['id']) 

1845 

1846 self._migration_complete_instance(context, share_ref, 

1847 src_share_instance['id'], 

1848 dest_share_instance['id']) 

1849 

1850 share_types.revert_allocated_share_type_quotas_during_migration( 

1851 context, dest_share_instance, src_share_instance['share_type_id'], 

1852 allow_deallocate_from_current_type=True) 

1853 

1854 self._migration_delete_instance(context, src_share_instance['id']) 

1855 

1856 def _migration_complete_instance(self, context, share_ref, 

1857 src_instance_id, dest_instance_id): 

1858 dest_updates = { 

1859 'status': constants.STATUS_AVAILABLE, 

1860 'progress': '100%' 

1861 } 

1862 if share_ref.get('replication_type'): 

1863 dest_updates['replica_state'] = constants.REPLICA_STATE_ACTIVE 

1864 

1865 self.db.share_instance_update(context, dest_instance_id, dest_updates) 

1866 

1867 self.db.share_instance_update(context, src_instance_id, 

1868 {'status': constants.STATUS_INACTIVE}) 

1869 

1870 def _migration_delete_instance(self, context, instance_id): 

1871 

1872 # refresh the share instance model 

1873 share_instance = self.db.share_instance_get( 

1874 context, instance_id, with_share_data=True) 

1875 

1876 rules = self.access_helper.get_and_update_share_instance_access_rules( 

1877 context, share_instance_id=instance_id) 

1878 

1879 self.access_helper.delete_share_instance_access_rules( 

1880 context, rules, instance_id) 

1881 

1882 snap_instances = self.db.share_snapshot_instance_get_all_with_filters( 

1883 context, {'share_instance_ids': [instance_id]}) 

1884 

1885 for instance in snap_instances: 

1886 self.db.share_snapshot_instance_delete(context, instance['id']) 

1887 

1888 self.db.share_instance_delete(context, instance_id) 

1889 LOG.info("Share instance %s: deleted successfully.", 

1890 instance_id) 

1891 

1892 self._check_delete_share_server(context, share_instance=share_instance) 

1893 

1894 @utils.require_driver_initialized 

1895 def migration_complete(self, context, src_instance_id, dest_instance_id): 

1896 

1897 src_share_instance = self.db.share_instance_get( 

1898 context, src_instance_id, with_share_data=True) 

1899 dest_share_instance = self.db.share_instance_get( 

1900 context, dest_instance_id, with_share_data=True) 

1901 

1902 share_ref = self.db.share_get(context, src_share_instance['share_id']) 

1903 

1904 LOG.info("Received request to finish Share Migration for " 

1905 "share %s.", share_ref['id']) 

1906 

1907 if share_ref['task_state'] == ( 

1908 constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE): 

1909 

1910 try: 

1911 self._migration_complete_driver( 

1912 context, share_ref, src_share_instance, 

1913 dest_share_instance) 

1914 

1915 except Exception: 

1916 msg = _("Driver migration completion failed for" 

1917 " share %s.") % share_ref['id'] 

1918 LOG.exception(msg) 

1919 

1920 # NOTE(ganso): If driver fails during migration-complete, 

1921 # all instances are set to error and it is up to the admin 

1922 # to fix the problem to either complete migration 

1923 # manually or clean it up. At this moment, data 

1924 # preservation at the source backend cannot be 

1925 # guaranteed. 

1926 

1927 self._restore_migrating_snapshots_status( 

1928 context, src_share_instance['id'], 

1929 errored_dest_instance_id=dest_share_instance['id']) 

1930 self.db.share_instance_update( 

1931 context, src_instance_id, 

1932 {'status': constants.STATUS_ERROR}) 

1933 self.db.share_instance_update( 

1934 context, dest_instance_id, 

1935 {'status': constants.STATUS_ERROR}) 

1936 self.db.share_update( 

1937 context, share_ref['id'], 

1938 {'task_state': constants.TASK_STATE_MIGRATION_ERROR}) 

1939 # NOTE(carloss): No need to deallocate quotas allocated during 

1940 # the migration request, since both share instances still exist 

1941 # even they are set to an error state. 

1942 raise exception.ShareMigrationFailed(reason=msg) 

1943 else: 

1944 try: 

1945 self._migration_complete_host_assisted( 

1946 context, share_ref, src_instance_id, 

1947 dest_instance_id) 

1948 except Exception: 

1949 msg = _("Host-assisted migration completion failed for" 

1950 " share %s.") % share_ref['id'] 

1951 LOG.exception(msg) 

1952 # NOTE(carloss): No need to deallocate quotas allocated during 

1953 # the migration request, since both source and destination 

1954 # instances will still exist 

1955 self.db.share_update( 

1956 context, share_ref['id'], 

1957 {'task_state': constants.TASK_STATE_MIGRATION_ERROR}) 

1958 self.db.share_instance_update( 

1959 context, src_instance_id, 

1960 {'status': constants.STATUS_AVAILABLE}) 

1961 raise exception.ShareMigrationFailed(reason=msg) 

1962 

1963 model_update = self._get_extra_specs_from_share_type( 

1964 context, dest_share_instance['share_type_id']) 

1965 

1966 model_update['task_state'] = constants.TASK_STATE_MIGRATION_SUCCESS 

1967 

1968 self.db.share_update( 

1969 context, dest_share_instance['share_id'], model_update) 

1970 

1971 LOG.info("Share Migration for share %s" 

1972 " completed successfully.", share_ref['id']) 

1973 

1974 def _get_extra_specs_from_share_type(self, context, share_type_id): 

1975 

1976 share_type = share_types.get_share_type(context, share_type_id) 

1977 

1978 return self.share_api.get_share_attributes_from_share_type(share_type) 

1979 

1980 def _migration_complete_host_assisted(self, context, share_ref, 

1981 src_instance_id, dest_instance_id): 

1982 

1983 src_share_instance = self.db.share_instance_get( 

1984 context, src_instance_id, with_share_data=True) 

1985 dest_share_instance = self.db.share_instance_get( 

1986 context, dest_instance_id, with_share_data=True) 

1987 

1988 helper = migration.ShareMigrationHelper( 

1989 context, self.db, self.access_helper) 

1990 

1991 task_state = share_ref['task_state'] 

1992 if task_state in (constants.TASK_STATE_DATA_COPYING_ERROR, 

1993 constants.TASK_STATE_DATA_COPYING_CANCELLED): 

1994 msg = _("Data copy of host assisted migration for share %s has not" 

1995 " completed successfully.") % share_ref['id'] 

1996 LOG.warning(msg) 

1997 helper.cleanup_new_instance(dest_share_instance) 

1998 cancelled = ( 

1999 task_state == constants.TASK_STATE_DATA_COPYING_CANCELLED) 

2000 suppress_errors = True 

2001 if cancelled: 

2002 suppress_errors = False 

2003 self._reset_read_only_access_rules( 

2004 context, src_instance_id, 

2005 supress_errors=suppress_errors, helper=helper) 

2006 self.db.share_instance_update( 

2007 context, src_instance_id, 

2008 {'status': constants.STATUS_AVAILABLE}) 

2009 if cancelled: 

2010 self.db.share_update( 

2011 context, share_ref['id'], 

2012 {'task_state': constants.TASK_STATE_MIGRATION_CANCELLED}) 

2013 

2014 LOG.info("Share Migration for share %s" 

2015 " was cancelled.", share_ref['id']) 

2016 return 

2017 else: 

2018 raise exception.ShareMigrationFailed(reason=msg) 

2019 

2020 elif task_state != constants.TASK_STATE_DATA_COPYING_COMPLETED: 

2021 msg = _("Data copy for migration of share %s has not completed" 

2022 " yet.") % share_ref['id'] 

2023 LOG.error(msg) 

2024 raise exception.ShareMigrationFailed(reason=msg) 

2025 

2026 self.db.share_update( 

2027 context, share_ref['id'], 

2028 {'task_state': constants.TASK_STATE_MIGRATION_COMPLETING}) 

2029 

2030 try: 

2031 helper.apply_new_access_rules(dest_share_instance, share_ref['id']) 

2032 except Exception: 

2033 msg = _("Failed to apply new access rules during migration " 

2034 "of share %s.") % share_ref['id'] 

2035 LOG.exception(msg) 

2036 helper.cleanup_new_instance(dest_share_instance) 

2037 self._reset_read_only_access_rules( 

2038 context, src_instance_id, helper=helper, 

2039 supress_errors=True) 

2040 self.db.share_instance_update( 

2041 context, src_instance_id, 

2042 {'status': constants.STATUS_AVAILABLE}) 

2043 

2044 raise exception.ShareMigrationFailed(reason=msg) 

2045 

2046 self._migration_complete_instance(context, share_ref, 

2047 src_share_instance['id'], 

2048 dest_share_instance['id']) 

2049 

2050 # NOTE(carloss): Won't revert allocated quotas for the share type here 

2051 # because the delete_instance_and_wait method will end up calling the 

2052 # delete_share_instance method here in the share manager. When the 

2053 # share instance deletion is requested in the share manager, Manila 

2054 # itself will take care of deallocating the existing quotas for the 

2055 # share instance 

2056 helper.delete_instance_and_wait(src_share_instance) 

2057 

2058 @utils.require_driver_initialized 

2059 def migration_cancel(self, context, src_instance_id, dest_instance_id): 

2060 

2061 src_share_instance = self.db.share_instance_get( 

2062 context, src_instance_id, with_share_data=True) 

2063 dest_share_instance = self.db.share_instance_get( 

2064 context, dest_instance_id, with_share_data=True) 

2065 

2066 share_ref = self.db.share_get(context, src_share_instance['share_id']) 

2067 

2068 if share_ref['task_state'] not in ( 

2069 constants.TASK_STATE_DATA_COPYING_COMPLETED, 

2070 constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE, 

2071 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS): 

2072 msg = _("Migration of share %s cannot be cancelled at this " 

2073 "moment.") % share_ref['id'] 

2074 raise exception.InvalidShare(reason=msg) 

2075 

2076 share_server = self._get_share_server(context, src_share_instance) 

2077 

2078 dest_share_server = self._get_share_server( 

2079 context, dest_share_instance) 

2080 

2081 helper = migration.ShareMigrationHelper( 

2082 context, self.db, self.access_helper) 

2083 

2084 if share_ref['task_state'] == ( 

2085 constants.TASK_STATE_DATA_COPYING_COMPLETED): 

2086 

2087 self.db.share_instance_update( 

2088 context, dest_share_instance['id'], 

2089 {'status': constants.STATUS_INACTIVE}) 

2090 

2091 helper.cleanup_new_instance(dest_share_instance) 

2092 

2093 else: 

2094 

2095 src_snap_instances, snapshot_mappings = ( 

2096 self._get_migrating_snapshots(context, src_share_instance, 

2097 dest_share_instance)) 

2098 

2099 self.driver.migration_cancel( 

2100 context, src_share_instance, dest_share_instance, 

2101 src_snap_instances, snapshot_mappings, share_server, 

2102 dest_share_server) 

2103 

2104 self._migration_delete_instance(context, dest_share_instance['id']) 

2105 self._restore_migrating_snapshots_status( 

2106 context, src_share_instance['id']) 

2107 

2108 self._reset_read_only_access_rules( 

2109 context, src_instance_id, supress_errors=False, 

2110 helper=helper) 

2111 

2112 self.db.share_instance_update( 

2113 context, src_instance_id, 

2114 {'status': constants.STATUS_AVAILABLE}) 

2115 

2116 self.db.share_update( 

2117 context, share_ref['id'], 

2118 {'task_state': constants.TASK_STATE_MIGRATION_CANCELLED}) 

2119 

2120 share_types.revert_allocated_share_type_quotas_during_migration( 

2121 context, src_share_instance, dest_share_instance['share_type_id']) 

2122 

2123 LOG.info("Share Migration for share %s" 

2124 " was cancelled.", share_ref['id']) 

2125 

2126 @utils.require_driver_initialized 

2127 def migration_get_progress(self, context, src_instance_id, 

2128 dest_instance_id): 

2129 

2130 src_share_instance = self.db.share_instance_get( 

2131 context, src_instance_id, with_share_data=True) 

2132 dest_share_instance = self.db.share_instance_get( 

2133 context, dest_instance_id, with_share_data=True) 

2134 

2135 share_ref = self.db.share_get(context, src_share_instance['share_id']) 

2136 

2137 # Confirm that it is driver migration scenario 

2138 if share_ref['task_state'] != ( 

2139 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS): 

2140 msg = _("Driver is not performing migration for" 

2141 " share %s at this moment.") % share_ref['id'] 

2142 raise exception.InvalidShare(reason=msg) 

2143 

2144 share_server = None 

2145 if share_ref.instance.get('share_server_id'): 2145 ↛ 2149line 2145 didn't jump to line 2149 because the condition on line 2145 was always true

2146 share_server = self.db.share_server_get( 

2147 context, src_share_instance['share_server_id']) 

2148 

2149 dest_share_server = None 

2150 if dest_share_instance.get('share_server_id'): 2150 ↛ 2154line 2150 didn't jump to line 2154 because the condition on line 2150 was always true

2151 dest_share_server = self.db.share_server_get( 

2152 context, dest_share_instance['share_server_id']) 

2153 

2154 src_snap_instances, snapshot_mappings = ( 

2155 self._get_migrating_snapshots(context, src_share_instance, 

2156 dest_share_instance)) 

2157 

2158 return self.driver.migration_get_progress( 

2159 context, src_share_instance, dest_share_instance, 

2160 src_snap_instances, snapshot_mappings, share_server, 

2161 dest_share_server) 

2162 

2163 def _get_share_instance(self, context, share): 

2164 if isinstance(share, str): 

2165 id = share 

2166 else: 

2167 id = share.instance['id'] 

2168 return self.db.share_instance_get(context, id, with_share_data=True) 

2169 

2170 @add_hooks 

2171 @utils.require_driver_initialized 

2172 def create_share_instance(self, context, share_instance_id, 

2173 request_spec=None, filter_properties=None, 

2174 snapshot_id=None): 

2175 """Creates a share instance.""" 

2176 context = context.elevated() 

2177 

2178 share_instance = self._get_share_instance(context, share_instance_id) 

2179 share_id = share_instance.get('share_id') 

2180 share_network_id = share_instance.get('share_network_id') 

2181 share = self.db.share_get(context, share_id) 

2182 

2183 self._notify_about_share_usage(context, share, 

2184 share_instance, "create.start") 

2185 

2186 if not share_instance['availability_zone']: 

2187 share_instance = self.db.share_instance_update( 

2188 context, share_instance_id, 

2189 {'availability_zone': self.availability_zone}, 

2190 with_share_data=True 

2191 ) 

2192 

2193 if share_network_id and not self.driver.driver_handles_share_servers: 

2194 self.db.share_instance_update( 

2195 context, share_instance_id, {'status': constants.STATUS_ERROR}) 

2196 self.message_api.create( 

2197 context, 

2198 message_field.Action.CREATE, 

2199 share['project_id'], 

2200 resource_type=message_field.Resource.SHARE, 

2201 resource_id=share_id, 

2202 detail=message_field.Detail.UNEXPECTED_NETWORK) 

2203 raise exception.ManilaException(_( 

2204 "Creation of share instance %s failed: driver does not expect " 

2205 "share-network to be provided with current " 

2206 "configuration.") % share_instance_id) 

2207 

2208 if snapshot_id is not None: 

2209 snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) 

2210 parent_share_server_id = ( 

2211 snapshot_ref['share']['instance']['share_server_id']) 

2212 else: 

2213 snapshot_ref = None 

2214 parent_share_server_id = None 

2215 

2216 share_group_ref = None 

2217 if share_instance.get('share_group_id'): 2217 ↛ 2218line 2217 didn't jump to line 2218 because the condition on line 2217 was never true

2218 share_group_ref = self.db.share_group_get( 

2219 context, share_instance['share_group_id']) 

2220 

2221 if share_network_id or parent_share_server_id: 

2222 try: 

2223 share_server, share_instance = ( 

2224 self._provide_share_server_for_share( 

2225 context, share_network_id, share_instance, 

2226 snapshot=snapshot_ref, 

2227 share_group=share_group_ref, 

2228 ) 

2229 ) 

2230 except exception.PortLimitExceeded: 

2231 with excutils.save_and_reraise_exception(): 

2232 error = ("Creation of share instance %s failed: " 

2233 "failed to allocate network") 

2234 LOG.error(error, share_instance_id) 

2235 self.db.share_instance_update( 

2236 context, share_instance_id, 

2237 {'status': constants.STATUS_ERROR} 

2238 ) 

2239 self.message_api.create( 

2240 context, 

2241 message_field.Action.CREATE, 

2242 share['project_id'], 

2243 resource_type=message_field.Resource.SHARE, 

2244 resource_id=share_id, 

2245 detail=(message_field.Detail 

2246 .SHARE_NETWORK_PORT_QUOTA_LIMIT_EXCEEDED)) 

2247 except exception.SecurityServiceFailedAuth: 

2248 with excutils.save_and_reraise_exception(): 

2249 error = ("Provision of share server failed: " 

2250 "failed to authenticate user " 

2251 "against security server.") 

2252 LOG.error(error) 

2253 self.db.share_instance_update( 

2254 context, share_instance_id, 

2255 {'status': constants.STATUS_ERROR} 

2256 ) 

2257 self.message_api.create( 

2258 context, 

2259 message_field.Action.CREATE, 

2260 share['project_id'], 

2261 resource_type=message_field.Resource.SHARE, 

2262 resource_id=share_id, 

2263 detail=(message_field.Detail 

2264 .SECURITY_SERVICE_FAILED_AUTH)) 

2265 except exception.IpAddressGenerationFailureClient: 

2266 with excutils.save_and_reraise_exception(): 

2267 error = ("Creation of share instance %s failed: " 

2268 "No Free IP's in neutron subnet.") 

2269 LOG.error(error, share_instance_id) 

2270 self.db.share_instance_update( 

2271 context, share_instance_id, 

2272 {'status': constants.STATUS_ERROR} 

2273 ) 

2274 self.message_api.create( 

2275 context, 

2276 message_field.Action.CREATE, 

2277 share['project_id'], 

2278 resource_type=message_field.Resource.SHARE, 

2279 resource_id=share_id, 

2280 detail=message_field.Detail.NEUTRON_SUBNET_FULL) 

2281 except Exception: 

2282 with excutils.save_and_reraise_exception(): 

2283 error = ("Creation of share instance %s failed: " 

2284 "failed to get share server.") 

2285 LOG.error(error, share_instance_id) 

2286 self.db.share_instance_update( 

2287 context, share_instance_id, 

2288 {'status': constants.STATUS_ERROR} 

2289 ) 

2290 self.message_api.create( 

2291 context, 

2292 message_field.Action.CREATE, 

2293 share['project_id'], 

2294 resource_type=message_field.Resource.SHARE, 

2295 resource_id=share_id, 

2296 detail=message_field.Detail.NO_SHARE_SERVER) 

2297 

2298 else: 

2299 share_server = None 

2300 

2301 if share_network_id and self.driver.driver_handles_share_servers: 

2302 proto = share_instance.get('share_proto').lower() 

2303 ret_types = ( 

2304 self.driver.dhss_mandatory_security_service_association.get( 

2305 proto)) 

2306 if ret_types: 

2307 share_network = self.db.share_network_get(context, 

2308 share_network_id) 

2309 share_network_ss = [] 

2310 for security_service in share_network['security_services']: 

2311 share_network_ss.append(security_service['type'].lower()) 

2312 for types in ret_types: 2312 ↛ 2331line 2312 didn't jump to line 2331 because the loop on line 2312 didn't complete

2313 if types not in share_network_ss: 

2314 self.db.share_instance_update( 

2315 context, share_instance_id, 

2316 {'status': constants.STATUS_ERROR} 

2317 ) 

2318 self.message_api.create( 

2319 context, 

2320 message_field.Action.CREATE, 

2321 share['project_id'], 

2322 resource_type=message_field.Resource.SHARE, 

2323 resource_id=share_id, 

2324 detail=(message_field.Detail 

2325 .MISSING_SECURITY_SERVICE)) 

2326 raise exception.InvalidRequest(_( 

2327 "Share network security service association is " 

2328 "mandatory for protocol %s.") % 

2329 share_instance.get('share_proto')) 

2330 

2331 status = constants.STATUS_AVAILABLE 

2332 try: 

2333 if snapshot_ref: 

2334 # NOTE(dviroel): we need to provide the parent share info to 

2335 # assist drivers that create shares from snapshot in different 

2336 # pools or back ends 

2337 parent_share_instance = self.db.share_instance_get( 

2338 context, snapshot_ref['share']['instance']['id'], 

2339 with_share_data=True) 

2340 parent_share_dict = self._get_share_instance_dict( 

2341 context, parent_share_instance) 

2342 model_update = self.driver.create_share_from_snapshot( 

2343 context, share_instance, snapshot_ref.instance, 

2344 share_server=share_server, parent_share=parent_share_dict) 

2345 if isinstance(model_update, list): 

2346 # NOTE(dviroel): the driver that doesn't implement the new 

2347 # model_update will return only the export locations 

2348 export_locations = model_update 

2349 else: 

2350 # NOTE(dviroel): share status is mandatory when answering 

2351 # a model update. If not provided, won't be possible to 

2352 # determine if was successfully created. 

2353 status = model_update.get('status') 

2354 if status is None: 2354 ↛ 2355line 2354 didn't jump to line 2355 because the condition on line 2354 was never true

2355 msg = _("Driver didn't provide a share status.") 

2356 raise exception.InvalidShareInstance(reason=msg) 

2357 export_locations = model_update.get('export_locations') 

2358 else: 

2359 export_locations = self.driver.create_share( 

2360 context, share_instance, share_server=share_server) 

2361 if status not in [constants.STATUS_AVAILABLE, 

2362 constants.STATUS_CREATING_FROM_SNAPSHOT]: 

2363 msg = _('Driver returned an invalid status: %s') % status 

2364 raise exception.InvalidShareInstance(reason=msg) 

2365 

2366 share_backend_info = ( 

2367 self.driver.get_optional_share_creation_data( 

2368 share, share_server=share_server)) 

2369 if share_backend_info: 

2370 metadata_updates = share_backend_info.get("metadata") 

2371 if metadata_updates: 2371 ↛ 2374line 2371 didn't jump to line 2374 because the condition on line 2371 was always true

2372 self.db.share_metadata_update( 

2373 context, share_id, metadata_updates, False) 

2374 if export_locations: 

2375 self.db.export_locations_update( 

2376 context, share_instance['id'], export_locations) 

2377 

2378 except Exception as e: 

2379 with excutils.save_and_reraise_exception(): 

2380 LOG.error("Share instance %s failed on creation.", 

2381 share_instance_id) 

2382 detail_data = getattr(e, 'detail_data', {}) 

2383 

2384 def get_export_location(details): 

2385 if not isinstance(details, dict): 2385 ↛ 2386line 2385 didn't jump to line 2386 because the condition on line 2385 was never true

2386 return None 

2387 return details.get('export_locations', 

2388 details.get('export_location')) 

2389 

2390 export_locations = get_export_location(detail_data) 

2391 

2392 if export_locations: 

2393 self.db.export_locations_update( 

2394 context, share_instance['id'], export_locations) 

2395 else: 

2396 LOG.warning('Share instance information in exception ' 

2397 'can not be written to db because it ' 

2398 'contains %s and it is not a dictionary.', 

2399 detail_data) 

2400 self.db.share_instance_update( 

2401 context, share_instance_id, 

2402 {'status': constants.STATUS_ERROR} 

2403 ) 

2404 self.message_api.create( 

2405 context, 

2406 message_field.Action.CREATE, 

2407 share['project_id'], 

2408 resource_type=message_field.Resource.SHARE, 

2409 resource_id=share_id, 

2410 exception=e) 

2411 else: 

2412 LOG.info("Share instance %s created successfully.", 

2413 share_instance_id) 

2414 progress = '100%' if status == constants.STATUS_AVAILABLE else '0%' 

2415 updates = { 

2416 'status': status, 

2417 'launched_at': timeutils.utcnow(), 

2418 'progress': progress 

2419 } 

2420 if share.get('replication_type'): 

2421 updates['replica_state'] = constants.REPLICA_STATE_ACTIVE 

2422 

2423 self.db.share_instance_update(context, share_instance_id, updates) 

2424 

2425 self._notify_about_share_usage(context, share, 

2426 share_instance, "create.end") 

2427 

2428 def _update_share_instance_access_rules_state(self, context, 

2429 share_instance_id, state): 

2430 """Update the access_rules_status for the share instance.""" 

2431 self.access_helper.get_and_update_share_instance_access_rules_status( 

2432 context, status=state, share_instance_id=share_instance_id) 

2433 

2434 def _get_replica_snapshots_for_snapshot(self, context, snapshot_id, 

2435 active_replica_id, 

2436 share_replica_id, 

2437 with_share_data=True): 

2438 """Return dict of snapshot instances of active and replica instances. 

2439 

2440 This method returns a dict of snapshot instances for snapshot 

2441 referred to by snapshot_id. The dict contains the snapshot instance 

2442 pertaining to the 'active' replica and the snapshot instance 

2443 pertaining to the replica referred to by share_replica_id. 

2444 """ 

2445 filters = { 

2446 'snapshot_ids': snapshot_id, 

2447 'share_instance_ids': (share_replica_id, active_replica_id), 

2448 } 

2449 instance_list = self.db.share_snapshot_instance_get_all_with_filters( 

2450 context, filters, with_share_data=with_share_data) 

2451 

2452 snapshots = { 

2453 'active_replica_snapshot': self._get_snapshot_instance_dict( 

2454 context, 

2455 list(filter(lambda x: 

2456 x['share_instance_id'] == active_replica_id, 

2457 instance_list))[0]), 

2458 'share_replica_snapshot': self._get_snapshot_instance_dict( 

2459 context, 

2460 list(filter(lambda x: 

2461 x['share_instance_id'] == share_replica_id, 

2462 instance_list))[0]), 

2463 } 

2464 

2465 return snapshots 

2466 

2467 @add_hooks 

2468 @utils.require_driver_initialized 

2469 @locked_share_replica_operation 

2470 def create_share_replica(self, context, share_replica_id, share_id=None, 

2471 request_spec=None, filter_properties=None): 

2472 """Create a share replica.""" 

2473 context = context.elevated() 

2474 share_replica = self.db.share_replica_get( 

2475 context, share_replica_id, with_share_data=True, 

2476 with_share_server=True) 

2477 

2478 if not share_replica['availability_zone']: 

2479 share_replica = self.db.share_replica_update( 

2480 context, share_replica['id'], 

2481 {'availability_zone': self.availability_zone}, 

2482 with_share_data=True 

2483 ) 

2484 

2485 _active_replica = ( 

2486 self.db.share_replicas_get_available_active_replica( 

2487 context, share_replica['share_id'], with_share_data=True, 

2488 with_share_server=True)) 

2489 if not _active_replica: 

2490 self.db.share_replica_update( 

2491 context, share_replica['id'], 

2492 {'status': constants.STATUS_ERROR, 

2493 'replica_state': constants.STATUS_ERROR}) 

2494 self.message_api.create( 

2495 context, 

2496 message_field.Action.CREATE, 

2497 share_replica['project_id'], 

2498 resource_type=message_field.Resource.SHARE_REPLICA, 

2499 resource_id=share_replica['id'], 

2500 detail=message_field.Detail.NO_ACTIVE_REPLICA) 

2501 msg = _("An 'active' replica must exist in 'available' " 

2502 "state to create a new replica for share %s.") 

2503 raise exception.ReplicationException( 

2504 reason=msg % share_replica['share_id']) 

2505 

2506 # We need the share_network_id in case of 

2507 # driver_handles_share_server=True 

2508 share_network_id = share_replica.get('share_network_id', None) 

2509 if xor(bool(share_network_id), 

2510 self.driver.driver_handles_share_servers): 

2511 self.db.share_replica_update( 

2512 context, share_replica['id'], 

2513 {'status': constants.STATUS_ERROR, 

2514 'replica_state': constants.STATUS_ERROR}) 

2515 self.message_api.create( 

2516 context, 

2517 message_field.Action.CREATE, 

2518 share_replica['project_id'], 

2519 resource_type=message_field.Resource.SHARE_REPLICA, 

2520 resource_id=share_replica['id'], 

2521 detail=message_field.Detail.UNEXPECTED_NETWORK) 

2522 raise exception.InvalidDriverMode( 

2523 "The share-network value provided does not match with the " 

2524 "current driver configuration.") 

2525 

2526 if share_network_id: 2526 ↛ 2548line 2526 didn't jump to line 2548 because the condition on line 2526 was always true

2527 try: 

2528 share_server, share_replica = ( 

2529 self._provide_share_server_for_share( 

2530 context, share_network_id, share_replica) 

2531 ) 

2532 except Exception: 

2533 with excutils.save_and_reraise_exception(): 

2534 LOG.error("Failed to get share server " 

2535 "for share replica creation.") 

2536 self.db.share_replica_update( 

2537 context, share_replica['id'], 

2538 {'status': constants.STATUS_ERROR, 

2539 'replica_state': constants.STATUS_ERROR}) 

2540 self.message_api.create( 

2541 context, 

2542 message_field.Action.CREATE, 

2543 share_replica['project_id'], 

2544 resource_type=message_field.Resource.SHARE_REPLICA, 

2545 resource_id=share_replica['id'], 

2546 detail=message_field.Detail.NO_SHARE_SERVER) 

2547 else: 

2548 share_server = None 

2549 

2550 # Map the existing access rules for the share to 

2551 # the replica in the DB. 

2552 share_access_rules = self.db.share_instance_access_copy( 

2553 context, share_replica['share_id'], share_replica['id']) 

2554 

2555 # Get snapshots for the share. 

2556 share_snapshots = self.db.share_snapshot_get_all_for_share( 

2557 context, share_id) 

2558 # Get the required data for snapshots that have 'aggregate_status' 

2559 # set to 'available'. 

2560 available_share_snapshots = [ 

2561 self._get_replica_snapshots_for_snapshot( 

2562 context, x['id'], _active_replica['id'], share_replica_id) 

2563 for x in share_snapshots 

2564 if x['aggregate_status'] == constants.STATUS_AVAILABLE] 

2565 

2566 replica_list = ( 

2567 self.db.share_replicas_get_all_by_share( 

2568 context, share_replica['share_id'], 

2569 with_share_data=True, with_share_server=True) 

2570 ) 

2571 

2572 replica_list = [self._get_share_instance_dict(context, r) 

2573 for r in replica_list] 

2574 share_replica = self._get_share_instance_dict(context, share_replica) 

2575 

2576 try: 

2577 replica_ref = self.driver.create_replica( 

2578 context, replica_list, share_replica, 

2579 share_access_rules, available_share_snapshots, 

2580 share_server=share_server) or {} 

2581 

2582 except Exception as excep: 

2583 with excutils.save_and_reraise_exception(): 

2584 LOG.error("Share replica %s failed on creation.", 

2585 share_replica['id']) 

2586 self.db.share_replica_update( 

2587 context, share_replica['id'], 

2588 {'status': constants.STATUS_ERROR, 

2589 'replica_state': constants.STATUS_ERROR}) 

2590 self._update_share_instance_access_rules_state( 

2591 context, share_replica['id'], constants.STATUS_ERROR) 

2592 self.message_api.create( 

2593 context, 

2594 message_field.Action.CREATE, 

2595 share_replica['project_id'], 

2596 resource_type=message_field.Resource.SHARE_REPLICA, 

2597 resource_id=share_replica['id'], 

2598 exception=excep) 

2599 

2600 if replica_ref.get('export_locations'): 2600 ↛ 2610line 2600 didn't jump to line 2610 because the condition on line 2600 was always true

2601 if isinstance(replica_ref.get('export_locations'), list): 

2602 self.db.export_locations_update( 

2603 context, share_replica['id'], 

2604 replica_ref.get('export_locations')) 

2605 else: 

2606 msg = ('Invalid export locations passed to the share ' 

2607 'manager.') 

2608 LOG.warning(msg) 

2609 

2610 if replica_ref.get('replica_state'): 

2611 self.db.share_replica_update( 

2612 context, share_replica['id'], 

2613 {'status': constants.STATUS_AVAILABLE, 

2614 'replica_state': replica_ref.get('replica_state'), 

2615 'progress': '100%'}) 

2616 

2617 reported_access_rules_status = replica_ref.get('access_rules_status') 

2618 if reported_access_rules_status in (None, "active"): 2618 ↛ 2628line 2618 didn't jump to line 2628 because the condition on line 2618 was always true

2619 # update all rules to "active" 

2620 conditionally_change = {'queued_to_apply': 'active'} 

2621 self.access_helper.get_and_update_share_instance_access_rules( 

2622 context, share_instance_id=share_replica['id'], 

2623 conditionally_change=conditionally_change) 

2624 # update "access_rules_status" on the replica 

2625 self._update_share_instance_access_rules_state( 

2626 context, share_replica['id'], 

2627 constants.STATUS_ACTIVE) 

2628 elif replica_ref.get('share_access_rules'): 

2629 # driver would like to update individual access rules 

2630 share_access_rules_dict = { 

2631 rule['id']: rule for rule in share_access_rules} 

2632 for rule_update in replica_ref.get('share_access_rules'): 

2633 self.access_helper.get_and_update_share_instance_access_rule( 

2634 context, 

2635 rule_update['id'], 

2636 {'state': rule_update['state']}, 

2637 share_instance_id=share_replica['id']) 

2638 share_access_rules_dict.pop(rule_update['id']) 

2639 for rule_id in share_access_rules_dict: 

2640 self.access_helper.get_and_update_share_instance_access_rule( 

2641 context, 

2642 rule_id, 

2643 {'state': 'active'}, 

2644 share_instance_id=share_replica['id']) 

2645 self._update_share_instance_access_rules_state( 

2646 context, share_replica['id'], 

2647 replica_ref.get('access_rules_status')) 

2648 

2649 LOG.info("Share replica %s created successfully.", 

2650 share_replica['id']) 

2651 

2652 @add_hooks 

2653 @utils.require_driver_initialized 

2654 @locked_share_replica_operation 

2655 def delete_share_replica(self, context, share_replica_id, share_id=None, 

2656 force=False): 

2657 """Delete a share replica.""" 

2658 context = context.elevated() 

2659 share_replica = self.db.share_replica_get( 

2660 context, share_replica_id, with_share_data=True, 

2661 with_share_server=True) 

2662 

2663 # Grab all the snapshot instances that belong to this replica. 

2664 replica_snapshots = ( 

2665 self.db.share_snapshot_instance_get_all_with_filters( 

2666 context, {'share_instance_ids': share_replica_id}, 

2667 with_share_data=True) 

2668 ) 

2669 

2670 replica_list = ( 

2671 self.db.share_replicas_get_all_by_share( 

2672 context, share_replica['share_id'], 

2673 with_share_data=True, with_share_server=True) 

2674 ) 

2675 

2676 replica_list = [self._get_share_instance_dict(context, r) 

2677 for r in replica_list] 

2678 replica_snapshots = [self._get_snapshot_instance_dict(context, s) 

2679 for s in replica_snapshots] 

2680 share_server = self._get_share_server(context, share_replica) 

2681 share_replica = self._get_share_instance_dict(context, share_replica) 

2682 

2683 try: 

2684 self.access_helper.update_access_rules( 

2685 context, 

2686 share_replica_id, 

2687 delete_all_rules=True, 

2688 share_server=share_server 

2689 ) 

2690 except Exception as excep: 

2691 with excutils.save_and_reraise_exception() as exc_context: 

2692 # Set status to 'error' from 'deleting' since 

2693 # access_rules_status has been set to 'error'. 

2694 self.db.share_replica_update( 

2695 context, share_replica['id'], 

2696 {'status': constants.STATUS_ERROR}) 

2697 self.message_api.create( 

2698 context, 

2699 message_field.Action.DELETE_ACCESS_RULES, 

2700 share_replica['project_id'], 

2701 resource_type=message_field.Resource.SHARE_REPLICA, 

2702 resource_id=share_replica['id'], 

2703 exception=excep) 

2704 if force: 

2705 msg = _("The driver was unable to delete access rules " 

2706 "for the replica: %s. Will attempt to delete " 

2707 "the replica anyway.") 

2708 LOG.exception(msg, share_replica['id']) 

2709 exc_context.reraise = False 

2710 

2711 try: 

2712 self.driver.delete_replica( 

2713 context, replica_list, replica_snapshots, share_replica, 

2714 share_server=share_server) 

2715 except Exception as excep: 

2716 with excutils.save_and_reraise_exception() as exc_context: 

2717 if force: 

2718 msg = _("The driver was unable to delete the share " 

2719 "replica: %s on the backend. Since " 

2720 "this operation is forced, the replica will be " 

2721 "deleted from Manila's database. A cleanup on " 

2722 "the backend may be necessary.") 

2723 LOG.exception(msg, share_replica['id']) 

2724 exc_context.reraise = False 

2725 else: 

2726 self.db.share_replica_update( 

2727 context, share_replica['id'], 

2728 {'status': constants.STATUS_ERROR_DELETING, 

2729 'replica_state': constants.STATUS_ERROR}) 

2730 self.message_api.create( 

2731 context, 

2732 message_field.Action.DELETE, 

2733 share_replica['project_id'], 

2734 resource_type=message_field.Resource.SHARE_REPLICA, 

2735 resource_id=share_replica['id'], 

2736 exception=excep) 

2737 

2738 for replica_snapshot in replica_snapshots: 

2739 self.db.share_snapshot_instance_delete( 

2740 context, replica_snapshot['id']) 

2741 

2742 self.db.share_replica_delete(context, share_replica['id']) 

2743 LOG.info("Share replica %s deleted successfully.", 

2744 share_replica['id']) 

2745 

2746 @add_hooks 

2747 @utils.require_driver_initialized 

2748 @locked_share_replica_operation 

2749 def promote_share_replica(self, context, share_replica_id, share_id=None, 

2750 quiesce_wait_time=None): 

2751 """Promote a share replica to active state.""" 

2752 context = context.elevated() 

2753 share_replica = self.db.share_replica_get( 

2754 context, share_replica_id, with_share_data=True, 

2755 with_share_server=True) 

2756 replication_type = share_replica['replication_type'] 

2757 if replication_type == constants.REPLICATION_TYPE_READABLE: 

2758 ensure_old_active_replica_to_readonly = True 

2759 else: 

2760 ensure_old_active_replica_to_readonly = False 

2761 share_server = self._get_share_server(context, share_replica) 

2762 

2763 # Get list of all replicas for share 

2764 replica_list = ( 

2765 self.db.share_replicas_get_all_by_share( 

2766 context, share_replica['share_id'], 

2767 with_share_data=True, with_share_server=True) 

2768 ) 

2769 

2770 try: 

2771 old_active_replica = list(filter( 

2772 lambda r: ( 

2773 r['replica_state'] == constants.REPLICA_STATE_ACTIVE), 

2774 replica_list))[0] 

2775 except IndexError: 

2776 self.db.share_replica_update( 

2777 context, share_replica['id'], 

2778 {'status': constants.STATUS_AVAILABLE}) 

2779 msg = _("Share %(share)s has no replica with 'replica_state' " 

2780 "set to %(state)s. Promoting %(replica)s is not " 

2781 "possible.") 

2782 self.message_api.create( 

2783 context, 

2784 message_field.Action.PROMOTE, 

2785 share_replica['project_id'], 

2786 resource_type=message_field.Resource.SHARE_REPLICA, 

2787 resource_id=share_replica['id'], 

2788 detail=message_field.Detail.NO_ACTIVE_REPLICA) 

2789 raise exception.ReplicationException( 

2790 reason=msg % {'share': share_replica['share_id'], 

2791 'state': constants.REPLICA_STATE_ACTIVE, 

2792 'replica': share_replica['id']}) 

2793 

2794 access_rules = self.db.share_access_get_all_for_share( 

2795 context, share_replica['share_id']) 

2796 

2797 replica_list = [self._get_share_instance_dict(context, r) 

2798 for r in replica_list] 

2799 share_replica = self._get_share_instance_dict(context, share_replica) 

2800 

2801 try: 

2802 updated_replica_list = ( 

2803 self.driver.promote_replica( 

2804 context, replica_list, share_replica, access_rules, 

2805 share_server=share_server, 

2806 quiesce_wait_time=quiesce_wait_time) 

2807 ) 

2808 except Exception as excep: 

2809 with excutils.save_and_reraise_exception(): 

2810 # (NOTE) gouthamr: If the driver throws an exception at 

2811 # this stage, there is a good chance that the replicas are 

2812 # somehow altered on the backend. We loop through the 

2813 # replicas and set their 'status's to 'error' and 

2814 # leave the 'replica_state' unchanged. This also changes the 

2815 # 'status' of the replica that failed to promote to 'error' as 

2816 # before this operation. The backend may choose to update 

2817 # the actual replica_state during the replica_monitoring 

2818 # stage. 

2819 updates = {'status': constants.STATUS_ERROR} 

2820 for replica_ref in replica_list: 

2821 self.db.share_replica_update( 

2822 context, replica_ref['id'], updates) 

2823 self.message_api.create( 

2824 context, 

2825 message_field.Action.PROMOTE, 

2826 replica_ref['project_id'], 

2827 resource_type=message_field.Resource.SHARE_REPLICA, 

2828 resource_id=replica_ref['id'], 

2829 exception=excep) 

2830 

2831 # Set any 'creating' snapshots on the currently active replica to 

2832 # 'error' since we cannot guarantee they will finish 'creating'. 

2833 active_replica_snapshot_instances = ( 

2834 self.db.share_snapshot_instance_get_all_with_filters( 

2835 context, {'share_instance_ids': share_replica['id']}) 

2836 ) 

2837 for instance in active_replica_snapshot_instances: 

2838 if instance['status'] in (constants.STATUS_CREATING, 

2839 constants.STATUS_DELETING): 

2840 msg = ("The replica snapshot instance %(instance)s was " 

2841 "in %(state)s. Since it was not in %(available)s " 

2842 "state when the replica was promoted, it will be " 

2843 "set to %(error)s.") 

2844 payload = { 

2845 'instance': instance['id'], 

2846 'state': instance['status'], 

2847 'available': constants.STATUS_AVAILABLE, 

2848 'error': constants.STATUS_ERROR, 

2849 } 

2850 LOG.info(msg, payload) 

2851 self.db.share_snapshot_instance_update( 

2852 context, instance['id'], 

2853 {'status': constants.STATUS_ERROR}) 

2854 

2855 if not updated_replica_list: 

2856 self.db.share_replica_update( 

2857 context, old_active_replica['id'], 

2858 {'replica_state': constants.REPLICA_STATE_OUT_OF_SYNC, 

2859 'cast_rules_to_readonly': 

2860 ensure_old_active_replica_to_readonly}) 

2861 self.db.share_replica_update( 

2862 context, share_replica['id'], 

2863 {'status': constants.STATUS_AVAILABLE, 

2864 'replica_state': constants.REPLICA_STATE_ACTIVE, 

2865 'cast_rules_to_readonly': False}) 

2866 else: 

2867 while updated_replica_list: 

2868 # NOTE(vponomaryov): update 'active' replica last. 

2869 for updated_replica in updated_replica_list: 2869 ↛ 2876line 2869 didn't jump to line 2876 because the loop on line 2869 didn't complete

2870 if (updated_replica['id'] == share_replica['id'] and 

2871 len(updated_replica_list) > 1): 

2872 continue 

2873 updated_replica_list.remove(updated_replica) 

2874 break 

2875 

2876 updated_export_locs = updated_replica.get( 

2877 'export_locations') 

2878 if updated_export_locs is not None \ 

2879 and isinstance(updated_export_locs, list): 

2880 self.db.export_locations_update( 

2881 context, updated_replica['id'], 

2882 updated_export_locs) 

2883 

2884 updated_replica_state = updated_replica.get( 

2885 'replica_state') 

2886 updates = {} 

2887 # Change the promoted replica's status from 'available' to 

2888 # 'replication_change' and unset cast_rules_to_readonly 

2889 if updated_replica['id'] == share_replica['id']: 

2890 updates['cast_rules_to_readonly'] = False 

2891 updates['status'] = constants.STATUS_AVAILABLE 

2892 elif updated_replica['id'] == old_active_replica['id']: 

2893 updates['cast_rules_to_readonly'] = ( 

2894 ensure_old_active_replica_to_readonly) 

2895 if updated_replica_state == constants.STATUS_ERROR: 2895 ↛ 2896line 2895 didn't jump to line 2896 because the condition on line 2895 was never true

2896 updates['status'] = constants.STATUS_ERROR 

2897 if updated_replica_state is not None: 

2898 updates['replica_state'] = updated_replica_state 

2899 if updates: 

2900 self.db.share_replica_update( 

2901 context, updated_replica['id'], updates) 

2902 

2903 if updated_replica.get('access_rules_status'): 2903 ↛ 2904line 2903 didn't jump to line 2904 because the condition on line 2903 was never true

2904 self._update_share_instance_access_rules_state( 

2905 context, share_replica['id'], 

2906 updated_replica.get('access_rules_status')) 

2907 

2908 LOG.info("Share replica %s: promoted to active state " 

2909 "successfully.", share_replica['id']) 

2910 

2911 @periodic_task.periodic_task(spacing=CONF.replica_state_update_interval) 

2912 @utils.require_driver_initialized 

2913 def periodic_share_replica_update(self, context): 

2914 LOG.debug("Updating status of share replica instances.") 

2915 # we will need: id, host, replica_state, share_id 

2916 replicas = self.db.share_replicas_get_all(context, 

2917 with_share_data=False, 

2918 with_share_server=False) 

2919 

2920 # Filter only non-active replicas belonging to this backend 

2921 def qualified_replica(r): 

2922 return (share_utils.extract_host(r['host']) == 

2923 share_utils.extract_host(self.host) and 

2924 r['replica_state'] != constants.REPLICA_STATE_ACTIVE) 

2925 

2926 replicas = list(filter(lambda x: qualified_replica(x), replicas)) 

2927 for replica in replicas: 

2928 self._share_replica_update( 

2929 context, replica['id'], share_id=replica['share_id']) 

2930 

2931 @add_hooks 

2932 @utils.require_driver_initialized 

2933 def update_share_replica(self, context, share_replica_id, share_id=None): 

2934 """Initiated by the force_update API.""" 

2935 self._share_replica_update( 

2936 context, share_replica_id, share_id=share_id) 

2937 

2938 @locked_share_replica_operation 

2939 def _share_replica_update(self, context, share_replica_id, share_id=None): 

2940 # share_id is used by the locked_share_replica_operation decorator 

2941 # Grab the replica: 

2942 try: 

2943 # _get_share_instance_dict will fetch share server 

2944 share_replica = self.db.share_replica_get( 

2945 context, share_replica_id, with_share_data=True, 

2946 with_share_server=False) 

2947 except exception.ShareReplicaNotFound: 

2948 # Replica may have been deleted, nothing to do here 

2949 return 

2950 

2951 # We don't poll for replicas that are busy in some operation, 

2952 # or if they are the 'active' instance. 

2953 if (share_replica['status'] in constants.TRANSITIONAL_STATUSES 

2954 or share_replica['status'] == constants.STATUS_ERROR_DELETING 

2955 or share_replica['replica_state'] == 

2956 constants.REPLICA_STATE_ACTIVE): 

2957 return 

2958 

2959 share_server = self._get_share_server(context, share_replica) 

2960 

2961 access_rules = self.db.share_access_get_all_for_share( 

2962 context, share_replica['share_id']) 

2963 

2964 LOG.debug("Updating status of share share_replica %s: ", 

2965 share_replica['id']) 

2966 

2967 # _get_share_instance_dict will fetch share server 

2968 replica_list = ( 

2969 self.db.share_replicas_get_all_by_share( 

2970 context, share_replica['share_id'], 

2971 with_share_data=True, with_share_server=False) 

2972 ) 

2973 

2974 _active_replica = next((x for x in replica_list 

2975 if x['replica_state'] == 

2976 constants.REPLICA_STATE_ACTIVE), None) 

2977 

2978 if _active_replica is None: 2978 ↛ 2979line 2978 didn't jump to line 2979 because the condition on line 2978 was never true

2979 if share_replica['replica_state'] != constants.STATUS_ERROR: 

2980 # only log warning if replica_state was not already in error 

2981 msg = (("Replica parent share %(id)s has no active " 

2982 "replica.") % {'id': share_replica['share_id']}) 

2983 LOG.warning(msg) 

2984 self.db.share_replica_update(context, share_replica['id'], 

2985 {'replica_state': 

2986 constants.STATUS_ERROR}) 

2987 # without a related active replica, we cannot act on any 

2988 # non-active replica 

2989 return 

2990 

2991 # Get snapshots for the share. 

2992 share_snapshots = self.db.share_snapshot_get_all_for_share( 

2993 context, share_replica['share_id']) 

2994 

2995 # Get the required data for snapshots that have 'aggregate_status' 

2996 # set to 'available'. 

2997 available_share_snapshots = [ 

2998 self._get_replica_snapshots_for_snapshot( 

2999 context, x['id'], _active_replica['id'], share_replica['id']) 

3000 for x in share_snapshots 

3001 if x['aggregate_status'] == constants.STATUS_AVAILABLE] 

3002 

3003 replica_list = [self._get_share_instance_dict(context, r) 

3004 for r in replica_list] 

3005 

3006 share_replica = self._get_share_instance_dict(context, share_replica) 

3007 

3008 try: 

3009 replica_state = self.driver.update_replica_state( 

3010 context, replica_list, share_replica, access_rules, 

3011 available_share_snapshots, share_server=share_server) 

3012 except Exception as excep: 

3013 msg = ("Driver error when updating replica " 

3014 "state for replica %s.") 

3015 LOG.exception(msg, share_replica['id']) 

3016 self.db.share_replica_update( 

3017 context, share_replica['id'], 

3018 {'replica_state': constants.STATUS_ERROR, 

3019 'status': constants.STATUS_ERROR}) 

3020 self.message_api.create( 

3021 context, 

3022 message_field.Action.UPDATE, 

3023 share_replica['project_id'], 

3024 resource_type=message_field.Resource.SHARE_REPLICA, 

3025 resource_id=share_replica['id'], 

3026 exception=excep) 

3027 return 

3028 

3029 if replica_state in (constants.REPLICA_STATE_IN_SYNC, 

3030 constants.REPLICA_STATE_OUT_OF_SYNC, 

3031 constants.STATUS_ERROR): 

3032 self.db.share_replica_update(context, share_replica['id'], 

3033 {'replica_state': replica_state}) 

3034 elif replica_state: 

3035 msg = (("Replica %(id)s cannot be set to %(state)s " 

3036 "through update call.") % 

3037 {'id': share_replica['id'], 'state': replica_state}) 

3038 LOG.warning(msg) 

3039 

3040 def _validate_share_and_driver_mode(self, share_instance): 

3041 driver_dhss = self.driver.driver_handles_share_servers 

3042 

3043 share_dhss = share_types.parse_boolean_extra_spec( 

3044 'driver_handles_share_servers', 

3045 share_types.get_share_type_extra_specs( 

3046 share_instance['share_type_id'], 

3047 constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS)) 

3048 

3049 if driver_dhss != share_dhss: 

3050 msg = _("Driver mode of share %(share)s being managed is " 

3051 "incompatible with mode DHSS=%(dhss)s configured for" 

3052 " this backend.") % {'share': share_instance['share_id'], 

3053 'dhss': driver_dhss} 

3054 raise exception.InvalidShare(reason=msg) 

3055 

3056 return driver_dhss 

3057 

3058 @add_hooks 

3059 @utils.require_driver_initialized 

3060 def manage_share(self, context, share_id, driver_options): 

3061 context = context.elevated() 

3062 share_ref = self.db.share_get(context, share_id) 

3063 share_instance = self._get_share_instance(context, share_ref) 

3064 share_type = share_types.get_share_type( 

3065 context, share_instance['share_type_id']) 

3066 share_type_extra_specs = self._get_extra_specs_from_share_type( 

3067 context, share_instance['share_type_id']) 

3068 share_type_supports_replication = share_type_extra_specs.get( 

3069 'replication_type', None) 

3070 

3071 project_id = share_ref['project_id'] 

3072 

3073 try: 

3074 

3075 driver_dhss = self._validate_share_and_driver_mode(share_instance) 

3076 

3077 if driver_dhss is True: 

3078 share_server = self._get_share_server(context, share_instance) 

3079 

3080 share_update = ( 

3081 self.driver.manage_existing_with_server( 

3082 share_instance, driver_options, share_server) 

3083 or {} 

3084 ) 

3085 else: 

3086 share_update = ( 

3087 self.driver.manage_existing( 

3088 share_instance, driver_options) 

3089 or {} 

3090 ) 

3091 

3092 if not share_update.get('size'): 

3093 # NOTE(haixin)if failed to get real size of share, will not 

3094 # commit quota usages. 

3095 msg = _("Driver cannot calculate share size.") 

3096 raise exception.InvalidShare(reason=msg) 

3097 else: 

3098 share_types.provision_filter_on_size(context, 

3099 share_type, 

3100 share_update.get('size')) 

3101 try: 

3102 values = {'per_share_gigabytes': share_update.get('size')} 

3103 QUOTAS.limit_check(context, project_id=context.project_id, 

3104 **values) 

3105 except exception.OverQuota as e: 

3106 quotas = e.kwargs['quotas'] 

3107 LOG.warning("Requested share size %(size)d is larger than " 

3108 "maximum allowed limit %(limit)d.", 

3109 {'size': share_update.get('size'), 

3110 'limit': quotas['per_share_gigabytes']}) 

3111 

3112 deltas = { 

3113 'project_id': project_id, 

3114 'user_id': context.user_id, 

3115 'shares': 1, 

3116 'gigabytes': share_update['size'], 

3117 'share_type_id': share_instance['share_type_id'], 

3118 } 

3119 

3120 if share_type_supports_replication: 

3121 deltas.update({'share_replicas': 1, 

3122 'replica_gigabytes': share_update['size']}) 

3123 

3124 # NOTE(carloss): Allowing OverQuota to do not compromise this 

3125 # operation. If this hit OverQuota error while managing a share, 

3126 # the admin would need to reset the state of the share and 

3127 # delete or force delete the share (bug 1863298). Allowing 

3128 # OverQuota makes this operation work properly and the admin will 

3129 # need to adjust quotas afterwards. 

3130 reservations = QUOTAS.reserve(context, overquota_allowed=True, 

3131 **deltas) 

3132 QUOTAS.commit( 

3133 context, reservations, project_id=project_id, 

3134 share_type_id=share_instance['share_type_id'], 

3135 ) 

3136 

3137 share_update.update({ 

3138 'status': constants.STATUS_AVAILABLE, 

3139 'launched_at': timeutils.utcnow(), 

3140 'availability_zone': self.availability_zone, 

3141 }) 

3142 

3143 # If the share was managed with `replication_type` extra-spec, the 

3144 # instance becomes an `active` replica. 

3145 if share_ref.get('replication_type'): 

3146 share_update['replica_state'] = constants.REPLICA_STATE_ACTIVE 

3147 

3148 # NOTE(vponomaryov): we should keep only those export locations 

3149 # that driver has calculated to avoid incompatibilities with one 

3150 # provided by user. 

3151 if 'export_locations' in share_update: 

3152 self.db.export_locations_update( 

3153 context, share_instance['id'], 

3154 share_update.pop('export_locations'), 

3155 delete=True) 

3156 

3157 self.db.share_update(context, share_id, share_update) 

3158 except Exception: 

3159 # NOTE(haixin) we should set size 0 because we don't know the real 

3160 # size of the size, and we will skip quota cuts when 

3161 # delete/unmanage share. 

3162 self.db.share_update( 

3163 context, share_id, 

3164 {'status': constants.STATUS_MANAGE_ERROR, 'size': 0}) 

3165 raise 

3166 

3167 @add_hooks 

3168 @utils.require_driver_initialized 

3169 def manage_snapshot(self, context, snapshot_id, driver_options): 

3170 

3171 context = context.elevated() 

3172 snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) 

3173 

3174 snapshot_instance = self.db.share_snapshot_instance_get( 

3175 context, snapshot_ref.instance['id'], with_share_data=True 

3176 ) 

3177 project_id = snapshot_ref['project_id'] 

3178 

3179 driver_dhss = self.driver.driver_handles_share_servers 

3180 

3181 try: 

3182 if driver_dhss is True: 

3183 

3184 share_server = self._get_share_server(context, 

3185 snapshot_ref['share']) 

3186 

3187 snapshot_update = ( 

3188 self.driver.manage_existing_snapshot_with_server( 

3189 snapshot_instance, driver_options, share_server) 

3190 or {} 

3191 ) 

3192 else: 

3193 snapshot_update = ( 

3194 self.driver.manage_existing_snapshot( 

3195 snapshot_instance, driver_options) 

3196 or {} 

3197 ) 

3198 

3199 if not snapshot_update.get('size'): 3199 ↛ 3200line 3199 didn't jump to line 3200 because the condition on line 3199 was never true

3200 snapshot_update['size'] = snapshot_ref['share']['size'] 

3201 LOG.warning("Cannot get the size of the snapshot " 

3202 "%(snapshot_id)s. Using the size of " 

3203 "the share instead.", 

3204 {'snapshot_id': snapshot_id}) 

3205 

3206 self._update_quota_usages(context, project_id, { 

3207 "snapshots": 1, 

3208 "snapshot_gigabytes": snapshot_update['size'], 

3209 }) 

3210 

3211 snapshot_export_locations = snapshot_update.pop( 

3212 'export_locations', []) 

3213 

3214 if snapshot_instance['share']['mount_snapshot_support']: 

3215 

3216 for el in snapshot_export_locations: 

3217 values = { 

3218 'share_snapshot_instance_id': snapshot_instance['id'], 

3219 'path': el['path'], 

3220 'is_admin_only': el['is_admin_only'], 

3221 } 

3222 

3223 self.db.share_snapshot_instance_export_location_create( 

3224 context, values) 

3225 

3226 snapshot_update.update({ 

3227 'status': constants.STATUS_AVAILABLE, 

3228 'progress': '100%', 

3229 }) 

3230 snapshot_update.pop('id', None) 

3231 self.db.share_snapshot_update(context, snapshot_id, 

3232 snapshot_update) 

3233 except Exception: 

3234 # NOTE(vponomaryov): set size as 1 because design expects size 

3235 # to be set, it also will allow us to handle delete/unmanage 

3236 # operations properly with this errored snapshot according to 

3237 # quotas. 

3238 self.db.share_snapshot_update( 

3239 context, snapshot_id, 

3240 {'status': constants.STATUS_MANAGE_ERROR, 'size': 1}) 

3241 raise 

3242 

3243 def _update_quota_usages(self, context, project_id, usages): 

3244 user_id = context.user_id 

3245 for resource, usage in usages.items(): 

3246 try: 

3247 current_usage = self.db.quota_usage_get( 

3248 context, project_id, resource, user_id) 

3249 self.db.quota_usage_update( 

3250 context, project_id, user_id, resource, 

3251 in_use=current_usage['in_use'] + usage) 

3252 except exception.QuotaUsageNotFound: 

3253 self.db.quota_usage_create(context, project_id, 

3254 user_id, resource, usage) 

3255 

3256 @add_hooks 

3257 @utils.require_driver_initialized 

3258 def unmanage_share(self, context, share_id): 

3259 context = context.elevated() 

3260 share_ref = self.db.share_get(context, share_id) 

3261 share_instance = self._get_share_instance(context, share_ref) 

3262 share_server = None 

3263 project_id = share_ref['project_id'] 

3264 replicas = self.db.share_replicas_get_all_by_share( 

3265 context, share_id) 

3266 supports_replication = len(replicas) > 0 

3267 

3268 def share_manage_set_error_status(msg, exception): 

3269 status = {'status': constants.STATUS_UNMANAGE_ERROR} 

3270 self.db.share_update(context, share_id, status) 

3271 LOG.error(msg, exception) 

3272 

3273 dhss = self.driver.driver_handles_share_servers 

3274 

3275 try: 

3276 if dhss is True: 

3277 share_server = self._get_share_server(context, share_instance) 

3278 self.driver.unmanage_with_server(share_instance, share_server) 

3279 else: 

3280 self.driver.unmanage(share_instance) 

3281 

3282 except exception.InvalidShare as e: 

3283 share_manage_set_error_status( 

3284 ("Share can not be unmanaged: %s."), e) 

3285 return 

3286 

3287 # NOTE(haixin) we will skip quota cuts when unmanag share with 

3288 # 'error_manage' status, because we have not commit quota usages when 

3289 # we failed to manage the share. 

3290 if share_ref['status'] != constants.STATUS_MANAGE_ERROR_UNMANAGING: 3290 ↛ 3316line 3290 didn't jump to line 3316 because the condition on line 3290 was always true

3291 deltas = { 

3292 'project_id': project_id, 

3293 'shares': -1, 

3294 'gigabytes': -share_ref['size'], 

3295 'share_type_id': share_instance['share_type_id'], 

3296 } 

3297 # NOTE(carloss): while unmanaging a share, a share will not 

3298 # contain replicas other than the active one. So there is no need 

3299 # to recalculate the amount of share replicas to be deallocated. 

3300 if supports_replication: 

3301 deltas.update({'share_replicas': -1, 

3302 'replica_gigabytes': -share_ref['size']}) 

3303 try: 

3304 reservations = QUOTAS.reserve(context, **deltas) 

3305 QUOTAS.commit( 

3306 context, reservations, project_id=project_id, 

3307 share_type_id=share_instance['share_type_id'], 

3308 ) 

3309 except Exception as e: 

3310 # Note(imalinovskiy): 

3311 # Quota reservation errors here are not fatal, because 

3312 # unmanage is administrator API and he/she could update user 

3313 # quota usages later if it's required. 

3314 LOG.warning("Failed to update quota usages: %s.", e) 

3315 

3316 if self.configuration.safe_get('unmanage_remove_access_rules'): 

3317 try: 

3318 self.access_helper.update_access_rules( 

3319 context, 

3320 share_instance['id'], 

3321 delete_all_rules=True, 

3322 share_server=share_server 

3323 ) 

3324 except Exception as e: 

3325 share_manage_set_error_status( 

3326 ("Can not remove access rules of share: %s."), e) 

3327 return 

3328 

3329 self.db.share_instance_delete(context, share_instance['id']) 

3330 

3331 # NOTE(ganso): Since we are unmanaging a share that is still within a 

3332 # share server, we need to prevent the share server from being 

3333 # auto-deleted. 

3334 if share_server and share_server['is_auto_deletable']: 

3335 self.db.share_server_update(context, share_server['id'], 

3336 {'is_auto_deletable': False}) 

3337 msg = ("Since share %(share)s has been un-managed from share " 

3338 "server %(server)s. This share server must be removed " 

3339 "manually, either by un-managing or by deleting it. The " 

3340 "share network subnets %(subnets)s and share network " 

3341 "%(network)s cannot be deleted unless this share server " 

3342 "has been removed.") 

3343 msg_args = { 

3344 'share': share_id, 

3345 'server': share_server['id'], 

3346 'subnets': share_server['share_network_subnet_ids'], 

3347 'network': share_instance['share_network_id'] 

3348 } 

3349 LOG.warning(msg, msg_args) 

3350 

3351 LOG.info("Share %s: unmanaged successfully.", share_id) 

3352 

3353 @add_hooks 

3354 @utils.require_driver_initialized 

3355 def unmanage_snapshot(self, context, snapshot_id): 

3356 status = {'status': constants.STATUS_UNMANAGE_ERROR} 

3357 

3358 context = context.elevated() 

3359 snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) 

3360 share_server = self._get_share_server(context, 

3361 snapshot_ref['share']) 

3362 

3363 snapshot_instance = self.db.share_snapshot_instance_get( 

3364 context, snapshot_ref.instance['id'], with_share_data=True 

3365 ) 

3366 

3367 project_id = snapshot_ref['project_id'] 

3368 

3369 if self.configuration.safe_get('unmanage_remove_access_rules'): 

3370 try: 

3371 self.snapshot_access_helper.update_access_rules( 

3372 context, 

3373 snapshot_instance['id'], 

3374 delete_all_rules=True, 

3375 share_server=share_server) 

3376 except Exception: 

3377 LOG.exception( 

3378 ("Cannot remove access rules of snapshot %s."), 

3379 snapshot_id) 

3380 self.db.share_snapshot_update(context, snapshot_id, status) 

3381 return 

3382 

3383 dhss = self.driver.driver_handles_share_servers 

3384 

3385 try: 

3386 if dhss: 

3387 self.driver.unmanage_snapshot_with_server( 

3388 snapshot_instance, share_server) 

3389 else: 

3390 self.driver.unmanage_snapshot(snapshot_instance) 

3391 except exception.UnmanageInvalidShareSnapshot as e: 

3392 self.db.share_snapshot_update(context, snapshot_id, status) 

3393 LOG.error("Share snapshot cannot be unmanaged: %s.", e) 

3394 return 

3395 

3396 try: 

3397 share_type_id = snapshot_ref['share']['instance']['share_type_id'] 

3398 reservations = QUOTAS.reserve( 

3399 context, 

3400 project_id=project_id, 

3401 snapshots=-1, 

3402 snapshot_gigabytes=-snapshot_ref['size'], 

3403 share_type_id=share_type_id, 

3404 ) 

3405 QUOTAS.commit( 

3406 context, reservations, project_id=project_id, 

3407 share_type_id=share_type_id, 

3408 ) 

3409 except Exception as e: 

3410 # Note(imalinovskiy): 

3411 # Quota reservation errors here are not fatal, because 

3412 # unmanage is administrator API and he/she could update user 

3413 # quota usages later if it's required. 

3414 LOG.warning("Failed to update quota usages: %s.", e) 

3415 

3416 self.db.share_snapshot_instance_delete( 

3417 context, snapshot_instance['id']) 

3418 

3419 @add_hooks 

3420 @utils.require_driver_initialized 

3421 def manage_share_server(self, context, share_server_id, identifier, 

3422 driver_opts): 

3423 

3424 if self.driver.driver_handles_share_servers is False: 

3425 msg = _("Cannot manage share server %s in a " 

3426 "backend configured with driver_handles_share_servers" 

3427 " set to False.") % share_server_id 

3428 raise exception.ManageShareServerError(reason=msg) 

3429 

3430 server = self.db.share_server_get(context, share_server_id) 

3431 

3432 try: 

3433 # NOTE(felipe_rodrigues): Manila does not support manage share 

3434 # server with multiple allocations, so it can get the first 

3435 # subnet_id element. 

3436 share_network_subnet = self.db.share_network_subnet_get( 

3437 context, server['share_network_subnet_ids'][0]) 

3438 share_network = self.db.share_network_get( 

3439 context, share_network_subnet['share_network_id']) 

3440 

3441 number_allocations = ( 

3442 self.driver.get_network_allocations_number()) 

3443 

3444 if self.driver.admin_network_api: 

3445 number_allocations += ( 

3446 self.driver.get_admin_network_allocations_number()) 

3447 

3448 if number_allocations > 0: 3448 ↛ 3490line 3448 didn't jump to line 3490 because the condition on line 3448 was always true

3449 

3450 # allocations obtained from the driver that still need to 

3451 # be validated 

3452 remaining_allocations = ( 

3453 self.driver.get_share_server_network_info( 

3454 context, server, identifier, driver_opts)) 

3455 

3456 if len(remaining_allocations) > 0: 

3457 

3458 if self.driver.admin_network_api: 

3459 remaining_allocations = ( 

3460 self.driver.admin_network_api. 

3461 manage_network_allocations( 

3462 context, remaining_allocations, server)) 

3463 

3464 # allocations that are managed are removed from 

3465 # remaining_allocations 

3466 

3467 remaining_allocations = ( 

3468 self.driver.network_api. 

3469 manage_network_allocations( 

3470 context, remaining_allocations, server, 

3471 share_network, share_network_subnet)) 

3472 

3473 # We require that all allocations are managed, else we 

3474 # may have problems deleting this share server 

3475 if len(remaining_allocations) > 0: 

3476 msg = ("Failed to manage all allocations. " 

3477 "Allocations %s were not " 

3478 "managed." % remaining_allocations) 

3479 raise exception.ManageShareServerError(reason=msg) 

3480 

3481 else: 

3482 # if there should be allocations, but the driver 

3483 # doesn't return any something is wrong 

3484 

3485 msg = ("Driver did not return required network " 

3486 "allocations to be managed. Required number " 

3487 "of allocations is %s." % number_allocations) 

3488 raise exception.ManageShareServerError(reason=msg) 

3489 

3490 new_identifier, backend_details = self.driver.manage_server( 

3491 context, server, identifier, driver_opts) 

3492 

3493 if not new_identifier: 

3494 new_identifier = server['id'] 

3495 

3496 if backend_details is None or not isinstance( 

3497 backend_details, dict): 

3498 backend_details = {} 

3499 

3500 for security_service in share_network['security_services']: 

3501 ss_type = security_service['type'] 

3502 data = { 

3503 'name': security_service['name'], 

3504 'ou': security_service['ou'], 

3505 'default_ad_site': security_service['default_ad_site'], 

3506 'domain': security_service['domain'], 

3507 'server': security_service['server'], 

3508 'dns_ip': security_service['dns_ip'], 

3509 'user': security_service['user'], 

3510 'type': ss_type, 

3511 'password': security_service['password'], 

3512 } 

3513 backend_details.update({ 

3514 'security_service_' + ss_type: jsonutils.dumps(data) 

3515 }) 

3516 

3517 if backend_details: 3517 ↛ 3521line 3517 didn't jump to line 3521 because the condition on line 3517 was always true

3518 self.db.share_server_backend_details_set( 

3519 context, server['id'], backend_details) 

3520 

3521 self.db.share_server_update( 

3522 context, share_server_id, 

3523 {'status': constants.STATUS_ACTIVE, 

3524 'identifier': new_identifier, 

3525 'network_allocation_update_support': ( 

3526 self.driver.network_allocation_update_support), 

3527 'share_replicas_migration_support': ( 

3528 self.driver.share_replicas_migration_support)}) 

3529 

3530 except Exception: 

3531 msg = "Error managing share server %s" 

3532 LOG.exception(msg, share_server_id) 

3533 self.db.share_server_update( 

3534 context, share_server_id, 

3535 {'status': constants.STATUS_MANAGE_ERROR}) 

3536 raise 

3537 

3538 LOG.info("Share server %s managed successfully.", share_server_id) 

3539 

3540 @add_hooks 

3541 @utils.require_driver_initialized 

3542 def unmanage_share_server(self, context, share_server_id, force=False): 

3543 

3544 server = self.db.share_server_get( 

3545 context, share_server_id) 

3546 server_details = server['backend_details'] 

3547 

3548 security_services = [] 

3549 for ss_name in constants.SECURITY_SERVICES_ALLOWED_TYPES: 

3550 ss = server_details.get('security_service_' + ss_name) 

3551 if ss: 3551 ↛ 3549line 3551 didn't jump to line 3549 because the condition on line 3551 was always true

3552 security_services.append(jsonutils.loads(ss)) 

3553 

3554 try: 

3555 self.driver.unmanage_server(server_details, security_services) 

3556 except NotImplementedError: 

3557 if not force: 

3558 LOG.error("Did not unmanage share server %s since the driver " 

3559 "does not support managing share servers and no " 

3560 "``force`` option was supplied.", 

3561 share_server_id) 

3562 self.db.share_server_update( 

3563 context, share_server_id, 

3564 {'status': constants.STATUS_UNMANAGE_ERROR}) 

3565 return 

3566 

3567 try: 

3568 

3569 if self.driver.get_network_allocations_number() > 0: 

3570 # NOTE(ganso): This will already remove admin allocations. 

3571 self.driver.network_api.unmanage_network_allocations( 

3572 context, share_server_id) 

3573 elif (self.driver.get_admin_network_allocations_number() > 0 

3574 and self.driver.admin_network_api): 

3575 # NOTE(ganso): This is here in case there are only admin 

3576 # allocations. 

3577 self.driver.admin_network_api.unmanage_network_allocations( 

3578 context, share_server_id) 

3579 self.db.share_server_delete(context, share_server_id) 

3580 except Exception: 

3581 msg = "Error unmanaging share server %s" 

3582 LOG.exception(msg, share_server_id) 

3583 self.db.share_server_update( 

3584 context, share_server_id, 

3585 {'status': constants.STATUS_UNMANAGE_ERROR}) 

3586 raise 

3587 

3588 LOG.info("Share server %s unmanaged successfully.", share_server_id) 

3589 

3590 @add_hooks 

3591 @utils.require_driver_initialized 

3592 def revert_to_snapshot(self, context, snapshot_id, 

3593 reservations): 

3594 context = context.elevated() 

3595 snapshot = self.db.share_snapshot_get(context, snapshot_id) 

3596 share = snapshot['share'] 

3597 share_id = share['id'] 

3598 share_instance_id = snapshot.instance.share_instance_id 

3599 share_access_rules = ( 

3600 self.access_helper.get_share_instance_access_rules( 

3601 context, filters={'state': constants.STATUS_ACTIVE}, 

3602 share_instance_id=share_instance_id)) 

3603 snapshot_access_rules = ( 

3604 self.snapshot_access_helper.get_snapshot_instance_access_rules( 

3605 context, snapshot.instance['id'])) 

3606 

3607 if share.get('has_replicas'): 

3608 self._revert_to_replicated_snapshot( 

3609 context, share, snapshot, reservations, share_access_rules, 

3610 snapshot_access_rules, share_id=share_id) 

3611 else: 

3612 self._revert_to_snapshot(context, share, snapshot, reservations, 

3613 share_access_rules, snapshot_access_rules) 

3614 

3615 def _revert_to_snapshot(self, context, share, snapshot, reservations, 

3616 share_access_rules, snapshot_access_rules): 

3617 

3618 share_server = self._get_share_server(context, share) 

3619 share_id = share['id'] 

3620 snapshot_id = snapshot['id'] 

3621 project_id = share['project_id'] 

3622 user_id = share['user_id'] 

3623 

3624 snapshot_instance = self.db.share_snapshot_instance_get( 

3625 context, snapshot.instance['id'], with_share_data=True) 

3626 share_type_id = snapshot_instance["share_instance"]["share_type_id"] 

3627 

3628 # Make primitive to pass the information to the driver 

3629 snapshot_instance_dict = self._get_snapshot_instance_dict( 

3630 context, snapshot_instance, snapshot=snapshot) 

3631 

3632 try: 

3633 updated_share_size = self.driver.revert_to_snapshot( 

3634 context, 

3635 snapshot_instance_dict, 

3636 share_access_rules, 

3637 snapshot_access_rules, 

3638 share_server=share_server) 

3639 except Exception as excep: 

3640 with excutils.save_and_reraise_exception(): 

3641 

3642 msg = ('Share %(share)s could not be reverted ' 

3643 'to snapshot %(snap)s.') 

3644 msg_args = {'share': share_id, 'snap': snapshot_id} 

3645 LOG.exception(msg, msg_args) 

3646 

3647 if reservations: 

3648 QUOTAS.rollback( 

3649 context, reservations, project_id=project_id, 

3650 user_id=user_id, share_type_id=share_type_id, 

3651 ) 

3652 

3653 self.db.share_update( 

3654 context, share_id, 

3655 {'status': constants.STATUS_REVERTING_ERROR}) 

3656 self.db.share_snapshot_update( 

3657 context, snapshot_id, 

3658 {'status': constants.STATUS_AVAILABLE}) 

3659 self.message_api.create( 

3660 context, 

3661 message_field.Action.REVERT_TO_SNAPSHOT, 

3662 share['project_id'], 

3663 resource_type=message_field.Resource.SHARE, 

3664 resource_id=share_id, 

3665 exception=excep) 

3666 

3667 # fail-safe in case driver returned size is None or invalid 

3668 if not updated_share_size: 

3669 updated_share_size = snapshot['size'] 

3670 else: 

3671 try: 

3672 int(updated_share_size) 

3673 except ValueError: 

3674 updated_share_size = snapshot['size'] 

3675 

3676 if reservations: 

3677 if updated_share_size == snapshot['size']: 

3678 QUOTAS.commit( 

3679 context, reservations, project_id=project_id, 

3680 user_id=user_id, share_type_id=share_type_id, 

3681 ) 

3682 else: 

3683 # NOTE(kpdev): The driver tells us that the share size wasn't 

3684 # modified to the snapshot's size; so no need to commit quota 

3685 # changes 

3686 QUOTAS.rollback( 

3687 context, reservations, project_id=project_id, 

3688 user_id=user_id, share_type_id=share_type_id, 

3689 ) 

3690 if updated_share_size != share['size']: 3690 ↛ 3691line 3690 didn't jump to line 3691 because the condition on line 3690 was never true

3691 LOG.error("Driver returned an unexpected size %d on " 

3692 "revert to snapshot operation. You need to " 

3693 "adjust the quota", updated_share_size) 

3694 

3695 self.db.share_update( 

3696 context, share_id, 

3697 {'status': constants.STATUS_AVAILABLE, 'size': updated_share_size}) 

3698 self.db.share_snapshot_update( 

3699 context, snapshot_id, {'status': constants.STATUS_AVAILABLE}) 

3700 

3701 msg = ('Share %(share)s reverted to snapshot %(snap)s ' 

3702 'successfully.') 

3703 msg_args = {'share': share_id, 'snap': snapshot_id} 

3704 LOG.info(msg, msg_args) 

3705 

3706 def _get_share_details_from_instance(self, context, share_instance_id): 

3707 share_instance = self._get_share_instance(context, share_instance_id) 

3708 share = self.db.share_get(context, share_instance.get('share_id')) 

3709 share_server = self._get_share_server(context, share_instance) 

3710 return (share, share_instance, share_server) 

3711 

3712 @add_hooks 

3713 @utils.require_driver_initialized 

3714 def delete_share_instance(self, context, share_instance_id, force=False, 

3715 deferred_delete=False): 

3716 """Delete a share instance.""" 

3717 context = context.elevated() 

3718 share, share_instance, share_server = ( 

3719 self._get_share_details_from_instance(context, share_instance_id)) 

3720 self._notify_about_share_usage(context, share, 

3721 share_instance, "delete.start") 

3722 

3723 error_state = None 

3724 if deferred_delete: 

3725 try: 

3726 self.db.update_share_instance_quota_usages( 

3727 context, share_instance_id) 

3728 LOG.info( 

3729 "Share instance %s had its deletion deferred. Quota was " 

3730 "reclaimed and the share driver will proceed with the " 

3731 "deletion.", share_instance_id 

3732 ) 

3733 except Exception: 

3734 LOG.warning( 

3735 "Error occurred during quota usage update. Administrator " 

3736 "must rectify quotas.") 

3737 

3738 snap_instances = ( 

3739 self.db.share_snapshot_instance_get_all_with_filters( 

3740 context, {'share_instance_ids': share_instance_id})) 

3741 if snap_instances: 3741 ↛ 3748line 3741 didn't jump to line 3748 because the condition on line 3741 was never true

3742 # The share has some snapshot instances whose deletion 

3743 # was deferred. We relegate deletion of the share to 

3744 # a periodic task so it can be processed after 

3745 # all its snapshots are deleted. So we're deliberately 

3746 # setting the share instance's status to 

3747 # "error_deferred_deleting" 

3748 self.db.share_instance_update( 

3749 context, 

3750 share_instance_id, 

3751 {'status': constants.STATUS_ERROR_DEFERRED_DELETING}) 

3752 return 

3753 

3754 try: 

3755 self.access_helper.update_access_rules( 

3756 context, 

3757 share_instance_id, 

3758 delete_all_rules=True, 

3759 share_server=share_server 

3760 ) 

3761 except exception.ShareResourceNotFound: 

3762 LOG.warning("Share instance %s does not exist in the " 

3763 "backend.", share_instance_id) 

3764 except Exception as excep: 

3765 with excutils.save_and_reraise_exception() as exc_context: 

3766 if force: 

3767 msg = ("The driver was unable to delete access rules " 

3768 "for the instance: %s. Will attempt to delete " 

3769 "the instance anyway.") 

3770 LOG.error(msg, share_instance_id) 

3771 exc_context.reraise = False 

3772 else: 

3773 error_state = constants.STATUS_ERROR_DELETING 

3774 if deferred_delete: 3774 ↛ 3775line 3774 didn't jump to line 3775 because the condition on line 3774 was never true

3775 error_state = constants.STATUS_ERROR_DEFERRED_DELETING 

3776 exc_context.reraise = False 

3777 self.db.share_instance_update( 

3778 context, 

3779 share_instance_id, 

3780 {'status': error_state}) 

3781 self.message_api.create( 

3782 context, 

3783 message_field.Action.DELETE_ACCESS_RULES, 

3784 share_instance['project_id'], 

3785 resource_type=message_field.Resource.SHARE, 

3786 resource_id=share_instance_id, 

3787 exception=excep) 

3788 

3789 if error_state == constants.STATUS_ERROR_DEFERRED_DELETING and ( 3789 ↛ 3791line 3789 didn't jump to line 3791 because the condition on line 3789 was never true

3790 not force): 

3791 return 

3792 

3793 try: 

3794 self.driver.delete_share(context, share_instance, 

3795 share_server=share_server) 

3796 except exception.ShareResourceNotFound: 

3797 LOG.warning("Share instance %s does not exist in the " 

3798 "backend.", share_instance_id) 

3799 except Exception as excep: 

3800 with excutils.save_and_reraise_exception() as exc_context: 

3801 if force: 

3802 msg = ("The driver was unable to delete the share " 

3803 "instance: %s on the backend. Since this " 

3804 "operation is forced, the instance will be " 

3805 "deleted from Manila's database. A cleanup on " 

3806 "the backend may be necessary.") 

3807 LOG.error(msg, share_instance_id) 

3808 exc_context.reraise = False 

3809 else: 

3810 error_state = constants.STATUS_ERROR_DELETING 

3811 if deferred_delete: 3811 ↛ 3812line 3811 didn't jump to line 3812 because the condition on line 3811 was never true

3812 error_state = constants.STATUS_ERROR_DEFERRED_DELETING 

3813 exc_context.reraise = False 

3814 self.db.share_instance_update( 

3815 context, 

3816 share_instance_id, 

3817 {'status': error_state}) 

3818 self.message_api.create( 

3819 context, 

3820 message_field.Action.DELETE, 

3821 share_instance['project_id'], 

3822 resource_type=message_field.Resource.SHARE, 

3823 resource_id=share_instance_id, 

3824 exception=excep) 

3825 

3826 if error_state == constants.STATUS_ERROR_DEFERRED_DELETING and ( 3826 ↛ 3828line 3826 didn't jump to line 3828 because the condition on line 3826 was never true

3827 not force): 

3828 return 

3829 

3830 need_to_update_usages = True 

3831 if share_instance['status'] in ( 

3832 constants.STATUS_DEFERRED_DELETING, 

3833 constants.STATUS_ERROR_DEFERRED_DELETING 

3834 ): 

3835 need_to_update_usages = False 

3836 

3837 self.db.share_instance_delete( 

3838 context, share_instance_id, 

3839 need_to_update_usages=need_to_update_usages) 

3840 

3841 LOG.info("Share instance %s: deleted successfully.", 

3842 share_instance_id) 

3843 

3844 self._check_delete_share_server(context, share_instance=share_instance) 

3845 

3846 self._notify_about_share_usage(context, share, 

3847 share_instance, "delete.end") 

3848 

3849 def _check_delete_share_server(self, context, share_instance=None, 

3850 share_server=None, remote_host=False): 

3851 

3852 if CONF.delete_share_server_with_last_share: 

3853 if share_instance and not share_server: 3853 ↛ 3855line 3853 didn't jump to line 3855 because the condition on line 3853 was always true

3854 share_server = self._get_share_server(context, share_instance) 

3855 if (share_server and len(share_server.share_instances) == 0 

3856 and share_server.is_auto_deletable is True): 

3857 LOG.debug("Scheduled deletion of share-server " 

3858 "with id '%s' automatically by " 

3859 "deletion of last share.", share_server['id']) 

3860 if remote_host: 3860 ↛ 3861line 3860 didn't jump to line 3861 because the condition on line 3860 was never true

3861 rpcapi = share_rpcapi.ShareAPI() 

3862 rpcapi.delete_share_server(context, share_server) 

3863 else: 

3864 self.delete_share_server(context, share_server) 

3865 

3866 @periodic_task.periodic_task( 

3867 spacing=CONF.periodic_deferred_delete_interval) 

3868 @utils.require_driver_initialized 

3869 def do_deferred_share_deletion(self, ctxt): 

3870 LOG.debug("Checking for shares in 'deferred_deleting' status to " 

3871 "process their deletion.") 

3872 ctxt = ctxt.elevated() 

3873 share_instances = self.db.share_instance_get_all( 

3874 ctxt, 

3875 filters={ 

3876 'status': constants.STATUS_ERROR_DEFERRED_DELETING, 

3877 'host': self.host, 

3878 }, 

3879 ) 

3880 

3881 for share_instance in share_instances: 

3882 share_instance_id = share_instance['id'] 

3883 share, share_instance, share_server = ( 

3884 self._get_share_details_from_instance( 

3885 ctxt, 

3886 share_instance_id 

3887 ) 

3888 ) 

3889 

3890 snap_instances = ( 

3891 self.db.share_snapshot_instance_get_all_with_filters( 

3892 ctxt, {'share_instance_ids': share_instance_id})) 

3893 if snap_instances: 3893 ↛ 3894line 3893 didn't jump to line 3894 because the condition on line 3893 was never true

3894 LOG.warning("Snapshot instances are present for the " 

3895 "share instance: %s.", share_instance_id) 

3896 continue 

3897 

3898 try: 

3899 self.access_helper.update_access_rules( 

3900 ctxt, 

3901 share_instance_id, 

3902 delete_all_rules=True, 

3903 share_server=share_server 

3904 ) 

3905 except Exception: 

3906 msg = ("The driver was unable to delete access rules " 

3907 "for the instance: %s.") 

3908 LOG.error(msg, share_instance_id) 

3909 continue 

3910 

3911 try: 

3912 self.driver.delete_share(ctxt, share_instance, 

3913 share_server=share_server) 

3914 except exception.ShareResourceNotFound: 

3915 LOG.warning("Share instance %s does not exist in the " 

3916 "backend.", share_instance_id) 

3917 except Exception: 

3918 msg = ("The driver was unable to delete the share " 

3919 "instance: %s on the backend. ") 

3920 LOG.error(msg, share_instance_id) 

3921 continue 

3922 

3923 self.db.share_instance_delete(ctxt, share_instance_id) 

3924 LOG.info("Share instance %s: deferred deleted successfully.", 

3925 share_instance_id) 

3926 self._check_delete_share_server(ctxt, 

3927 share_instance=share_instance) 

3928 self._notify_about_share_usage(ctxt, share, 

3929 share_instance, "delete.end") 

3930 

3931 @periodic_task.periodic_task(spacing=600) 

3932 @utils.require_driver_initialized 

3933 def delete_free_share_servers(self, ctxt): 

3934 if not (self.driver.driver_handles_share_servers and 

3935 self.configuration.automatic_share_server_cleanup): 

3936 return 

3937 LOG.info("Check for unused share servers to delete.") 

3938 updated_before = timeutils.utcnow() - datetime.timedelta( 

3939 minutes=self.configuration.unused_share_server_cleanup_interval) 

3940 servers = self.db.share_server_get_all_unused_deletable(ctxt, 

3941 self.host, 

3942 updated_before) 

3943 for server in servers: 

3944 try: 

3945 self.delete_share_server(ctxt, server) 

3946 except exception.ShareServerNotFound: 

3947 continue 

3948 except Exception: 

3949 LOG.exception( 

3950 "Unable to delete share server %s, will retry in the next " 

3951 "run.", server['id']) 

3952 

3953 @periodic_task.periodic_task( 

3954 spacing=CONF.check_for_expired_shares_in_recycle_bin_interval) 

3955 @utils.require_driver_initialized 

3956 def delete_expired_share(self, ctxt): 

3957 LOG.debug("Check for expired share in recycle bin to delete.") 

3958 expired_shares = self.db.share_get_all_expired(ctxt) 

3959 

3960 for share in expired_shares: 

3961 if share['status'] == constants.STATUS_ERROR_DELETING: 

3962 LOG.info("Share %s was soft-deleted but a prior deletion " 

3963 "attempt failed. Resetting status and re-attempting " 

3964 "deletion", share['id']) 

3965 # reset share status to error in order to try deleting again 

3966 update_data = {'status': constants.STATUS_ERROR} 

3967 self.db.share_update(ctxt, share['id'], update_data) 

3968 else: 

3969 LOG.info("share %s has expired, will be deleted", share['id']) 

3970 self.share_api.delete(ctxt, share) 

3971 

3972 @periodic_task.periodic_task( 

3973 spacing=CONF.check_for_expired_transfers) 

3974 def delete_expired_transfers(self, ctxt): 

3975 LOG.info("Checking for expired transfers.") 

3976 expired_transfers = self.db.transfer_get_all_expired(ctxt) 

3977 

3978 for transfer in expired_transfers: 

3979 LOG.debug("Transfer %s has expired, will be destroyed.", 

3980 transfer['id']) 

3981 self.transfer_api.delete(ctxt, transfer_id=transfer['id']) 

3982 

3983 @utils.require_driver_initialized 

3984 def transfer_accept(self, context, share_id, new_user, 

3985 new_project, clear_rules): 

3986 # need elevated context as we haven't "given" the share yet 

3987 elevated_context = context.elevated() 

3988 share_ref = self.db.share_get(elevated_context, share_id) 

3989 access_rules = self.db.share_access_get_all_for_share( 

3990 elevated_context, share_id) 

3991 share_instances = self.db.share_instance_get_all_by_share( 

3992 elevated_context, share_id) 

3993 share_server = self._get_share_server(context, share_ref) 

3994 

3995 for share_instance in share_instances: 

3996 share_instance = self.db.share_instance_get(context, 

3997 share_instance['id'], 

3998 with_share_data=True) 

3999 if clear_rules and access_rules: 

4000 try: 

4001 self.access_helper.update_access_rules( 

4002 context, 

4003 share_instance['id'], 

4004 delete_all_rules=True 

4005 ) 

4006 access_rules = [] 

4007 except Exception: 

4008 with excutils.save_and_reraise_exception(): 

4009 msg = ( 

4010 "Can not remove access rules for share " 

4011 "instance %(si)s belonging to share %(shr)s.") 

4012 msg_payload = { 

4013 'si': share_instance['id'], 

4014 'shr': share_id, 

4015 } 

4016 LOG.error(msg, msg_payload) 

4017 try: 

4018 self.driver.transfer_accept(context, share_instance, 

4019 new_user, 

4020 new_project, 

4021 access_rules=access_rules, 

4022 share_server=share_server) 

4023 except exception.DriverCannotTransferShareWithRules as e: 

4024 with excutils.save_and_reraise_exception(): 

4025 self.message_api.create( 

4026 context, 

4027 message_field.Action.TRANSFER_ACCEPT, 

4028 new_project, 

4029 resource_type=message_field.Resource.SHARE, 

4030 resource_id=share_id, 

4031 detail=(message_field.Detail. 

4032 DRIVER_FAILED_TRANSFER_ACCEPT)) 

4033 msg = _("The backend failed to accept the share: %s.") 

4034 LOG.error(msg, e) 

4035 

4036 msg = ('Share %(share_id)s has transfer from %(old_project_id)s to ' 

4037 '%(new_project_id)s completed successfully.') 

4038 msg_args = { 

4039 "share_id": share_id, 

4040 "old_project_id": share_ref['project_id'], 

4041 "new_project_id": context.project_id 

4042 } 

4043 LOG.info(msg, msg_args) 

4044 

4045 @add_hooks 

4046 @utils.require_driver_initialized 

4047 def create_snapshot(self, context, share_id, snapshot_id): 

4048 """Create snapshot for share.""" 

4049 snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) 

4050 share_server = self._get_share_server( 

4051 context, snapshot_ref['share']['instance']) 

4052 snapshot_instance = self.db.share_snapshot_instance_get( 

4053 context, snapshot_ref.instance['id'], with_share_data=True 

4054 ) 

4055 snapshot_instance_id = snapshot_instance['id'] 

4056 

4057 snapshot_instance = self._get_snapshot_instance_dict( 

4058 context, snapshot_instance) 

4059 

4060 try: 

4061 

4062 model_update = self.driver.create_snapshot( 

4063 context, snapshot_instance, share_server=share_server) or {} 

4064 

4065 except Exception as excep: 

4066 with excutils.save_and_reraise_exception(): 

4067 self.db.share_snapshot_instance_update( 

4068 context, 

4069 snapshot_instance_id, 

4070 {'status': constants.STATUS_ERROR}) 

4071 self.message_api.create( 

4072 context, 

4073 message_field.Action.CREATE, 

4074 snapshot_ref['project_id'], 

4075 resource_type=message_field.Resource.SHARE_SNAPSHOT, 

4076 resource_id=snapshot_instance_id, 

4077 exception=excep) 

4078 

4079 snapshot_export_locations = model_update.pop('export_locations', []) 

4080 

4081 if snapshot_instance['share']['mount_snapshot_support']: 

4082 

4083 for el in snapshot_export_locations: 

4084 values = { 

4085 'share_snapshot_instance_id': snapshot_instance_id, 

4086 'path': el['path'], 

4087 'is_admin_only': el['is_admin_only'], 

4088 } 

4089 

4090 self.db.share_snapshot_instance_export_location_create(context, 

4091 values) 

4092 

4093 if model_update.get('status') in (None, constants.STATUS_AVAILABLE): 4093 ↛ 4097line 4093 didn't jump to line 4097 because the condition on line 4093 was always true

4094 model_update['status'] = constants.STATUS_AVAILABLE 

4095 model_update['progress'] = '100%' 

4096 

4097 self.db.share_snapshot_instance_update( 

4098 context, snapshot_instance_id, model_update) 

4099 

4100 def _delete_snapshot_quota(self, context, snapshot): 

4101 share_type_id = snapshot['share']['instance']['share_type_id'] 

4102 reservations = None 

4103 try: 

4104 reservations = QUOTAS.reserve( 

4105 context, project_id=snapshot['project_id'], snapshots=-1, 

4106 snapshot_gigabytes=-snapshot['size'], 

4107 user_id=snapshot['user_id'], 

4108 share_type_id=share_type_id, 

4109 ) 

4110 except Exception: 

4111 LOG.exception("Failed to update quota usages while deleting " 

4112 "snapshot %s.", snapshot['id']) 

4113 

4114 if reservations: 

4115 QUOTAS.commit( 

4116 context, reservations, project_id=snapshot['project_id'], 

4117 user_id=snapshot['user_id'], 

4118 share_type_id=share_type_id, 

4119 ) 

4120 

4121 @add_hooks 

4122 @utils.require_driver_initialized 

4123 def delete_snapshot(self, context, snapshot_id, force=False, 

4124 deferred_delete=False): 

4125 """Delete share snapshot.""" 

4126 context = context.elevated() 

4127 snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) 

4128 

4129 share_server = self._get_share_server( 

4130 context, snapshot_ref['share']['instance']) 

4131 snapshot_instance = self.db.share_snapshot_instance_get( 

4132 context, snapshot_ref.instance['id'], with_share_data=True) 

4133 snapshot_instance_id = snapshot_instance['id'] 

4134 

4135 snapshot_instance = self._get_snapshot_instance_dict( 

4136 context, snapshot_instance) 

4137 

4138 share_ref = self.db.share_get(context, snapshot_ref['share_id']) 

4139 

4140 if deferred_delete: 

4141 try: 

4142 self._delete_snapshot_quota(context, snapshot_ref) 

4143 LOG.info( 

4144 "Snapshot instance %s had its deletion deferred. Quota " 

4145 "was reclaimed and the share driver will proceed with " 

4146 "the deletion.", snapshot_instance['id'] 

4147 ) 

4148 except Exception: 

4149 LOG.warning( 

4150 "Error occured during quota usage update. Administrator " 

4151 "must rectify quotas.") 

4152 

4153 if share_ref['mount_snapshot_support']: 

4154 try: 

4155 self.snapshot_access_helper.update_access_rules( 

4156 context, snapshot_instance['id'], delete_all_rules=True, 

4157 share_server=share_server) 

4158 except Exception: 

4159 LOG.exception( 

4160 ("Failed to remove access rules for snapshot %s."), 

4161 snapshot_instance['id']) 

4162 LOG.warning("The driver was unable to remove access rules " 

4163 "for snapshot %s. Moving on.", 

4164 snapshot_instance['snapshot_id']) 

4165 

4166 try: 

4167 self.driver.delete_snapshot(context, snapshot_instance, 

4168 share_server=share_server) 

4169 except Exception as excep: 

4170 with excutils.save_and_reraise_exception() as exc: 

4171 if force: 

4172 msg = _("The driver was unable to delete the " 

4173 "snapshot %s on the backend. Since this " 

4174 "operation is forced, the snapshot will " 

4175 "be deleted from Manila's database. A cleanup on " 

4176 "the backend may be necessary.") 

4177 LOG.exception(msg, snapshot_id) 

4178 exc.reraise = False 

4179 else: 

4180 error_state = constants.STATUS_ERROR_DELETING 

4181 if deferred_delete: 4181 ↛ 4182line 4181 didn't jump to line 4182 because the condition on line 4181 was never true

4182 error_state = constants.STATUS_ERROR_DEFERRED_DELETING 

4183 exc.reraise = False 

4184 self.db.share_snapshot_instance_update( 

4185 context, 

4186 snapshot_instance_id, 

4187 {'status': error_state}) 

4188 self.message_api.create( 

4189 context, 

4190 message_field.Action.DELETE, 

4191 snapshot_ref['project_id'], 

4192 resource_type=message_field.Resource.SHARE_SNAPSHOT, 

4193 resource_id=snapshot_instance_id, 

4194 exception=excep) 

4195 

4196 snapshot_instance = self.db.share_snapshot_instance_get( 

4197 context, snapshot_ref.instance['id']) 

4198 if snapshot_instance['status'] == ( 4198 ↛ 4200line 4198 didn't jump to line 4200 because the condition on line 4198 was never true

4199 constants.STATUS_ERROR_DEFERRED_DELETING) and not force: 

4200 return 

4201 

4202 self.db.share_snapshot_instance_delete(context, snapshot_instance_id) 

4203 if snapshot_instance['status'] not in ( 

4204 constants.STATUS_DEFERRED_DELETING, 

4205 constants.STATUS_ERROR_DEFERRED_DELETING 

4206 ): 

4207 self._delete_snapshot_quota(context, snapshot_ref) 

4208 

4209 @periodic_task.periodic_task( 

4210 spacing=CONF.periodic_deferred_delete_interval) 

4211 @utils.require_driver_initialized 

4212 def do_deferred_snapshot_deletion(self, ctxt): 

4213 LOG.debug("Checking for snapshots in 'deferred_deleting' status to " 

4214 "process their deletion.") 

4215 ctxt = ctxt.elevated() 

4216 snapshot_instances = ( 

4217 self.db.share_snapshot_instance_get_all_with_filters( 

4218 ctxt, {'statuses': constants.STATUS_ERROR_DEFERRED_DELETING})) 

4219 

4220 for snapshot_instance in snapshot_instances: 

4221 snapshot_instance_id = snapshot_instance['id'] 

4222 share_server = self._get_share_server( 

4223 ctxt, snapshot_instance['share_instance']) 

4224 snapshot_instance = self._get_snapshot_instance_dict( 

4225 ctxt, snapshot_instance) 

4226 

4227 try: 

4228 self.driver.delete_snapshot(ctxt, snapshot_instance, 

4229 share_server=share_server) 

4230 except Exception: 

4231 continue 

4232 self.db.share_snapshot_instance_delete(ctxt, 

4233 snapshot_instance_id) 

4234 

4235 @add_hooks 

4236 @utils.require_driver_initialized 

4237 @locked_share_replica_operation 

4238 def create_replicated_snapshot(self, context, snapshot_id, share_id=None): 

4239 """Create a snapshot for a replicated share.""" 

4240 # Grab the snapshot and replica information from the DB. 

4241 snapshot = self.db.share_snapshot_get(context, snapshot_id) 

4242 share_server = self._get_share_server(context, snapshot['share']) 

4243 replica_snapshots = ( 

4244 self.db.share_snapshot_instance_get_all_with_filters( 

4245 context, {'snapshot_ids': snapshot['id']}, 

4246 with_share_data=True) 

4247 ) 

4248 replica_list = ( 

4249 self.db.share_replicas_get_all_by_share( 

4250 context, share_id, with_share_data=True, 

4251 with_share_server=True) 

4252 ) 

4253 

4254 # Make primitives to pass the information to the driver. 

4255 

4256 replica_list = [self._get_share_instance_dict(context, r) 

4257 for r in replica_list] 

4258 replica_snapshots = [self._get_snapshot_instance_dict(context, s) 

4259 for s in replica_snapshots] 

4260 updated_instances = [] 

4261 

4262 try: 

4263 updated_instances = self.driver.create_replicated_snapshot( 

4264 context, replica_list, replica_snapshots, 

4265 share_server=share_server) or [] 

4266 except Exception: 

4267 with excutils.save_and_reraise_exception(): 

4268 for instance in replica_snapshots: 

4269 self.db.share_snapshot_instance_update( 

4270 context, instance['id'], 

4271 {'status': constants.STATUS_ERROR}) 

4272 

4273 for instance in updated_instances: 

4274 if instance['status'] == constants.STATUS_AVAILABLE: 4274 ↛ 4276line 4274 didn't jump to line 4276 because the condition on line 4274 was always true

4275 instance.update({'progress': '100%'}) 

4276 self.db.share_snapshot_instance_update( 

4277 context, instance['id'], instance) 

4278 

4279 def _find_active_replica_on_host(self, replica_list): 

4280 """Find the active replica matching this manager's host.""" 

4281 for replica in replica_list: 4281 ↛ exitline 4281 didn't return from function '_find_active_replica_on_host' because the loop on line 4281 didn't complete

4282 if (replica['replica_state'] == constants.REPLICA_STATE_ACTIVE and 4282 ↛ 4281line 4282 didn't jump to line 4281 because the condition on line 4282 was always true

4283 share_utils.extract_host(replica['host']) == self.host): 

4284 return replica 

4285 

4286 @locked_share_replica_operation 

4287 def _revert_to_replicated_snapshot(self, context, share, snapshot, 

4288 reservations, share_access_rules, 

4289 snapshot_access_rules, share_id=None): 

4290 

4291 share_server = self._get_share_server(context, share) 

4292 snapshot_id = snapshot['id'] 

4293 project_id = share['project_id'] 

4294 user_id = share['user_id'] 

4295 

4296 # Get replicas, including an active replica 

4297 replica_list = self.db.share_replicas_get_all_by_share( 

4298 context, share_id, with_share_data=True, with_share_server=True) 

4299 active_replica = self._find_active_replica_on_host(replica_list) 

4300 

4301 # Get snapshot instances, including one on an active replica 

4302 replica_snapshots = ( 

4303 self.db.share_snapshot_instance_get_all_with_filters( 

4304 context, {'snapshot_ids': snapshot_id}, 

4305 with_share_data=True)) 

4306 snapshot_instance_filters = { 

4307 'share_instance_ids': active_replica['id'], 

4308 'snapshot_ids': snapshot_id, 

4309 } 

4310 active_replica_snapshot = ( 

4311 self.db.share_snapshot_instance_get_all_with_filters( 

4312 context, snapshot_instance_filters))[0] 

4313 

4314 # Make primitives to pass the information to the driver 

4315 replica_list = [self._get_share_instance_dict(context, replica) 

4316 for replica in replica_list] 

4317 active_replica = self._get_share_instance_dict(context, active_replica) 

4318 replica_snapshots = [self._get_snapshot_instance_dict(context, s) 

4319 for s in replica_snapshots] 

4320 active_replica_snapshot = self._get_snapshot_instance_dict( 

4321 context, active_replica_snapshot, snapshot=snapshot) 

4322 

4323 try: 

4324 self.driver.revert_to_replicated_snapshot( 

4325 context, active_replica, replica_list, active_replica_snapshot, 

4326 replica_snapshots, share_access_rules, 

4327 snapshot_access_rules, share_server=share_server) 

4328 except Exception: 

4329 with excutils.save_and_reraise_exception(): 

4330 

4331 msg = ('Share %(share)s could not be reverted ' 

4332 'to snapshot %(snap)s.') 

4333 msg_args = {'share': share_id, 'snap': snapshot_id} 

4334 LOG.exception(msg, msg_args) 

4335 

4336 if reservations: 

4337 QUOTAS.rollback( 

4338 context, reservations, project_id=project_id, 

4339 user_id=user_id, 

4340 share_type_id=active_replica['share_type_id'], 

4341 ) 

4342 

4343 self.db.share_replica_update( 

4344 context, active_replica['id'], 

4345 {'status': constants.STATUS_REVERTING_ERROR}) 

4346 self.db.share_snapshot_instance_update( 

4347 context, active_replica_snapshot['id'], 

4348 {'status': constants.STATUS_AVAILABLE}) 

4349 

4350 if reservations: 

4351 QUOTAS.commit( 

4352 context, reservations, project_id=project_id, user_id=user_id, 

4353 share_type_id=active_replica['share_type_id'], 

4354 ) 

4355 

4356 self.db.share_update(context, share_id, {'size': snapshot['size']}) 

4357 self.db.share_replica_update( 

4358 context, active_replica['id'], 

4359 {'status': constants.STATUS_AVAILABLE}) 

4360 self.db.share_snapshot_instance_update( 

4361 context, active_replica_snapshot['id'], 

4362 {'status': constants.STATUS_AVAILABLE}) 

4363 

4364 msg = ('Share %(share)s reverted to snapshot %(snap)s ' 

4365 'successfully.') 

4366 msg_args = {'share': share_id, 'snap': snapshot_id} 

4367 LOG.info(msg, msg_args) 

4368 

4369 @add_hooks 

4370 @utils.require_driver_initialized 

4371 @locked_share_replica_operation 

4372 def delete_replicated_snapshot(self, context, snapshot_id, 

4373 share_id=None, force=False): 

4374 """Delete a snapshot from a replicated share.""" 

4375 # Grab the replica and snapshot information from the DB. 

4376 snapshot = self.db.share_snapshot_get(context, snapshot_id) 

4377 share_server = self._get_share_server(context, snapshot['share']) 

4378 replica_snapshots = ( 

4379 self.db.share_snapshot_instance_get_all_with_filters( 

4380 context, {'snapshot_ids': snapshot['id']}, 

4381 with_share_data=True) 

4382 ) 

4383 replica_list = ( 

4384 self.db.share_replicas_get_all_by_share( 

4385 context, share_id, with_share_data=True, 

4386 with_share_server=True) 

4387 ) 

4388 

4389 replica_list = [self._get_share_instance_dict(context, r) 

4390 for r in replica_list] 

4391 replica_snapshots = [self._get_snapshot_instance_dict(context, s) 

4392 for s in replica_snapshots] 

4393 deleted_instances = [] 

4394 updated_instances = [] 

4395 db_force_delete_msg = _('The driver was unable to delete some or all ' 

4396 'of the share replica snapshots on the ' 

4397 'backend/s. Since this operation is forced, ' 

4398 'the replica snapshots will be deleted from ' 

4399 'Manila.') 

4400 

4401 try: 

4402 

4403 updated_instances = self.driver.delete_replicated_snapshot( 

4404 context, replica_list, replica_snapshots, 

4405 share_server=share_server) or [] 

4406 

4407 except Exception: 

4408 with excutils.save_and_reraise_exception() as e: 

4409 if force: 

4410 # Can delete all instances if forced. 

4411 deleted_instances = replica_snapshots 

4412 LOG.exception(db_force_delete_msg) 

4413 e.reraise = False 

4414 else: 

4415 for instance in replica_snapshots: 

4416 self.db.share_snapshot_instance_update( 

4417 context, instance['id'], 

4418 {'status': constants.STATUS_ERROR_DELETING}) 

4419 

4420 if not deleted_instances: 

4421 if force: 

4422 # Ignore model updates on 'force' delete. 

4423 LOG.warning(db_force_delete_msg) 

4424 deleted_instances = replica_snapshots 

4425 else: 

4426 deleted_instances = list(filter( 

4427 lambda x: x['status'] == constants.STATUS_DELETED, 

4428 updated_instances)) 

4429 updated_instances = list(filter( 

4430 lambda x: x['status'] != constants.STATUS_DELETED, 

4431 updated_instances)) 

4432 

4433 for instance in deleted_instances: 

4434 self.db.share_snapshot_instance_delete(context, instance['id']) 

4435 

4436 for instance in updated_instances: 

4437 self.db.share_snapshot_instance_update( 

4438 context, instance['id'], instance) 

4439 

4440 @periodic_task.periodic_task(spacing=CONF.replica_state_update_interval) 

4441 @utils.require_driver_initialized 

4442 def periodic_share_replica_snapshot_update(self, context): 

4443 LOG.debug("Updating status of share replica snapshots.") 

4444 transitional_statuses = (constants.STATUS_CREATING, 

4445 constants.STATUS_DELETING) 

4446 # we will need: id, host, replica_state 

4447 replicas = self.db.share_replicas_get_all(context, 

4448 with_share_data=False, 

4449 with_share_server=False) 

4450 

4451 def qualified_replica(r): 

4452 # Filter non-active replicas belonging to this backend 

4453 return (share_utils.extract_host(r['host']) == 

4454 share_utils.extract_host(self.host) and 

4455 r['replica_state'] != constants.REPLICA_STATE_ACTIVE) 

4456 

4457 host_replicas = list(filter( 

4458 lambda x: qualified_replica(x), replicas)) 

4459 transitional_replica_snapshots = [] 

4460 

4461 # Get snapshot instances for each replica that are in 'creating' or 

4462 # 'deleting' states. 

4463 for replica in host_replicas: 4463 ↛ 4464line 4463 didn't jump to line 4464 because the loop on line 4463 never started

4464 filters = { 

4465 'share_instance_ids': replica['id'], 

4466 'statuses': transitional_statuses, 

4467 } 

4468 # we will need: id, snapshot_id, share_instance_id and 

4469 # share['share_id'] 

4470 replica_snapshots = ( 

4471 self.db.share_snapshot_instance_get_all_with_filters( 

4472 context, filters, with_share_data=True) 

4473 ) 

4474 transitional_replica_snapshots.extend(replica_snapshots) 

4475 

4476 for replica_snapshot in transitional_replica_snapshots: 4476 ↛ 4477line 4476 didn't jump to line 4477 because the loop on line 4476 never started

4477 replica_snapshots = ( 

4478 self.db.share_snapshot_instance_get_all_with_filters( 

4479 context, 

4480 {'snapshot_ids': replica_snapshot['snapshot_id']}, 

4481 with_share_data=False) 

4482 ) 

4483 share_id = replica_snapshot['share']['share_id'] 

4484 self._update_replica_snapshot( 

4485 context, replica_snapshot, 

4486 replica_snapshots=replica_snapshots, share_id=share_id) 

4487 

4488 @locked_share_replica_operation 

4489 def _update_replica_snapshot(self, context, replica_snapshot, 

4490 replica_snapshots=None, share_id=None): 

4491 # share_id is used by the locked_share_replica_operation decorator 

4492 # Re-grab the replica, now with share data: 

4493 try: 

4494 # _get_share_instance_dict will fetch share server 

4495 share_replica = self.db.share_replica_get( 

4496 context, replica_snapshot['share_instance_id'], 

4497 with_share_data=True, with_share_server=False) 

4498 replica_snapshot = self.db.share_snapshot_instance_get( 

4499 context, replica_snapshot['id'], with_share_data=True) 

4500 except exception.NotFound: 

4501 # Replica may have been deleted, try to cleanup the snapshot 

4502 # instance 

4503 try: 

4504 self.db.share_snapshot_instance_delete( 

4505 context, replica_snapshot['id']) 

4506 except exception.ShareSnapshotInstanceNotFound: 

4507 # snapshot instance has been deleted, nothing to do here 

4508 pass 

4509 return 

4510 

4511 msg_payload = { 

4512 'snapshot_instance': replica_snapshot['id'], 

4513 'replica': share_replica['id'], 

4514 } 

4515 

4516 LOG.debug("Updating status of replica snapshot %(snapshot_instance)s: " 

4517 "on replica: %(replica)s", msg_payload) 

4518 

4519 # Grab all the replica and snapshot information. 

4520 # _get_share_instance_dict will fetch share server 

4521 replica_list = ( 

4522 self.db.share_replicas_get_all_by_share( 

4523 context, share_replica['share_id'], 

4524 with_share_data=True, with_share_server=False) 

4525 ) 

4526 

4527 replica_list = [self._get_share_instance_dict(context, r) 

4528 for r in replica_list] 

4529 replica_snapshots = replica_snapshots or [] 

4530 

4531 # Convert data to primitives to send to the driver. 

4532 

4533 replica_snapshots = [self._get_snapshot_instance_dict(context, s) 

4534 for s in replica_snapshots] 

4535 replica_snapshot = self._get_snapshot_instance_dict( 

4536 context, replica_snapshot) 

4537 share_replica = self._get_share_instance_dict(context, share_replica) 

4538 share_server = share_replica['share_server'] 

4539 snapshot_update = None 

4540 

4541 try: 

4542 

4543 snapshot_update = self.driver.update_replicated_snapshot( 

4544 context, replica_list, share_replica, replica_snapshots, 

4545 replica_snapshot, share_server=share_server) or {} 

4546 

4547 except exception.SnapshotResourceNotFound: 

4548 if replica_snapshot['status'] == constants.STATUS_DELETING: 4548 ↛ 4554line 4548 didn't jump to line 4554 because the condition on line 4548 was always true

4549 LOG.info('Snapshot %(snapshot_instance)s on replica ' 

4550 '%(replica)s has been deleted.', msg_payload) 

4551 self.db.share_snapshot_instance_delete( 

4552 context, replica_snapshot['id']) 

4553 else: 

4554 LOG.exception("Replica snapshot %s was not found on " 

4555 "the backend.", replica_snapshot['id']) 

4556 self.db.share_snapshot_instance_update( 

4557 context, replica_snapshot['id'], 

4558 {'status': constants.STATUS_ERROR}) 

4559 except Exception: 

4560 LOG.exception("Driver error while updating replica snapshot: " 

4561 "%s", replica_snapshot['id']) 

4562 self.db.share_snapshot_instance_update( 

4563 context, replica_snapshot['id'], 

4564 {'status': constants.STATUS_ERROR}) 

4565 

4566 if snapshot_update: 

4567 snapshot_status = snapshot_update.get('status') 

4568 if snapshot_status == constants.STATUS_AVAILABLE: 4568 ↛ 4570line 4568 didn't jump to line 4570 because the condition on line 4568 was always true

4569 snapshot_update['progress'] = '100%' 

4570 self.db.share_snapshot_instance_update( 

4571 context, replica_snapshot['id'], snapshot_update) 

4572 

4573 @add_hooks 

4574 @utils.require_driver_initialized 

4575 def update_access(self, context, share_instance_id): 

4576 """Allow/Deny/Update access to some share.""" 

4577 share_instance = self._get_share_instance(context, share_instance_id) 

4578 share_server_id = share_instance.get('share_server_id') 

4579 

4580 self.update_access_for_instances(context, [share_instance_id], 

4581 share_server_id=share_server_id) 

4582 

4583 def update_access_for_instances(self, context, share_instance_ids, 

4584 share_server_id=None): 

4585 """Allow/Deny access to shares that belong to the same share server.""" 

4586 share_server = None 

4587 if share_server_id: 4587 ↛ 4590line 4587 didn't jump to line 4590 because the condition on line 4587 was always true

4588 share_server = self.db.share_server_get(context, share_server_id) 

4589 

4590 for instance_id in share_instance_ids: 

4591 LOG.debug("Received request to update access for share instance" 

4592 " %s.", instance_id) 

4593 

4594 self.access_helper.update_access_rules( 

4595 context, 

4596 instance_id, 

4597 share_server=share_server) 

4598 

4599 @periodic_task.periodic_task(spacing=CONF.periodic_interval) 

4600 @utils.require_driver_initialized 

4601 def _report_driver_status(self, context): 

4602 LOG.info('Updating share status') 

4603 share_stats = self.driver.get_share_stats(refresh=True) 

4604 

4605 if not share_stats: 

4606 return 

4607 

4608 if self.driver.driver_handles_share_servers: 

4609 share_stats['server_pools_mapping'] = ( 

4610 self._get_servers_pool_mapping(context) 

4611 ) 

4612 

4613 self.update_service_capabilities(share_stats) 

4614 

4615 @periodic_task.periodic_task(spacing=CONF.periodic_hooks_interval) 

4616 @utils.require_driver_initialized 

4617 def _execute_periodic_hook(self, context): 

4618 """Executes periodic-based hooks.""" 

4619 # TODO(vponomaryov): add also access rules and share servers 

4620 share_instances = ( 

4621 self.db.share_instance_get_all_by_host( 

4622 context=context, host=self.host)) 

4623 periodic_hook_data = self.driver.get_periodic_hook_data( 

4624 context=context, share_instances=share_instances) 

4625 for hook in self.hooks: 

4626 hook.execute_periodic_hook( 

4627 context=context, periodic_hook_data=periodic_hook_data) 

4628 

4629 def _get_servers_pool_mapping(self, context): 

4630 """Get info about relationships between pools and share_servers.""" 

4631 share_servers = self.db.share_server_get_all_by_host(context, 

4632 self.host) 

4633 return {server['id']: self.driver.get_share_server_pools(server) 

4634 for server in share_servers} 

4635 

4636 @add_hooks 

4637 @utils.require_driver_initialized 

4638 def publish_service_capabilities(self, context): 

4639 """Collect driver status and then publish it.""" 

4640 self._report_driver_status(context) 

4641 self._publish_service_capabilities(context) 

4642 

4643 def _form_server_setup_info(self, context, share_server, share_network, 

4644 share_network_subnets): 

4645 share_server_id = share_server['id'] 

4646 # Network info is used by driver for setting up share server 

4647 # and getting server info on share creation. 

4648 admin_network_allocations = ( 

4649 self.db.network_allocations_get_for_share_server( 

4650 context, share_server_id, label='admin')) 

4651 

4652 # NOTE(felipe_rodrigues): items in the network_info list contain 

4653 # same values for the keys: server_id, admin_network_allocations, 

4654 # security_services and backend_details. 

4655 network_info = [] 

4656 for share_network_subnet in share_network_subnets: 

4657 network_allocations = ( 

4658 self.db.network_allocations_get_for_share_server( 

4659 context, share_server_id, label='user', 

4660 subnet_id=share_network_subnet['id'])) 

4661 # NOTE(vponomaryov): following network_info fields are deprecated: 

4662 # 'segmentation_id', 'cidr' and 'network_type'. 

4663 # And they should be used from network allocations directly. 

4664 # They should be removed right after no one uses them. 

4665 network_info.append({ 

4666 'server_id': share_server['id'], 

4667 'segmentation_id': share_network_subnet['segmentation_id'], 

4668 'cidr': share_network_subnet['cidr'], 

4669 'neutron_net_id': share_network_subnet['neutron_net_id'], 

4670 'neutron_subnet_id': share_network_subnet['neutron_subnet_id'], 

4671 'security_services': share_network['security_services'], 

4672 'network_allocations': network_allocations, 

4673 'admin_network_allocations': admin_network_allocations, 

4674 'backend_details': share_server.get('backend_details'), 

4675 'network_type': share_network_subnet['network_type'], 

4676 'subnet_metadata': share_network_subnet['subnet_metadata'] 

4677 }) 

4678 return network_info 

4679 

4680 def _handle_setup_server_error(self, context, share_server_id, e): 

4681 details = getattr(e, "detail_data", {}) 

4682 if isinstance(details, dict): 

4683 server_details = details.get("server_details", {}) 

4684 if not isinstance(server_details, dict): 

4685 LOG.debug( 

4686 ("Cannot save non-dict data (%(data)s) provided as " 

4687 "'server details' of failed share server '%(server)s'."), 

4688 {"server": share_server_id, "data": server_details}) 

4689 else: 

4690 invalid_details = [] 

4691 for key, value in server_details.items(): 

4692 try: 

4693 self.db.share_server_backend_details_set( 

4694 context, share_server_id, {key: value}) 

4695 except Exception: 

4696 invalid_details.append("%(key)s: %(value)s" % { 

4697 'key': str(key), 

4698 'value': str(value) 

4699 }) 

4700 if invalid_details: 

4701 LOG.debug( 

4702 ("Following server details cannot be written to db : " 

4703 "%s"), str("\n".join(invalid_details))) 

4704 else: 

4705 LOG.debug( 

4706 ("Cannot save non-dict data (%(data)s) provided as 'detail " 

4707 "data' of failed share server '%(server)s'."), 

4708 {"server": share_server_id, "data": details}) 

4709 

4710 self.db.share_server_update( 

4711 context, share_server_id, {'status': constants.STATUS_ERROR}) 

4712 

4713 def _setup_server(self, context, share_server, metadata): 

4714 subnets = share_server['share_network_subnets'] 

4715 if not subnets: 4715 ↛ 4716line 4715 didn't jump to line 4716 because the condition on line 4715 was never true

4716 raise exception.NetworkBadConfigurationException( 

4717 reason="share server does not have subnet") 

4718 

4719 # all subnets reside on same share network, get it from the first one. 

4720 share_network_id = subnets[0]['share_network_id'] 

4721 try: 

4722 share_network = self.db.share_network_get(context, 

4723 share_network_id) 

4724 for share_network_subnet in subnets: 

4725 self.driver.allocate_network( 

4726 context, share_server, share_network, share_network_subnet) 

4727 self.driver.allocate_admin_network(context, share_server) 

4728 

4729 # Get share_network_subnets in case they were updated. 

4730 share_network_subnets = ( 

4731 self.db.share_network_subnet_get_all_by_share_server_id( 

4732 context, share_server['id'])) 

4733 

4734 network_info_list = self._form_server_setup_info( 

4735 context, share_server, share_network, share_network_subnets) 

4736 for network_info in network_info_list: 

4737 self._validate_segmentation_id(network_info) 

4738 

4739 # NOTE(vponomaryov): Save security services data to share server 

4740 # details table to remove dependency from share network after 

4741 # creation operation. It will allow us to delete share server and 

4742 # share network separately without dependency on each other. 

4743 for security_service in network_info_list[0]['security_services']: 

4744 ss_type = security_service['type'] 

4745 data = { 

4746 'name': security_service['name'], 

4747 'ou': security_service['ou'], 

4748 'domain': security_service['domain'], 

4749 'server': security_service['server'], 

4750 'dns_ip': security_service['dns_ip'], 

4751 'user': security_service['user'], 

4752 'type': ss_type, 

4753 'password': security_service['password'], 

4754 'default_ad_site': security_service['default_ad_site'], 

4755 } 

4756 self.db.share_server_backend_details_set( 

4757 context, share_server['id'], 

4758 {'security_service_' + ss_type: jsonutils.dumps(data)}) 

4759 

4760 server_info = self.driver.setup_server( 

4761 network_info_list, metadata=metadata) 

4762 

4763 self.driver.update_network_allocation(context, share_server) 

4764 self.driver.update_admin_network_allocation(context, share_server) 

4765 

4766 if server_info and isinstance(server_info, dict): 

4767 self.db.share_server_backend_details_set( 

4768 context, share_server['id'], server_info) 

4769 return self.db.share_server_update( 

4770 context, share_server['id'], 

4771 {'status': constants.STATUS_ACTIVE, 

4772 'identifier': server_info.get( 

4773 'identifier', share_server['id'])}) 

4774 except Exception as e: 

4775 with excutils.save_and_reraise_exception(): 

4776 self._handle_setup_server_error(context, share_server['id'], e) 

4777 self.driver.deallocate_network(context, share_server['id']) 

4778 

4779 def _validate_segmentation_id(self, network_info): 

4780 """Raises exception if the segmentation type is incorrect.""" 

4781 if (network_info['network_type'] in (None, 'flat') and 

4782 network_info['segmentation_id']): 

4783 msg = _('A segmentation ID %(vlan_id)s was specified but can not ' 

4784 'be used with a network of type %(seg_type)s; the ' 

4785 'segmentation ID option must be omitted or set to 0') 

4786 raise exception.NetworkBadConfigurationException( 

4787 reason=msg % {'vlan_id': network_info['segmentation_id'], 

4788 'seg_type': network_info['network_type']}) 

4789 elif (network_info['network_type'] == 'vlan' 

4790 and (network_info['segmentation_id'] is None 

4791 or int(network_info['segmentation_id']) > 4094 

4792 or int(network_info['segmentation_id']) < 1)): 

4793 msg = _('A segmentation ID %s was specified but is not valid for ' 

4794 'a VLAN network type; the segmentation ID must be an ' 

4795 'integer value in the range of [1,4094]') 

4796 raise exception.NetworkBadConfigurationException( 

4797 reason=msg % network_info['segmentation_id']) 

4798 elif (network_info['network_type'] == 'vxlan' 

4799 and (network_info['segmentation_id'] is None 

4800 or int(network_info['segmentation_id']) > 16777215 

4801 or int(network_info['segmentation_id']) < 1)): 

4802 msg = _('A segmentation ID %s was specified but is not valid for ' 

4803 'a VXLAN network type; the segmentation ID must be an ' 

4804 'integer value in the range of [1,16777215]') 

4805 raise exception.NetworkBadConfigurationException( 

4806 reason=msg % network_info['segmentation_id']) 

4807 elif (network_info['network_type'] == 'gre' 

4808 and (network_info['segmentation_id'] is None 

4809 or int(network_info['segmentation_id']) > 4294967295 

4810 or int(network_info['segmentation_id']) < 1)): 

4811 msg = _('A segmentation ID %s was specified but is not valid for ' 

4812 'a GRE network type; the segmentation ID must be an ' 

4813 'integer value in the range of [1, 4294967295]') 

4814 raise exception.NetworkBadConfigurationException( 

4815 reason=msg % network_info['segmentation_id']) 

4816 

4817 @add_hooks 

4818 @utils.require_driver_initialized 

4819 def delete_share_server(self, context, share_server): 

4820 

4821 subnet_id = (share_server['share_network_subnet_ids'][0] 

4822 if share_server['share_network_subnet_ids'] else None) 

4823 

4824 @utils.synchronized( 

4825 "share_manager_%s" % subnet_id) 

4826 def _wrapped_delete_share_server(): 

4827 # NOTE(vponomaryov): Verify that there are no dependent shares. 

4828 # Without this verification we can get here exception in next case: 

4829 # share-server-delete API was called after share creation scheduled 

4830 # and share_server reached ACTIVE status, but before update 

4831 # of share_server_id field for share. If so, after lock realese 

4832 # this method starts executing when amount of dependent shares 

4833 # has been changed. 

4834 server_id = share_server['id'] 

4835 try: 

4836 server = self.db.share_server_get( 

4837 context, server_id) 

4838 except exception.ShareServerNotFound: 

4839 raise 

4840 

4841 shares = self.db.share_instance_get_all_by_share_server( 

4842 context, server_id) 

4843 

4844 if shares: 

4845 raise exception.ShareServerInUse(share_server_id=server_id) 

4846 

4847 server_details = share_server['backend_details'] 

4848 

4849 self.db.share_server_update(context, server_id, 

4850 {'status': constants.STATUS_DELETING}) 

4851 try: 

4852 LOG.debug("Deleting network of share server '%s'", server_id) 

4853 share_net = None 

4854 share_net_subnet = None 

4855 if subnet_id: 

4856 try: 

4857 share_net_subnet = self.db.share_network_subnet_get( 

4858 context, subnet_id) 

4859 share_net = self.db.share_network_get( 

4860 context, share_net_subnet['share_network_id']) 

4861 except Exception: 

4862 LOG.warning('Share network subnet not found during ' 

4863 'deletion of share server.') 

4864 self.driver.deallocate_network(context, share_server['id'], 

4865 share_net, 

4866 share_net_subnet) 

4867 

4868 application_credential_id = server.get( 

4869 'application_credential_id') 

4870 if application_credential_id: 4870 ↛ 4872line 4870 didn't jump to line 4872 because the condition on line 4870 was never true

4871 # Delete application credentials for barbican user 

4872 try: 

4873 barbican_api.delete_application_credentials( 

4874 context, application_credential_id) 

4875 except Exception: 

4876 LOG.warning('Application credentials not found ' 

4877 'during deletion of share server.') 

4878 

4879 encryption_key_ref = server.get('encryption_key_ref') 

4880 barbican_api.delete_secret_access(context, 

4881 encryption_key_ref) 

4882 

4883 LOG.debug("Deleting share server '%s'", server_id) 

4884 security_services = [] 

4885 for ss_name in constants.SECURITY_SERVICES_ALLOWED_TYPES: 

4886 ss = server_details.get('security_service_' + ss_name) 

4887 if ss: 

4888 security_services.append(jsonutils.loads(ss)) 

4889 

4890 self.driver.teardown_server( 

4891 server_details=server_details, 

4892 security_services=security_services) 

4893 except Exception: 

4894 with excutils.save_and_reraise_exception(): 

4895 LOG.error( 

4896 "Share server '%s' failed on deletion.", 

4897 server_id) 

4898 self.db.share_server_update( 

4899 context, server_id, {'status': constants.STATUS_ERROR}) 

4900 else: 

4901 encryption_key_ref = server.get('encryption_key_ref') 

4902 if encryption_key_ref: 4902 ↛ 4903line 4902 didn't jump to line 4903 because the condition on line 4902 was never true

4903 self._delete_encryption_keys_quota(context) 

4904 self.db.share_server_delete(context, share_server['id']) 

4905 

4906 _wrapped_delete_share_server() 

4907 LOG.info( 

4908 "Share server '%s' has been deleted successfully.", 

4909 share_server['id']) 

4910 

4911 def _delete_encryption_keys_quota(self, context): 

4912 reservations = None 

4913 try: 

4914 reservations = QUOTAS.reserve( 

4915 context, project_id=context.project_id, 

4916 encryption_keys=-1, 

4917 ) 

4918 except Exception: 

4919 LOG.exception("Failed to update encryption_keys quota " 

4920 "usages while deleting share server.") 

4921 

4922 if reservations: 4922 ↛ exitline 4922 didn't return from function '_delete_encryption_keys_quota' because the condition on line 4922 was always true

4923 QUOTAS.commit( 

4924 context, reservations, project_id=context.project_id, 

4925 ) 

4926 

4927 @add_hooks 

4928 @utils.require_driver_initialized 

4929 def extend_share(self, context, share_id, new_size, reservations): 

4930 context = context.elevated() 

4931 share = self.db.share_get(context, share_id) 

4932 share_instance = self._get_share_instance(context, share) 

4933 share_server = self._get_share_server(context, share_instance) 

4934 project_id = share['project_id'] 

4935 user_id = share['user_id'] 

4936 

4937 self._notify_about_share_usage(context, share, 

4938 share_instance, "extend.start") 

4939 

4940 try: 

4941 self.driver.extend_share( 

4942 share_instance, new_size, share_server=share_server) 

4943 except Exception as e: 

4944 LOG.exception("Extend share failed.", resource=share) 

4945 self.message_api.create( 

4946 context, 

4947 message_field.Action.EXTEND, 

4948 project_id, 

4949 resource_type=message_field.Resource.SHARE, 

4950 resource_id=share_id, 

4951 detail=message_field.Detail.DRIVER_FAILED_EXTEND) 

4952 try: 

4953 self.db.share_update( 

4954 context, share['id'], 

4955 {'status': constants.STATUS_EXTENDING_ERROR} 

4956 ) 

4957 raise exception.ShareExtendingError( 

4958 reason=str(e), share_id=share_id) 

4959 finally: 

4960 QUOTAS.rollback( 

4961 context, reservations, project_id=project_id, 

4962 user_id=user_id, 

4963 share_type_id=share_instance['share_type_id'], 

4964 ) 

4965 

4966 # we give the user_id of the share, to update the quota usage 

4967 # for the user, who created the share, because on share delete 

4968 # only this quota will be decreased 

4969 QUOTAS.commit( 

4970 context, reservations, project_id=project_id, 

4971 user_id=user_id, share_type_id=share_instance['share_type_id'], 

4972 ) 

4973 

4974 share_update = { 

4975 'size': int(new_size), 

4976 # NOTE(u_glide): translation to lower case should be removed in 

4977 # a row with usage of upper case of share statuses in all places 

4978 'status': constants.STATUS_AVAILABLE.lower() 

4979 } 

4980 share = self.db.share_update(context, share['id'], share_update) 

4981 

4982 LOG.info("Extend share completed successfully.", resource=share) 

4983 

4984 self._notify_about_share_usage(context, share, 

4985 share_instance, "extend.end") 

4986 

4987 @add_hooks 

4988 @utils.require_driver_initialized 

4989 def shrink_share(self, context, share_id, new_size): 

4990 context = context.elevated() 

4991 share = self.db.share_get(context, share_id) 

4992 share_instance = self._get_share_instance(context, share) 

4993 share_server = self._get_share_server(context, share_instance) 

4994 project_id = share['project_id'] 

4995 user_id = share['user_id'] 

4996 new_size = int(new_size) 

4997 replicas = self.db.share_replicas_get_all_by_share( 

4998 context, share['id']) 

4999 supports_replication = len(replicas) > 0 

5000 

5001 self._notify_about_share_usage(context, share, 

5002 share_instance, "shrink.start") 

5003 

5004 def error_occurred(exc, msg, status=constants.STATUS_SHRINKING_ERROR): 

5005 if isinstance(exc, NotImplementedError): 

5006 msg = _("Shrink share operation not supported.") 

5007 status = constants.STATUS_AVAILABLE 

5008 self.message_api.create( 

5009 context, 

5010 message_field.Action.SHRINK, 

5011 share['project_id'], 

5012 resource_type=message_field.Resource.SHARE, 

5013 resource_id=share['id'], 

5014 detail=message_field.Detail.DRIVER_FAILED_SHRINK) 

5015 LOG.exception(msg, resource=share) 

5016 self.db.share_update(context, share['id'], {'status': status}) 

5017 

5018 raise exception.ShareShrinkingError( 

5019 reason=str(exc), share_id=share_id) 

5020 

5021 reservations = None 

5022 

5023 try: 

5024 size_decrease = int(share['size']) - new_size 

5025 # we give the user_id of the share, to update the quota usage 

5026 # for the user, who created the share, because on share delete 

5027 # only this quota will be decreased 

5028 deltas = { 

5029 'project_id': project_id, 

5030 'user_id': user_id, 

5031 'share_type_id': share_instance['share_type_id'], 

5032 'gigabytes': -size_decrease, 

5033 } 

5034 # NOTE(carloss): if the share supports replication we need 

5035 # to query all its replicas and calculate the final size to 

5036 # deallocate (amount of replicas * size to decrease). 

5037 if supports_replication: 

5038 replica_gigs_to_deallocate = len(replicas) * size_decrease 

5039 deltas.update( 

5040 {'replica_gigabytes': -replica_gigs_to_deallocate}) 

5041 reservations = QUOTAS.reserve(context, **deltas) 

5042 except Exception as e: 

5043 error_occurred( 

5044 e, ("Failed to update quota on share shrinking.")) 

5045 

5046 try: 

5047 self.driver.shrink_share( 

5048 share_instance, new_size, share_server=share_server) 

5049 # NOTE(u_glide): Replace following except block by error notification 

5050 # when Manila has such mechanism. It's possible because drivers 

5051 # shouldn't shrink share when this validation error occurs. 

5052 except Exception as e: 

5053 if isinstance(e, exception.ShareShrinkingPossibleDataLoss): 

5054 msg = ("Shrink share failed due to possible data loss.") 

5055 status = constants.STATUS_AVAILABLE 

5056 error_params = {'msg': msg, 'status': status} 

5057 self.message_api.create( 

5058 context, 

5059 message_field.Action.SHRINK, 

5060 share['project_id'], 

5061 resource_type=message_field.Resource.SHARE, 

5062 resource_id=share_id, 

5063 detail=message_field.Detail.DRIVER_REFUSED_SHRINK) 

5064 else: 

5065 error_params = {'msg': ("Shrink share failed.")} 

5066 

5067 try: 

5068 error_occurred(e, **error_params) 

5069 finally: 

5070 QUOTAS.rollback( 

5071 context, reservations, project_id=project_id, 

5072 user_id=user_id, 

5073 share_type_id=share_instance['share_type_id'], 

5074 ) 

5075 

5076 QUOTAS.commit( 

5077 context, reservations, project_id=project_id, 

5078 user_id=user_id, share_type_id=share_instance['share_type_id'], 

5079 ) 

5080 

5081 share_update = { 

5082 'size': new_size, 

5083 'status': constants.STATUS_AVAILABLE 

5084 } 

5085 share = self.db.share_update(context, share['id'], share_update) 

5086 

5087 LOG.info("Shrink share completed successfully.", resource=share) 

5088 

5089 self._notify_about_share_usage(context, share, 

5090 share_instance, "shrink.end") 

5091 

5092 @utils.require_driver_initialized 

5093 def create_share_group(self, context, share_group_id): 

5094 context = context.elevated() 

5095 share_group_ref = self.db.share_group_get(context, share_group_id) 

5096 share_group_ref['host'] = self.host 

5097 shares = self.db.share_instance_get_all_by_share_group_id( 

5098 context, share_group_id) 

5099 

5100 source_share_group_snapshot_id = share_group_ref.get( 

5101 "source_share_group_snapshot_id") 

5102 snap_ref = None 

5103 parent_share_server_id = None 

5104 if source_share_group_snapshot_id: 

5105 snap_ref = self.db.share_group_snapshot_get( 

5106 context, source_share_group_snapshot_id) 

5107 for member in snap_ref['share_group_snapshot_members']: 5107 ↛ 5108line 5107 didn't jump to line 5108 because the loop on line 5107 never started

5108 member['share'] = self.db.share_instance_get( 

5109 context, member['share_instance_id'], with_share_data=True) 

5110 if 'share_group' in snap_ref: 

5111 parent_share_server_id = snap_ref['share_group'][ 

5112 'share_server_id'] 

5113 

5114 status = constants.STATUS_AVAILABLE 

5115 

5116 share_network_id = share_group_ref.get('share_network_id') 

5117 share_server = None 

5118 

5119 if parent_share_server_id and self.driver.driver_handles_share_servers: 

5120 share_server = self.db.share_server_get(context, 

5121 parent_share_server_id) 

5122 share_network_id = ( 

5123 share_server['share_network_id']) 

5124 

5125 if share_network_id and not self.driver.driver_handles_share_servers: 

5126 self.db.share_group_update( 

5127 context, share_group_id, {'status': constants.STATUS_ERROR}) 

5128 msg = _("Driver does not expect share-network to be provided " 

5129 "with current configuration.") 

5130 raise exception.InvalidInput(reason=msg) 

5131 

5132 if not share_server and share_network_id: 

5133 

5134 availability_zone_id = self._get_az_for_share_group( 

5135 context, share_group_ref) 

5136 subnets = ( 

5137 self.db.share_network_subnets_get_all_by_availability_zone_id( 

5138 context, share_network_id, availability_zone_id)) 

5139 

5140 if not subnets: 

5141 raise exception.ShareNetworkSubnetNotFound( 

5142 share_network_subnet_id=None) 

5143 try: 

5144 share_server, share_group_ref = ( 

5145 self._provide_share_server_for_share_group( 

5146 context, share_network_id, subnets, share_group_ref, 

5147 share_group_snapshot=snap_ref, 

5148 ) 

5149 ) 

5150 except Exception: 

5151 with excutils.save_and_reraise_exception(): 

5152 LOG.error("Failed to get share server" 

5153 " for share group creation.") 

5154 self.db.share_group_update( 

5155 context, share_group_id, 

5156 {'status': constants.STATUS_ERROR}) 

5157 self.message_api.create( 

5158 context, 

5159 message_field.Action.CREATE, 

5160 share_group_ref['project_id'], 

5161 resource_type=message_field.Resource.SHARE_GROUP, 

5162 resource_id=share_group_id, 

5163 detail=message_field.Detail.NO_SHARE_SERVER) 

5164 

5165 try: 

5166 # TODO(ameade): Add notification for create.start 

5167 LOG.info("Share group %s: creating", share_group_id) 

5168 

5169 model_update, share_update_list = None, None 

5170 

5171 share_group_ref['shares'] = shares 

5172 if snap_ref: 

5173 model_update, share_update_list = ( 

5174 self.driver.create_share_group_from_share_group_snapshot( 

5175 context, share_group_ref, snap_ref, 

5176 share_server=share_server)) 

5177 else: 

5178 model_update = self.driver.create_share_group( 

5179 context, share_group_ref, share_server=share_server) 

5180 

5181 if model_update: 

5182 share_group_ref = self.db.share_group_update( 

5183 context, share_group_ref['id'], model_update) 

5184 

5185 if share_update_list: 

5186 for share in share_update_list: 

5187 values = copy.deepcopy(share) 

5188 # NOTE(dviroel): To keep backward compatibility we can't 

5189 # keep 'status' as a mandatory parameter. We'll set its 

5190 # value to 'available' as default. 

5191 i_status = values.get('status', constants.STATUS_AVAILABLE) 

5192 if i_status not in [ 

5193 constants.STATUS_AVAILABLE, 

5194 constants.STATUS_CREATING_FROM_SNAPSHOT]: 

5195 msg = _( 

5196 'Driver returned an invalid status %s') % i_status 

5197 raise exception.InvalidShareInstance(reason=msg) 

5198 values['status'] = i_status 

5199 values['progress'] = ( 

5200 '100%' if i_status == constants.STATUS_AVAILABLE 

5201 else '0%') 

5202 values.pop('id') 

5203 export_locations = values.pop('export_locations') 

5204 self.db.share_instance_update(context, share['id'], values) 

5205 self.db.export_locations_update( 

5206 context, share['id'], export_locations) 

5207 

5208 except Exception: 

5209 with excutils.save_and_reraise_exception(): 

5210 self.db.share_group_update( 

5211 context, 

5212 share_group_ref['id'], 

5213 {'status': constants.STATUS_ERROR, 

5214 'availability_zone_id': self._get_az_for_share_group( 

5215 context, share_group_ref), 

5216 'consistent_snapshot_support': self.driver._stats[ 

5217 'share_group_stats'].get( 

5218 'consistent_snapshot_support')}) 

5219 for share in shares: 

5220 self.db.share_instance_update( 

5221 context, share['id'], 

5222 {'status': constants.STATUS_ERROR}) 

5223 LOG.error("Share group %s: create failed", share_group_id) 

5224 

5225 now = timeutils.utcnow() 

5226 for share in shares: 5226 ↛ 5227line 5226 didn't jump to line 5227 because the loop on line 5226 never started

5227 self.db.share_instance_update( 

5228 context, share['id'], {'status': constants.STATUS_AVAILABLE}) 

5229 self.db.share_group_update( 

5230 context, 

5231 share_group_ref['id'], 

5232 {'status': status, 

5233 'created_at': now, 

5234 'availability_zone_id': self._get_az_for_share_group( 

5235 context, share_group_ref), 

5236 'consistent_snapshot_support': self.driver._stats[ 

5237 'share_group_stats'].get('consistent_snapshot_support')}) 

5238 LOG.info("Share group %s: created successfully", share_group_id) 

5239 

5240 # TODO(ameade): Add notification for create.end 

5241 

5242 return share_group_ref['id'] 

5243 

5244 def _get_az_for_share_group(self, context, share_group_ref): 

5245 if not share_group_ref['availability_zone_id']: 5245 ↛ 5246line 5245 didn't jump to line 5246 because the condition on line 5245 was never true

5246 return self.db.availability_zone_get( 

5247 context, self.availability_zone)['id'] 

5248 return share_group_ref['availability_zone_id'] 

5249 

5250 @utils.require_driver_initialized 

5251 def delete_share_group(self, context, share_group_id): 

5252 context = context.elevated() 

5253 share_group_ref = self.db.share_group_get(context, share_group_id) 

5254 share_group_ref['host'] = self.host 

5255 share_group_ref['shares'] = ( 

5256 self.db.share_instance_get_all_by_share_group_id( 

5257 context, share_group_id)) 

5258 

5259 # TODO(ameade): Add notification for delete.start 

5260 

5261 try: 

5262 LOG.info("Share group %s: deleting", share_group_id) 

5263 share_server = None 

5264 if share_group_ref.get('share_server_id'): 5264 ↛ 5265line 5264 didn't jump to line 5265 because the condition on line 5264 was never true

5265 share_server = self.db.share_server_get( 

5266 context, share_group_ref['share_server_id']) 

5267 model_update = self.driver.delete_share_group( 

5268 context, share_group_ref, share_server=share_server) 

5269 

5270 if model_update: 

5271 share_group_ref = self.db.share_group_update( 

5272 context, share_group_ref['id'], model_update) 

5273 

5274 except Exception: 

5275 with excutils.save_and_reraise_exception(): 

5276 self.db.share_group_update( 

5277 context, 

5278 share_group_ref['id'], 

5279 {'status': constants.STATUS_ERROR}) 

5280 LOG.error("Share group %s: delete failed", 

5281 share_group_ref['id']) 

5282 

5283 self.db.share_group_destroy(context, share_group_id) 

5284 LOG.info("Share group %s: deleted successfully", share_group_id) 

5285 

5286 # TODO(ameade): Add notification for delete.end 

5287 

5288 @utils.require_driver_initialized 

5289 def create_share_group_snapshot(self, context, share_group_snapshot_id): 

5290 context = context.elevated() 

5291 snap_ref = self.db.share_group_snapshot_get( 

5292 context, share_group_snapshot_id) 

5293 for member in snap_ref['share_group_snapshot_members']: 

5294 member['share'] = self.db.share_instance_get( 

5295 context, member['share_instance_id'], with_share_data=True) 

5296 

5297 status = constants.STATUS_AVAILABLE 

5298 now = timeutils.utcnow() 

5299 updated_members_ids = [] 

5300 

5301 try: 

5302 LOG.info("Share group snapshot %s: creating", 

5303 share_group_snapshot_id) 

5304 share_server = None 

5305 if snap_ref['share_group'].get('share_server_id'): 5305 ↛ 5306line 5305 didn't jump to line 5306 because the condition on line 5305 was never true

5306 share_server = self.db.share_server_get( 

5307 context, snap_ref['share_group']['share_server_id']) 

5308 snapshot_update, member_update_list = ( 

5309 self.driver.create_share_group_snapshot( 

5310 context, snap_ref, share_server=share_server)) 

5311 

5312 for update in (member_update_list or []): 

5313 # NOTE(vponomaryov): we expect that each member is a dict 

5314 # and has required 'id' key and some optional keys 

5315 # to be updated such as 'provider_location'. It is planned 

5316 # to have here also 'export_locations' when it is supported. 

5317 member_id = update.pop('id', None) 

5318 if not member_id: 

5319 LOG.warning( 

5320 "One of share group snapshot '%s' members does not " 

5321 "have reference ID. Its update was skipped.", 

5322 share_group_snapshot_id) 

5323 continue 

5324 # TODO(vponomaryov): remove following condition when 

5325 # sgs members start supporting export locations. 

5326 if 'export_locations' in update: 5326 ↛ 5334line 5326 didn't jump to line 5334 because the condition on line 5326 was always true

5327 LOG.debug( 

5328 "Removing 'export_locations' data from " 

5329 "share group snapshot member '%s' update because " 

5330 "export locations are not supported.", 

5331 member_id) 

5332 update.pop('export_locations') 

5333 

5334 db_update = { 

5335 'updated_at': now, 

5336 'status': update.pop('status', status) 

5337 } 

5338 if 'provider_location' in update: 5338 ↛ 5341line 5338 didn't jump to line 5341 because the condition on line 5338 was always true

5339 db_update['provider_location'] = ( 

5340 update.pop('provider_location')) 

5341 if 'size' in update: 5341 ↛ 5344line 5341 didn't jump to line 5344 because the condition on line 5341 was always true

5342 db_update['size'] = int(update.pop('size')) 

5343 

5344 updated_members_ids.append(member_id) 

5345 self.db.share_group_snapshot_member_update( 

5346 context, member_id, db_update) 

5347 

5348 if update: 5348 ↛ 5312line 5348 didn't jump to line 5312 because the condition on line 5348 was always true

5349 LOG.debug( 

5350 "Share group snapshot ID='%(sgs_id)s', " 

5351 "share group snapshot member ID='%(sgsm_id)s'. " 

5352 "Following keys of sgs member were not updated " 

5353 "as not allowed: %(keys)s.", 

5354 {'sgs_id': share_group_snapshot_id, 

5355 'sgsm_id': member_id, 

5356 'keys': ', '.join(update)}) 

5357 

5358 if snapshot_update: 

5359 snap_ref = self.db.share_group_snapshot_update( 

5360 context, snap_ref['id'], snapshot_update) 

5361 

5362 except Exception: 

5363 with excutils.save_and_reraise_exception(): 

5364 self.db.share_group_snapshot_update( 

5365 context, 

5366 snap_ref['id'], 

5367 {'status': constants.STATUS_ERROR}) 

5368 LOG.error("Share group snapshot %s: create failed", 

5369 share_group_snapshot_id) 

5370 

5371 for member in (snap_ref.get('share_group_snapshot_members') or []): 

5372 if member['id'] in updated_members_ids: 

5373 continue 

5374 update = {'status': status, 'updated_at': now} 

5375 self.db.share_group_snapshot_member_update( 

5376 context, member['id'], update) 

5377 

5378 self.db.share_group_snapshot_update( 

5379 context, snap_ref['id'], 

5380 {'status': status, 'updated_at': now}) 

5381 LOG.info("Share group snapshot %s: created successfully", 

5382 share_group_snapshot_id) 

5383 

5384 return snap_ref['id'] 

5385 

5386 @utils.require_driver_initialized 

5387 def delete_share_group_snapshot(self, context, share_group_snapshot_id): 

5388 context = context.elevated() 

5389 snap_ref = self.db.share_group_snapshot_get( 

5390 context, share_group_snapshot_id) 

5391 for member in snap_ref['share_group_snapshot_members']: 

5392 member['share'] = self.db.share_instance_get( 

5393 context, member['share_instance_id'], with_share_data=True) 

5394 

5395 snapshot_update = False 

5396 

5397 try: 

5398 LOG.info("Share group snapshot %s: deleting", 

5399 share_group_snapshot_id) 

5400 

5401 share_server = None 

5402 if snap_ref['share_group'].get('share_server_id'): 

5403 share_server = self.db.share_server_get( 

5404 context, snap_ref['share_group']['share_server_id']) 

5405 

5406 snapshot_update, member_update_list = ( 

5407 self.driver.delete_share_group_snapshot( 

5408 context, snap_ref, share_server=share_server)) 

5409 

5410 if member_update_list: 

5411 snapshot_update = snapshot_update or {} 

5412 snapshot_update['share_group_snapshot_members'] = [] 

5413 for update in (member_update_list or []): 

5414 snapshot_update['share_group_snapshot_members'].append(update) 

5415 

5416 if snapshot_update: 

5417 snap_ref = self.db.share_group_snapshot_update( 

5418 context, snap_ref['id'], snapshot_update) 

5419 

5420 except Exception: 

5421 with excutils.save_and_reraise_exception(): 

5422 self.db.share_group_snapshot_update( 

5423 context, 

5424 snap_ref['id'], 

5425 {'status': constants.STATUS_ERROR}) 

5426 LOG.error("Share group snapshot %s: delete failed", 

5427 snap_ref['name']) 

5428 

5429 self.db.share_group_snapshot_destroy(context, share_group_snapshot_id) 

5430 

5431 LOG.info("Share group snapshot %s: deleted successfully", 

5432 share_group_snapshot_id) 

5433 

5434 def _get_share_server_dict(self, context, share_server): 

5435 share_server_ref = { 

5436 'id': share_server.get('id'), 

5437 'project_id': share_server.get('project_id'), 

5438 'updated_at': share_server.get('updated_at'), 

5439 'status': share_server.get('status'), 

5440 'host': share_server.get('host'), 

5441 'share_network_name': share_server.get('share_network_name'), 

5442 'share_network_id': share_server.get('share_network_id'), 

5443 'created_at': share_server.get('created_at'), 

5444 'backend_details': share_server.get('backend_details'), 

5445 'share_network_subnet_ids': 

5446 share_server.get('share_network_subnet_ids', []), 

5447 'is_auto_deletable': share_server.get('is_auto_deletable', None), 

5448 'identifier': share_server.get('identifier', None), 

5449 'network_allocations': share_server.get('network_allocations', 

5450 None), 

5451 } 

5452 return share_server_ref 

5453 

5454 def _get_export_location_dict(self, context, export_location): 

5455 export_location_ref = { 

5456 'id': export_location.get('uuid'), 

5457 'path': export_location.get('path'), 

5458 'created_at': export_location.get('created_at'), 

5459 'updated_at': export_location.get('updated_at'), 

5460 'share_instance_id': 

5461 export_location.get('share_instance_id', None), 

5462 'is_admin_only': export_location.get('is_admin_only', None), 

5463 } 

5464 return export_location_ref 

5465 

5466 def _get_share_instance_dict(self, context, share_instance): 

5467 # TODO(gouthamr): remove method when the db layer returns primitives 

5468 share_instance_ref = { 

5469 'id': share_instance.get('id'), 

5470 'name': share_instance.get('name'), 

5471 'share_id': share_instance.get('share_id'), 

5472 'host': share_instance.get('host'), 

5473 'status': share_instance.get('status'), 

5474 'replica_state': share_instance.get('replica_state'), 

5475 'availability_zone_id': share_instance.get('availability_zone_id'), 

5476 'share_network_id': share_instance.get('share_network_id'), 

5477 'share_server_id': share_instance.get('share_server_id'), 

5478 'deleted': share_instance.get('deleted'), 

5479 'terminated_at': share_instance.get('terminated_at'), 

5480 'launched_at': share_instance.get('launched_at'), 

5481 'scheduled_at': share_instance.get('scheduled_at'), 

5482 'updated_at': share_instance.get('updated_at'), 

5483 'deleted_at': share_instance.get('deleted_at'), 

5484 'created_at': share_instance.get('created_at'), 

5485 'share_server': self._get_share_server(context, share_instance), 

5486 'access_rules_status': share_instance.get('access_rules_status'), 

5487 # Share details 

5488 'user_id': share_instance.get('user_id'), 

5489 'project_id': share_instance.get('project_id'), 

5490 'size': share_instance.get('size'), 

5491 'display_name': share_instance.get('display_name'), 

5492 'display_description': share_instance.get('display_description'), 

5493 'snapshot_id': share_instance.get('snapshot_id'), 

5494 'share_proto': share_instance.get('share_proto'), 

5495 'share_type_id': share_instance.get('share_type_id'), 

5496 'is_public': share_instance.get('is_public'), 

5497 'share_group_id': share_instance.get('share_group_id'), 

5498 'source_share_group_snapshot_member_id': share_instance.get( 

5499 'source_share_group_snapshot_member_id'), 

5500 'availability_zone': share_instance.get('availability_zone'), 

5501 'mount_point_name': share_instance.get('mount_point_name'), 

5502 } 

5503 if share_instance_ref['share_server']: 

5504 share_instance_ref['share_server'] = self._get_share_server_dict( 

5505 context, share_instance_ref['share_server'] 

5506 ) 

5507 share_instance_ref['export_locations'] = [ 

5508 self._get_export_location_dict(context, el) for 

5509 el in share_instance.get('export_locations') or [] 

5510 ] 

5511 return share_instance_ref 

5512 

5513 def _get_snapshot_instance_dict(self, context, snapshot_instance, 

5514 snapshot=None): 

5515 # TODO(gouthamr): remove method when the db layer returns primitives 

5516 snapshot_instance_ref = { 

5517 'name': snapshot_instance.get('name'), 

5518 'share_id': snapshot_instance.get('share_id'), 

5519 'share_name': snapshot_instance.get('share_name'), 

5520 'status': snapshot_instance.get('status'), 

5521 'id': snapshot_instance.get('id'), 

5522 'deleted': snapshot_instance.get('deleted') or False, 

5523 'created_at': snapshot_instance.get('created_at'), 

5524 'share': snapshot_instance.get('share'), 

5525 'updated_at': snapshot_instance.get('updated_at'), 

5526 'share_instance_id': snapshot_instance.get('share_instance_id'), 

5527 'snapshot_id': snapshot_instance.get('snapshot_id'), 

5528 'progress': snapshot_instance.get('progress'), 

5529 'deleted_at': snapshot_instance.get('deleted_at'), 

5530 'provider_location': snapshot_instance.get('provider_location'), 

5531 } 

5532 

5533 if snapshot: 

5534 snapshot_instance_ref.update({ 

5535 'size': snapshot.get('size'), 

5536 }) 

5537 

5538 return snapshot_instance_ref 

5539 

5540 def snapshot_update_access(self, context, snapshot_instance_id): 

5541 snapshot_instance = self.db.share_snapshot_instance_get( 

5542 context, snapshot_instance_id, with_share_data=True) 

5543 

5544 share_server = self._get_share_server( 

5545 context, snapshot_instance['share_instance']) 

5546 

5547 self.snapshot_access_helper.update_access_rules( 

5548 context, snapshot_instance['id'], share_server=share_server) 

5549 

5550 def _notify_about_share_usage(self, context, share, share_instance, 

5551 event_suffix, extra_usage_info=None): 

5552 share_utils.notify_about_share_usage( 

5553 context, share, share_instance, event_suffix, 

5554 extra_usage_info=extra_usage_info, host=self.host) 

5555 

5556 @utils.require_driver_initialized 

5557 def create_backup(self, context, backup): 

5558 share_id = backup['share_id'] 

5559 backup_id = backup['id'] 

5560 share = self.db.share_get(context, share_id) 

5561 share_instance = self._get_share_instance(context, share) 

5562 

5563 LOG.info('Create backup started, backup: %(backup)s share: ' 

5564 '%(share)s.', {'backup': backup_id, 'share': share_id}) 

5565 try: 

5566 share_server = self._get_share_server(context, share) 

5567 self.driver.create_backup(context, share_instance, backup, 

5568 share_server=share_server) 

5569 except Exception as err: 

5570 with excutils.save_and_reraise_exception(): 

5571 LOG.error("Failed to create share backup %s by driver.", 

5572 backup_id) 

5573 self.db.share_update( 

5574 context, share_id, 

5575 {'status': constants.STATUS_AVAILABLE}) 

5576 self.db.share_backup_update( 

5577 context, backup_id, 

5578 {'status': constants.STATUS_ERROR, 'fail_reason': err}) 

5579 

5580 @periodic_task.periodic_task( 

5581 spacing=CONF.driver_backup_continue_update_interval) 

5582 @utils.require_driver_initialized 

5583 def create_backup_continue(self, context): 

5584 """Invokes driver to continue backup of share.""" 

5585 filters = {'status': constants.STATUS_CREATING, 

5586 'host': self.host, 

5587 'topic': CONF.share_topic} 

5588 backups = self.db.share_backups_get_all(context, filters) 

5589 for backup in backups: 5589 ↛ 5590line 5589 didn't jump to line 5590 because the loop on line 5589 never started

5590 backup_id = backup['id'] 

5591 share_id = backup['share_id'] 

5592 share = self.db.share_get(context, share_id) 

5593 share_instance = self._get_share_instance(context, share) 

5594 result = {} 

5595 try: 

5596 share_server = self._get_share_server(context, share_instance) 

5597 result = self.driver.create_backup_continue( 

5598 context, share_instance, backup, share_server=share_server) 

5599 progress = result.get('total_progress', '0') 

5600 self.db.share_backup_update(context, backup_id, 

5601 {'progress': progress}) 

5602 if progress == '100': 

5603 self.db.share_update( 

5604 context, share_id, 

5605 {'status': constants.STATUS_AVAILABLE}) 

5606 self.db.share_backup_update( 

5607 context, backup_id, 

5608 {'status': constants.STATUS_AVAILABLE}) 

5609 LOG.info("Created share backup %s successfully.", 

5610 backup_id) 

5611 except Exception: 

5612 LOG.warning("Failed to get progress of share %(share)s " 

5613 "backing up in share_backup %(backup).", 

5614 {'share': share_id, 'backup': backup_id}) 

5615 self.db.share_update( 

5616 context, share_id, 

5617 {'status': constants.STATUS_AVAILABLE}) 

5618 self.db.share_backup_update( 

5619 context, backup_id, 

5620 {'status': constants.STATUS_ERROR, 'progress': '0'}) 

5621 

5622 def delete_backup(self, context, backup): 

5623 LOG.info('Delete backup started, backup: %s.', backup['id']) 

5624 

5625 backup_id = backup['id'] 

5626 project_id = backup['project_id'] 

5627 share_id = backup['share_id'] 

5628 share = self.db.share_get(context, share_id) 

5629 share_instance = self._get_share_instance(context, share) 

5630 try: 

5631 share_server = self._get_share_server(context, share_instance) 

5632 self.driver.delete_backup(context, backup, share_instance, 

5633 share_server=share_server) 

5634 except Exception: 

5635 with excutils.save_and_reraise_exception(): 

5636 LOG.error("Failed to delete share backup %s.", backup_id) 

5637 self.db.share_backup_update( 

5638 context, backup_id, 

5639 {'status': constants.STATUS_ERROR_DELETING}) 

5640 

5641 try: 

5642 reserve_opts = { 

5643 'backups': -1, 

5644 'backup_gigabytes': -backup['size'], 

5645 } 

5646 reservations = QUOTAS.reserve( 

5647 context, project_id=project_id, **reserve_opts) 

5648 except Exception as e: 

5649 reservations = None 

5650 LOG.warning("Failed to update backup quota for %(pid)s: %(err)s.", 

5651 {'pid': project_id, 'err': e}) 

5652 

5653 if reservations: 

5654 QUOTAS.commit(context, reservations, project_id=project_id) 

5655 

5656 self.db.share_backup_delete(context, backup_id) 

5657 LOG.info("Share backup %s deleted successfully.", backup_id) 

5658 

5659 def restore_backup(self, context, backup, share_id): 

5660 LOG.info('Restore backup started, backup: %(backup_id)s ' 

5661 'share: %(share_id)s.', 

5662 {'backup_id': backup['id'], 'share_id': share_id}) 

5663 

5664 backup_id = backup['id'] 

5665 backup_share_id = backup['share_id'] 

5666 share = self.db.share_get(context, share_id) 

5667 share_instance = self._get_share_instance(context, share) 

5668 

5669 try: 

5670 if (self.driver.restore_to_target_support is False and 5670 ↛ 5689line 5670 didn't jump to line 5689 because the condition on line 5670 was always true

5671 share_id != backup_share_id): 

5672 

5673 self.message_api.create( 

5674 context, 

5675 message_field.Action.RESTORE_BACKUP, 

5676 share['project_id'], 

5677 resource_type=message_field.Resource.SHARE, 

5678 resource_id=share['id'], 

5679 detail=message_field.Detail.TARGETED_RESTORE_UNSUPPORTED 

5680 ) 

5681 

5682 msg = _("Cannot restore backup %(backup)s to target share " 

5683 "%(share)s as share driver does not provide support " 

5684 " for targeted restores") % ( 

5685 {'backup': backup_id, 'share': share_id}) 

5686 LOG.exception(msg) 

5687 raise exception.BackupException(reason=msg) 

5688 

5689 share_server = self._get_share_server(context, share_instance) 

5690 self.driver.restore_backup(context, backup, share_instance, 

5691 share_server=share_server) 

5692 except Exception: 

5693 with excutils.save_and_reraise_exception(): 

5694 LOG.error("Failed to restore backup %(backup)s to share " 

5695 "%(share)s by driver.", 

5696 {'backup': backup_id, 'share': share_id}) 

5697 self.db.share_update( 

5698 context, share_id, 

5699 {'status': constants.STATUS_BACKUP_RESTORING_ERROR}) 

5700 self.db.share_backup_update( 

5701 context, backup['id'], 

5702 {'status': constants.STATUS_ERROR}) 

5703 

5704 @periodic_task.periodic_task( 

5705 spacing=CONF.driver_restore_continue_update_interval) 

5706 @utils.require_driver_initialized 

5707 def restore_backup_continue(self, context): 

5708 filters = {'status': constants.STATUS_RESTORING, 

5709 'host': self.host, 

5710 'topic': CONF.share_topic} 

5711 backups = self.db.share_backups_get_all(context, filters) 

5712 for backup in backups: 5712 ↛ 5713line 5712 didn't jump to line 5713 because the loop on line 5712 never started

5713 backup_id = backup['id'] 

5714 try: 

5715 filters = { 

5716 'source_backup_id': backup_id, 

5717 } 

5718 shares = self.db.share_get_all(context, filters) 

5719 except Exception: 

5720 LOG.warning('Failed to get shares for backup %s', backup_id) 

5721 continue 

5722 

5723 for share in shares: 

5724 if share['status'] != constants.STATUS_BACKUP_RESTORING: 

5725 continue 

5726 

5727 share_id = share['id'] 

5728 share_instance = self._get_share_instance(context, share) 

5729 try: 

5730 share_server = self._get_share_server( 

5731 context, share_instance) 

5732 result = self.driver.restore_backup_continue( 

5733 context, backup, share_instance, 

5734 share_server=share_server) 

5735 progress = result.get('total_progress', '0') 

5736 self.db.share_backup_update( 

5737 context, backup_id, {'restore_progress': progress}) 

5738 

5739 if progress == '100': 

5740 self.db.share_update( 

5741 context, share_id, 

5742 {'status': constants.STATUS_AVAILABLE}) 

5743 self.db.share_backup_update( 

5744 context, backup_id, 

5745 {'status': constants.STATUS_AVAILABLE}) 

5746 LOG.info("Share backup %s restored successfully.", 

5747 backup_id) 

5748 except Exception: 

5749 LOG.exception("Failed to get progress of share_backup " 

5750 "%(backup)s restoring in share %(share).", 

5751 {'share': share_id, 'backup': backup_id}) 

5752 self.db.share_update( 

5753 context, share_id, 

5754 {'status': constants.STATUS_BACKUP_RESTORING_ERROR}) 

5755 self.db.share_backup_update( 

5756 context, backup_id, 

5757 {'status': constants.STATUS_AVAILABLE, 

5758 'restore_progress': '0'}) 

5759 

5760 @periodic_task.periodic_task( 

5761 spacing=CONF.share_usage_size_update_interval, 

5762 enabled=CONF.enable_gathering_share_usage_size) 

5763 @utils.require_driver_initialized 

5764 def update_share_usage_size(self, context): 

5765 """Invokes driver to gather usage size of shares.""" 

5766 updated_share_instances = [] 

5767 share_instances = self.db.share_instance_get_all_by_host( 

5768 context, host=self.host, with_share_data=True) 

5769 

5770 if share_instances: 5770 ↛ 5777line 5770 didn't jump to line 5777 because the condition on line 5770 was always true

5771 try: 

5772 updated_share_instances = self.driver.update_share_usage_size( 

5773 context, share_instances) 

5774 except Exception: 

5775 LOG.exception("Gather share usage size failure.") 

5776 

5777 for si in updated_share_instances: 

5778 share_instance = self._get_share_instance(context, si['id']) 

5779 share = self.db.share_get(context, share_instance['share_id']) 

5780 self._notify_about_share_usage( 

5781 context, share, share_instance, "consumed.size", 

5782 extra_usage_info={'used_size': si['used_size'], 

5783 'gathered_at': si['gathered_at']}) 

5784 

5785 @periodic_task.periodic_task(spacing=CONF.periodic_interval) 

5786 @utils.require_driver_initialized 

5787 def periodic_share_status_update(self, context): 

5788 """Invokes share driver to update shares status.""" 

5789 LOG.debug("Updating status of share instances.") 

5790 share_instances = self.db.share_instance_get_all_by_host( 

5791 context, self.host, with_share_data=True, 

5792 status=constants.STATUS_CREATING_FROM_SNAPSHOT) 

5793 

5794 for si in share_instances: 

5795 si_dict = self._get_share_instance_dict(context, si) 

5796 self._update_share_status(context, si_dict) 

5797 

5798 def _update_share_status(self, context, share_instance): 

5799 share_server = self._get_share_server(context, share_instance) 

5800 if share_server is not None: 5800 ↛ 5801line 5800 didn't jump to line 5801 because the condition on line 5800 was never true

5801 share_server = self._get_share_server_dict(context, 

5802 share_server) 

5803 try: 

5804 data_updates = self.driver.get_share_status(share_instance, 

5805 share_server) 

5806 except Exception: 

5807 LOG.exception( 

5808 ("Unexpected driver error occurred while updating status for " 

5809 "share instance %(id)s that belongs to share '%(share_id)s'"), 

5810 {'id': share_instance['id'], 

5811 'share_id': share_instance['share_id']} 

5812 ) 

5813 data_updates = { 

5814 'status': constants.STATUS_ERROR 

5815 } 

5816 

5817 status = data_updates.get('status') 

5818 if status == constants.STATUS_ERROR: 

5819 msg = ("Status of share instance %(id)s that belongs to share " 

5820 "%(share_id)s was updated to '%(status)s'." 

5821 % {'id': share_instance['id'], 

5822 'share_id': share_instance['share_id'], 

5823 'status': status}) 

5824 LOG.error(msg) 

5825 self.db.share_instance_update( 

5826 context, share_instance['id'], 

5827 {'status': constants.STATUS_ERROR, 

5828 'progress': None}) 

5829 self.message_api.create( 

5830 context, 

5831 message_field.Action.UPDATE, 

5832 share_instance['project_id'], 

5833 resource_type=message_field.Resource.SHARE, 

5834 resource_id=share_instance['share_id'], 

5835 detail=message_field.Detail.DRIVER_FAILED_CREATING_FROM_SNAP) 

5836 return 

5837 

5838 export_locations = data_updates.get('export_locations') 

5839 progress = data_updates.get('progress') 

5840 

5841 statuses_requiring_update = [ 

5842 constants.STATUS_AVAILABLE, 

5843 constants.STATUS_CREATING_FROM_SNAPSHOT] 

5844 

5845 if status in statuses_requiring_update: 5845 ↛ 5861line 5845 didn't jump to line 5861 because the condition on line 5845 was always true

5846 si_updates = { 

5847 'status': status, 

5848 } 

5849 progress = ('100%' if status == constants.STATUS_AVAILABLE 

5850 else progress) 

5851 if progress is not None: 5851 ↛ 5853line 5851 didn't jump to line 5853 because the condition on line 5851 was always true

5852 si_updates.update({'progress': progress}) 

5853 self.db.share_instance_update( 

5854 context, share_instance['id'], si_updates) 

5855 msg = ("Status of share instance %(id)s that belongs to share " 

5856 "%(share_id)s was updated to '%(status)s'." 

5857 % {'id': share_instance['id'], 

5858 'share_id': share_instance['share_id'], 

5859 'status': status}) 

5860 LOG.debug(msg) 

5861 if export_locations: 5861 ↛ exitline 5861 didn't return from function '_update_share_status' because the condition on line 5861 was always true

5862 self.db.export_locations_update( 

5863 context, share_instance['id'], export_locations) 

5864 

5865 def _validate_check_compatibility_result( 

5866 self, context, resource_id, share_instances, snapshot_instances, 

5867 driver_compatibility, dest_host, nondisruptive, writable, 

5868 preserve_snapshots, resource_type='share'): 

5869 resource_exception = ( 

5870 exception.ShareMigrationFailed 

5871 if resource_type == 'share' 

5872 else exception.ShareServerMigrationFailed) 

5873 if not driver_compatibility.get('compatible'): 

5874 msg = _("Destination host %(host)s is not compatible with " 

5875 "%(resource_type)s %(resource_id)s's source backend for " 

5876 "driver-assisted migration.") % { 

5877 'host': dest_host, 

5878 'resource_id': resource_id, 

5879 'resource_type': resource_type, 

5880 } 

5881 raise resource_exception(reason=msg) 

5882 

5883 if (not driver_compatibility.get('nondisruptive') and 

5884 nondisruptive): 

5885 msg = _("Driver cannot perform a non-disruptive migration of " 

5886 "%(resource_type)s %(resource_id)s.") % { 

5887 'resource_type': resource_type, 

5888 'resource_id': resource_id 

5889 } 

5890 raise resource_exception(reason=msg) 

5891 

5892 if not driver_compatibility.get('writable') and writable: 

5893 msg = _("Driver cannot perform migration of %(resource_type)s " 

5894 "%(resource_id)s while remaining writable.") % { 

5895 'resource_type': resource_type, 

5896 'resource_id': resource_id 

5897 } 

5898 raise resource_exception(reason=msg) 

5899 

5900 if (not driver_compatibility.get('preserve_snapshots') 

5901 and preserve_snapshots): 

5902 msg = _("Driver cannot perform migration of %(resource_type)s " 

5903 "%(resource_id)s while preserving snapshots.") % { 

5904 'resource_type': resource_type, 

5905 'resource_id': resource_id 

5906 } 

5907 raise resource_exception(reason=msg) 

5908 

5909 if (not driver_compatibility.get('preserve_snapshots') 5909 ↛ exitline 5909 didn't return from function '_validate_check_compatibility_result' because the condition on line 5909 was always true

5910 and snapshot_instances): 

5911 msg = _("Driver does not support preserving snapshots. The " 

5912 "migration of the %(resource_type)s %(resource_id)s " 

5913 "cannot proceed while it has snapshots.") % { 

5914 'resource_type': resource_type, 

5915 'resource_id': resource_id 

5916 } 

5917 raise resource_exception(reason=msg) 

5918 

5919 def _update_resource_status(self, context, status, task_state=None, 

5920 share_instance_ids=None, 

5921 snapshot_instance_ids=None): 

5922 fields = {'status': status} 

5923 if task_state: 

5924 fields['task_state'] = task_state 

5925 if share_instance_ids: 

5926 self.db.share_instance_status_update( 

5927 context, share_instance_ids, fields) 

5928 if snapshot_instance_ids: 

5929 self.db.share_snapshot_instances_status_update( 

5930 context, snapshot_instance_ids, fields) 

5931 

5932 def _share_server_migration_start_driver( 

5933 self, context, source_share_server, dest_host, writable, 

5934 nondisruptive, preserve_snapshots, new_share_network_id): 

5935 

5936 share_instances = self.db.share_instance_get_all_by_share_server( 

5937 context, source_share_server['id'], with_share_data=True) 

5938 share_instance_ids = [x.id for x in share_instances] 

5939 

5940 snapshot_instances = ( 

5941 self.db.share_snapshot_instance_get_all_with_filters( 

5942 context, {'share_instance_ids': share_instance_ids})) 

5943 snapshot_instance_ids = [x.id for x in snapshot_instances] 

5944 

5945 old_share_network = self.db.share_network_get( 

5946 context, share_instances[0]['share_network_id']) 

5947 new_share_network = self.db.share_network_get( 

5948 context, new_share_network_id) 

5949 

5950 service_host = share_utils.extract_host(dest_host) 

5951 service = self.db.service_get_by_args( 

5952 context, service_host, 'manila-share') 

5953 

5954 # NOTE(dviroel): We'll build a list of request specs and send it to 

5955 # the driver so vendors have a chance to validate if the destination 

5956 # host meets the requirements before starting the migration. 

5957 shares_request_spec = ( 

5958 self.share_api.get_share_server_migration_request_spec_dict( 

5959 context, 

5960 share_instances, 

5961 snapshot_instances, 

5962 availability_zone_id=service['availability_zone_id'], 

5963 share_network_id=new_share_network_id)) 

5964 

5965 extended_allocs = None 

5966 dest_share_server = None 

5967 try: 

5968 # NOTE: Extend network allocations to destination host, i.e. create 

5969 # inactive port bindings on the destination host. Refresh 

5970 # network_allocations field in source_share_server with the new 

5971 # bindings, so that correct segmentation id is used during 

5972 # compatibility check and migration. 

5973 if CONF.server_migration_extend_neutron_network: 5973 ↛ 5974line 5973 didn't jump to line 5974 because the condition on line 5973 was never true

5974 extended_allocs = ( 

5975 self.driver.network_api.extend_network_allocations( 

5976 context, source_share_server)) 

5977 source_share_server['network_allocations'] = extended_allocs 

5978 

5979 compatibility = ( 

5980 self.driver.share_server_migration_check_compatibility( 

5981 context, source_share_server, dest_host, old_share_network, 

5982 new_share_network, shares_request_spec)) 

5983 

5984 self._validate_check_compatibility_result( 

5985 context, source_share_server, share_instances, 

5986 snapshot_instances, compatibility, dest_host, nondisruptive, 

5987 writable, preserve_snapshots, resource_type='share server') 

5988 

5989 create_server_on_backend = not compatibility.get('nondisruptive') 

5990 dest_share_server = self._provide_share_server_for_migration( 

5991 context, source_share_server, new_share_network_id, 

5992 service['availability_zone_id'], dest_host, 

5993 create_on_backend=create_server_on_backend) 

5994 

5995 net_changes_identified = False 

5996 if not create_server_on_backend: 

5997 dest_share_server = self.db.share_server_get( 

5998 context, dest_share_server['id']) 

5999 net_changes_identified = ( 

6000 not share_utils.is_az_subnets_compatible( 

6001 dest_share_server['share_network_subnets'], 

6002 source_share_server['share_network_subnets'])) 

6003 

6004 # NOTE(carloss): Even though the share back end won't need to 

6005 # create a share server, if a network change was identified, 

6006 # there is need to allocate new interfaces to the share server, 

6007 # so the back end can set up the new ips considering 

6008 # the new networking parameters when completing the migration. 

6009 # In such case, the migration will be disruptive, since the old 

6010 # allocations will be replaced by the new ones. 

6011 if net_changes_identified: 6011 ↛ 6027line 6011 didn't jump to line 6027 because the condition on line 6011 was always true

6012 share_network_subnets = ( 

6013 self.db. 

6014 share_network_subnet_get_all_by_share_server_id( 

6015 context, dest_share_server['id'])) 

6016 for share_network_subnet in share_network_subnets: 

6017 self.driver.allocate_network( 

6018 context, dest_share_server, new_share_network, 

6019 share_network_subnet) 

6020 self.driver.allocate_admin_network( 

6021 context, dest_share_server) 

6022 # Refresh the share server so it will have the network 

6023 # allocations when sent to the driver 

6024 dest_share_server = self.db.share_server_get( 

6025 context, dest_share_server['id']) 

6026 

6027 self.db.share_server_update( 

6028 context, dest_share_server['id'], 

6029 {'status': constants.STATUS_SERVER_MIGRATING_TO, 

6030 'task_state': constants.TASK_STATE_MIGRATION_IN_PROGRESS, 

6031 'source_share_server_id': source_share_server['id']}) 

6032 

6033 if not compatibility.get('writable'): 

6034 # NOTE(dviroel): Only modify access rules to read-only if the 

6035 # driver doesn't support 'writable'. 

6036 self._cast_access_rules_to_readonly_for_server( 

6037 context, share_instances, source_share_server, 

6038 dest_host=source_share_server['host']) 

6039 

6040 LOG.debug("Initiating driver migration for share server %s.", 

6041 source_share_server['id']) 

6042 

6043 self.db.share_server_update( 

6044 context, source_share_server['id'], 

6045 {'task_state': ( 

6046 constants.TASK_STATE_MIGRATION_DRIVER_STARTING)}) 

6047 self.db.share_server_update( 

6048 context, dest_share_server['id'], 

6049 {'task_state': ( 

6050 constants.TASK_STATE_MIGRATION_DRIVER_STARTING)}) 

6051 

6052 server_info = self.driver.share_server_migration_start( 

6053 context, source_share_server, dest_share_server, 

6054 share_instances, snapshot_instances) 

6055 

6056 backend_details = ( 

6057 server_info.get('backend_details') if server_info else None) 

6058 if extended_allocs: 6058 ↛ 6059line 6058 didn't jump to line 6059 because the condition on line 6058 was never true

6059 backend_details = backend_details or {} 

6060 backend_details['segmentation_id'] = ( 

6061 extended_allocs[0]['segmentation_id']) 

6062 if backend_details: 6062 ↛ 6066line 6062 didn't jump to line 6066 because the condition on line 6062 was always true

6063 self.db.share_server_backend_details_set( 

6064 context, dest_share_server['id'], backend_details) 

6065 

6066 self.db.share_server_update( 

6067 context, source_share_server['id'], 

6068 {'task_state': ( 

6069 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS)}) 

6070 self.db.share_server_update( 

6071 context, dest_share_server['id'], 

6072 {'task_state': ( 

6073 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS)}) 

6074 

6075 except Exception: 

6076 # Rollback status changes for affected resources 

6077 self._update_resource_status( 

6078 context, constants.STATUS_AVAILABLE, 

6079 share_instance_ids=share_instance_ids, 

6080 snapshot_instance_ids=snapshot_instance_ids) 

6081 # Rollback port bindings on destination host 

6082 if extended_allocs: 6082 ↛ 6083line 6082 didn't jump to line 6083 because the condition on line 6082 was never true

6083 self.driver.network_api.delete_extended_allocations( 

6084 context, source_share_server, dest_share_server) 

6085 # Rollback read only access rules 

6086 self._reset_read_only_access_rules_for_server( 

6087 context, share_instances, source_share_server, 

6088 dest_host=source_share_server['host']) 

6089 if dest_share_server: 6089 ↛ 6102line 6089 didn't jump to line 6102 because the condition on line 6089 was always true

6090 self.db.share_server_update( 

6091 context, dest_share_server['id'], 

6092 {'task_state': constants.TASK_STATE_MIGRATION_ERROR, 

6093 'status': constants.STATUS_ERROR}) 

6094 if not create_server_on_backend: 6094 ↛ 6095line 6094 didn't jump to line 6095 because the condition on line 6094 was never true

6095 if net_changes_identified: 

6096 self.driver.deallocate_network( 

6097 context, dest_share_server['id']) 

6098 self.db.share_server_delete( 

6099 context, dest_share_server['id']) 

6100 else: 

6101 self.delete_share_server(context, dest_share_server) 

6102 msg = _("Driver-assisted migration of share server %s " 

6103 "failed.") % source_share_server['id'] 

6104 LOG.exception(msg) 

6105 raise exception.ShareServerMigrationFailed(reason=msg) 

6106 

6107 return True 

6108 

6109 @add_hooks 

6110 @utils.require_driver_initialized 

6111 def share_server_migration_check(self, context, share_server_id, dest_host, 

6112 writable, nondisruptive, 

6113 preserve_snapshots, new_share_network_id): 

6114 driver_result = {} 

6115 result = { 

6116 'compatible': False, 

6117 'writable': None, 

6118 'preserve_snapshots': None, 

6119 'nondisruptive': None, 

6120 'share_network_id': new_share_network_id, 

6121 'migration_cancel': None, 

6122 'migration_get_progress': None 

6123 } 

6124 

6125 if not self.driver.driver_handles_share_servers: 

6126 LOG.error('This operation is supported only on backends that ' 

6127 'handles share servers.') 

6128 return result 

6129 

6130 share_server = self.db.share_server_get(context, share_server_id) 

6131 share_instances = self.db.share_instance_get_all_by_share_server( 

6132 context, share_server_id, with_share_data=True) 

6133 share_instance_ids = [x.id for x in share_instances] 

6134 

6135 snapshot_instances = ( 

6136 self.db.share_snapshot_instance_get_all_with_filters( 

6137 context, {'share_instance_ids': share_instance_ids})) 

6138 

6139 old_share_network = self.db.share_network_get( 

6140 context, share_instances[0]['share_network_id']) 

6141 new_share_network = self.db.share_network_get( 

6142 context, new_share_network_id) 

6143 

6144 service_host = share_utils.extract_host(dest_host) 

6145 service = self.db.service_get_by_args( 

6146 context, service_host, 'manila-share') 

6147 

6148 # NOTE: Extend network allocations to destination host, i.e. create 

6149 # inactive port bindings on destination host with the same ports in the 

6150 # share network subnet. Refresh share_server with new network 

6151 # allocations, so that correct segmentation id is used in the 

6152 # compatibility check. 

6153 if CONF.server_migration_extend_neutron_network: 6153 ↛ 6154line 6153 didn't jump to line 6154 because the condition on line 6153 was never true

6154 try: 

6155 allocs = self.driver.network_api.extend_network_allocations( 

6156 context, share_server) 

6157 share_server['network_allocations'] = allocs 

6158 except Exception: 

6159 LOG.warning( 

6160 'Failed to extend network allocations for ' 

6161 'share server %s.', share_server['id']) 

6162 return result 

6163 

6164 # NOTE(dviroel): We'll build a list of request specs and send it to 

6165 # the driver so vendors have a chance to validate if the destination 

6166 # host meets the requirements before starting the migration. 

6167 shares_request_spec = ( 

6168 self.share_api.get_share_server_migration_request_spec_dict( 

6169 context, 

6170 share_instances, 

6171 snapshot_instances, 

6172 availability_zone_id=service['availability_zone_id'], 

6173 share_network_id=new_share_network_id)) 

6174 

6175 try: 

6176 driver_result = ( 

6177 self.driver.share_server_migration_check_compatibility( 

6178 context, share_server, dest_host, old_share_network, 

6179 new_share_network, shares_request_spec)) 

6180 

6181 self._validate_check_compatibility_result( 

6182 context, share_server, share_instances, 

6183 snapshot_instances, driver_result, dest_host, nondisruptive, 

6184 writable, preserve_snapshots, resource_type='share server') 

6185 

6186 except Exception: 

6187 # Update driver result to not compatible since it didn't pass in 

6188 # the validations. 

6189 driver_result['compatible'] = False 

6190 

6191 # NOTE: Delete port bindings on destination host after compatibility 

6192 # check 

6193 if CONF.server_migration_extend_neutron_network: 6193 ↛ 6194line 6193 didn't jump to line 6194 because the condition on line 6193 was never true

6194 self.driver.network_api.delete_extended_allocations( 

6195 context, share_server) 

6196 

6197 result.update(driver_result) 

6198 

6199 return result 

6200 

6201 @add_hooks 

6202 @utils.require_driver_initialized 

6203 def share_server_migration_start( 

6204 self, context, share_server_id, dest_host, writable, 

6205 nondisruptive, preserve_snapshots, new_share_network_id=None): 

6206 """Migrates a share server from current host to another host.""" 

6207 LOG.debug("Entered share_server_migration_start method for share " 

6208 "server %s.", share_server_id) 

6209 

6210 self.db.share_server_update( 

6211 context, share_server_id, 

6212 {'task_state': constants.TASK_STATE_MIGRATION_IN_PROGRESS}) 

6213 

6214 share_server = self.db.share_server_get(context, share_server_id) 

6215 

6216 try: 

6217 if not self.driver.driver_handles_share_servers: 

6218 LOG.error('This operation is supported only on backends that ' 

6219 'handle share servers.') 

6220 raise exception.OperationNotSupportedByDriverMode() 

6221 

6222 self._share_server_migration_start_driver( 

6223 context, share_server, dest_host, writable, nondisruptive, 

6224 preserve_snapshots, new_share_network_id) 

6225 except Exception: 

6226 LOG.exception( 

6227 ("The driver could not migrate the share server " 

6228 "%(server)s"), {'server': share_server_id}) 

6229 self.db.share_server_update( 

6230 context, share_server_id, 

6231 {'task_state': constants.TASK_STATE_MIGRATION_ERROR, 

6232 'status': constants.STATUS_ACTIVE}) 

6233 

6234 @periodic_task.periodic_task( 

6235 spacing=CONF.server_migration_driver_continue_update_interval) 

6236 @add_hooks 

6237 @utils.require_driver_initialized 

6238 def share_server_migration_driver_continue(self, context): 

6239 """Invokes driver to continue migration of share server.""" 

6240 

6241 # Searching for destination share servers 

6242 share_servers = self.db.share_server_get_all_by_host( 

6243 context, self.host, 

6244 filters={'status': constants.STATUS_SERVER_MIGRATING_TO}) 

6245 

6246 dest_updates_on_error = { 

6247 'task_state': constants.TASK_STATE_MIGRATION_ERROR, 

6248 'status': constants.STATUS_ERROR, 

6249 } 

6250 src_updates_on_error = { 

6251 'task_state': constants.TASK_STATE_MIGRATION_ERROR, 

6252 'status': constants.STATUS_ACTIVE, 

6253 } 

6254 updates_on_finished = { 

6255 'task_state': constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE 

6256 } 

6257 for dest_share_server in share_servers: 

6258 if dest_share_server['task_state'] == ( 6258 ↛ 6257line 6258 didn't jump to line 6257 because the condition on line 6258 was always true

6259 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS): 

6260 src_share_server_id = dest_share_server.get( 

6261 'source_share_server_id') 

6262 if src_share_server_id is None: 6262 ↛ 6263line 6262 didn't jump to line 6263 because the condition on line 6262 was never true

6263 msg = _('Destination share server %s does not have a ' 

6264 'source share server id.' 

6265 ) % dest_share_server['id'] 

6266 LOG.error(msg) 

6267 self.db.share_server_update( 

6268 context, dest_share_server['id'], 

6269 dest_updates_on_error) 

6270 continue 

6271 msg_args = { 

6272 'src_id': src_share_server_id, 

6273 'dest_id': dest_share_server['id'] 

6274 } 

6275 src_share_server = self.db.share_server_get( 

6276 context, src_share_server_id) 

6277 if not src_share_server: 

6278 msg = _('Destination share server %(dest_id)s refers to ' 

6279 'a source share server %(src_id)s that does not ' 

6280 'exists.') % msg_args 

6281 LOG.error(msg) 

6282 self.db.share_server_update( 

6283 context, dest_share_server['id'], 

6284 dest_updates_on_error) 

6285 continue 

6286 if (src_share_server['status'] != 6286 ↛ 6288line 6286 didn't jump to line 6288 because the condition on line 6286 was never true

6287 constants.STATUS_SERVER_MIGRATING): 

6288 msg = _('Destination share server %(dest_id)s refers to ' 

6289 'a source share server %(src_id)s that is not ' 

6290 ' being migrated.') % msg_args 

6291 LOG.error(msg) 

6292 self.db.share_server_update( 

6293 context, dest_share_server['id'], 

6294 dest_updates_on_error) 

6295 continue 

6296 

6297 share_instances = ( 

6298 self.db.share_instance_get_all_by_share_server( 

6299 context, src_share_server_id, with_share_data=True)) 

6300 share_instance_ids = [x.id for x in share_instances] 

6301 

6302 snapshot_instances = ( 

6303 self.db.share_snapshot_instance_get_all_with_filters( 

6304 context, 

6305 {'share_instance_ids': share_instance_ids})) 

6306 snapshot_instance_ids = [x.id for x in snapshot_instances] 

6307 

6308 try: 

6309 finished = self.driver.share_server_migration_continue( 

6310 context, src_share_server, dest_share_server, 

6311 share_instances, snapshot_instances) 

6312 

6313 if finished: 

6314 self.db.share_server_update( 

6315 context, src_share_server['id'], 

6316 updates_on_finished) 

6317 self.db.share_server_update( 

6318 context, dest_share_server['id'], 

6319 updates_on_finished) 

6320 msg = _("Share server migration for share %s " 

6321 "completed first phase successfully." 

6322 ) % src_share_server['id'] 

6323 LOG.info(msg) 

6324 else: 

6325 src_share_server = self.db.share_server_get( 

6326 context, src_share_server['id']) 

6327 if (src_share_server['task_state'] == 6327 ↛ 6329line 6327 didn't jump to line 6329 because the condition on line 6327 was never true

6328 constants.TASK_STATE_MIGRATION_CANCELLED): 

6329 msg = _("Share server migration for share %s was " 

6330 "cancelled.") % src_share_server['id'] 

6331 LOG.warning(msg) 

6332 except Exception: 

6333 self._update_resource_status( 

6334 context, constants.STATUS_AVAILABLE, 

6335 share_instance_ids=share_instance_ids, 

6336 snapshot_instance_ids=snapshot_instance_ids) 

6337 self._reset_read_only_access_rules_for_server( 

6338 context, share_instances, src_share_server, 

6339 dest_host=dest_share_server['host']) 

6340 self.db.share_server_update( 

6341 context, dest_share_server['id'], 

6342 dest_updates_on_error) 

6343 if src_share_server: 6343 ↛ 6348line 6343 didn't jump to line 6348 because the condition on line 6343 was always true

6344 self.db.share_server_update( 

6345 context, src_share_server['id'], 

6346 src_updates_on_error) 

6347 

6348 msg = _("Migration of share server %s has failed.") 

6349 LOG.exception(msg, src_share_server['id']) 

6350 

6351 @add_hooks 

6352 @utils.require_driver_initialized 

6353 def share_server_migration_complete(self, context, src_share_server_id, 

6354 dest_share_server_id): 

6355 """Invokes driver to complete the migration of share server.""" 

6356 dest_server = self.db.share_server_get(context, dest_share_server_id) 

6357 src_server = self.db.share_server_get(context, src_share_server_id) 

6358 

6359 share_instances = ( 

6360 self.db.share_instance_get_all_by_share_server( 

6361 context, src_share_server_id, with_share_data=True)) 

6362 share_instance_ids = [x.id for x in share_instances] 

6363 

6364 snapshot_instances = ( 

6365 self.db.share_snapshot_instance_get_all_with_filters( 

6366 context, 

6367 {'share_instance_ids': share_instance_ids})) 

6368 snapshot_instance_ids = [x.id for x in snapshot_instances] 

6369 

6370 updates_on_error = { 

6371 'task_state': constants.TASK_STATE_MIGRATION_ERROR, 

6372 'status': constants.STATUS_ERROR, 

6373 } 

6374 try: 

6375 self._server_migration_complete_driver(context, 

6376 src_server, 

6377 share_instances, 

6378 snapshot_instances, 

6379 dest_server) 

6380 except Exception: 

6381 msg = _("Driver migration completion failed for" 

6382 " share server %s.") % src_share_server_id 

6383 LOG.exception(msg) 

6384 self._update_resource_status( 

6385 context, constants.STATUS_ERROR, 

6386 share_instance_ids=share_instance_ids, 

6387 snapshot_instance_ids=snapshot_instance_ids) 

6388 self.db.share_server_update( 

6389 context, src_share_server_id, updates_on_error) 

6390 self.db.share_server_update( 

6391 context, dest_share_server_id, updates_on_error) 

6392 msg_args = { 

6393 'source_id': src_share_server_id, 

6394 'dest_id': dest_share_server_id 

6395 } 

6396 msg = _('Share server migration from %(source_id)s to %(dest_id)s ' 

6397 'has failed in migration-complete phase.') % msg_args 

6398 raise exception.ShareServerMigrationFailed(reason=msg) 

6399 

6400 server_update_args = { 

6401 'task_state': constants.TASK_STATE_MIGRATION_SUCCESS, 

6402 'status': constants.STATUS_ACTIVE 

6403 } 

6404 

6405 # Migration mechanism reused the share server 

6406 if not dest_server['identifier']: 

6407 server_update_args['identifier'] = src_server['identifier'] 

6408 

6409 # Update share server status for success scenario 

6410 self.db.share_server_update( 

6411 context, dest_share_server_id, server_update_args) 

6412 self._update_resource_status( 

6413 context, constants.STATUS_AVAILABLE, 

6414 share_instance_ids=share_instance_ids, 

6415 snapshot_instance_ids=snapshot_instance_ids) 

6416 

6417 LOG.info("Share Server Migration for share server %s was completed " 

6418 "with success.", src_share_server_id) 

6419 

6420 def _server_migration_complete_driver(self, context, source_share_server, 

6421 share_instances, 

6422 snapshot_instances, 

6423 dest_share_server): 

6424 

6425 self.db.share_server_update( 

6426 context, source_share_server['id'], 

6427 {'task_state': constants.TASK_STATE_MIGRATION_COMPLETING}) 

6428 self.db.share_server_update( 

6429 context, dest_share_server['id'], 

6430 {'task_state': constants.TASK_STATE_MIGRATION_COMPLETING}) 

6431 

6432 # Retrieve network allocations reserved for the new share server 

6433 dest_snss = dest_share_server['share_network_subnets'] 

6434 dest_sn_id = dest_snss[0]['share_network_id'] 

6435 dest_sn = self.db.share_network_get(context, dest_sn_id) 

6436 dest_snss = self.db.share_network_subnet_get_all_by_share_server_id( 

6437 context, dest_share_server['id']) 

6438 

6439 existing_allocations = ( 

6440 self.db.network_allocations_get_for_share_server( 

6441 context, dest_share_server['id'])) 

6442 migration_reused_network_allocations = len(existing_allocations) == 0 

6443 migration_extended_network_allocations = ( 

6444 CONF.server_migration_extend_neutron_network) 

6445 

6446 server_to_get_allocations = ( 

6447 dest_share_server 

6448 if not migration_reused_network_allocations 

6449 else source_share_server) 

6450 

6451 new_network_allocations = self._form_server_setup_info( 

6452 context, server_to_get_allocations, dest_sn, dest_snss) 

6453 

6454 model_update = self.driver.share_server_migration_complete( 

6455 context, source_share_server, dest_share_server, share_instances, 

6456 snapshot_instances, new_network_allocations) 

6457 

6458 alloc_update = { 

6459 'share_server_id': dest_share_server['id'] 

6460 } 

6461 subnet_update = {} 

6462 

6463 if migration_extended_network_allocations: 6463 ↛ 6470line 6463 didn't jump to line 6470 because the condition on line 6463 was never true

6464 # NOTE: Network allocations are extended to the destination host on 

6465 # previous (migration_start) step, i.e. port bindings are created 

6466 # on destination host with existing ports. The network allocations 

6467 # will be cut over on this (migration_complete) step, i.e. port 

6468 # bindings on destination host will be activated and bindings on 

6469 # source host will be deleted. 

6470 updated_allocations = ( 

6471 self.driver.network_api.cutover_network_allocations( 

6472 context, source_share_server)) 

6473 segmentation_id = self.db.share_server_backend_details_get_item( 

6474 context, dest_share_server['id'], 'segmentation_id') 

6475 alloc_update.update({ 

6476 'segmentation_id': segmentation_id 

6477 }) 

6478 subnet_update.update({ 

6479 'segmentation_id': segmentation_id, 

6480 }) 

6481 elif migration_reused_network_allocations: 

6482 updated_allocations = ( 

6483 self.db.network_allocations_get_for_share_server( 

6484 context, source_share_server["id"])) 

6485 else: 

6486 network_allocations = [] 

6487 for net_allocation in new_network_allocations: 

6488 network_allocations += net_allocation['network_allocations'] 

6489 

6490 updated_allocations = [ 

6491 *network_allocations, 

6492 *new_network_allocations[0]['admin_network_allocations'] 

6493 ] 

6494 

6495 for allocation in updated_allocations: 

6496 allocation_id = allocation['id'] 

6497 self.db.network_allocation_update( 

6498 context, allocation_id, alloc_update) 

6499 

6500 if subnet_update: 6500 ↛ 6501line 6500 didn't jump to line 6501 because the condition on line 6500 was never true

6501 for subnet in dest_snss: 

6502 self.db.share_network_subnet_update(context, subnet['id'], 

6503 subnet_update) 

6504 

6505 # If share server doesn't have an identifier, we didn't ask the driver 

6506 # to create a brand new server - this was a nondisruptive migration 

6507 share_server_was_reused = not dest_share_server['identifier'] 

6508 if share_server_was_reused: 6508 ↛ 6523line 6508 didn't jump to line 6523 because the condition on line 6508 was always true

6509 driver_backend_details = model_update.get( 

6510 'server_backend_details') 

6511 # Clean up the previous backend details set for migration details 

6512 if driver_backend_details: 6512 ↛ 6513line 6512 didn't jump to line 6513 because the condition on line 6512 was never true

6513 self.db.share_server_backend_details_delete( 

6514 context, dest_share_server['id']) 

6515 backend_details = ( 

6516 driver_backend_details 

6517 or source_share_server.get("backend_details")) 

6518 if backend_details: 6518 ↛ 6519line 6518 didn't jump to line 6519 because the condition on line 6518 was never true

6519 for k, v in backend_details.items(): 

6520 self.db.share_server_backend_details_set( 

6521 context, dest_share_server['id'], {k: v}) 

6522 

6523 host_value = share_utils.extract_host(dest_share_server['host']) 

6524 service = self.db.service_get_by_args( 

6525 context, host_value, 'manila-share') 

6526 new_az_id = service['availability_zone_id'] 

6527 

6528 share_updates = model_update.get('share_updates', {}) 

6529 for share_instance in share_instances: 

6530 share_update = share_updates.get(share_instance['id'], {}) 

6531 new_share_host = share_utils.append_host( 

6532 dest_share_server['host'], share_update.get('pool_name')) 

6533 # Update share instance with new values 

6534 instance_update = { 

6535 'share_server_id': dest_share_server['id'], 

6536 'host': new_share_host, 

6537 'share_network_id': dest_sn_id, 

6538 'availability_zone_id': new_az_id, 

6539 } 

6540 self.db.share_instance_update( 

6541 context, share_instance['id'], instance_update) 

6542 # Try to update info returned in the model update 

6543 if not share_update: 6543 ↛ 6544line 6543 didn't jump to line 6544 because the condition on line 6543 was never true

6544 continue 

6545 # Update export locations 

6546 update_export_location = ( 

6547 share_updates[share_instance['id']].get('export_locations')) 

6548 if update_export_location: 6548 ↛ 6529line 6548 didn't jump to line 6529 because the condition on line 6548 was always true

6549 self.db.export_locations_update( 

6550 context, share_instance['id'], update_export_location) 

6551 

6552 snapshot_updates = model_update.get('snapshot_updates', {}) 

6553 for snap_instance in snapshot_instances: 

6554 model_update = snapshot_updates.get(snap_instance['id'], {}) 

6555 snapshot_export_locations = model_update.pop( 

6556 'export_locations', []) 

6557 if model_update: 6557 ↛ 6558line 6557 didn't jump to line 6558 because the condition on line 6557 was never true

6558 self.db.share_snapshot_instance_update( 

6559 context, snap_instance['id'], model_update) 

6560 

6561 if snapshot_export_locations: 6561 ↛ 6553line 6561 didn't jump to line 6553 because the condition on line 6561 was always true

6562 export_locations_update = [] 

6563 for exp_location in snapshot_export_locations: 

6564 updated_el = { 

6565 'path': exp_location['path'], 

6566 'is_admin_only': exp_location['is_admin_only'], 

6567 } 

6568 export_locations_update.append(updated_el) 

6569 self.db.share_snapshot_instance_export_locations_update( 

6570 context, snap_instance['id'], export_locations_update) 

6571 

6572 # Reset read only access since migration has finished 

6573 self._reset_read_only_access_rules_for_server( 

6574 context, share_instances, source_share_server, 

6575 dest_host=source_share_server['host']) 

6576 

6577 # NOTE(dviroel): Setting the source share server to INACTIVE to avoid 

6578 # being reused for new shares, since it may have some invalid 

6579 # configurations and most of the drivers don't check for compatible 

6580 # share servers on share creation. 

6581 self.db.share_server_update( 

6582 context, source_share_server['id'], 

6583 {'task_state': constants.TASK_STATE_MIGRATION_SUCCESS, 

6584 'status': constants.STATUS_INACTIVE}) 

6585 

6586 if share_server_was_reused: 6586 ↛ 6590line 6586 didn't jump to line 6590 because the condition on line 6586 was always true

6587 self.driver.deallocate_network(context, source_share_server['id']) 

6588 self.db.share_server_delete(context, source_share_server['id']) 

6589 else: 

6590 source_share_server = self._get_share_server_dict( 

6591 context, source_share_server) 

6592 rpcapi = share_rpcapi.ShareAPI() 

6593 rpcapi.delete_share_server(context, source_share_server) 

6594 

6595 @add_hooks 

6596 @utils.require_driver_initialized 

6597 def share_server_migration_cancel(self, context, src_share_server_id, 

6598 dest_share_server_id): 

6599 share_server = self.db.share_server_get(context, src_share_server_id) 

6600 dest_share_server = self.db.share_server_get(context, 

6601 dest_share_server_id) 

6602 

6603 if share_server['task_state'] not in ( 

6604 constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE, 

6605 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS): 

6606 msg = _("Migration of share server %s cannot be cancelled at this " 

6607 "moment.") % src_share_server_id 

6608 raise exception.InvalidShareServer(reason=msg) 

6609 

6610 share_instances = ( 

6611 self.db.share_instance_get_all_by_share_server( 

6612 context, src_share_server_id, with_share_data=True)) 

6613 share_instance_ids = [x.id for x in share_instances] 

6614 

6615 snapshot_instances = ( 

6616 self.db.share_snapshot_instance_get_all_with_filters( 

6617 context, 

6618 {'share_instance_ids': share_instance_ids})) 

6619 snapshot_instance_ids = [x.id for x in snapshot_instances] 

6620 

6621 # Avoid new migration continue and cancel calls while cancelling the 

6622 # migration, which can take some time to finish. The cancel in progress 

6623 # state will help administrator to identify if the operation is still 

6624 # in progress. 

6625 self.db.share_server_update( 

6626 context, share_server['id'], 

6627 {'task_state': constants.TASK_STATE_MIGRATION_CANCEL_IN_PROGRESS}) 

6628 

6629 self.driver.share_server_migration_cancel( 

6630 context, share_server, dest_share_server, 

6631 share_instances, snapshot_instances) 

6632 

6633 if CONF.server_migration_extend_neutron_network: 6633 ↛ 6634line 6633 didn't jump to line 6634 because the condition on line 6633 was never true

6634 self.driver.network_api.delete_extended_allocations( 

6635 context, share_server) 

6636 

6637 # NOTE(dviroel): After cancelling the migration we should set the new 

6638 # share server to INVALID since it may contain an invalid configuration 

6639 # to be reused. We also cleanup the source_share_server_id to unblock 

6640 # new migrations. 

6641 self.db.share_server_update( 

6642 context, dest_share_server_id, 

6643 {'task_state': constants.TASK_STATE_MIGRATION_CANCELLED, 

6644 'status': constants.STATUS_INACTIVE}) 

6645 

6646 self._check_delete_share_server(context, 

6647 share_server=dest_share_server) 

6648 

6649 self._update_resource_status( 

6650 context, constants.STATUS_AVAILABLE, 

6651 share_instance_ids=share_instance_ids, 

6652 snapshot_instance_ids=snapshot_instance_ids) 

6653 

6654 self._reset_read_only_access_rules_for_server( 

6655 context, share_instances, share_server, 

6656 dest_host=share_server['host']) 

6657 

6658 self.db.share_server_update( 

6659 context, share_server['id'], 

6660 {'task_state': constants.TASK_STATE_MIGRATION_CANCELLED, 

6661 'status': constants.STATUS_ACTIVE}) 

6662 

6663 LOG.info("Share Server Migration for share server %s was cancelled.", 

6664 share_server['id']) 

6665 

6666 @add_hooks 

6667 @utils.require_driver_initialized 

6668 def share_server_migration_get_progress( 

6669 self, context, src_share_server_id, dest_share_server_id): 

6670 

6671 src_share_server = self.db.share_server_get(context, 

6672 src_share_server_id) 

6673 if src_share_server['task_state'] != ( 6673 ↛ 6675line 6673 didn't jump to line 6675 because the condition on line 6673 was never true

6674 constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS): 

6675 msg = _("Driver is not performing migration for" 

6676 " share server %s at this moment.") % src_share_server_id 

6677 raise exception.InvalidShareServer(reason=msg) 

6678 

6679 dest_share_server = self.db.share_server_get(context, 

6680 dest_share_server_id) 

6681 share_instances = ( 

6682 self.db.share_instance_get_all_by_share_server( 

6683 context, src_share_server_id, with_share_data=True)) 

6684 share_instance_ids = [x.id for x in share_instances] 

6685 

6686 snapshot_instances = ( 

6687 self.db.share_snapshot_instance_get_all_with_filters( 

6688 context, 

6689 {'share_instance_ids': share_instance_ids})) 

6690 

6691 return self.driver.share_server_migration_get_progress( 

6692 context, src_share_server, dest_share_server, share_instances, 

6693 snapshot_instances) 

6694 

6695 @locked_share_network_operation 

6696 def _check_share_network_update_finished( 

6697 self, context, share_network_id=None): 

6698 # Check if this share network is already active 

6699 share_network = self.db.share_network_get(context, share_network_id) 

6700 if share_network['status'] == constants.STATUS_NETWORK_ACTIVE: 

6701 return 

6702 

6703 share_servers = self.db.share_server_get_all_with_filters( 

6704 context, {'share_network_id': share_network_id} 

6705 ) 

6706 

6707 if all([ss['status'] != constants.STATUS_SERVER_NETWORK_CHANGE 6707 ↛ exitline 6707 didn't return from function '_check_share_network_update_finished' because the condition on line 6707 was always true

6708 for ss in share_servers]): 

6709 # All share servers have updated their configuration 

6710 self.db.share_network_update( 

6711 context, share_network_id, 

6712 {'status': constants.STATUS_NETWORK_ACTIVE}) 

6713 

6714 def _update_share_network_security_service( 

6715 self, context, share_network_id, new_security_service_id, 

6716 current_security_service_id=None, check_only=False): 

6717 

6718 new_security_service = self.db.security_service_get( 

6719 context, new_security_service_id) 

6720 

6721 current_security_service = None 

6722 if current_security_service_id: 6722 ↛ 6726line 6722 didn't jump to line 6726 because the condition on line 6722 was always true

6723 current_security_service = self.db.security_service_get( 

6724 context, current_security_service_id) 

6725 

6726 new_ss_type = new_security_service['type'] 

6727 backend_details_data = { 

6728 'name': new_security_service['name'], 

6729 'ou': new_security_service['ou'], 

6730 'default_ad_site': new_security_service['default_ad_site'], 

6731 'domain': new_security_service['domain'], 

6732 'server': new_security_service['server'], 

6733 'dns_ip': new_security_service['dns_ip'], 

6734 'user': new_security_service['user'], 

6735 'type': new_ss_type, 

6736 'password': new_security_service['password'], 

6737 } 

6738 

6739 share_network = self.db.share_network_get( 

6740 context, share_network_id) 

6741 

6742 share_servers = self.db.share_server_get_all_by_host( 

6743 context, self.host, 

6744 filters={'share_network_id': share_network_id}) 

6745 

6746 for share_server in share_servers: 

6747 

6748 # Get share_network_subnet in case it was updated. 

6749 share_network_subnets = ( 

6750 self.db.share_network_subnet_get_all_by_share_server_id( 

6751 context, share_server['id'])) 

6752 

6753 network_info = self._form_server_setup_info( 

6754 context, share_server, share_network, share_network_subnets) 

6755 

6756 share_instances = ( 

6757 self.db.share_instance_get_all_by_share_server( 

6758 context, share_server['id'], with_share_data=True)) 

6759 share_instance_ids = [sn.id for sn in share_instances] 

6760 

6761 share_instances_rules = [] 

6762 for share_instance_id in share_instance_ids: 

6763 instance_rules = { 

6764 'share_instance_id': share_instance_id, 

6765 'access_rules': ( 

6766 self.db.share_access_get_all_for_instance( 

6767 context, share_instance_id)) 

6768 } 

6769 share_instances_rules.append(instance_rules) 

6770 

6771 # Only check if the driver supports this kind of update. 

6772 if check_only: 

6773 if self.driver.check_update_share_server_security_service( 

6774 context, share_server, network_info, 

6775 share_instances, share_instances_rules, 

6776 new_security_service, 

6777 current_security_service=current_security_service): 

6778 # Check the next share server. 

6779 continue 

6780 else: 

6781 # At least one share server doesn't support this update 

6782 return False 

6783 

6784 # NOTE(dviroel): We always do backend details update since it 

6785 # should be the expected configuration for this share server. Any 

6786 # issue with this operation should be fixed by the admin which will 

6787 # guarantee that storage and backend_details configurations match. 

6788 self.db.share_server_backend_details_set( 

6789 context, share_server['id'], 

6790 {'security_service_' + new_ss_type: jsonutils.dumps( 

6791 backend_details_data)}) 

6792 try: 

6793 updates = self.driver.update_share_server_security_service( 

6794 context, share_server, network_info, 

6795 share_instances, share_instances_rules, 

6796 new_security_service, 

6797 current_security_service=current_security_service) or {} 

6798 except Exception: 

6799 operation = 'add' 

6800 sec_serv_info = ('new security service %s' 

6801 % new_security_service_id) 

6802 if current_security_service_id: 6802 ↛ 6807line 6802 didn't jump to line 6807 because the condition on line 6802 was always true

6803 operation = 'update' 

6804 sec_serv_info = ('current security service %s and ' 

6805 % current_security_service_id + 

6806 sec_serv_info) 

6807 msg = _("Share server %(server_id)s has failed on security " 

6808 "service %(operation)s operation for " 

6809 "%(sec_serv_ids)s.") % { 

6810 'server_id': share_server['id'], 

6811 'operation': operation, 

6812 'sec_serv_ids': sec_serv_info, 

6813 } 

6814 LOG.exception(msg) 

6815 # Set share server to error. Security service configuration 

6816 # must be fixed before restoring it to active again. 

6817 self.db.share_server_update( 

6818 context, share_server['id'], 

6819 {'status': constants.STATUS_ERROR}) 

6820 

6821 if current_security_service: 6821 ↛ 6835line 6821 didn't jump to line 6835 because the condition on line 6821 was always true

6822 # NOTE(dviroel): An already configured security service has 

6823 # failed on update operation. We will set all share 

6824 # instances to 'error'. 

6825 if share_instance_ids: 6825 ↛ 6835line 6825 didn't jump to line 6835 because the condition on line 6825 was always true

6826 self.db.share_instance_status_update( 

6827 context, share_instance_ids, 

6828 {'status': constants.STATUS_ERROR}) 

6829 # Update share instance access rules status 

6830 (self.access_helper 

6831 .update_share_instances_access_rules_status( 

6832 context, constants.SHARE_INSTANCE_RULES_ERROR, 

6833 share_instance_ids)) 

6834 # Go to the next share server 

6835 continue 

6836 

6837 # Update access rules based on drivers updates 

6838 for instance_id, rules_updates in updates.items(): 

6839 self.access_helper.process_driver_rule_updates( 

6840 context, rules_updates, instance_id) 

6841 

6842 msg = _("Security service was successfully updated on share " 

6843 "server %s.") % share_server['id'] 

6844 LOG.info(msg) 

6845 self.db.share_server_update( 

6846 context, share_server['id'], 

6847 {'status': constants.STATUS_ACTIVE}) 

6848 

6849 if check_only: 

6850 # All share servers support the requested update 

6851 return True 

6852 

6853 # Check if all share servers have already finished their updates in 

6854 # order to properly update share network status 

6855 self._check_share_network_update_finished( 

6856 context, share_network_id=share_network['id']) 

6857 

6858 def update_share_network_security_service( 

6859 self, context, share_network_id, new_security_service_id, 

6860 current_security_service_id=None): 

6861 self._update_share_network_security_service( 

6862 context, share_network_id, new_security_service_id, 

6863 current_security_service_id=current_security_service_id, 

6864 check_only=False) 

6865 

6866 def check_update_share_network_security_service( 

6867 self, context, share_network_id, new_security_service_id, 

6868 current_security_service_id=None): 

6869 is_supported = self._update_share_network_security_service( 

6870 context, share_network_id, new_security_service_id, 

6871 current_security_service_id=current_security_service_id, 

6872 check_only=True) 

6873 self._update_share_network_security_service_operations( 

6874 context, share_network_id, is_supported, 

6875 new_security_service_id=new_security_service_id, 

6876 current_security_service_id=current_security_service_id) 

6877 

6878 @api.locked_security_service_update_operation 

6879 def _update_share_network_security_service_operations( 

6880 self, context, share_network_id, is_supported, 

6881 new_security_service_id=None, 

6882 current_security_service_id=None): 

6883 update_check_key = self.share_api.get_security_service_update_key( 

6884 'hosts_check', new_security_service_id, 

6885 current_security_service_id) 

6886 current_hosts_info = self.db.async_operation_data_get( 

6887 context, share_network_id, update_check_key) 

6888 if current_hosts_info: 6888 ↛ 6889line 6888 didn't jump to line 6889 because the condition on line 6888 was never true

6889 current_hosts = json.loads(current_hosts_info) 

6890 current_hosts[self.host] = is_supported 

6891 self.db.async_operation_data_update( 

6892 context, share_network_id, 

6893 {update_check_key: json.dumps(current_hosts)}) 

6894 else: 

6895 LOG.debug('A share network security service check was requested ' 

6896 'but no entries were found in database. Ignoring call ' 

6897 'and returning.') 

6898 

6899 @api.locked_share_server_update_allocations_operation 

6900 def _update_share_server_allocations_check_operation( 

6901 self, context, is_supported, share_network_id=None, 

6902 availability_zone_id=None): 

6903 update_key = self.share_api.get_share_server_update_allocations_key( 

6904 share_network_id, availability_zone_id) 

6905 current_hosts_info = self.db.async_operation_data_get( 

6906 context, share_network_id, update_key) 

6907 if current_hosts_info: 

6908 current_hosts = json.loads(current_hosts_info) 

6909 current_hosts[self.host] = is_supported 

6910 self.db.async_operation_data_update( 

6911 context, share_network_id, 

6912 {update_key: json.dumps(current_hosts)}) 

6913 else: 

6914 LOG.debug('A share network subnet create check was requested ' 

6915 'but no entries were found in database. Ignoring call ' 

6916 'and returning.') 

6917 

6918 def _get_subnet_allocations(self, context, share_server_id, 

6919 share_network_subnet): 

6920 

6921 network_allocations = ( 

6922 self.db.network_allocations_get_for_share_server( 

6923 context, share_server_id, label='user', 

6924 subnet_id=share_network_subnet['id'])) 

6925 

6926 return { 

6927 'share_network_subnet_id': share_network_subnet['id'], 

6928 'neutron_net_id': share_network_subnet['neutron_net_id'], 

6929 'neutron_subnet_id': share_network_subnet['neutron_subnet_id'], 

6930 'network_allocations': network_allocations, 

6931 } 

6932 

6933 def _form_network_allocations(self, context, share_server_id, 

6934 share_network_subnets): 

6935 

6936 subnet_allocations = [] 

6937 for share_network_subnet in share_network_subnets: 

6938 subnet_allocations.append(self._get_subnet_allocations( 

6939 context, share_server_id, share_network_subnet)) 

6940 

6941 admin_network_allocations = ( 

6942 self.db.network_allocations_get_for_share_server( 

6943 context, share_server_id, label='admin')) 

6944 

6945 return { 

6946 'admin_network_allocations': admin_network_allocations, 

6947 'subnets': subnet_allocations, 

6948 } 

6949 

6950 def check_update_share_server_network_allocations( 

6951 self, context, share_network_id, new_share_network_subnet): 

6952 

6953 share_network = self.db.share_network_get( 

6954 context, share_network_id) 

6955 az_subnets = ( 

6956 self.db.share_network_subnets_get_all_by_availability_zone_id( 

6957 context, share_network_id, 

6958 new_share_network_subnet['availability_zone_id'], 

6959 fallback_to_default=False) 

6960 ) 

6961 self.driver.network_api.include_network_info(new_share_network_subnet) 

6962 

6963 # all subnets have the same set of share servers, so do the check from 

6964 # servers in the first subnet. 

6965 share_servers = az_subnets[0]['share_servers'] if az_subnets else [] 

6966 is_supported = True 

6967 for share_server in share_servers: 

6968 

6969 current_network_allocations = self._form_network_allocations( 

6970 context, share_server['id'], az_subnets) 

6971 

6972 share_instances = ( 

6973 self.db.share_instance_get_all_by_share_server( 

6974 context, share_server['id'], with_share_data=True)) 

6975 share_instance_ids = [sn.id for sn in share_instances] 

6976 

6977 share_instances_rules = [] 

6978 for share_instance_id in share_instance_ids: 

6979 instance_rules = { 

6980 'share_instance_id': share_instance_id, 

6981 'access_rules': ( 

6982 self.db.share_access_get_all_for_instance( 

6983 context, share_instance_id)) 

6984 } 

6985 share_instances_rules.append(instance_rules) 

6986 

6987 if self.driver.check_update_share_server_network_allocations( 

6988 context, share_server, current_network_allocations, 

6989 new_share_network_subnet, 

6990 share_network['security_services'], 

6991 share_instances, share_instances_rules): 

6992 # Check the next share server. 

6993 continue 

6994 else: 

6995 # At least one share server doesn't support this update. 

6996 is_supported = False 

6997 break 

6998 

6999 self._update_share_server_allocations_check_operation( 

7000 context, is_supported, share_network_id=share_network_id, 

7001 availability_zone_id=( 

7002 new_share_network_subnet['availability_zone_id'])) 

7003 

7004 def _do_update_share_server_network_allocations( 

7005 self, context, share_server, share_network, new_subnet, 

7006 current_network_allocations, share_instances, 

7007 snapshot_instance_ids): 

7008 

7009 self.driver.allocate_network( 

7010 context, share_server, share_network, new_subnet) 

7011 new_network_allocations = self._get_subnet_allocations( 

7012 context, share_server['id'], new_subnet) 

7013 if not new_network_allocations['network_allocations']: 

7014 raise exception.AllocationsNotFoundForShareServer( 

7015 share_server_id=share_server['id']) 

7016 

7017 # NOTE(felipe_rodrigues): all allocations have the same network 

7018 # segmentation info, so validation from the first one. 

7019 self._validate_segmentation_id( 

7020 new_network_allocations['network_allocations'][0]) 

7021 

7022 model_update = self.driver.update_share_server_network_allocations( 

7023 context, share_server, current_network_allocations, 

7024 new_network_allocations, share_network['security_services'], 

7025 share_instances, snapshot_instance_ids) 

7026 

7027 self.driver.update_network_allocation(context, share_server) 

7028 

7029 driver_backend_details = model_update.get('server_details') 

7030 if driver_backend_details: 7030 ↛ 7034line 7030 didn't jump to line 7034 because the condition on line 7030 was always true

7031 self.db.share_server_backend_details_set( 

7032 context, share_server['id'], driver_backend_details) 

7033 

7034 share_updates = model_update.get('share_updates', {}) 

7035 for share_instance_id, export_locations in share_updates.items(): 

7036 self.db.export_locations_update( 

7037 context, share_instance_id, export_locations) 

7038 

7039 snapshot_updates = model_update.get('snapshot_updates', {}) 

7040 for snap_instance_id, model_update in snapshot_updates.items(): 

7041 snapshot_export_locations = model_update.pop( 

7042 'export_locations', []) 

7043 if model_update: 7043 ↛ 7047line 7043 didn't jump to line 7047 because the condition on line 7043 was always true

7044 self.db.share_snapshot_instance_update( 

7045 context, snap_instance_id, model_update) 

7046 

7047 if snapshot_export_locations: 7047 ↛ 7040line 7047 didn't jump to line 7040 because the condition on line 7047 was always true

7048 export_locations_update = [] 

7049 for exp_location in snapshot_export_locations: 

7050 updated_el = { 

7051 'path': exp_location['path'], 

7052 'is_admin_only': exp_location['is_admin_only'], 

7053 } 

7054 export_locations_update.append(updated_el) 

7055 self.db.share_snapshot_instance_export_locations_update( 

7056 context, snap_instance_id, export_locations_update) 

7057 

7058 def update_share_server_network_allocations( 

7059 self, context, share_network_id, new_share_network_subnet_id): 

7060 

7061 share_network = self.db.share_network_get( 

7062 context, share_network_id) 

7063 new_subnet = self.db.share_network_subnet_get( 

7064 context, new_share_network_subnet_id) 

7065 current_subnets = ( 

7066 self.db.share_network_subnets_get_all_by_availability_zone_id( 

7067 context, share_network_id, 

7068 new_subnet['availability_zone_id'], 

7069 fallback_to_default=False) 

7070 ) 

7071 current_subnets = [subnet for subnet in current_subnets 

7072 if subnet['id'] != new_share_network_subnet_id] 

7073 share_servers = ( 

7074 self.db.share_server_get_all_by_host_and_or_share_subnet( 

7075 context, host=self.host, 

7076 share_subnet_id=new_share_network_subnet_id)) 

7077 for share_server in share_servers: 

7078 

7079 share_server_id = share_server['id'] 

7080 current_network_allocations = self._form_network_allocations( 

7081 context, share_server_id, current_subnets) 

7082 share_instances = ( 

7083 self.db.share_instance_get_all_by_share_server( 

7084 context, share_server_id, with_share_data=True)) 

7085 share_instance_ids = [x['id'] for x in share_instances] 

7086 snapshot_instances = ( 

7087 self.db.share_snapshot_instance_get_all_with_filters( 

7088 context, 

7089 {'share_instance_ids': share_instance_ids})) 

7090 snapshot_instance_ids = [x['id'] for x in snapshot_instances] 

7091 

7092 try: 

7093 self._do_update_share_server_network_allocations( 

7094 context, share_server, share_network, new_subnet, 

7095 current_network_allocations, share_instances, 

7096 snapshot_instances) 

7097 except Exception as e: 

7098 msg = ('Failed to update allocations of share server ' 

7099 '%(server_id)s on subnet %(subnet_id)s: %(e)s.') 

7100 data = { 

7101 'server_id': share_server_id, 

7102 'subnet_id': new_share_network_subnet_id, 

7103 'e': str(e), 

7104 } 

7105 LOG.exception(msg, data) 

7106 

7107 # Set resources to error. Allocations configuration must be 

7108 # fixed before restoring it to active again. 

7109 self._handle_setup_server_error(context, share_server_id, e) 

7110 self._update_resource_status( 

7111 context, constants.STATUS_ERROR, 

7112 share_instance_ids=share_instance_ids, 

7113 snapshot_instance_ids=snapshot_instance_ids) 

7114 

7115 continue 

7116 

7117 msg = _( 

7118 "Network allocations was successfully updated on share " 

7119 "server %s.") % share_server['id'] 

7120 LOG.info(msg) 

7121 self.db.share_server_update( 

7122 context, share_server['id'], 

7123 {'status': constants.STATUS_ACTIVE}) 

7124 

7125 # Check if all share servers have already finished their updates in 

7126 # order to properly update share network status. 

7127 self._check_share_network_update_finished( 

7128 context, share_network_id=share_network['id']) 

7129 

7130 def update_share_from_metadata(self, context, share_id, metadata): 

7131 share = self.db.share_get(context, share_id) 

7132 share_instance = self._get_share_instance(context, share) 

7133 share_server = self._get_share_server(context, share_instance) 

7134 try: 

7135 self.driver.update_share_from_metadata(context, share_instance, 

7136 metadata, share_server) 

7137 self.message_api.create( 

7138 context, 

7139 message_field.Action.UPDATE_METADATA, 

7140 share['project_id'], 

7141 resource_type=message_field.Resource.SHARE, 

7142 resource_id=share_id, 

7143 detail=message_field.Detail.UPDATE_METADATA_SUCCESS) 

7144 except Exception: 

7145 self.message_api.create( 

7146 context, 

7147 message_field.Action.UPDATE_METADATA, 

7148 share['project_id'], 

7149 resource_type=message_field.Resource.SHARE, 

7150 resource_id=share_id, 

7151 detail=message_field.Detail.UPDATE_METADATA_FAILURE) 

7152 

7153 def update_share_network_subnet_from_metadata(self, context, 

7154 share_network_id, 

7155 share_network_subnet_id, 

7156 share_server_id, 

7157 metadata): 

7158 share_network = self.db.share_network_get(context, share_network_id) 

7159 share_network_subnet = self.db.share_network_subnet_get( 

7160 context, share_network_subnet_id) 

7161 share_server = self.db.share_server_get(context, share_server_id) 

7162 

7163 try: 

7164 self.driver.update_share_network_subnet_from_metadata( 

7165 context, 

7166 share_network, 

7167 share_network_subnet, 

7168 share_server, 

7169 metadata) 

7170 self.message_api.create( 

7171 context, 

7172 message_field.Action.UPDATE_METADATA, 

7173 share_network['project_id'], 

7174 resource_type=message_field.Resource.SHARE_NETWORK_SUBNET, 

7175 resource_id=share_network_subnet_id, 

7176 detail=message_field.Detail.UPDATE_METADATA_SUCCESS) 

7177 except Exception as e: 

7178 if isinstance(e, NotImplementedError): 

7179 LOG.debug("Not passing the updates of share network subnet " 

7180 "metadata to share driver since the required driver " 

7181 "interface is not implemented.") 

7182 self.message_api.create( 

7183 context, 

7184 message_field.Action.UPDATE_METADATA, 

7185 share_network['project_id'], 

7186 resource_type=message_field.Resource.SHARE_NETWORK_SUBNET, 

7187 resource_id=share_network_subnet_id, 

7188 detail=message_field.Detail.UPDATE_METADATA_FAILURE)