Coverage for manila/share/drivers/zadara/zadara.py: 52%

404 statements  

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

1# All Rights Reserved. 

2# 

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

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

5# a copy of the License at 

6# 

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

8# 

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

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

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

12# License for the specific language governing permissions and limitations 

13# under the License. 

14 

15""" 

16Shared File system services driver for Zadara 

17Virtual Private Storage Array (VPSA). 

18""" 

19 

20import socket 

21 

22from oslo_config import cfg 

23from oslo_log import log as logging 

24from oslo_utils import netutils 

25from oslo_utils import strutils 

26 

27from manila import exception as manila_exception 

28from manila.i18n import _ 

29from manila.share import api 

30from manila.share import driver 

31from manila.share.drivers.zadara import common 

32 

33CONF = cfg.CONF 

34CONF.register_opts(common.zadara_opts) 

35 

36LOG = logging.getLogger(__name__) 

37 

38manila_opts = [ 

39 cfg.StrOpt('zadara_share_name_template', 

40 default='OS_share-%s', 

41 help='VPSA - Default template for VPSA share names'), 

42 cfg.StrOpt('zadara_share_snap_name_template', 

43 default='OS_share-snapshot-%s', 

44 help='VPSA - Default template for VPSA share names'), 

45 cfg.StrOpt('zadara_driver_ssl_cert_path', 

46 default=None, 

47 help='Can be used to specify a non default path to a ' 

48 'CA_BUNDLE file or directory with certificates ' 

49 'of trusted CAs, which will be used to validate ' 

50 'the backend')] 

51 

52 

53class ZadaraVPSAShareDriver(driver.ShareDriver): 

54 """Zadara VPSA Share driver. 

55 

56 Version history:: 

57 

58 20.12-01 - Driver changes intended and aligned with 

59 openstack latest release. 

60 20.12-02 - Fixed #18723 - Manila: Parsing the export location in a 

61 more generic way while managing the vpsa share 

62 20.12-03 - Adding the metadata support while creating share to 

63 configure vpsa. 

64 20.12-20 - IPv6 connectivity support for Manila driver 

65 20.12-21 - Adding unit tests and fixing review comments from the 

66 openstack community. 

67 20.12-22 - Addressing review comments from the manila community. 

68 20.12-23 - Addressing review comments from the manila community. 

69 20.12-24 - Addressing review comments from the manila community. 

70 20.12-25 - Support host assisted share migration 

71 """ 

72 

73 VERSION = '20.12-25' 

74 

75 # ThirdPartySystems wiki page 

76 CI_WIKI_NAME = "ZadaraStorage_VPSA_CI" 

77 

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

79 """Do initialization.""" 

80 super(ZadaraVPSAShareDriver, self).__init__(False, *args, **kwargs) 

81 self.vpsa = None 

82 self.configuration.append_config_values(common.zadara_opts) 

83 self.configuration.append_config_values(manila_opts) 

84 self.api = api.API() 

85 # The valid list of share options that can be specified 

86 # as the metadata while creating manila share 

87 self.share_options = ['smbguest', 'smbonly', 'smbwindowsacl', 

88 'smbfilecreatemask', 'smbbrowseable', 

89 'smbhiddenfiles', 'smbhideunreadable', 

90 'smbhideunwriteable', 'smbhidedotfiles', 

91 'smbstoredosattributes', 'smbdircreatemask', 

92 'smbmaparchive', 'smbencryptionmode', 

93 'smbenableoplocks', 'smbaiosize', 

94 'nfsrootsquash', 'nfsallsquash', 

95 'nfsanongid', 'nfsanonuid', 

96 'atimeupdate', 'readaheadkb', 'crypt', 

97 'compress', 'dedupe', 'attachpolicies'] 

98 

99 def _check_access_key_validity(self): 

100 try: 

101 self.vpsa._check_access_key_validity() 

102 except common.exception.ZadaraInvalidAccessKey: 

103 raise manila_exception.ZadaraManilaInvalidAccessKey() 

104 

105 def do_setup(self, context): 

106 """Any initialization the share driver does while starting. 

107 

108 Establishes initial connection with VPSA and retrieves access_key. 

109 Need to pass driver_ssl_cert_path here (and not fetch it from the 

110 config opts directly in common code), because this config option is 

111 different for different drivers and so cannot be figured in the 

112 common code. 

113 """ 

114 driver_ssl_cert_path = self.configuration.zadara_driver_ssl_cert_path 

