Coverage for manila/share/drivers/dell_emc/plugins/unity/connection.py: 94%

477 statements  

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

1# Copyright (c) 2016 EMC Corporation. 

2# All Rights Reserved. 

3# 

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

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

6# a copy of the License at 

7# 

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

9# 

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

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

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

13# License for the specific language governing permissions and limitations 

14# under the License. 

15"""Unity backend for the EMC Manila driver.""" 

16import random 

17 

18from oslo_config import cfg 

19from oslo_log import log 

20from oslo_utils import excutils 

21from oslo_utils import importutils 

22from oslo_utils import netutils 

23 

24storops = importutils.try_import('storops') 

25if storops: 25 ↛ 30line 25 didn't jump to line 30 because the condition on line 25 was always true

26 # pylint: disable=import-error 

27 from storops import exception as storops_ex 

28 from storops.unity import enums 

29 

30from manila.common import constants as const 

31from manila import exception 

32from manila.i18n import _ 

33from manila.share.drivers.dell_emc.common.enas import utils as enas_utils 

34from manila.share.drivers.dell_emc.plugins import base as driver 

35from manila.share.drivers.dell_emc.plugins.unity import client 

36from manila.share.drivers.dell_emc.plugins.unity import utils as unity_utils 

37from manila.share import utils as share_utils 

38from manila import utils 

39 

40"""Version history: 

41 7.0.0 - Supports DHSS=False mode 

42 7.0.1 - Fix parsing management IPv6 address 

43 7.0.2 - Bugfix: failed to delete CIFS share if wrong access was set 

44 8.0.0 - Supports manage/unmanage share server/share/snapshot 

45 9.0.0 - Implements default filter function 

46 9.0.1 - Bugfix: remove enable ace process when creating cifs share 

47 9.0.2 - Bugfix: fix the driver startup issue with LACP ports configured 

48""" 

49 

50VERSION = "9.0.2" 

51 

52LOG = log.getLogger(__name__) 

53SUPPORTED_NETWORK_TYPES = (None, 'flat', 'vlan') 

54 

55UNITY_OPTS = [ 

56 cfg.StrOpt('unity_server_meta_pool', 

57 required=True, 

58 help='Pool to persist the meta-data of NAS server.'), 

59 cfg.ListOpt('unity_share_data_pools', 

60 help='Comma separated list of pools that can be used to ' 

61 'persist share data.'), 

62 cfg.ListOpt('unity_ethernet_ports', 

63 help='Comma separated list of ports that can be used for ' 

64 'share server interfaces. Members of the list ' 

65 'can be Unix-style glob expressions.'), 

66 cfg.StrOpt('unity_share_server', 

67 help='NAS server used for creating share when driver ' 

68 'is in DHSS=False mode. It is required when ' 

69 'driver_handles_share_servers=False in manila.conf.'), 

70 cfg.StrOpt('report_default_filter_function', 

71 default=False, 

72 help='Whether or not report default filter function.'), 

73] 

74 

75CONF = cfg.CONF 

76CONF.register_opts(UNITY_OPTS) 

77 

78 

79@enas_utils.decorate_all_methods(enas_utils.log_enter_exit, 

80 debug_only=True) 

81class UnityStorageConnection(driver.StorageConnection): 

82 """Implements Unity specific functionality for EMC Manila driver.""" 

83 

84 IP_ALLOCATIONS = 1 

85 

86 @enas_utils.log_enter_exit 

87 def __init__(self, *args, **kwargs): 

88 super(UnityStorageConnection, self).__init__(*args, **kwargs) 

89 if 'configuration' in kwargs: 89 ↛ 90line 89 didn't jump to line 90 because the condition on line 89 was never true

90 kwargs['configuration'].append_config_values(UNITY_OPTS) 

91 

92 self.client = None 

93 self.pool_set = None 

94 self.nas_server_pool = None 

95 self.reserved_percentage = None 

96 self.reserved_snapshot_percentage = None 

97 self.reserved_share_extend_percentage = None 

98 self.max_over_subscription_ratio = None 

99 self.port_ids_conf = None 

100 self.unity_share_server = None 

101 self.ipv6_implemented = True 

102 self.revert_to_snap_support = True 

103 self.shrink_share_support = True 

104 self.manage_existing_support = True 

105 self.manage_existing_with_server_support = True 

106 self.manage_existing_snapshot_support = True 

107 self.manage_snapshot_with_server_support = True 

108 self.manage_server_support = True 

109 self.get_share_server_network_info_support = True 

110 

111 # props from super class. 

112 self.driver_handles_share_servers = (True, False) 

113 self.dhss_mandatory_security_service_association = { 

114 'nfs': None, 

115 'cifs': ['active_directory', ] 

116 } 

117 

118 def connect(self, emc_share_driver, context): 

