From 005ff1100a11c83808471ece313d0f9001f4653c Mon Sep 17 00:00:00 2001 From: Frederic Lepied Date: Tue, 23 Jul 2013 11:20:27 -0700 Subject: [PATCH] Factorize code between nfs.py and glusterfs.py Suppressed most duplicated code between nfs.py and glusterfs.py. Change-Id: I8a25aa6e3b8947a1d8e75dc611fac217e3009c41 Fixes: bug #1204213 --- cinder/volume/drivers/glusterfs.py | 142 +------------- cinder/volume/drivers/nfs.py | 297 ++++++++++++++++------------- 2 files changed, 173 insertions(+), 266 deletions(-) diff --git a/cinder/volume/drivers/glusterfs.py b/cinder/volume/drivers/glusterfs.py index 3dea75f13f1..ea79fc8c46a 100644 --- a/cinder/volume/drivers/glusterfs.py +++ b/cinder/volume/drivers/glusterfs.py @@ -52,6 +52,11 @@ class GlusterfsDriver(nfs.RemoteFsDriver): as block device on hypervisor. """ + driver_volume_type = 'glusterfs' + driver_prefix = 'glusterfs' + volume_backend_name = 'GlusterFS' + version = VERSION + def __init__(self, *args, **kwargs): super(GlusterfsDriver, self).__init__(*args, **kwargs) self.configuration.append_config_values(volume_opts) @@ -87,96 +92,6 @@ def check_for_setup_error(self): """Just to override parent behavior.""" pass - def create_cloned_volume(self, volume, src_vref): - raise NotImplementedError() - - def create_volume(self, volume): - """Creates a volume.""" - - self._ensure_shares_mounted() - - volume['provider_location'] = self._find_share(volume['size']) - - LOG.info(_('casted to %s') % volume['provider_location']) - - self._do_create_volume(volume) - - return {'provider_location': volume['provider_location']} - - def delete_volume(self, volume): - """Deletes a logical volume.""" - - if not volume['provider_location']: - LOG.warn(_('Volume %s does not have provider_location specified, ' - 'skipping'), volume['name']) - return - - self._ensure_share_mounted(volume['provider_location']) - - mounted_path = self.local_path(volume) - - self._execute('rm', '-f', mounted_path, run_as_root=True) - - def ensure_export(self, ctx, volume): - """Synchronously recreates an export for a logical volume.""" - self._ensure_share_mounted(volume['provider_location']) - - def create_export(self, ctx, volume): - """Exports the volume. Can optionally return a Dictionary of changes - to the volume object to be persisted. - """ - pass - - def remove_export(self, ctx, volume): - """Removes an export for a logical volume.""" - pass - - def initialize_connection(self, volume, connector): - """Allow connection to connector and return connection info.""" - data = {'export': volume['provider_location'], - 'name': volume['name']} - if volume['provider_location'] in self.shares: - data['options'] = self.shares[volume['provider_location']] - return { - 'driver_volume_type': 'glusterfs', - 'data': data - } - - def terminate_connection(self, volume, connector, **kwargs): - """Disallow connection from connector.""" - pass - - def _do_create_volume(self, volume): - """Create a volume on given glusterfs_share. - :param volume: volume reference - """ - volume_path = self.local_path(volume) - volume_size = volume['size'] - - if self.configuration.glusterfs_sparsed_volumes: - self._create_sparsed_file(volume_path, volume_size) - else: - self._create_regular_file(volume_path, volume_size) - - self._set_rw_permissions_for_all(volume_path) - - def _ensure_shares_mounted(self): - """Look for GlusterFS shares in the flags and try to mount them - locally. - """ - self._mounted_shares = [] - - self._load_shares_config(self.configuration.glusterfs_shares_config) - - for share in self.shares.keys(): - try: - self._ensure_share_mounted(share) - self._mounted_shares.append(share) - except Exception as exc: - LOG.warning(_('Exception during mounting %s') % (exc,)) - - LOG.debug('Available shares %s' % str(self._mounted_shares)) - def _ensure_share_mounted(self, glusterfs_share): """Mount GlusterFS share. :param glusterfs_share: string @@ -238,6 +153,10 @@ def _get_available_capacity(self, glusterfs_share): return available, size + def _get_capacity_info(self, glusterfs_share): + available, size = self._get_available_capacity(glusterfs_share) + return size, available, size - available + def _mount_glusterfs(self, glusterfs_share, mount_path, ensure=False): """Mount GlusterFS share to mount path.""" self._execute('mkdir', '-p', mount_path) @@ -247,45 +166,4 @@ def _mount_glusterfs(self, glusterfs_share, mount_path, ensure=False): if self.shares.get(glusterfs_share) is not None: command.extend(self.shares[glusterfs_share].split()) - try: - self._execute(*command, run_as_root=True) - except exception.ProcessExecutionError as exc: - if ensure and 'already mounted' in exc.stderr: - LOG.warn(_("%s is already mounted"), glusterfs_share) - else: - raise - - def get_volume_stats(self, refresh=False): - """Get volume stats. - - If 'refresh' is True, update the stats first. - """ - if refresh or not self._stats: - self._update_volume_stats() - - return self._stats - - def _update_volume_stats(self): - """Retrieve stats info from volume group.""" - - data = {} - backend_name = self.configuration.safe_get('volume_backend_name') - data['volume_backend_name'] = backend_name or 'GlusterFS' - data['vendor_name'] = 'Open Source' - data['driver_version'] = VERSION - data['storage_protocol'] = 'glusterfs' - - self._ensure_shares_mounted() - - global_capacity = 0 - global_free = 0 - for nfs_share in self._mounted_shares: - free, capacity = self._get_available_capacity(nfs_share) - global_capacity += capacity - global_free += free - - data['total_capacity_gb'] = global_capacity / 1024.0 ** 3 - data['free_capacity_gb'] = global_free / 1024.0 ** 3 - data['reserved_percentage'] = 0 - data['QoS_support'] = False - self._stats = data + self._do_mount(command, ensure, glusterfs_share) diff --git a/cinder/volume/drivers/nfs.py b/cinder/volume/drivers/nfs.py index b851c4eb87d..cf4803100db 100644 --- a/cinder/volume/drivers/nfs.py +++ b/cinder/volume/drivers/nfs.py @@ -70,11 +70,103 @@ def check_for_setup_error(self): """Just to override parent behavior.""" pass + def initialize_connection(self, volume, connector): + """Allow connection to connector and return connection info. + + :param volume: volume reference + :param connector: connector reference + """ + data = {'export': volume['provider_location'], + 'name': volume['name']} + if volume['provider_location'] in self.shares: + data['options'] = self.shares[volume['provider_location']] + return { + 'driver_volume_type': self.driver_volume_type, + 'data': data + } + def create_volume(self, volume): + """Creates a volume. + + :param volume: volume reference + """ + self._ensure_shares_mounted() + + volume['provider_location'] = self._find_share(volume['size']) + + LOG.info(_('casted to %s') % volume['provider_location']) + + self._do_create_volume(volume) + + return {'provider_location': volume['provider_location']} + + def _do_create_volume(self, volume): + """Create a volume on given remote share. + + :param volume: volume reference + """ + volume_path = self.local_path(volume) + volume_size = volume['size'] + + if getattr(self.configuration, + self.driver_prefix + '_sparsed_volumes'): + self._create_sparsed_file(volume_path, volume_size) + else: + self._create_regular_file(volume_path, volume_size) + + self._set_rw_permissions_for_all(volume_path) + + def _ensure_shares_mounted(self): + """Look for remote shares in the flags and tries to mount them + locally. + """ + self._mounted_shares = [] + + self._load_shares_config(getattr(self.configuration, + self.driver_prefix + + '_shares_config')) + + for share in self.shares.keys(): + try: + self._ensure_share_mounted(share) + self._mounted_shares.append(share) + except Exception as exc: + LOG.warning(_('Exception during mounting %s') % (exc,)) + + LOG.debug('Available shares %s' % str(self._mounted_shares)) + + def create_cloned_volume(self, volume, src_vref): raise NotImplementedError() def delete_volume(self, volume): - raise NotImplementedError() + """Deletes a logical volume. + + :param volume: volume reference + """ + if not volume['provider_location']: + LOG.warn(_('Volume %s does not have provider_location specified, ' + 'skipping'), volume['name']) + return + + self._ensure_share_mounted(volume['provider_location']) + + mounted_path = self.local_path(volume) + + self._execute('rm', '-f', mounted_path, run_as_root=True) + + def ensure_export(self, ctx, volume): + """Synchronously recreates an export for a logical volume.""" + self._ensure_share_mounted(volume['provider_location']) + + def create_export(self, ctx, volume): + """Exports the volume. Can optionally return a Dictionary of changes + to the volume object to be persisted. + """ + pass + + def remove_export(self, ctx, volume): + """Removes an export for a logical volume.""" + pass def delete_snapshot(self, snapshot): """Do nothing for this driver, but allow manager to handle deletion @@ -82,9 +174,6 @@ def delete_snapshot(self, snapshot): """ pass - def ensure_export(self, ctx, volume): - raise NotImplementedError() - def _create_sparsed_file(self, path, size): """Creates file with 0 disk usage.""" self._execute('truncate', '-s', '%sG' % size, @@ -185,11 +274,80 @@ def _load_shares_config(self, share_file): def _get_mount_point_for_share(self, path): raise NotImplementedError() + def terminate_connection(self, volume, connector, **kwargs): + """Disallow connection from connector.""" + pass + + def get_volume_stats(self, refresh=False): + """Get volume stats. + + If 'refresh' is True, update the stats first. + """ + if refresh or not self._stats: + self._update_volume_stats() + + return self._stats + + def _update_volume_stats(self): + """Retrieve stats info from volume group.""" + + data = {} + backend_name = self.configuration.safe_get('volume_backend_name') + data['volume_backend_name'] = backend_name or self.volume_backend_name + data['vendor_name'] = 'Open Source' + data['driver_version'] = self.version + data['storage_protocol'] = self.driver_volume_type + + self._ensure_shares_mounted() + + global_capacity = 0 + global_free = 0 + for share in self._mounted_shares: + capacity, free, used = self._get_capacity_info(share) + global_capacity += capacity + global_free += free + + data['total_capacity_gb'] = global_capacity / float(units.GiB) + data['free_capacity_gb'] = global_free / float(units.GiB) + data['reserved_percentage'] = 0 + data['QoS_support'] = False + self._stats = data + + def _do_mount(self, cmd, ensure, share): + """Finalize mount command. + + :param cmd: command to do the actual mount + :param ensure: boolean to allow remounting a share with a warning + :param share: description of the share for error reporting + """ + try: + self._execute(*cmd, run_as_root=True) + except exception.ProcessExecutionError as exc: + if ensure and 'already mounted' in exc.stderr: + LOG.warn(_("%s is already mounted"), share) + else: + raise + + def _get_capacity_info(self, nfs_share): + raise NotImplementedError() + + def _find_share(self, volume_size_in_gib): + raise NotImplementedError() + + def _ensure_share_mounted(self, nfs_share): + raise NotImplementedError() + class NfsDriver(RemoteFsDriver): """NFS based cinder driver. Creates file on NFS share for using it as block device on hypervisor. """ + + driver_volume_type = 'nfs' + driver_prefix = 'nfs' + volume_backend_name = 'Generic_NFS' + version = VERSION + def __init__(self, *args, **kwargs): super(NfsDriver, self).__init__(*args, **kwargs) self.configuration.append_config_values(volume_opts) @@ -233,94 +391,6 @@ def do_setup(self, context): else: raise - def create_cloned_volume(self, volume, src_vref): - raise NotImplementedError() - - def create_volume(self, volume): - """Creates a volume""" - - self._ensure_shares_mounted() - - volume['provider_location'] = self._find_share(volume['size']) - - LOG.info(_('casted to %s') % volume['provider_location']) - - self._do_create_volume(volume) - - return {'provider_location': volume['provider_location']} - - def delete_volume(self, volume): - """Deletes a logical volume.""" - - if not volume['provider_location']: - LOG.warn(_('Volume %s does not have provider_location specified, ' - 'skipping'), volume['name']) - return - - self._ensure_share_mounted(volume['provider_location']) - - mounted_path = self.local_path(volume) - - self._execute('rm', '-f', mounted_path, run_as_root=True) - - def ensure_export(self, ctx, volume): - """Synchronously recreates an export for a logical volume.""" - self._ensure_share_mounted(volume['provider_location']) - - def create_export(self, ctx, volume): - """Exports the volume. Can optionally return a Dictionary of changes - to the volume object to be persisted. - """ - pass - - def remove_export(self, ctx, volume): - """Removes an export for a logical volume.""" - pass - - def initialize_connection(self, volume, connector): - """Allow connection to connector and return connection info.""" - data = {'export': volume['provider_location'], - 'name': volume['name']} - if volume['provider_location'] in self.shares: - data['options'] = self.shares[volume['provider_location']] - return { - 'driver_volume_type': 'nfs', - 'data': data - } - - def terminate_connection(self, volume, connector, **kwargs): - """Disallow connection from connector.""" - pass - - def _do_create_volume(self, volume): - """Create a volume on given nfs_share. - :param volume: volume reference - """ - volume_path = self.local_path(volume) - volume_size = volume['size'] - - if self.configuration.nfs_sparsed_volumes: - self._create_sparsed_file(volume_path, volume_size) - else: - self._create_regular_file(volume_path, volume_size) - - self._set_rw_permissions_for_all(volume_path) - - def _ensure_shares_mounted(self): - """Look for NFS shares in the flags and tries to mount them locally.""" - self._mounted_shares = [] - - self._load_shares_config(self.configuration.nfs_shares_config) - - for share in self.shares.keys(): - try: - self._ensure_share_mounted(share) - self._mounted_shares.append(share) - except Exception as exc: - LOG.warning(_('Exception during mounting %s') % (exc,)) - - LOG.debug('Available shares %s' % str(self._mounted_shares)) - def _ensure_share_mounted(self, nfs_share): mount_path = self._get_mount_point_for_share(nfs_share) self._mount_nfs(nfs_share, mount_path, ensure=True) @@ -427,45 +497,4 @@ def _mount_nfs(self, nfs_share, mount_path, ensure=False): nfs_cmd.extend(self.shares[nfs_share].split()) nfs_cmd.extend([nfs_share, mount_path]) - try: - self._execute(*nfs_cmd, run_as_root=True) - except exception.ProcessExecutionError as exc: - if ensure and 'already mounted' in exc.stderr: - LOG.warn(_("%s is already mounted"), nfs_share) - else: - raise - - def get_volume_stats(self, refresh=False): - """Get volume stats. - - If 'refresh' is True, run update the stats first. - """ - if refresh or not self._stats: - self._update_volume_stats() - - return self._stats - - def _update_volume_stats(self): - """Retrieve stats info from volume group.""" - - data = {} - backend_name = self.configuration.safe_get('volume_backend_name') - data["volume_backend_name"] = backend_name or 'Generic_NFS' - data["vendor_name"] = 'Open Source' - data["driver_version"] = VERSION - data["storage_protocol"] = 'nfs' - - self._ensure_shares_mounted() - - global_capacity = 0 - global_free = 0 - for nfs_share in self._mounted_shares: - capacity, free, allocated = self._get_capacity_info(nfs_share) - global_capacity += capacity - global_free += free - - data['total_capacity_gb'] = global_capacity / float(units.GiB) - data['free_capacity_gb'] = global_free / float(units.GiB) - data['reserved_percentage'] = 0 - data['QoS_support'] = False - self._stats = data + self._do_mount(nfs_cmd, ensure, nfs_share)