115 self.vpsa = common.ZadaraVPSAConnection(self.configuration, 

116 driver_ssl_cert_path, False) 

117 

118 def check_for_setup_error(self): 

119 """Returns an error (exception) if prerequisites aren't met.""" 

120 self._check_access_key_validity() 

121 

122 def vpsa_send_cmd(self, cmd, **kwargs): 

123 try: 

124 response = self.vpsa.send_cmd(cmd, **kwargs) 

125 except common.exception.UnknownCmd as e: 

126 raise manila_exception.ZadaraUnknownCmd(cmd=e.cmd) 

127 except common.exception.SessionRequestException as e: 

128 raise manila_exception.ZadaraSessionRequestException(msg=e.msg) 

129 except common.exception.BadHTTPResponseStatus as e: 

130 raise manila_exception.ZadaraBadHTTPResponseStatus(status=e.status) 

131 except common.exception.FailedCmdWithDump as e: 

132 raise manila_exception.ZadaraFailedCmdWithDump(status=e.status, 

133 data=e.data) 

134 except common.exception.ZadaraInvalidAccessKey: 

135 raise manila_exception.ZadaraManilaInvalidAccessKey() 

136 return response 

137 

138 def _get_zadara_share_template_name(self, share_id): 

139 return self.configuration.zadara_share_name_template % share_id 

140 

141 def _get_share_export_location(self, share): 

142 export_location = '' 

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

144 

145 share_name = self._get_zadara_share_template_name(share['id']) 

146 vpsa_volume = self.vpsa._get_vpsa_volume(share_name) 

147 if not vpsa_volume: 

148 msg = (_('VPSA volume for share %s ' 

149 'could not be found.') % share['id']) 

150 LOG.error(msg) 

151 raise manila_exception.ZadaraShareNotFound(name=share['id']) 

152 

153 if share_proto == 'NFS': 

154 export_location = vpsa_volume['nfs_export_path'] 

155 if share_proto == 'CIFS': 

156 export_location = vpsa_volume['smb_export_path'] 

157 return export_location 

158 

159 def _check_share_protocol(self, share): 

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

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

162 msg = _("Only NFS or CIFS protocol are currently supported. " 

163 "Share provided %(share)s with protocol " 

164 "%(proto)s.") % {'share': share['id'], 

165 'proto': share['share_proto']} 

166 LOG.error(msg) 

167 raise manila_exception.ZadaraInvalidProtocol( 

168 protocol_type=share_proto) 

169 

170 def is_valid_metadata(self, metadata): 

171 LOG.debug('Metadata while creating share: %(metadata)s', 

172 {'metadata': metadata}) 

173 for key, value in metadata.items(): 173 ↛ 174line 173 didn't jump to line 174 because the loop on line 173 never started

174 if key in self.share_options: 

175 # Check for the values allowed with provided metadata 

176 if key in ['smbguest', 'smbonly', 'smbwindowsacl', 

177 'smbbrowseable', 'smbhideunreadable', 

178 'smbhideunwriteable', 'smbhidedotfiles', 

179 'smbstoredosattributes', 'smbmaparchive', 

180 'smbenableoplocks', 'nfsrootsquash', 

181 'nfsallsquash', 'atimeupdate', 'crypt', 

182 'compress', 'dedupe', 'attachpolicies']: 

183 if value in ['YES', 'NO']: 

184 continue 

185 else: 

186 return False 

187 if key in ['smbfilecreatemask', 'smbdircreatemask']: 

188 if value.isdigit(): 

189 # The valid permissions should be for user,group,other 

190 # with another special digit for attributes. Ex:0755 

191 if len(value) != 4: 

192 return False 

193 # No special permission bits for suid,sgid, 

194 # stickybit are allowed for vpsa share. 

195 if int(value[0]) != 0: 

196 return False 

197 # The permissions are always specified in octal 

198 for i in range(1, len(value)): 

199 if int(value[i]) > 7: 

200 return False 

201 continue 

202 else: 

203 return False 

204 if key == 'smbaiosize': 

205 if value.isdigit() and value in ['16384', '1']: 

206 continue 

207 else: 

208 return False 

209 if key == 'smbencryptionmode': 

210 if value in ['off', 'desired', 'required']: 

211 continue 

212 else: 

213 return False 

214 if key in ['nfsanongid', 'nfsanonuid']: 