119 """Connect to Unity storage.""" 

120 config = emc_share_driver.configuration 

121 storage_ip = enas_utils.convert_ipv6_format_if_needed( 

122 config.emc_nas_server) 

123 username = config.emc_nas_login 

124 password = config.emc_nas_password 

125 self.client = client.UnityClient(storage_ip, username, password) 

126 

127 pool_conf = config.safe_get('unity_share_data_pools') 

128 self.pool_set = self._get_managed_pools(pool_conf) 

129 

130 self.reserved_percentage = config.safe_get( 

131 'reserved_share_percentage') 

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

133 self.reserved_percentage = 0 

134 

135 self.reserved_snapshot_percentage = config.safe_get( 

136 'reserved_share_from_snapshot_percentage') 

137 if self.reserved_snapshot_percentage is None: 137 ↛ 138line 137 didn't jump to line 138 because the condition on line 137 was never true

138 self.reserved_snapshot_percentage = self.reserved_percentage 

139 

140 self.reserved_share_extend_percentage = config.safe_get( 

141 'reserved_share_extend_percentage') 

142 if self.reserved_share_extend_percentage is None: 142 ↛ 143line 142 didn't jump to line 143 because the condition on line 142 was never true

143 self.reserved_share_extend_percentage = self.reserved_percentage 

144 

145 self.max_over_subscription_ratio = config.safe_get( 

146 'max_over_subscription_ratio') 

147 self.port_ids_conf = config.safe_get('unity_ethernet_ports') 

148 self.unity_share_server = config.safe_get('unity_share_server') 

149 self.driver_handles_share_servers = config.safe_get( 

150 'driver_handles_share_servers') 

151 if (not self.driver_handles_share_servers) and ( 

152 not self.unity_share_server): 

153 msg = ("Make sure there is NAS server name " 

154 "configured for share creation when driver " 

155 "is in DHSS=False mode.") 

156 raise exception.BadConfigurationException(reason=msg) 

157 self.validate_port_configuration(self.port_ids_conf) 

158 pool_name = config.unity_server_meta_pool 

159 self._config_pool(pool_name) 

160 self.report_default_filter_function = config.safe_get( 

161 'report_default_filter_function') 

162 

163 def get_server_name(self, share_server=None): 

164 if not self.driver_handles_share_servers: 

165 return self.unity_share_server 

166 else: 

167 return self._get_server_name(share_server) 

168 

169 def validate_port_configuration(self, port_ids_conf): 

170 """Initializes the SP and ports based on the port option.""" 

171 

172 ports = self.client.get_file_ports() 

173 

174 sp_ports_map, unmanaged_port_ids = unity_utils.match_ports( 

175 ports, port_ids_conf) 

176 

177 if not sp_ports_map: 

178 msg = (_("All the specified storage ports to be managed " 

179 "do not exist. Please check your configuration " 

180 "unity_ethernet_ports in manila.conf. " 

181 "The available ports in the backend are %s.") % 

182 ",".join([port.get_id() for port in ports])) 

183 raise exception.BadConfigurationException(reason=msg) 

184 

185 if unmanaged_port_ids: 185 ↛ 186line 185 didn't jump to line 186 because the condition on line 185 was never true

186 LOG.info("The following specified ports are not managed by " 

187 "the backend: %(unmanaged)s. This host will only " 

188 "manage the storage ports: %(exist)s", 

189 {'unmanaged': ",".join(unmanaged_port_ids), 

190 'exist': ",".join(map(",".join, 

191 sp_ports_map.values()))}) 

192 else: 

193 LOG.debug("Ports: %s will be managed.", 

194 ",".join(map(",".join, sp_ports_map.values()))) 

195 

196 if len(sp_ports_map) == 1: 

197 LOG.info("Only ports of %s are configured. Configure ports " 

198 "of both SPA and SPB to use both of the SPs.", 

199 list(sp_ports_map)[0]) 

200 

201 return sp_ports_map 

202 

203 def check_for_setup_error(self): 

204 """Check for setup error.""" 

205 

206 def manage_existing(self, share, driver_options, share_server=None): 

207 """Manages a share that exists on backend. 

208 

209 :param share: Share that will be managed. 

210 :param driver_options: Driver-specific options provided by admin. 

211 :param share_server: Share server name provided by admin in DHSS=True. 

212 :returns: Returns a dict with share size and export location. 

213 """ 

214 export_locations = share['export_locations'] 

215 if not export_locations: 

216 message = ("Failed to manage existing share: %s, missing " 

217 "export locations." % share['id']) 

218 raise exception.ManageInvalidShare(reason=message) 

219 

220 try: 

221 share_size = int(driver_options.get("size", 0)) 

222 except (ValueError, TypeError): 

