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
« 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.
15"""
16Shared File system services driver for Zadara
17Virtual Private Storage Array (VPSA).
18"""
20import socket
22from oslo_config import cfg
23from oslo_log import log as logging
24from oslo_utils import netutils
25from oslo_utils import strutils
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
33CONF = cfg.CONF
34CONF.register_opts(common.zadara_opts)
36LOG = logging.getLogger(__name__)
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')]
53class ZadaraVPSAShareDriver(driver.ShareDriver):
54 """Zadara VPSA Share driver.
56 Version history::
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 """
73 VERSION = '20.12-25'
75 # ThirdPartySystems wiki page
76 CI_WIKI_NAME = "ZadaraStorage_VPSA_CI"
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']
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()
105 def do_setup(self, context):
106 """Any initialization the share driver does while starting.
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)
118 def check_for_setup_error(self):
119 """Returns an error (exception) if prerequisites aren't met."""
120 self._check_access_key_validity()
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
138 def _get_zadara_share_template_name(self, share_id):
139 return self.configuration.zadara_share_name_template % share_id
141 def _get_share_export_location(self, share):
142 export_location = ''
143 share_proto = share['share_proto'].upper()
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'])
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
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)
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
226 def create_share(self, context, share, share_server=None):
227 """Create a Zadara share and export it.
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'])
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'])
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'])
255 export_location = self._get_share_export_location(share)
256 return {'path': export_location}
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'
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'
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()
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)
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'])
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)
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)
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
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'])
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']}
328 LOG.debug('Attach properties: %(properties)s',
329 {'properties': strutils.mask_password(properties)})
330 return {'driver_volume_type': share['share_proto'], 'data': properties}
332 def delete_share(self, context, share, share_server=None):
333 """Delete share. Auto detach from all servers.
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
344 self.vpsa._detach_vpsa_volume(vpsa_vol=volume)
346 # Delete volume associate with the share
347 self.vpsa_send_cmd('delete_volume', vpsa_vol=volume['name'])
349 def _deny_access(self, context, share, access, share_server=None):
350 """Deny access to the share from the host.
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']
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()
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
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
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
384 self.vpsa._detach_vpsa_volume(vpsa_vol=volume,
385 vpsa_srv=vpsa_srv)
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
431 def extend_share(self, share, new_size, share_server=None):
432 """Extend an existing share.
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'])
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'])
453 def _ensure_share(self, context, share, share_server=None):
454 """Ensure that the share has a backend volume and it is exported.
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'])
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'])
479 def _update_share_stats(self):
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'])
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 )
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)
526 def create_snapshot(self, context, snapshot, share_server=None):
527 """Creates a snapshot."""
528 LOG.debug('Create snapshot: %s', snapshot['id'])
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'])
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'])
549 return {'provider_location': data['snapshot_name']}
551 def delete_snapshot(self, context, snapshot, share_server=None):
552 """Deletes a snapshot."""
553 LOG.debug('Delete snapshot: %s', snapshot['id'])
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
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
575 self.vpsa_send_cmd('delete_snapshot',
576 snap_id=snap_id)
578 def create_share_from_snapshot(self, context, share, snapshot,
579 share_server=None, parent_share=None):
580 """Creates a share from a snapshot.
582 """
583 LOG.debug('Creating share from snapshot: %s', snapshot['id'])
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'])
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)
604 self._check_share_protocol(share)
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)
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'])
615 export_location = self._get_share_export_location(share)
616 return [{'path': export_location}]
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
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
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
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)
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)
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
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)
682 def manage_existing(self, share, driver_options):
683 # Check whether the specified protocol is supported or not.
684 self._check_share_protocol(share)
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']})
690 # Find the backend vpsa volume for the provided export location
691 vpsa_volume = self._extract_vpsa_volume_from_share(share)
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)
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)
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)
719 return {'size': vpsa_volume['provisioned_capacity'],
720 'export_locations': share['export_locations'][0]['path']}
722 def unmanage(self, share):
723 """Removes the specified volume from Manila management"""
724 pass
726 def manage_existing_snapshot(self, snapshot, driver_options):
727 share = snapshot['share']
728 share_name = self._get_zadara_share_template_name(share['id'])
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'])
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)
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
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'])
761 def unmanage_snapshot(self, snapshot):
762 """Removes the specified snapshot from Manila management"""
763 pass
765 def get_configured_ip_versions(self):
766 """"Get allowed IP versions.
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()
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]
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 }
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