215 if value.isdigit() and int(value) != 0: 

216 continue 

217 else: 

218 return False 

219 if key == 'readaheadkb': 

220 if value in ['16', '64', '128', '256', '512']: 

221 continue 

222 else: 

223 return False 

224 return True 

225 

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

227 """Create a Zadara share and export it. 

228 

229 :param context: A RequestContext. 

230 :param share: A Share. 

231 :param share_server: Not used currently 

232 :return: The export locations dictionary. 

233 """ 

234 # Check share's protocol. 

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

236 self._check_share_protocol(share) 

237 share_name = self._get_zadara_share_template_name(share['id']) 

238 

239 # Collect the share metadata provided and validate it 

240 metadata = self.api.get_share_metadata(context, 

241 {'id': share['share_id']}) 

242 if not self.is_valid_metadata(metadata): 242 ↛ 243line 242 didn't jump to line 243 because the condition on line 242 was never true

243 raise manila_exception.ManilaException(_( 

244 "Not a valid metadata provided for the share %s") 

245 % share['id']) 

246 

247 data = self.vpsa_send_cmd('create_volume', 

248 name=share_name, 

249 size=share['size'], 

250 metadata=metadata) 

251 if data['status'] != 0: 251 ↛ 252line 251 didn't jump to line 252 because the condition on line 251 was never true

252 raise manila_exception.ZadaraVPSAVolumeShareFailed( 

253 error=data['status']) 

254 

255 export_location = self._get_share_export_location(share) 

256 return {'path': export_location} 

257 

258 def _allow_access(self, context, share, access): 

259 """Allow access to the share.""" 

260 access_type = access['access_type'] 

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

262 if share_proto == 'CIFS': 262 ↛ 263line 262 didn't jump to line 263 because the condition on line 262 was never true

263 share_proto = 'SMB' 

264 

265 if access_type != 'ip': 

266 raise manila_exception.ZadaraInvalidShareAccessType() 

267 access_ip = access['access_to'] 

268 access_level = 'YES' 

269 if access['access_level'] == 'rw': 269 ↛ 273line 269 didn't jump to line 273 because the condition on line 269 was always true

270 access_level = 'NO' 

271 

272 # First: Check Active controller: if not valid, raise exception 

273 ctrl = self.vpsa._get_active_controller_details() 

274 if not ctrl: 

275 raise manila_exception.ZadaraVPSANoActiveController() 

276 

277 # Get volume name 

278 vol_name = self._get_zadara_share_template_name(share['id']) 

279 vpsa_volume = self.vpsa._get_vpsa_volume(vol_name) 

280 

281 if not vpsa_volume: 281 ↛ 282line 281 didn't jump to line 282 because the condition on line 281 was never true

282 msg = (_('VPSA volume for share %s ' 

283 'could not be found.') % share['id']) 

284 LOG.error(msg) 

285 raise manila_exception.ZadaraShareNotFound(name=share['id']) 

286 

287 # Get/Create server name for given IP 

288 vpsa_srv = self.vpsa._create_vpsa_server(iscsi_ip=access_ip) 

289 if not vpsa_srv: 289 ↛ 290line 289 didn't jump to line 290 because the condition on line 289 was never true

290 raise manila_exception.ZadaraServerCreateFailure(name=access_ip) 

291 

292 servers = self.vpsa._get_servers_attached_to_volume(vpsa_volume) 

293 attach = None 

294 for server in servers: 294 ↛ 295line 294 didn't jump to line 295 because the loop on line 294 never started

295 if server == vpsa_srv: 

296 attach = server 

297 break 

298 # Attach volume to server 

299 if attach is None: 299 ↛ 306line 299 didn't jump to line 306 because the condition on line 299 was always true

300 self.vpsa_send_cmd('attach_volume', 

301 vpsa_srv=vpsa_srv, 

302 vpsa_vol=vpsa_volume['name'], 

303 share_proto=share_proto, 

304 read_only=access_level) 

305 

306 data = self.vpsa_send_cmd('list_vol_attachments', 

307 vpsa_vol=vpsa_volume['name']) 

308 server = None 

309 servers = data.get('servers', []) 

310 for srv in servers: 310 ↛ 315line 310 didn't jump to line 315 because the loop on line 310 didn't complete

311 if srv['iscsi_ip'] == access_ip: 311 ↛ 310line 311 didn't jump to line 310 because the condition on line 311 was always true

312 server = srv 

313 break 