223 msg = _("The driver options' size to manage the share " 

224 "%(share_id)s, should be an integer, in format " 

225 "driver-options size=<SIZE>. Value specified: " 

226 "%(size)s.") % {'share_id': share['id'], 

227 'size': driver_options.get("size")} 

228 raise exception.ManageInvalidShare(reason=msg) 

229 

230 if not share_size: 

231 msg = _("Share %(share_id)s has no specified size. " 

232 "Using default value 1, set size in driver options if you " 

233 "want.") % {'share_id': share['id']} 

234 LOG.warning(msg) 

235 share_size = 1 

236 

237 share_id = unity_utils.get_share_backend_id(share) 

238 backend_share = self.client.get_share(share_id, 

239 share['share_proto']) 

240 if not backend_share: 240 ↛ 241line 240 didn't jump to line 241 because the condition on line 240 was never true

241 message = ("Could not find the share in backend, please make sure " 

242 "the export location is right.") 

243 raise exception.ManageInvalidShare(reason=message) 

244 

245 # Check the share server when in DHSS=true mode 

246 if share_server: 

247 backend_share_server = self._get_server_name(share_server) 

248 if not backend_share_server: 248 ↛ 249line 248 didn't jump to line 249 because the condition on line 248 was never true

249 message = ("Could not find the backend share server: %s, " 

250 "please make sure that share server with the " 

251 "specified name exists in the backend.", 

252 share_server) 

253 raise exception.BadConfigurationException(message) 

254 LOG.info("Share %(shr_path)s is being managed with ID " 

255 "%(shr_id)s.", 

256 {'shr_path': share['export_locations'][0]['path'], 

257 'shr_id': share['id']}) 

258 # export_locations was not changed, return original value 

259 return {"size": share_size, 'export_locations': { 

260 'path': share['export_locations'][0]['path']}} 

261 

262 def manage_existing_with_server(self, share, driver_options, share_server): 

263 return self.manage_existing(share, driver_options, share_server) 

264 

265 def manage_existing_snapshot(self, snapshot, driver_options, 

266 share_server=None): 

267 """Brings an existing snapshot under Manila management.""" 

268 try: 

269 snapshot_size = int(driver_options.get("size", 0)) 

270 except (ValueError, TypeError): 

271 msg = _("The size in driver options to manage snapshot " 

272 "%(snap_id)s should be an integer, in format " 

273 "driver-options size=<SIZE>. Value passed: " 

274 "%(size)s.") % {'snap_id': snapshot['id'], 

275 'size': driver_options.get("size")} 

276 raise exception.ManageInvalidShareSnapshot(reason=msg) 

277 

278 if not snapshot_size: 

279 msg = _("Snapshot %(snap_id)s has no specified size. " 

280 "Use default value 1, set size in driver options if you " 

281 "want.") % {'snap_id': snapshot['id']} 

282 LOG.info(msg) 

283 snapshot_size = 1 

284 provider_location = snapshot.get('provider_location') 

285 snap = self.client.get_snapshot(provider_location) 

286 if not snap: 286 ↛ 287line 286 didn't jump to line 287 because the condition on line 286 was never true

287 message = ("Could not find a snapshot in the backend with " 

288 "provider_location: %s, please make sure " 

289 "the snapshot exists in the backend." 

290 % provider_location) 

291 raise exception.ManageInvalidShareSnapshot(reason=message) 

292 

293 LOG.info("Snapshot %(provider_location)s in Unity will be managed " 

294 "with ID %(snapshot_id)s.", 

295 {'provider_location': snapshot.get('provider_location'), 

296 'snapshot_id': snapshot['id']}) 

297 return {"size": snapshot_size, "provider_location": provider_location} 

298 

299 def manage_existing_snapshot_with_server(self, snapshot, driver_options, 

300 share_server): 

301 return self.manage_existing_snapshot(snapshot, driver_options, 

302 share_server) 

303 

304 def manage_server(self, context, share_server, identifier, driver_options): 

305 """Manage the share server and return compiled back end details. 

306 

307 :param context: Current context. 

308 :param share_server: Share server model. 

309 :param identifier: A driver-specific share server identifier 

310 :param driver_options: Dictionary of driver options to assist managing 

311 the share server 

312 :return: Identifier and dictionary with back end details to be saved 

313 in the database. 

314 

315 Example:: 

316 

317 'my_new_server_identifier',{'server_name': 'my_old_server'} 

318 

319 """ 

320 nas_server = self.client.get_nas_server(identifier) 

321 if not nas_server: 321 ↛ 322line 321 didn't jump to line 322 because the condition on line 321 was never true

322 message = ("Could not find the backend share server by server " 

323 "name: %s, please make sure the share server is " 

324 "existing in the backend." % identifier) 

325 raise exception.ManageInvalidShare(reason=message) 

326 return identifier, driver_options 

327 