314 

315 if server is None: 315 ↛ 316line 315 didn't jump to line 316 because the condition on line 315 was never true

316 raise manila_exception.ZadaraAttachmentsNotFound( 

317 name=vpsa_volume['name']) 

318 

319 ctrl_ip = self.vpsa._get_target_host(ctrl['ip']) 

320 properties = {'target_discovered': False, 

321 'target_portal': (('%s:%s') % (ctrl_ip, '3260')), 

322 'target_ip': server['iscsi_ip'], 

323 'id': share['id'], 

324 'auth_method': 'CHAP', 

325 'auth_username': ctrl['chap_user'], 

326 'auth_password': ctrl['chap_passwd']} 

327 

328 LOG.debug('Attach properties: %(properties)s', 

329 {'properties': strutils.mask_password(properties)}) 

330 return {'driver_volume_type': share['share_proto'], 'data': properties} 

331 

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

333 """Delete share. Auto detach from all servers. 

334 

335 """ 

336 # Get share name 

337 share_name = self._get_zadara_share_template_name(share['id']) 

338 volume = self.vpsa._get_vpsa_volume(share_name) 

339 if not volume: 

340 LOG.warning('Volume %s could not be found. ' 

341 'It might be already deleted', share['id']) 

342 return 

343 

344 self.vpsa._detach_vpsa_volume(vpsa_vol=volume) 

345 

346 # Delete volume associate with the share 

347 self.vpsa_send_cmd('delete_volume', vpsa_vol=volume['name']) 

348 

349 def _deny_access(self, context, share, access, share_server=None): 

350 """Deny access to the share from the host. 

351 

352 """ 

353 access_type = access['access_type'] 

354 if access_type != 'ip': 354 ↛ 355line 354 didn't jump to line 355 because the condition on line 354 was never true

355 LOG.warning('Only ip access type is allowed for zadara vpsa.') 

356 return 

357 access_ip = access['access_to'] 

358 

359 # First: Check Active controller: if not valid, raise exception 

360 ctrl = self.vpsa._get_active_controller_details() 

361 if not ctrl: 361 ↛ 362line 361 didn't jump to line 362 because the condition on line 361 was never true

362 raise manila_exception.ZadaraVPSANoActiveController() 

363 

364 # Get share name 

365 share_name = self._get_zadara_share_template_name(share['id']) 

366 volume = self.vpsa._get_vpsa_volume(share_name) 

367 if not volume: 367 ↛ 368line 367 didn't jump to line 368 because the condition on line 367 was never true

368 LOG.warning('Volume %s could not be found. ' 

369 'It might be already deleted', share['id']) 

370 return 

371 

372 vpsa_srv = self.vpsa._get_server_name(access_ip, True) 

373 if not vpsa_srv: 373 ↛ 377line 373 didn't jump to line 377 because the condition on line 373 was always true

374 LOG.warning('VPSA server %s could not be found.', access_ip) 

375 return 

376 

377 servers_list = self.vpsa._get_servers_attached_to_volume(volume) 

378 if vpsa_srv not in servers_list: 

379 LOG.warning('VPSA server %(access_ip)s not attached ' 

380 'to volume %(volume)s.', 

381 {'access_ip': access_ip, 'volume': share['id']}) 

382 return 

383 

384 self.vpsa._detach_vpsa_volume(vpsa_vol=volume, 

385 vpsa_srv=vpsa_srv) 

386 

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

388 delete_rules, update_rules, share_server=None): 

389 access_updates = {} 

390 if not (add_rules or delete_rules): 

391 # add_rules and delete_rules can be empty lists, in cases 

392 # like share migration for zadara driver, when the access 

393 # level is to be changed for all existing rules. For zadara 

394 # backend, we delete and re-add all the existing rules. 

395 for access_rule in access_rules: 

396 self._deny_access(context, share, access_rule) 

397 try: 

398 self._allow_access(context, share, access_rule) 

399 except manila_exception.ZadaraInvalidShareAccessType: 

400 LOG.error("Only ip access type allowed for Zadara share. " 

401 "Failed to allow %(access_level)s access to " 

402 "%(access_to)s for rule %(id)s. Setting rule " 

403 "to 'error' state.", 

404 {'access_level': access_rule['access_level'], 

405 'access_to': access_rule['access_to'], 

406 'id': access_rule['access_id']}) 

407 access_updates.update( 

408 {access_rule['access_id']: {'state': 'error'}}) 

409 else: 

410 if add_rules: 

411 # Add rules for accessing share 

412 for access_rule in add_rules: 

413 try: 

414 self._allow_access(context, share, access_rule) 

415 except manila_exception.ZadaraInvalidShareAccessType: 

416 LOG.error("Only ip access type allowed for Zadara " 

417 "share. Failed to allow %(access_level)s " 

418 "access to %(access_to)s for rule %(id)s. " 

419 "Setting rule to 'error' state.", 

420 {'access_level': access_rule['access_level'], 

421 'access_to': access_rule['access_to'], 

422 'id': access_rule['access_id']}) 

423 access_updates.update( 

424 {access_rule['access_id']: {'state': 'error'}}) 

425 if delete_rules: 

426 # Delete access rules for provided share 

427 for access_rule in delete_rules: 

428 self._deny_access(context, share, access_rule) 

429 return access_updates 

430 

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

432 """Extend an existing share. 

433 

434 """ 

435 # Get the backend volume name for the share 

436 share_name = self._get_zadara_share_template_name(share['id']) 

437 vpsa_volume = self.vpsa._get_vpsa_volume(share_name) 

438 if not vpsa_volume: 

439 msg = (_('VPSA volume for share %s ' 

440 'could not be found.') % share['id']) 

441 LOG.error(msg) 

442 raise manila_exception.ZadaraShareNotFound(name=share['id']) 

443 

444 size = vpsa_volume['virtual_capacity'] 

445 expand_size = new_size - size 

446 data = self.vpsa_send_cmd('expand_volume', 

447 vpsa_vol=vpsa_volume['name'], 

448 size=expand_size) 

449 if data['status'] != 0: 449 ↛ 450line 449 didn't jump to line 450 because the condition on line 449 was never true

450 raise manila_exception.ZadaraExtendShareFailed( 

451 error=data['status']) 

452 

453 def _ensure_share(self, context, share, share_server=None): 

454 """Ensure that the share has a backend volume and it is exported. 

455 

456 """ 

457 # Get the backend volume name for the share 

458 share_name = self._get_zadara_share_template_name(share['id']) 

459 vpsa_volume = self.vpsa._get_vpsa_volume(share_name) 

460 if not vpsa_volume: 

461 msg = (_('VPSA volume for share %s ' 

462 'could not be found.') % share['id']) 

463 LOG.error(msg) 

464 raise manila_exception.ZadaraShareNotFound(name=share['id']) 

465 

466 export_locations = share['export_locations'] 

467 if export_locations: 

468 return export_locations 

469 else: 

470 servers_list = (self.vpsa._get_servers_attached_to_volume( 

471 vpsa_volume)) 

472 if len(servers_list) != 0: 

473 msg = (_('Servers attached to the VPSA volume %s without ' 

474 'any locations exported.') % vpsa_volume['name']) 

475 LOG.error(msg) 

476 raise manila_exception.ZadaraShareNotValid( 

477 name=share['id']) 

478 

479 def _update_share_stats(self): 

480 

481 backend_name = self.configuration.share_backend_name 

482 dhss = self.configuration.driver_handles_share_servers 

483 vpsa_poolname = self.configuration.zadara_vpsa_poolname 

484 (total, free, provisioned) = ( 

485 self.vpsa._get_pool_capacity(vpsa_poolname)) 

486 ctrl = self.vpsa._get_active_controller_details() 

487 if not ctrl: 487 ↛ 488line 487 didn't jump to line 488 because the condition on line 487 was never true

488 raise manila_exception.ZadaraVPSANoActiveController() 

489 ipv4_support = not netutils.is_valid_ipv6(ctrl['ip']) 

490 

491 # VPSA backend pool 

492 single_pool = dict( 

493 pool_name=vpsa_poolname, 

494 total_capacity_gb=total, 

495 free_capacity_gb=free, 

496 allocated_capacity_gb=(total - free), 

497 provisioned_capacity_gb=provisioned, 

498 reserved_percentage=self.configuration.reserved_share_percentage, 

499 reserved_snapshot_percentage=( 

500 self.configuration.reserved_share_from_snapshot_percentage 

501 or self.configuration.reserved_share_percentage), 

502 reserved_share_extend_percentage=( 

503 self.configuration.reserved_share_extend_percentage 

504 or self.configuration.reserved_share_percentage), 

505 compression=[True, False], 

506 dedupe=[True, False], 

507 thin_provisioning=True 

508 ) 