328 def get_share_server_network_info( 

329 self, context, share_server, identifier, driver_options): 

330 """Obtain network allocations used by share server. 

331 

332 :param context: Current context. 

333 :param share_server: Share server model. 

334 :param identifier: A driver-specific share server identifier 

335 :param driver_options: Dictionary of driver options to assist managing 

336 the share server 

337 :return: The containing IP address allocated in the backend, Unity 

338 only supports single IP address 

339 Example:: 

340 

341 ['10.10.10.10'] or ['fd11::2000'] 

342 

343 """ 

344 containing_ips = [] 

345 nas_server = self.client.get_nas_server(identifier) 

346 if nas_server: 346 ↛ 349line 346 didn't jump to line 349 because the condition on line 346 was always true

347 for file_interface in nas_server.file_interface: 

348 containing_ips.append(file_interface.ip_address) 

349 return containing_ips 

350 

351 def create_share(self, context, share, share_server=None): 

352 """Create a share and export it based on protocol used.""" 

353 share_name = share['id'] 

354 size = share['size'] 

355 

356 # Check share's protocol. 

357 # Throw an exception immediately if it is an invalid protocol. 

358 share_proto = share['share_proto'].upper() 

359 proto_enum = self._get_proto_enum(share_proto) 

360 

361 # Get pool name from share host field 

362 pool_name = self._get_pool_name_from_host(share['host']) 

363 # Get share server name from share server or manila.conf. 

364 server_name = self.get_server_name(share_server) 

365 pool = self.client.get_pool(pool_name) 

366 try: 

367 nas_server = self.client.get_nas_server(server_name) 

368 except storops_ex.UnityResourceNotFoundError: 

369 message = (_("Failed to get NAS server %(server)s when " 

370 "creating the share %(share)s.") % 

371 {'server': server_name, 'share': share_name}) 

372 LOG.exception(message) 

373 raise exception.EMCUnityError(err=message) 

374 

375 locations = None 

376 if share_proto == 'CIFS': 

377 filesystem = self.client.create_filesystem( 

378 pool, nas_server, share_name, 

379 size, proto=proto_enum) 

380 self.client.create_cifs_share(filesystem, share_name) 

381 

382 locations = self._get_cifs_location( 

383 nas_server.file_interface, share_name) 

384 elif share_proto == 'NFS': 384 ↛ 391line 384 didn't jump to line 391 because the condition on line 384 was always true

385 self.client.create_nfs_filesystem_and_share( 

386 pool, nas_server, share_name, size) 

387 

388 locations = self._get_nfs_location( 

389 nas_server.file_interface, share_name) 

390 

391 return locations 

392 

393 def create_share_from_snapshot(self, context, share, snapshot, 

394 share_server=None, parent_share=None): 

395 """Create a share from a snapshot - clone a snapshot.""" 

396 share_name = share['id'] 

397 

398 # Check share's protocol. 

399 # Throw an exception immediately if it is an invalid protocol. 

400 share_proto = share['share_proto'].upper() 

401 self._validate_share_protocol(share_proto) 

402 

403 # Get share server name from share server 

404 server_name = self.get_server_name(share_server) 

405 

406 try: 

407 nas_server = self.client.get_nas_server(server_name) 

408 except storops_ex.UnityResourceNotFoundError: 

409 message = (_("Failed to get NAS server %(server)s when " 

410 "creating the share %(share)s.") % 

411 {'server': server_name, 'share': share_name}) 

412 LOG.exception(message) 

413 raise exception.EMCUnityError(err=message) 

414 snapshot_id = unity_utils.get_snapshot_id(snapshot) 

415 backend_snap = self.client.create_snap_of_snap(snapshot_id, 

416 share_name) 

417 

418 locations = None 

419 if share_proto == 'CIFS': 

420 self.client.create_cifs_share(backend_snap, share_name) 

421 

422 locations = self._get_cifs_location( 

423 nas_server.file_interface, share_name) 

424 elif share_proto == 'NFS': 424 ↛ 430line 424 didn't jump to line 430 because the condition on line 424 was always true

425 self.client.create_nfs_share(backend_snap, share_name) 

426 

427 locations = self._get_nfs_location( 

428 nas_server.file_interface, share_name) 

429 

430 return locations 

431 

432 def delete_share(self, context, share, share_server=None): 

433 """Delete a share.""" 

434 share_name = unity_utils.get_share_backend_id(share) 

435 try: 

436 backend_share = self.client.get_share(share_name, 

437 share['share_proto']) 

438 except storops_ex.UnityResourceNotFoundError: 

439 LOG.warning("Share %s is not found when deleting the share", 

440 share_name) 

441 return 

442 

443 # Share created by the API create_share_from_snapshot() 

444 if self._is_share_from_snapshot(backend_share): 