509 

510 data = dict( 

511 share_backend_name=backend_name, 

512 driver_handles_share_servers=dhss, 

513 vendor_name='Zadara Storage', 

514 driver_version=self.VERSION, 

515 storage_protocol='NFS_CIFS', 

516 pools=[single_pool], 

517 snapshot_support=True, 

518 create_share_from_snapshot_support=True, 

519 revert_to_snapshot_support=False, 

520 mount_snapshot_support=False, 

521 ipv4_support=ipv4_support, 

522 ipv6_support=not ipv4_support 

523 ) 

524 super(ZadaraVPSAShareDriver, self)._update_share_stats(data) 

525 

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

527 """Creates a snapshot.""" 

528 LOG.debug('Create snapshot: %s', snapshot['id']) 

529 

530 # Retrieve the CG name for the base volume 

531 share = snapshot['share'] 

532 volume_name = self._get_zadara_share_template_name(share['id']) 

533 cg_name = self.vpsa._get_volume_cg_name(volume_name) 

534 if not cg_name: 

535 msg = (_('VPSA volume for share %s ' 

536 'could not be found.') % share['id']) 

537 LOG.error(msg) 

538 raise manila_exception.ZadaraShareNotFound(name=share['id']) 

539 

540 snap_name = (self.configuration.zadara_share_snap_name_template 

541 % snapshot['id']) 

542 data = self.vpsa_send_cmd('create_snapshot', 

543 cg_name=cg_name, 

544 snap_name=snap_name) 

545 if data['status'] != 0: 545 ↛ 546line 545 didn't jump to line 546 because the condition on line 545 was never true

546 raise manila_exception.ZadaraVPSASnapshotCreateFailed( 

547 name=share['id'], error=data['status']) 

548 

549 return {'provider_location': data['snapshot_name']} 

550 

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

552 """Deletes a snapshot.""" 

553 LOG.debug('Delete snapshot: %s', snapshot['id']) 

554 

555 # Retrieve the CG name for the base volume 

556 share = snapshot['share'] 

557 volume_name = self._get_zadara_share_template_name(share['id']) 

558 cg_name = self.vpsa._get_volume_cg_name(volume_name) 

559 if not cg_name: 

560 # If the volume isn't present, then don't attempt to delete 

561 LOG.warning('snapshot: original volume %s not found, ' 

562 'skipping delete operation', 

563 volume_name) 

564 return 

565 

566 snap_name = (self.configuration.zadara_share_snap_name_template 

567 % snapshot['id']) 

568 snap_id = self.vpsa._get_snap_id(cg_name, snap_name) 

569 if not snap_id: 

570 # If the snapshot isn't present, then don't attempt to delete 

571 LOG.warning('snapshot: snapshot %s not found, ' 

572 'skipping delete operation', snap_name) 

573 return 

574 

575 self.vpsa_send_cmd('delete_snapshot', 

576 snap_id=snap_id) 

577 

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

579 share_server=None, parent_share=None): 

580 """Creates a share from a snapshot. 

581 

582 """ 

583 LOG.debug('Creating share from snapshot: %s', snapshot['id']) 

584 

585 # Retrieve the CG name for the base volume 

586 volume_name = (self._get_zadara_share_template_name( 

587 snapshot['share_instance_id'])) 

588 cg_name = self.vpsa._get_volume_cg_name(volume_name) 

589 if not cg_name: 

590 msg = (_('VPSA volume for share %s ' 

591 'could not be found.') % share['id']) 

592 LOG.error(msg) 

593 raise manila_exception.ZadaraShareNotFound(name=share['id']) 

594 

595 snap_name = (self.configuration.zadara_share_snap_name_template 

596 % snapshot['id']) 

597 snap_id = self.vpsa._get_snap_id(cg_name, snap_name) 

598 if not snap_id: 

599 msg = _('Snapshot %(name)s not found') % {'name': snap_name} 

600 LOG.error(msg) 

601 raise manila_exception.ShareSnapshotNotFound( 

602 snapshot_id=snap_name) 

603 

604 self._check_share_protocol(share) 

605 

606 share_name = self._get_zadara_share_template_name(share['id']) 

607 self.vpsa_send_cmd('create_clone_from_snap', 

608 cg_name=cg_name, 

609 name=share_name, 

610 snap_id=snap_id) 

611 