445 filesystem = backend_share.snap.filesystem 

446 self.client.delete_snapshot(backend_share.snap) 

447 else: 

448 filesystem = backend_share.filesystem 

449 self.client.delete_share(backend_share) 

450 

451 if self._is_isolated_filesystem(filesystem): 

452 self.client.delete_filesystem(filesystem) 

453 

454 def extend_share(self, share, new_size, share_server=None): 

455 share_id = unity_utils.get_share_backend_id(share) 

456 backend_share = self.client.get_share(share_id, 

457 share['share_proto']) 

458 

459 if not self._is_share_from_snapshot(backend_share): 

460 self.client.extend_filesystem(backend_share.filesystem, 

461 new_size) 

462 else: 

463 share_id = share['id'] 

464 reason = ("Driver does not support extending a " 

465 "snapshot based share.") 

466 raise exception.ShareExtendingError(share_id=share_id, 

467 reason=reason) 

468 

469 def shrink_share(self, share, new_size, share_server=None): 

470 """Shrinks a share to new size. 

471 

472 :param share: Share that will be shrunk. 

473 :param new_size: New size of share. 

474 :param share_server: Data structure with share server information. 

475 Not used by this driver. 

476 """ 

477 share_id = unity_utils.get_share_backend_id(share) 

478 backend_share = self.client.get_share(share_id, 

479 share['share_proto']) 

480 if self._is_share_from_snapshot(backend_share): 

481 reason = ("Driver does not support shrinking a " 

482 "snapshot based share.") 

483 raise exception.ShareShrinkingError(share_id=share_id, 

484 reason=reason) 

485 self.client.shrink_filesystem(share_id, backend_share.filesystem, 

486 new_size) 

487 LOG.info("Share %(shr_id)s successfully shrunk to " 

488 "%(shr_size)sG.", 

489 {'shr_id': share_id, 

490 'shr_size': new_size}) 

491 

492 def create_snapshot(self, context, snapshot, share_server=None): 

493 """Create snapshot from share.""" 

494 share = snapshot['share'] 

495 share_name = unity_utils.get_share_backend_id( 

496 share) if share else snapshot['share_id'] 

497 share_proto = snapshot['share']['share_proto'] 

498 backend_share = self.client.get_share(share_name, share_proto) 

499 

500 snapshot_name = snapshot['id'] 

501 if self._is_share_from_snapshot(backend_share): 

502 self.client.create_snap_of_snap(backend_share.snap, snapshot_name) 

503 else: 

504 self.client.create_snapshot(backend_share.filesystem, 

505 snapshot_name) 

506 return {'provider_location': snapshot_name} 

507 

508 def delete_snapshot(self, context, snapshot, share_server=None): 

509 """Delete a snapshot.""" 

510 snapshot_id = unity_utils.get_snapshot_id(snapshot) 

511 snap = self.client.get_snapshot(snapshot_id) 

512 self.client.delete_snapshot(snap) 

513 

514 def update_access(self, context, share, access_rules, add_rules, 

515 delete_rules, share_server=None): 

516 # adding rules 

517 if add_rules: 

518 for rule in add_rules: 

519 self.allow_access(context, share, rule, share_server) 

520 

521 # deleting rules 

522 if delete_rules: 

523 for rule in delete_rules: 

524 self.deny_access(context, share, rule, share_server) 

525 

526 # recovery mode 

527 if not (add_rules or delete_rules): 

528 white_list = [] 

529 for rule in access_rules: 

530 self.allow_access(context, share, rule, share_server) 

531 white_list.append(rule['access_to']) 

532 self.clear_access(share, white_list) 

533 

534 def clear_access(self, share, white_list=None): 

535 share_proto = share['share_proto'].upper() 

536 share_name = unity_utils.get_share_backend_id(share) 

537 if share_proto == 'CIFS': 

538 self.client.cifs_clear_access(share_name, white_list) 

539 elif share_proto == 'NFS': 539 ↛ exitline 539 didn't return from function 'clear_access' because the condition on line 539 was always true

540 self.client.nfs_clear_access(share_name, white_list) 

541 

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

543 """Allow access to a share.""" 

544 access_level = access['access_level'] 

545 if access_level not in const.ACCESS_LEVELS: 

546 raise exception.InvalidShareAccessLevel(level=access_level) 

547 

548 share_proto = share['share_proto'].upper() 

549 

550 self._validate_share_protocol(share_proto) 

551 self._validate_share_access_type(share, access) 

552 

553 if share_proto == 'CIFS': 

554 self._cifs_allow_access(share, access) 

555 elif share_proto == 'NFS': 555 ↛ exitline 555 didn't return from function 'allow_access' because the condition on line 555 was always true

556 self._nfs_allow_access(share, access) 

557 

558 def deny_access(self, context, share, access, share_server): 