612 if share['size'] > snapshot['size']: 612 ↛ 613line 612 didn't jump to line 613 because the condition on line 612 was never true

613 self.extend_share(share, share['size']) 

614 

615 export_location = self._get_share_export_location(share) 

616 return [{'path': export_location}] 

617 

618 def _get_export_name_from_export_path(self, proto, export_path): 

619 if proto == 'nfs' and '\\' in export_path: 619 ↛ 620line 619 didn't jump to line 620 because the condition on line 619 was never true

620 return None 

621 if proto == 'cifs' and '/' in export_path: 621 ↛ 622line 621 didn't jump to line 622 because the condition on line 621 was never true

622 return None 

623 

624 # Extract the export name from the provided export path 

625 if proto == 'nfs': 625 ↛ 630line 625 didn't jump to line 630 because the condition on line 625 was always true

626 separator = '/' 

627 export_location = export_path.strip(separator) 

628 export_name = export_location.split(separator)[-1] 

629 else: 

630 separator = '\\' 

631 export_location = export_path.strip(separator) 

632 export_name = export_location.split(separator)[-1] 

633 return export_name 

634 

635 def _extract_vpsa_volume_from_share(self, share): 

636 """Returns a vpsa volume based on the export location""" 

637 if not share['export_locations'][0]['path']: 637 ↛ 638line 637 didn't jump to line 638 because the condition on line 637 was never true

638 return None 

639 

640 share_proto = share['share_proto'].lower() 

641 export_path = share['export_locations'][0]['path'] 

642 export_name = self._get_export_name_from_export_path(share_proto, 

643 export_path) 

644 if export_name is None: 644 ↛ 645line 644 didn't jump to line 645 because the condition on line 644 was never true

645 msg = (_('Please verify the specifed protocol and export path.')) 

646 LOG.error(msg) 

647 raise manila_exception.ManilaException(msg) 

648 

649 volume = None 

650 volumes = self.vpsa._get_all_vpsa_volumes() 

651 # Find the volume with the corresponding export name 

652 for vol in volumes: 652 ↛ 673line 652 didn't jump to line 673 because the loop on line 652 didn't complete

653 if share_proto == 'nfs': 653 ↛ 656line 653 didn't jump to line 656 because the condition on line 653 was always true

654 vol_export_path = vol.get('nfs_export_path', None) 

655 else: 

656 vol_export_path = vol.get('smb_export_path', None) 

657 

658 vol_export_name = self._get_export_name_from_export_path( 

659 share_proto, vol_export_path) 

660 if export_name == vol_export_name: 660 ↛ 665line 660 didn't jump to line 665 because the condition on line 660 was always true

661 volume = vol 

662 break 

663 

664 # Check the additional smb export paths of the volume 

665 if (share_proto == 'cifs' and 

666 vol['additional_smb_export_paths_count'] > 0): 

667 for additional_path in vol['additional_smb_export_paths']: 

668 vol_export_name = self._get_export_name_from_export_path( 

669 share_proto, additional_path) 

670 if export_name == vol_export_name: 

671 volume = vol 

672 break 

673 if volume: 673 ↛ 676line 673 didn't jump to line 676 because the condition on line 673 was always true

674 return volume 

675 else: 

676 msg = (_('Manage backend share could not be found. It might be ' 

677 'deleted or please verify the specifed protocol and ' 

678 'export path.')) 

679 LOG.error(msg) 

680 raise manila_exception.ManilaException(msg) 

681 

682 def manage_existing(self, share, driver_options): 

683 # Check whether the specified protocol is supported or not. 

684 self._check_share_protocol(share) 

685 

686 LOG.info("Share %(shr_path)s will be managed with share %(shr_name)s.", 

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

688 'shr_name': share['id']}) 

689 

690 # Find the backend vpsa volume for the provided export location 

691 vpsa_volume = self._extract_vpsa_volume_from_share(share) 

692 

693 # Check if the volume is available 

694 if vpsa_volume['status'] != 'Available': 694 ↛ 695line 694 didn't jump to line 695 because the condition on line 694 was never true

695 msg = (_('Existing share %(name)s is not available') 

696 % {'name': vpsa_volume['name']}) 

697 LOG.error(msg) 

698 raise manila_exception.ManilaException(msg) 

699 

700 new_share_name = self._get_zadara_share_template_name(share['id']) 

701 new_vpsa_share = self.vpsa._get_vpsa_volume(new_share_name) 