559 """Deny access to a share.""" 

560 share_proto = share['share_proto'].upper() 

561 

562 self._validate_share_protocol(share_proto) 

563 self._validate_share_access_type(share, access) 

564 

565 if share_proto == 'CIFS': 

566 self._cifs_deny_access(share, access) 

567 elif share_proto == 'NFS': 567 ↛ exitline 567 didn't return from function 'deny_access' because the condition on line 567 was always true

568 self._nfs_deny_access(share, access) 

569 

570 def ensure_share(self, context, share, share_server): 

571 """Ensure that the share is exported.""" 

572 share_name = unity_utils.get_share_backend_id(share) 

573 share_proto = share['share_proto'] 

574 

575 backend_share = self.client.get_share(share_name, share_proto) 

576 if not backend_share.existed: 

577 raise exception.ShareNotFound(share_id=share_name) 

578 

579 def update_share_stats(self, stats_dict): 

580 """Communicate with EMCNASClient to get the stats.""" 

581 stats_dict['driver_version'] = VERSION 

582 stats_dict['pools'] = [] 

583 

584 for pool in self.client.get_pool(): 

585 if pool.name in self.pool_set: 585 ↛ 584line 585 didn't jump to line 584 because the condition on line 585 was always true

586 # the unit of following numbers are GB 

587 total_size = float(pool.size_total) 

588 used_size = float(pool.size_used) 

589 

590 pool_stat = { 

591 'pool_name': pool.name, 

592 'thin_provisioning': True, 

593 'total_capacity_gb': enas_utils.bytes_to_gb(total_size), 

594 'free_capacity_gb': 

595 enas_utils.bytes_to_gb(total_size - used_size), 

596 'allocated_capacity_gb': enas_utils.bytes_to_gb(used_size), 

597 'provisioned_capacity_gb': 

598 enas_utils.bytes_to_gb(pool.size_subscribed), 

599 'qos': False, 

600 'reserved_percentage': self.reserved_percentage, 

601 'reserved_snapshot_percentage': 

602 self.reserved_snapshot_percentage, 

603 'reserved_share_extend_percentage': 

604 self.reserved_share_extend_percentage, 

605 'max_over_subscription_ratio': 

606 self.max_over_subscription_ratio, 

607 } 

608 stats_dict['pools'].append(pool_stat) 

609 

610 if not stats_dict.get('pools'): 

611 message = _("Failed to update storage pool.") 

612 LOG.error(message) 

613 raise exception.EMCUnityError(err=message) 

614 

615 def get_pool(self, share): 

616 """Get the pool name of the share.""" 

617 backend_share = self.client.get_share( 

618 share['id'], share['share_proto']) 

619 

620 return backend_share.filesystem.pool.name 

621 

622 def get_network_allocations_number(self): 

623 """Returns number of network allocations for creating VIFs.""" 

624 return self.IP_ALLOCATIONS 

625 

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

627 """Set up and configures share server with given network parameters.""" 

628 server_name = network_info['server_id'] 

629 segmentation_id = network_info['segmentation_id'] 

630 network = self.validate_network(network_info) 

631 mtu = network['mtu'] 

632 tenant = self.client.get_tenant(network_info['server_id'], 

633 segmentation_id) 

634 

635 sp_ports_map = unity_utils.find_ports_by_mtu( 

636 self.client.get_file_ports(), 

637 self.port_ids_conf, mtu) 

638 

639 sp = self._choose_sp(sp_ports_map) 

640 nas_server = self.client.create_nas_server(server_name, 

641 sp, 

642 self.nas_server_pool, 

643 tenant=tenant) 

644 sp = nas_server.home_sp 

645 port_id = self._choose_port(sp_ports_map, sp) 

646 try: 

647 self._create_network_interface(nas_server, network, port_id) 

648 

649 self._handle_security_services( 

650 nas_server, network_info['security_services']) 

651 

652 return {'share_server_name': server_name} 

653 

654 except Exception: 

655 with excutils.save_and_reraise_exception(): 

656 LOG.exception('Could not setup server.') 

657 server_details = {'share_server_name': server_name} 

658 self.teardown_server( 

659 server_details, network_info['security_services']) 

660 

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

662 """Teardown share server.""" 

663 if not server_details: 

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

665 return 

666 

667 server_name = server_details.get('share_server_name') 

668 if not server_name: 

669 LOG.debug('No share server found for server %s.', 

670 server_details.get('instance_id')) 

671 return 

672 

673 username = None 

674 password = None 

675 for security_service in security_services: 

676 if security_service['type'] == 'active_directory': 676 ↛ 675line 676 didn't jump to line 675 because the condition on line 676 was always true

677 username = security_service['user'] 

678 password = security_service['password'] 

679 break 

680 

681 self.client.delete_nas_server(server_name, username, password) 

682 

683 def _cifs_allow_access(self, share, access): 

684 """Allow access to CIFS share.""" 

685 self.client.cifs_allow_access( 

686 share['id'], access['access_to'], access['access_level']) 

687 

688 def _cifs_deny_access(self, share, access): 

689 """Deny access to CIFS share.""" 

690 self.client.cifs_deny_access(share['id'], access['access_to']) 

691 

692 def _config_pool(self, pool_name): 

693 try: 

694 self.nas_server_pool = self.client.get_pool(pool_name) 

695 except storops_ex.UnityResourceNotFoundError: 

696 message = (_("The storage pools %s to store NAS server " 

697 "configuration do not exist.") % pool_name) 

698 LOG.exception(message) 

699 raise exception.BadConfigurationException(reason=message) 

700 

701 @staticmethod 

702 def validate_network(network_info): 

703 network = network_info['network_allocations'][0] 

704 if network['network_type'] not in SUPPORTED_NETWORK_TYPES: 

705 msg = _('The specified network type %s is unsupported by ' 

706 'the EMC Unity driver') 

707 raise exception.NetworkBadConfigurationException( 

708 reason=msg % network['network_type']) 

709 return network 

710 

711 def _create_network_interface(self, nas_server, network, port_id): 

712 kargs = {'ip_addr': network['ip_address'], 

713 'gateway': network['gateway'], 

714 'vlan_id': network['segmentation_id'], 

715 'port_id': port_id} 

716 

717 if netutils.is_valid_ipv6_cidr(kargs['ip_addr']): 

718 kargs['netmask'] = None 

719 kargs['prefix_length'] = str(utils.cidr_to_prefixlen( 

720 network['cidr'])) 

721 else: 

722 kargs['netmask'] = utils.cidr_to_netmask(network['cidr']) 

723 

724 # Create the interfaces on NAS server 

725 self.client.create_interface(nas_server, **kargs) 

726 

727 def _choose_sp(self, sp_ports_map): 

728 sp = None 

729 if len(sp_ports_map.keys()) == 1: 729 ↛ 737line 729 didn't jump to line 737 because the condition on line 729 was always true

730 # Only one storage processor has usable ports, 

731 # create NAS server on that SP. 

732 sp = self.client.get_storage_processor( 

733 sp_id=list(sp_ports_map.keys())[0]) 

734 LOG.debug('All the usable ports belong to %s. ' 

735 'Creating NAS server on this SP without ' 

736 'load balance.', sp.get_id()) 

737 return sp 

738 

739 @staticmethod 

740 def _choose_port(sp_ports_map, sp): 

741 ports = sp_ports_map[sp.get_id()] 

742 return random.choice(list(ports)) 

743 

744 @staticmethod 

745 def _get_cifs_location(file_interfaces, share_name): 

746 return [ 

747 {'path': r'\\%(interface)s\%(share_name)s' % { 

748 'interface': enas_utils.export_unc_path(interface.ip_address), 

749 'share_name': share_name} 

750 } 

751 for interface in file_interfaces 

752 ] 

753 

754 def _get_managed_pools(self, pool_conf): 

755 # Get the real pools from the backend storage 

756 real_pools = set(pool.name for pool in self.client.get_pool()) 

757 

758 if not pool_conf: 

759 LOG.debug("No storage pool is specified, so all pools in storage " 

760 "system will be managed.") 

761 return real_pools 

762 

763 matched_pools, unmanaged_pools = unity_utils.do_match(real_pools, 

764 pool_conf) 

765 

766 if not matched_pools: 

767 msg = (_("All the specified storage pools to be managed " 

768 "do not exist. Please check your configuration " 

769 "emc_nas_pool_names in manila.conf. " 

770 "The available pools in the backend are %s") % 

771 ",".join(real_pools)) 

772 raise exception.BadConfigurationException(reason=msg) 

773 

774 if unmanaged_pools: 

775 LOG.info("The following specified storage pools " 

776 "are not managed by the backend: " 

777 "%(un_managed)s. This host will only manage " 

778 "the storage pools: %(exist)s", 

779 {'un_managed': ",".join(unmanaged_pools), 

780 'exist': ",".join(matched_pools)}) 

781 else: 

782 LOG.debug("Storage pools: %s will be managed.", 

783 ",".join(matched_pools)) 

784 

785 return matched_pools 

786 

787 @staticmethod 

788 def _get_nfs_location(file_interfaces, share_name): 

789 return [ 

790 {'path': '%(interface)s:/%(share_name)s' % { 

791 'interface': enas_utils.convert_ipv6_format_if_needed( 

792 interface.ip_address), 

793 'share_name': share_name} 

794 } 

795 for interface in file_interfaces 

796 ] 

797 

798 @staticmethod 

799 def _get_pool_name_from_host(host): 