702 if new_vpsa_share: 702 ↛ 703line 702 didn't jump to line 703 because the condition on line 702 was never true

703 msg = (_('Share %(new_name)s already exists') 

704 % {'new_name': new_share_name}) 

705 LOG.error(msg) 

706 raise manila_exception.ManilaException(msg) 

707 

708 # Rename the volume to the manila share specified name 

709 data = self.vpsa_send_cmd('rename_volume', 

710 vpsa_vol=vpsa_volume['name'], 

711 new_name=new_share_name) 

712 if data['status'] != 0: 712 ↛ 713line 712 didn't jump to line 713 because the condition on line 712 was never true

713 msg = (_('Renaming volume %(old_name)s to %(new_name)s ' 

714 'has failed.') % {'old_name': vpsa_volume['name'], 

715 'new_name': new_share_name}) 

716 LOG.error(msg) 

717 raise manila_exception.ManilaException(msg) 

718 

719 return {'size': vpsa_volume['provisioned_capacity'], 

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

721 

722 def unmanage(self, share): 

723 """Removes the specified volume from Manila management""" 

724 pass 

725 

726 def manage_existing_snapshot(self, snapshot, driver_options): 

727 share = snapshot['share'] 

728 share_name = self._get_zadara_share_template_name(share['id']) 

729 

730 vpsa_volume = self.vpsa._get_vpsa_volume(share_name) 

731 if not vpsa_volume: 731 ↛ 732line 731 didn't jump to line 732 because the condition on line 731 was never true

732 msg = (_('Volume %(name)s could not be found. ' 

733 'It might be already deleted') % {'name': share_name}) 

734 LOG.error(msg) 

735 raise manila_exception.ZadaraShareNotFound(name=share['id']) 

736 

737 # Check if the provider_location is specified 

738 if not snapshot['provider_location']: 738 ↛ 739line 738 didn't jump to line 739 because the condition on line 738 was never true

739 msg = (_('Provider location as snap id of the VPSA backend ' 

740 'should be provided')) 

741 LOG.error(msg) 

742 raise manila_exception.ManilaException(msg) 

743 

744 new_name = (self.configuration.zadara_share_snap_name_template 

745 % snapshot['id']) 

746 new_snap_id = self.vpsa._get_snap_id(vpsa_volume['cg_name'], 

747 new_name) 

748 if new_snap_id: 748 ↛ 749line 748 didn't jump to line 749 because the condition on line 748 was never true

749 msg = (_('Snapshot with name %s already exists') % new_name) 

750 LOG.debug(msg) 

751 return 

752 

753 data = self.vpsa_send_cmd('rename_snapshot', 

754 snap_id=snapshot['provider_location'], 

755 new_name=new_name) 

756 if data['status'] != 0: 756 ↛ 757line 756 didn't jump to line 757 because the condition on line 756 was never true

757 raise manila_exception.ZadaraVPSASnapshotManageFailed( 

758 snap_id=snapshot['provider_location'], 

759 error=data['status']) 

760 

761 def unmanage_snapshot(self, snapshot): 

762 """Removes the specified snapshot from Manila management""" 

763 pass 

764 

765 def get_configured_ip_versions(self): 

766 """"Get allowed IP versions. 

767 

768 The shares created should have export location as per the 

769 IP version. Currently, zadara backend doesn't support both 

770 ipv4 and ipv6. Collect the supported IP version from the 

771 vpsa's active controller 

772 """ 

773 ctrl = self.vpsa._get_active_controller_details() 

774 if not ctrl: 774 ↛ 775line 774 didn't jump to line 775 because the condition on line 774 was never true

775 raise manila_exception.ZadaraVPSANoActiveController() 

776 

777 if netutils.is_valid_ipv6(ctrl['ip']): 777 ↛ 778line 777 didn't jump to line 778 because the condition on line 777 was never true

778 return [6] 

779 else: 

780 return [4] 

781 

782 def get_backend_info(self, context): 

783 return { 

784 'version': self.VERSION, 

785 'vsa_feip': socket.gethostbyname(self.vpsa.conf.zadara_vpsa_host), 

786 'vsa_port': self.vpsa.conf.zadara_vpsa_port 

787 } 

788 

789 def ensure_shares(self, context, shares): 

790 updates = {} 

791 for share in shares: 

792 updates[share['id']] = { 

793 'export_locations': self._ensure_share(context, share)} 

794 return updates