800 pool_name = share_utils.extract_host(host, level='pool') 

801 if not pool_name: 

802 message = (_("Pool is not available in the share host %s.") % 

803 host) 

804 raise exception.InvalidHost(reason=message) 

805 

806 return pool_name 

807 

808 @staticmethod 

809 def _get_proto_enum(share_proto): 

810 share_proto = share_proto.upper() 

811 UnityStorageConnection._validate_share_protocol(share_proto) 

812 

813 if share_proto == 'CIFS': 

814 return enums.FSSupportedProtocolEnum.CIFS 

815 elif share_proto == 'NFS': 815 ↛ exitline 815 didn't return from function '_get_proto_enum' because the condition on line 815 was always true

816 return enums.FSSupportedProtocolEnum.NFS 

817 

818 @staticmethod 

819 def _get_server_name(share_server): 

820 if not share_server: 

821 msg = _('Share server not provided.') 

822 raise exception.InvalidInput(reason=msg) 

823 # Try to get share server name from property 'identifier' first in 

824 # case this is managed share server. 

825 server_name = share_server.get('identifier') or share_server.get( 

826 'backend_details', {}).get('share_server_name') 

827 

828 if server_name is None: 

829 msg = (_("Name of the share server %s not found.") 

830 % share_server['id']) 

831 LOG.error(msg) 

832 raise exception.InvalidInput(reason=msg) 

833 

834 return server_name 

835 

836 def _handle_security_services(self, nas_server, security_services): 

837 kerberos_enabled = False 

838 # Support 'active_directory' and 'kerberos' 

839 for security_service in security_services: 

840 service_type = security_service['type'] 

841 if service_type == 'active_directory': 

842 # Create DNS server for NAS server 

843 domain = security_service['domain'] 

844 dns_ip = security_service['dns_ip'] 

845 self.client.create_dns_server(nas_server, 

846 domain, 

847 dns_ip) 

848 

849 # Enable CIFS service 

850 username = security_service['user'] 

851 password = security_service['password'] 

852 self.client.enable_cifs_service(nas_server, 

853 domain=domain, 

854 username=username, 

855 password=password) 

856 elif service_type == 'kerberos': 856 ↛ 862line 856 didn't jump to line 862 because the condition on line 856 was always true

857 # Enable NFS service with kerberos 

858 kerberos_enabled = True 

859 # TODO(jay.xu): enable nfs service with kerberos 

860 LOG.warning('Kerberos is not supported by ' 

861 'EMC Unity manila driver plugin.') 

862 elif service_type == 'ldap': 

863 LOG.warning('LDAP is not supported by ' 

864 'EMC Unity manila driver plugin.') 

865 else: 

866 LOG.warning('Unknown security service type: %s.', 

867 service_type) 

868 

869 if not kerberos_enabled: 

870 # Enable NFS service without kerberos 

871 self.client.enable_nfs_service(nas_server) 

872 

873 def _nfs_allow_access(self, share, access): 

874 """Allow access to NFS share.""" 

875 self.client.nfs_allow_access( 

876 share['id'], access['access_to'], access['access_level']) 

877 

878 def _nfs_deny_access(self, share, access): 

879 """Deny access to NFS share.""" 

880 self.client.nfs_deny_access(share['id'], access['access_to']) 

881 

882 @staticmethod 

883 def _is_isolated_filesystem(filesystem): 

884 filesystem.update() 

885 return ( 

886 not filesystem.has_snap() and 

887 not (filesystem.cifs_share or filesystem.nfs_share) 

888 ) 

889 

890 @staticmethod 

891 def _is_share_from_snapshot(share): 

892 return True if share.snap else False 

893 

894 @staticmethod 

895 def _validate_share_access_type(share, access): 

896 reason = None 

897 share_proto = share['share_proto'].upper() 

898 

899 if share_proto == 'CIFS' and access['access_type'] != 'user': 

900 reason = _('Only user access type allowed for CIFS share.') 

901 elif share_proto == 'NFS' and access['access_type'] != 'ip': 

902 reason = _('Only IP access type allowed for NFS share.') 

903 

904 if reason: 

905 raise exception.InvalidShareAccess(reason=reason) 

906 

907 @staticmethod 

908 def _validate_share_protocol(share_proto): 

909 if share_proto not in ('NFS', 'CIFS'): 

910 raise exception.InvalidShare( 

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

912 share_proto)) 

913 

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

915 snapshot_access_rules, share_server=None): 

916 """Reverts a share (in place) to the specified snapshot.""" 

917 snapshot_id = unity_utils.get_snapshot_id(snapshot) 

918 return self.client.restore_snapshot(snapshot_id) 

919 

920 def get_default_filter_function(self): 

921 if self.report_default_filter_function: 

922 return "share.size >= 3" 

923 return None