diff --git a/etc/manila/rootwrap.d/share.filters b/etc/manila/rootwrap.d/share.filters index 537286fffa..57f2a63237 100644 --- a/etc/manila/rootwrap.d/share.filters +++ b/etc/manila/rootwrap.d/share.filters @@ -2,39 +2,6 @@ # This file should be owned by (and only-writeable by) the root user [Filters] -# manila/share/drivers/lvm.py: 'mkfs.ext4', '/dev/mapper/%s' -mkfs.ext4: CommandFilter, /sbin/mkfs.ext4, root - -# manila/share/drivers/lvm.py: 'exportfs', ... -exportfs: CommandFilter, /usr/sbin/exportfs, root - -# manila/share/drivers/lvm.py: 'smbd', '-s', '%s', '-D' -smbd: CommandFilter, /usr/sbin/smbd, root - -# manila/share/drivers/lvm.py: 'umount', '-f', '%s' -umount: CommandFilter, /bin/umount, root - -# manila/share/drivers/lvm.py: 'mount', '/dev/mapper/%s', '%s' -mount: CommandFilter, /bin/mount, root - -# manila/share/drivers/lvm.py: 'chmod', '777', '%s' -chmod: CommandFilter, /bin/chmod, root - -# manila/share/drivers/lvm.py: 'chown', 'nobody', '-R', '%s' -chown: CommandFilter, /bin/chown, root - -# manila/share/drivers/lvm.py: 'pkill', '-HUP', 'smbd' -pkill: CommandFilter, /usr/bin/pkill, root - -# manila/share/drivers/lvm.py: 'smbcontrol', 'all', 'close-share', '%s' -smbcontrol: CommandFilter, /usr/bin/smbcontrol, root - -# manila/share/drivers/lvm.py: 'net', 'conf', 'addshare', '%s', '%s', 'writeable=y', 'guest_ok=y -# manila/share/drivers/lvm.py: 'net', 'conf', 'delshare', '%s' -# manila/share/drivers/lvm.py: 'net', 'conf', 'setparm', '%s', '%s', '%s' -# manila/share/drivers/lvm.py: 'net', 'conf', 'getparm', '%s', 'hosts allow' -net: CommandFilter, /usr/bin/net, root - # manila/share/drivers/glusterfs.py: 'mkdir', '%s' mkdir: CommandFilter, /usr/bin/mkdir, root diff --git a/etc/manila/rootwrap.d/volume.filters b/etc/manila/rootwrap.d/volume.filters index 1d5b31bff0..980d730dcd 100644 --- a/etc/manila/rootwrap.d/volume.filters +++ b/etc/manila/rootwrap.d/volume.filters @@ -28,10 +28,6 @@ lvdisplay: CommandFilter, lvdisplay, root # manila/volume/driver.py: 'iscsiadm', '-m', 'node', '-T', ... iscsiadm: CommandFilter, iscsiadm, root -# manila/volume/drivers/lvm.py: 'shred', '-n3' -# manila/volume/drivers/lvm.py: 'shred', '-n0', '-z', '-s%dMiB' -shred: CommandFilter, shred, root - #manila/volume/.py: utils.temporary_chown(path, 0), ... chown: CommandFilter, chown, root diff --git a/manila/locale/manila.pot b/manila/locale/manila.pot index 1cfe686be8..3dface1c69 100644 --- a/manila/locale/manila.pot +++ b/manila/locale/manila.pot @@ -1505,7 +1505,7 @@ msgstr "" msgid "Error in gluster volume set: %s" msgstr "" -#: manila/share/drivers/glusterfs.py:144 manila/share/drivers/lvm.py:306 +#: manila/share/drivers/glusterfs.py:144 #, python-format msgid "%s is already mounted" msgstr "" @@ -1524,39 +1524,6 @@ msgstr "" msgid "GlusterFS control mount is not available" msgstr "" -#: manila/share/drivers/lvm.py:85 -#, python-format -msgid "share volume group %s doesn't exist" -msgstr "" - -#: manila/share/drivers/lvm.py:89 -msgid "share_export_ip isn't specified" -msgstr "" - -#: manila/share/drivers/lvm.py:145 -#, python-format -msgid "Error deleting volume: %s" -msgstr "" - -#: manila/share/drivers/lvm.py:147 -#, python-format -msgid "Volume not found: %s" -msgstr "" - -#: manila/share/drivers/lvm.py:186 -#, python-format -msgid "Error retrieving volume status: %s" -msgstr "" - -#: manila/share/drivers/lvm.py:561 -#, python-format -msgid "Share section %r already defined." -msgstr "" - -#: manila/share/drivers/lvm.py:591 -msgid "only ip access type allowed" -msgstr "" - #: manila/share/drivers/service_instance.py:130 msgid "Service instance user is not specified" msgstr "" diff --git a/manila/share/configuration.py b/manila/share/configuration.py index 2422a357f6..b8311a0afd 100644 --- a/manila/share/configuration.py +++ b/manila/share/configuration.py @@ -24,14 +24,14 @@ This module allows support for setting configurations either from default or from a particular CONF group, to be able to set multiple configurations for a given set of values. -For instance, two lvm configurations can be set by naming them in groups as +For instance, two generic configurations can be set by naming them in groups as - [lvm1] - volume_group=lvm-group-1 + [generic1] + share_backend_name=generic-backend-1 ... - [lvm2] - volume_group=lvm-group-2 + [generic2] + share_backend_name=generic-backend-2 ... And the configuration group name will be passed in so that all calls to diff --git a/manila/share/drivers/lvm.py b/manila/share/drivers/lvm.py deleted file mode 100644 index acdbfe6d6d..0000000000 --- a/manila/share/drivers/lvm.py +++ /dev/null @@ -1,620 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 NetApp -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -LVM Driver for shares. - -""" - -import ConfigParser -import math -import os -import re - -from manila import exception -from manila.openstack.common import importutils -from manila.openstack.common import log as logging -from manila.share import driver -from manila import utils - -from oslo.config import cfg - - -LOG = logging.getLogger(__name__) - -share_opts = [ - cfg.StrOpt('share_export_root', - default='$state_path/mnt', - help='Base folder where exported shares are located'), - cfg.StrOpt('share_export_ip', - default=None, - help='IP to be added to export string'), - cfg.StrOpt('smb_config_path', - default='$state_path/smb.conf', - help="Path to smb config"), - cfg.IntOpt('share_lvm_mirrors', - default=0, - help='If set, create lvms with multiple mirrors. Note that ' - 'this requires lvm_mirrors + 2 pvs with available space'), - cfg.StrOpt('share_volume_group', - default='stack-shares', - help='Name for the VG that will contain exported shares'), - cfg.ListOpt('share_lvm_helpers', - default=[ - 'CIFS=manila.share.drivers.lvm.CIFSNetConfHelper', - 'NFS=manila.share.drivers.lvm.NFSHelper', - ], - help='Specify list of share export helpers.'), -] - -CONF = cfg.CONF -CONF.register_opts(share_opts) - - -class LVMShareDriver(driver.ExecuteMixin, driver.ShareDriver): - """Executes commands relating to Shares.""" - - def __init__(self, db, *args, **kwargs): - """Do initialization.""" - super(LVMShareDriver, self).__init__(*args, **kwargs) - self.db = db - self.configuration.append_config_values(share_opts) - self._helpers = None - self.backend_name = self.configuration.safe_get( - 'share_backend_name') or 'LVM' - - def check_for_setup_error(self): - """Returns an error if prerequisites aren't met.""" - out, err = self._execute('vgs', '--noheadings', '-o', 'name', - run_as_root=True) - volume_groups = out.split() - if self.configuration.share_volume_group not in volume_groups: - msg = (_("share volume group %s doesn't exist") - % self.configuration.share_volume_group) - raise exception.InvalidParameterValue(err=msg) - if not self.configuration.share_export_ip: - msg = (_("share_export_ip isn't specified")) - raise exception.InvalidParameterValue(err=msg) - - def do_setup(self, context): - """Any initialization the volume driver does while starting.""" - super(LVMShareDriver, self).do_setup(context) - self._setup_helpers() - for helper in self._helpers.values(): - helper.init() - - def _setup_helpers(self): - """Initializes protocol-specific NAS drivers.""" - self._helpers = {} - for helper_str in self.configuration.share_lvm_helpers: - share_proto, _, import_str = helper_str.partition('=') - helper = importutils.import_class(import_str) - # TODO(rushiagr): better way to handle configuration - # instead of just passing to the helper - self._helpers[share_proto.upper()] = helper(self._execute, - self.configuration) - - def _local_path(self, share): - # NOTE(vish): stops deprecation warning - escaped_group = \ - self.configuration.share_volume_group.replace('-', '--') - escaped_name = share['name'].replace('-', '--') - return "/dev/mapper/%s-%s" % (escaped_group, escaped_name) - - def _allocate_container(self, share): - sizestr = '%sG' % share['size'] - cmd = ['lvcreate', '-L', sizestr, '-n', share['name'], - self.configuration.share_volume_group] - if self.configuration.share_lvm_mirrors: - cmd += ['-m', self.configuration.share_lvm_mirrors, '--nosync'] - terras = int(sizestr[:-1]) / 1024.0 - if terras >= 1.5: - rsize = int(2 ** math.ceil(math.log(terras) / math.log(2))) - # NOTE(vish): Next power of two for region size. See: - # http://red.ht/U2BPOD - cmd += ['-R', str(rsize)] - - self._try_execute(*cmd, run_as_root=True) - device_name = self._local_path(share) - self._execute('mkfs.ext4', device_name, run_as_root=True) - - def _deallocate_container(self, share_name): - """Deletes a logical volume for share.""" - # zero out old volumes to prevent data leaking between users - # TODO(ja): reclaiming space should be done lazy and low priority - try: - self._try_execute('lvremove', '-f', "%s/%s" % - (self.configuration.share_volume_group, - share_name), - run_as_root=True) - except exception.ProcessExecutionError as exc: - if "not found" not in exc.stderr: - LOG.error(_("Error deleting volume: %s") % exc.stderr) - raise - LOG.error(_("Volume not found: %s") % exc.stderr) - - def get_share_stats(self, refresh=False): - """Get share status. - - If 'refresh' is True, run update the stats first. - """ - if refresh: - self._update_share_status() - - return self._stats - - def _update_share_status(self): - """Retrieve status info from share volume group.""" - - LOG.debug("Updating share status") - data = {} - - # Note(zhiteng): These information are driver/backend specific, - # each driver may define these values in its own config options - # or fetch from driver specific configuration file. - data["share_backend_name"] = self.backend_name - data["vendor_name"] = 'Open Source' - data["driver_version"] = '1.0' - # TODO(rushiagr): Pick storage_protocol from the helper used. - data["storage_protocol"] = 'NFS_CIFS' - - data['total_capacity_gb'] = 0 - data['free_capacity_gb'] = 0 - data['reserved_percentage'] = \ - self.configuration.reserved_share_percentage - data['QoS_support'] = False - - try: - out, err = self._execute('vgs', '--noheadings', '--nosuffix', - '--unit=G', '-o', 'name,size,free', - self.configuration.share_volume_group, - run_as_root=True) - except exception.ProcessExecutionError as exc: - LOG.error(_("Error retrieving volume status: %s") % exc.stderr) - out = False - - if out: - share = out.split() - data['total_capacity_gb'] = float(share[1]) - data['free_capacity_gb'] = float(share[2]) - - self._stats = data - - def create_share(self, context, share, share_server=None): - self._allocate_container(share) - # create file system - device_name = self._local_path(share) - mount_path = self._get_mount_path(share) - location = self._get_helper(share).create_export(mount_path, - share['name']) - self._mount_device(share, device_name) - # TODO(rushiagr): what is the provider_location? realy needed? - return location - - def create_share_from_snapshot(self, context, share, snapshot, - share_server=None): - """Is called to create share from snapshot.""" - self._allocate_container(share) - device_name = self._local_path(snapshot) - self._copy_volume(device_name, self._local_path(share), - snapshot['share_size']) - mount_path = self._get_mount_path(share) - location = self._get_helper(share).create_export(mount_path, - share['name']) - self._mount_device(share, device_name) - # TODO(rushiagr): what is the provider_location? realy needed? - return location - - def delete_share(self, context, share, share_server=None): - self._remove_export(context, share) - self._delete_share(context, share) - self._deallocate_container(share['name']) - - def _remove_export(self, ctx, share): - """Removes an access rules for a share.""" - mount_path = self._get_mount_path(share) - if os.path.exists(mount_path): - # umount, may be busy - try: - self._execute('umount', '-f', mount_path, run_as_root=True) - except exception.ProcessExecutionError as exc: - if 'device is busy' in str(exc): - raise exception.ShareIsBusy(share_name=share['name']) - else: - LOG.info('Unable to umount: %s', exc) - # remove dir - try: - os.rmdir(mount_path) - except OSError: - LOG.info('Unable to delete %s', mount_path) - - def create_snapshot(self, context, snapshot, share_server=None): - """Creates a snapshot.""" - orig_lv_name = "%s/%s" % (self.configuration.share_volume_group, - snapshot['share_name']) - self._try_execute('lvcreate', '-L', '%sG' % snapshot['share_size'], - '--name', snapshot['name'], - '--snapshot', orig_lv_name, run_as_root=True) - - def ensure_share(self, ctx, share, share_server=None): - """Ensure that storage are mounted and exported.""" - device_name = self._local_path(share) - location = self._mount_device(share, device_name) - self._get_helper(share).create_export(location, share['name'], - recreate=True) - - def _delete_share(self, ctx, share): - """Delete a share.""" - try: - location = self._get_mount_path(share) - self._get_helper(share).remove_export(location, share['name']) - except exception.ProcessExecutionError: - LOG.info("Can't remove share %r" % share['id']) - except exception.InvalidShare as exc: - LOG.info(exc.message) - - def delete_snapshot(self, context, snapshot, share_server=None): - """Deletes a snapshot.""" - self._deallocate_container(snapshot['name']) - - def allow_access(self, ctx, share, access, share_server=None): - """Allow access to the share.""" - location = self._get_mount_path(share) - self._get_helper(share).allow_access(location, share['name'], - access['access_type'], - access['access_to']) - - def deny_access(self, ctx, share, access, share_server=None): - """Allow access to the share.""" - location = self._get_mount_path(share) - self._get_helper(share).deny_access(location, share['name'], - access['access_type'], - access['access_to']) - - def _get_helper(self, share): - if share['share_proto'].startswith('NFS'): - return self._helpers['NFS'] - elif share['share_proto'].startswith('CIFS'): - return self._helpers['CIFS'] - else: - raise exception.InvalidShare(reason='Wrong share type') - - def _mount_device(self, share, device_name): - """Mount LVM share and ignore if already mounted.""" - mount_path = self._get_mount_path(share) - self._execute('mkdir', '-p', mount_path) - try: - self._execute('mount', device_name, mount_path, - run_as_root=True, check_exit_code=True) - self._execute('chmod', '777', mount_path, - run_as_root=True, check_exit_code=True) - except exception.ProcessExecutionError as exc: - if 'already mounted' in exc.stderr: - LOG.warn(_("%s is already mounted"), device_name) - else: - raise - return mount_path - - def _get_mount_path(self, share): - """Returns path where share is mounted.""" - return os.path.join(self.configuration.share_export_root, - share['name']) - - def _copy_volume(self, srcstr, deststr, size_in_g): - # Use O_DIRECT to avoid thrashing the system buffer cache - extra_flags = ['iflag=direct', 'oflag=direct'] - - # Check whether O_DIRECT is supported - try: - self._execute('dd', 'count=0', 'if=%s' % srcstr, 'of=%s' % deststr, - *extra_flags, run_as_root=True) - except exception.ProcessExecutionError: - extra_flags = [] - - # Perform the copy - self._execute('dd', 'if=%s' % srcstr, 'of=%s' % deststr, - 'count=%d' % (size_in_g * 1024), 'bs=1M', - *extra_flags, run_as_root=True) - - -class NASHelperBase(object): - """Interface to work with share.""" - - def __init__(self, execute, config_object): - self.configuration = config_object - self._execute = execute - - def init(self): - pass - - def create_export(self, local_path, share_name, recreate=False): - """Create new export, delete old one if exists.""" - raise NotImplementedError() - - def remove_export(self, local_path, share_name): - """Remove export.""" - raise NotImplementedError() - - def allow_access(self, local_path, share_name, access_type, access): - """Allow access to the host.""" - raise NotImplementedError() - - def deny_access(self, local_path, share_name, access_type, access, - force=False): - """Deny access to the host.""" - raise NotImplementedError() - - -class NFSHelper(NASHelperBase): - """Interface to work with share.""" - - def __init__(self, execute, config_object): - super(NFSHelper, self).__init__(execute, config_object) - try: - self._execute('exportfs', check_exit_code=True, - run_as_root=True) - except exception.ProcessExecutionError: - raise exception.Error('NFS server not found') - - def create_export(self, local_path, share_name, recreate=False): - """Create new export, delete old one if exists.""" - return ':'.join([self.configuration.share_export_ip, local_path]) - - def remove_export(self, local_path, share_name): - """Remove export.""" - pass - - def allow_access(self, local_path, share_name, access_type, access): - """Allow access to the host""" - if access_type != 'ip': - reason = 'only ip access type allowed' - raise exception.InvalidShareAccess(reason) - # check if presents in export - out, _ = self._execute('exportfs', run_as_root=True) - out = re.search(re.escape(local_path) + '[\s\n]*' + re.escape(access), - out) - if out is not None: - raise exception.ShareAccessExists(access_type=access_type, - access=access) - - self._execute('exportfs', '-o', 'rw,no_subtree_check', - ':'.join([access, local_path]), run_as_root=True, - check_exit_code=True) - - def deny_access(self, local_path, share_name, access_type, access, - force=False): - """Deny access to the host.""" - self._execute('exportfs', '-u', ':'.join([access, local_path]), - run_as_root=True, check_exit_code=False) - - -class CIFSHelper(NASHelperBase): - """Class provides functionality to operate with cifs shares""" - - def __init__(self, execute, config_object): - """Store executor and configuration path.""" - super(CIFSHelper, self).__init__(execute, config_object) - self.config = self.configuration.smb_config_path - self.test_config = "%s_" % (self.config,) - - def init(self): - """Initialize environment.""" - self._recreate_config() - self._ensure_daemon_started() - - def create_export(self, local_path, share_name, recreate=False): - """Create new export, delete old one if exists.""" - parser = ConfigParser.ConfigParser() - parser.read(self.config) - # delete old one - if parser.has_section(share_name): - if recreate: - parser.remove_section(share_name) - else: - raise exception.Error('Section exists') - # Create new one - parser.add_section(share_name) - parser.set(share_name, 'path', local_path) - parser.set(share_name, 'browseable', 'yes') - parser.set(share_name, 'guest ok', 'yes') - parser.set(share_name, 'read only', 'no') - parser.set(share_name, 'writable', 'yes') - parser.set(share_name, 'create mask', '0755') - parser.set(share_name, 'hosts deny', '0.0.0.0/0') # denying all ips - parser.set(share_name, 'hosts allow', '127.0.0.1') - # NOTE(rushiagr): ensure that local_path dir is existing - if not os.path.exists(local_path): - os.makedirs(local_path) - self._execute('chown', 'nobody', '-R', local_path, run_as_root=True) - self._update_config(parser) - return '//%s/%s' % (self.configuration.share_export_ip, share_name) - - def remove_export(self, local_path, share_name): - """Remove export.""" - parser = ConfigParser.ConfigParser() - parser.read(self.config) - # delete old one - if parser.has_section(share_name): - parser.remove_section(share_name) - self._update_config(parser) - self._execute('smbcontrol', 'all', 'close-share', share_name, - run_as_root=True) - - def allow_access(self, local_path, share_name, access_type, access): - """Allow access to the host.""" - if access_type != 'ip': - reason = 'only ip access type allowed' - raise exception.InvalidShareAccess(reason) - parser = ConfigParser.ConfigParser() - parser.read(self.config) - - hosts = parser.get(share_name, 'hosts allow') - if access in hosts.split(): - raise exception.ShareAccessExists(access_type=access_type, - access=access) - hosts += ' %s' % (access,) - parser.set(share_name, 'hosts allow', hosts) - self._update_config(parser) - - def deny_access(self, local_path, share_name, access_type, access, - force=False): - """Deny access to the host.""" - parser = ConfigParser.ConfigParser() - try: - parser.read(self.config) - hosts = parser.get(share_name, 'hosts allow') - hosts = hosts.replace(' %s' % (access,), '', 1) - parser.set(share_name, 'hosts allow', hosts) - self._update_config(parser) - except ConfigParser.NoSectionError: - if not force: - raise - - def _ensure_daemon_started(self): - """ - FYI: smbd starts at least two processes. - """ - out, _ = self._execute(*'ps -C smbd -o args='.split(), - check_exit_code=False) - processes = [process.strip() for process in out.split('\n') - if process.strip()] - - cmd = 'smbd -s %s -D' % (self.config,) - - running = False - for process in processes: - if not process.endswith(cmd): - # alternatively exit - raise exception.Error('smbd already started with wrong config') - running = True - - if not running: - self._execute(*cmd.split(), run_as_root=True) - - def _recreate_config(self): - """create new SAMBA configuration file.""" - if os.path.exists(self.config): - os.unlink(self.config) - parser = ConfigParser.ConfigParser() - parser.add_section('global') - parser.set('global', 'security', 'user') - parser.set('global', 'server string', '%h server (Samba, Openstack)') - - self._update_config(parser, restart=False) - - def _update_config(self, parser, restart=True): - """Check if new configuration is correct and save it.""" - # Check that configuration is correct - with open(self.test_config, 'w') as fp: - parser.write(fp) - self._execute('testparm', '-s', self.test_config, - check_exit_code=True) - # save it - with open(self.config, 'w') as fp: - parser.write(fp) - # restart daemon if necessary - if restart: - self._execute(*'pkill -HUP smbd'.split(), run_as_root=True) - - -class CIFSNetConfHelper(NASHelperBase): - """Manage shares in samba server by net conf tool. - - Class provides functionality to operate with CIFS shares. Samba - server should be configured to use registry as configuration - backend to allow dynamically share managements. There are two ways - to done that, one of them is to add specific parameter in the - global configuration section at smb.conf: - - [global] - include = registry - - For more inforation see smb.conf(5). - """ - - def create_export(self, local_path, share_name, recreate=False): - """Create share at samba server.""" - create_cmd = ('net', 'conf', 'addshare', share_name, local_path, - 'writeable=y', 'guest_ok=y') - try: - self._execute(*create_cmd, run_as_root=True) - except exception.ProcessExecutionError as e: - if 'already exists' in e.stderr: - if recreate: - self._execute('net', 'conf', 'delshare', share_name, - run_as_root=True) - self._execute(*create_cmd, run_as_root=True) - else: - msg = _('Share section %r already defined.') % (share_name) - raise exception.ShareBackendException(msg=msg) - else: - raise - - parameters = { - 'browseable': 'yes', - 'create mask': '0755', - 'hosts deny': '0.0.0.0/0', # deny all - 'hosts allow': '127.0.0.1', - } - for name, value in parameters.items(): - self._execute('net', 'conf', 'setparm', share_name, name, value, - run_as_root=True) - return '//%s/%s' % (self.configuration.share_export_ip, share_name) - - def remove_export(self, local_path, share_name): - """Remove share definition from samba server.""" - try: - self._execute('net', 'conf', 'delshare', share_name, - run_as_root=True) - except exception.ProcessExecutionError as e: - if 'SBC_ERR_NO_SUCH_SERVICE' not in e.stderr: - raise - self._execute('smbcontrol', 'all', 'close-share', share_name, - run_as_root=True) - - def allow_access(self, local_path, share_name, access_type, access): - """Add to allow hosts additional access rule.""" - if access_type != 'ip': - reason = _('only ip access type allowed') - raise exception.InvalidShareAccess(reason=reason) - - hosts = self._get_allow_hosts(share_name) - if access in hosts: - raise exception.ShareAccessExists(access_type=access_type, - access=access) - hosts.append(access) - self._set_allow_hosts(hosts, share_name) - - def deny_access(self, local_path, share_name, access_type, access, - force=False): - """Remove from allow hosts permit rule.""" - try: - hosts = self._get_allow_hosts(share_name) - hosts.remove(access) - self._set_allow_hosts(hosts, share_name) - except exception.ProcessExecutionError as e: - if not ('does not exist' in e.stdout and force): - raise - - def _get_allow_hosts(self, share_name): - (out, _) = self._execute('net', 'conf', 'getparm', share_name, - 'hosts allow', run_as_root=True) - return out.split() - - def _set_allow_hosts(self, hosts, share_name): - value = ' '.join(hosts) - self._execute('net', 'conf', 'setparm', share_name, 'hosts allow', - value, run_as_root=True) diff --git a/manila/share/manager.py b/manila/share/manager.py index d31cdf69e3..59b5236b25 100644 --- a/manila/share/manager.py +++ b/manila/share/manager.py @@ -16,8 +16,7 @@ **Related Flags** -:share_driver: Used by :class:`ShareManager`. Defaults to - :class:`manila.share.drivers.lvm.LVMShareDriver`. +:share_driver: Used by :class:`ShareManager`. """ from manila.common import constants @@ -39,7 +38,7 @@ LOG = logging.getLogger(__name__) share_manager_opts = [ cfg.StrOpt('share_driver', - default='manila.share.drivers.lvm.LVMShareDriver', + default='manila.share.drivers.generic.GenericShareDriver', help='Driver to use for share creation'), cfg.BoolOpt('delete_share_server_with_last_share', default=False, diff --git a/manila/tests/conf_fixture.py b/manila/tests/conf_fixture.py index 23bfd52b5d..9a4c03f6f5 100644 --- a/manila/tests/conf_fixture.py +++ b/manila/tests/conf_fixture.py @@ -31,7 +31,6 @@ def set_defaults(conf): _safe_set_of_opts(conf, 'connection', "sqlite://", group='database') _safe_set_of_opts(conf, 'sqlite_synchronous', False) _safe_set_of_opts(conf, 'policy_file', _POLICY_PATH) - _safe_set_of_opts(conf, 'share_export_ip', '0.0.0.0') _safe_set_of_opts(conf, 'service_instance_user', 'fake_user') _safe_set_of_opts(conf, 'api_paste_config', _API_PASTE_PATH) _safe_set_of_opts(conf, 'share_driver', diff --git a/manila/tests/fake_driver.py b/manila/tests/fake_driver.py index d3c7a2a371..7b15f5ee0b 100644 --- a/manila/tests/fake_driver.py +++ b/manila/tests/fake_driver.py @@ -13,17 +13,21 @@ # under the License. from manila.openstack.common import log as logging -from manila.share.drivers import lvm +from manila.share.drivers import generic LOG = logging.getLogger(__name__) -class FakeShareDriver(lvm.LVMShareDriver): +class FakeShareDriver(generic.GenericShareDriver): """Logs calls instead of executing.""" def __init__(self, *args, **kwargs): super(FakeShareDriver, self).__init__(execute=self.fake_execute, *args, **kwargs) + def do_setup(self, context): + """Fake setup of Generic driver.""" + pass + def check_for_setup_error(self): """No setup necessary in fake mode.""" pass diff --git a/manila/tests/scheduler/test_filter_scheduler.py b/manila/tests/scheduler/test_filter_scheduler.py index 23500c5137..71011a8f99 100644 --- a/manila/tests/scheduler/test_filter_scheduler.py +++ b/manila/tests/scheduler/test_filter_scheduler.py @@ -36,7 +36,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): fake_context = context.RequestContext('user', 'project') request_spec = { 'share_properties': {'project_id': 1, 'size': 1}, - 'share_type': {'name': 'LVM_NFS'}, + 'share_type': {'name': 'NFS'}, 'share_id': ['fake-id1'], } self.assertRaises(exception.NoValidHost, sched.schedule_create_share, @@ -60,7 +60,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): fake_context = context.RequestContext('user', 'project') request_spec = { 'share_properties': {'project_id': 1, 'size': 1}, - 'share_type': {'name': 'LVM_NFS'}, + 'share_type': {'name': 'NFS'}, 'share_id': ['fake-id1'], } self.assertRaises(exception.NoValidHost, sched.schedule_create_share, @@ -77,7 +77,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): is_admin=True) fakes.mock_host_manager_db_calls(_mock_service_get_all_by_topic) request_spec = { - 'share_type': {'name': 'LVM_NFS'}, + 'share_type': {'name': 'NFS'}, 'share_properties': {'project_id': 1, 'size': 1}, } weighed_host = sched._schedule_share(fake_context, request_spec, {}) @@ -99,7 +99,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): self.flags(scheduler_max_attempts=1) sched = fakes.FakeFilterScheduler() request_spec = { - 'volume_type': {'name': 'LVM_iSCSI'}, + 'volume_type': {'name': 'iSCSI'}, 'share_properties': {'project_id': 1, 'size': 1}, } filter_properties = {} @@ -113,7 +113,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): self.flags(scheduler_max_attempts=2) sched = fakes.FakeFilterScheduler() request_spec = { - 'volume_type': {'name': 'LVM_iSCSI'}, + 'volume_type': {'name': 'iSCSI'}, 'share_properties': {'project_id': 1, 'size': 1}, } filter_properties = {} @@ -127,7 +127,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): self.flags(scheduler_max_attempts=2) sched = fakes.FakeFilterScheduler() request_spec = { - 'volume_type': {'name': 'LVM_iSCSI'}, + 'volume_type': {'name': 'iSCSI'}, 'share_properties': {'project_id': 1, 'size': 1}, } retry = dict(num_attempts=1) @@ -142,7 +142,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): self.flags(scheduler_max_attempts=2) sched = fakes.FakeFilterScheduler() request_spec = { - 'volume_type': {'name': 'LVM_iSCSI'}, + 'volume_type': {'name': 'iSCSI'}, 'share_properties': {'project_id': 1, 'size': 1}, } retry = dict(num_attempts=2) diff --git a/manila/tests/test_share_lvm.py b/manila/tests/test_share_lvm.py deleted file mode 100644 index 3099b9688b..0000000000 --- a/manila/tests/test_share_lvm.py +++ /dev/null @@ -1,682 +0,0 @@ -# Copyright 2012 NetApp -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Unit tests for the NFS driver module.""" - -import os - -import mock -from oslo.config import cfg - -from manila import context -from manila.db.sqlalchemy import models -from manila import exception -from manila.openstack.common import importutils -from manila.openstack.common import log as logging -from manila.share.configuration import Configuration -from manila.share.drivers import lvm -from manila import test -from manila.tests.db import fakes as db_fakes -from manila.tests import fake_utils - - -CONF = cfg.CONF - - -def fake_share(**kwargs): - share = { - 'id': 'fakeid', - 'name': 'fakename', - 'size': 1, - 'share_proto': 'NFS', - 'export_location': '127.0.0.1:/mnt/nfs/volume-00002', - } - share.update(kwargs) - return db_fakes.FakeModel(share) - - -def fake_snapshot(**kwargs): - snapshot = { - 'id': 'fakesnapshotid', - 'share_name': 'fakename', - 'share_id': 'fakeid', - 'name': 'fakesnapshotname', - 'share_size': 1, - 'share_proto': 'NFS', - 'export_location': '127.0.0.1:/mnt/nfs/volume-00002', - } - snapshot.update(kwargs) - return db_fakes.FakeModel(snapshot) - - -def fake_access(**kwargs): - access = { - 'id': 'fakeaccid', - 'access_type': 'ip', - 'access_to': '10.0.0.2', - 'state': 'active', - } - access.update(kwargs) - return db_fakes.FakeModel(access) - - -class LVMShareDriverTestCase(test.TestCase): - """Tests LVMShareDriver.""" - - def setUp(self): - super(LVMShareDriverTestCase, self).setUp() - fake_utils.stub_out_utils_execute(self.stubs) - self._execute = fake_utils.fake_execute - self._context = context.get_admin_context() - - CONF.set_default('share_volume_group', 'fakevg') - CONF.set_default('share_export_ip', '10.0.0.1') - - self._helper_cifs = mock.Mock() - self._helper_nfs = mock.Mock() - self.fake_conf = Configuration(None) - self._db = mock.Mock() - self._os = lvm.os = mock.Mock() - self._os.path.join = os.path.join - self._driver = lvm.LVMShareDriver(self._db, - execute=self._execute, - configuration=self.fake_conf) - self._driver._helpers = { - 'CIFS': self._helper_cifs, - 'NFS': self._helper_nfs, - } - - self.share = fake_share() - self.access = fake_access() - self.snapshot = fake_snapshot() - - # Used only to test compatibility with share manager - self.share_server = "fake_share_server" - - def tearDown(self): - super(LVMShareDriverTestCase, self).tearDown() - fake_utils.fake_execute_set_repliers([]) - fake_utils.fake_execute_clear_log() - - def test_do_setup(self): - CONF.set_default('share_lvm_helpers', ['NFS=fakenfs']) - lvm.importutils = mock.Mock() - lvm.importutils.import_class.return_value = self._helper_nfs - self._driver.do_setup(self._context) - lvm.importutils.import_class.assert_has_calls([ - mock.call('fakenfs') - ]) - - def test_check_for_setup_error(self): - def exec_runner(*ignore_args, **ignore_kwargs): - return '\n fake1\n fakevg\n fake2\n', '' - - expected_exec = [ - 'vgs --noheadings -o name', - ] - fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)]) - ret = self._driver.check_for_setup_error() - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_check_for_setup_error_no_vg(self): - def exec_runner(*ignore_args, **ignore_kwargs): - return '\n fake0\n fake1\n fake2\n', '' - - fake_utils.fake_execute_set_repliers([('vgs --noheadings -o name', - exec_runner)]) - self.assertRaises(exception.InvalidParameterValue, - self._driver.check_for_setup_error) - - def test_check_for_setup_error_no_export_ip(self): - def exec_runner(*ignore_args, **ignore_kwargs): - return '\n fake1\n fakevg\n fake2\n', '' - - fake_utils.fake_execute_set_repliers([('vgs --noheadings -o name', - exec_runner)]) - CONF.set_default('share_export_ip', None) - self.assertRaises(exception.InvalidParameterValue, - self._driver.check_for_setup_error) - - def test_local_path_normal(self): - share = fake_share(name='fake_sharename') - CONF.set_default('share_volume_group', 'fake_vg') - ret = self._driver._local_path(share) - self.assertEqual(ret, '/dev/mapper/fake_vg-fake_sharename') - - def test_local_path_escapes(self): - share = fake_share(name='fake-sharename') - CONF.set_default('share_volume_group', 'fake-vg') - ret = self._driver._local_path(share) - self.assertEqual(ret, '/dev/mapper/fake--vg-fake--sharename') - - def test_create_share(self): - self._helper_nfs.create_export.return_value = 'fakelocation' - self._driver._mount_device = mock.Mock() - ret = self._driver.create_share(self._context, self.share, - self.share_server) - CONF.set_default('share_lvm_mirrors', 0) - self._driver._mount_device.assert_called_with( - self.share, '/dev/mapper/fakevg-fakename') - expected_exec = [ - 'lvcreate -L 1G -n fakename fakevg', - 'mkfs.ext4 /dev/mapper/fakevg-fakename', - ] - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - self.assertEqual(ret, 'fakelocation') - - def test_create_share_from_snapshot(self): - CONF.set_default('share_lvm_mirrors', 0) - self._driver._mount_device = mock.Mock() - mount_share = '/dev/mapper/fakevg-fakename' - mount_snapshot = '/dev/mapper/fakevg-fakesnapshotname' - self._helper_nfs.create_export.return_value = 'fakelocation' - mount_path = self._get_mount_path(self.share) - - ret = self._driver.create_share_from_snapshot(self._context, - self.share, - self.snapshot, - self.share_server) - - self._driver._mount_device.assert_called_with(self.share, - mount_snapshot) - expected_exec = [ - 'lvcreate -L 1G -n fakename fakevg', - 'mkfs.ext4 /dev/mapper/fakevg-fakename', - ("dd count=0 if=%s of=%s iflag=direct oflag=direct" % - (mount_snapshot, mount_share)), - ("dd if=%s of=%s count=1024 bs=1M iflag=direct oflag=direct" % - (mount_snapshot, mount_share)), - ] - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_create_share_mirrors(self): - - share = fake_share(size='2048') - CONF.set_default('share_lvm_mirrors', 2) - self._helper_nfs.create_export.return_value = 'fakelocation' - self._driver._mount_device = mock.Mock() - ret = self._driver.create_share(self._context, share, - self.share_server) - self._driver._mount_device.assert_called_with( - share, '/dev/mapper/fakevg-fakename') - expected_exec = [ - 'lvcreate -L 2048G -n fakename fakevg -m 2 --nosync -R 2', - 'mkfs.ext4 /dev/mapper/fakevg-fakename', - ] - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - self.assertEqual(ret, 'fakelocation') - - def test_deallocate_container(self): - expected_exec = ['lvremove -f fakevg/fakename'] - ret = self._driver._deallocate_container(self.share['name']) - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_get_share_stats(self): - def exec_runner(*ignore_args, **ignore_kwargs): - return '\n fakevg 5.38 4.30\n', '' - - expected_exec = [ - 'vgs --noheadings --nosuffix --unit=G -o name,size,free fakevg', - ] - fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)]) - CONF.set_default('reserved_share_percentage', 1) - ret = self._driver.get_share_stats(refresh=True) - expected_ret = { - 'share_backend_name': 'LVM', - 'vendor_name': 'Open Source', - 'driver_version': '1.0', - 'storage_protocol': 'NFS_CIFS', - 'total_capacity_gb': 5.38, - 'free_capacity_gb': 4.30, - 'reserved_percentage': 1, - 'QoS_support': False, - } - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - self.assertEqual(ret, expected_ret) - - def test_get_share_stats_error(self): - def exec_runner(*ignore_args, **ignore_kwargs): - raise exception.ProcessExecutionError() - - expected_exec = [ - 'vgs --noheadings --nosuffix --unit=G -o name,size,free fakevg', - ] - fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)]) - CONF.set_default('reserved_share_percentage', 1) - ret = self._driver.get_share_stats(refresh=True) - expected_ret = { - 'share_backend_name': 'LVM', - 'vendor_name': 'Open Source', - 'driver_version': '1.0', - 'storage_protocol': 'NFS_CIFS', - 'total_capacity_gb': 0, - 'free_capacity_gb': 0, - 'reserved_percentage': 1, - 'QoS_support': False, - } - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - self.assertEqual(ret, expected_ret) - - def test_remove_export(self): - mount_path = self._get_mount_path(self.share) - self._os.path.exists.return_value = True - - self._driver._remove_export(self._context, self.share) - - expected_exec = [ - "umount -f %s" % (mount_path,), - ] - - self._os.path.exists.assert_called_with(mount_path) - self._os.rmdir.assert_called_with(mount_path) - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_remove_export_is_busy_error(self): - def exec_runner(*ignore_args, **ignore_kwargs): - raise exception.ProcessExecutionError(stderr='device is busy') - self._os.path.exists.return_value = True - mount_path = self._get_mount_path(self.share) - expected_exec = [ - "umount -f %s" % (mount_path), - ] - fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)]) - - self.assertRaises(exception.ShareIsBusy, self._driver._remove_export, - self._context, self.share) - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_remove_export_error(self): - def exec_runner(*ignore_args, **ignore_kwargs): - raise exception.ProcessExecutionError(stderr='fake error') - - mount_path = self._get_mount_path(self.share) - expected_exec = [ - "umount -f %s" % (mount_path), - ] - fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)]) - self._os.path.exists.return_value = True - self._driver._remove_export(self._context, self.share) - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_create_snapshot(self): - self._driver.create_snapshot(self._context, self.snapshot, - self.share_server) - expected_exec = [ - ("lvcreate -L 1G --name fakesnapshotname --snapshot %s/fakename" % - (CONF.share_volume_group,)), - ] - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_ensure_share(self): - device_name = '/dev/mapper/fakevg-fakename' - location = 'fake_location' - with mock.patch.object(self._driver, - '_mount_device', - mock.Mock(return_value=location)): - self._driver.ensure_share(self._context, self.share, - self.share_server) - self._driver._mount_device.assert_called_with(self.share, - device_name) - self._helper_nfs.create_export.assert_called_once_with( - location, self.share['name'], recreate=True) - - def test_delete_share(self): - mount_path = self._get_mount_path(self.share) - self._helper_nfs.remove_export(mount_path, self.share['name']) - self._driver._delete_share(self._context, self.share) - - def test_delete_snapshot(self): - expected_exec = ['lvremove -f fakevg/fakesnapshotname'] - self._driver.delete_snapshot(self._context, self.snapshot, - self.share_server) - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_delete_share_invalid_share(self): - self._driver._get_helper = mock.Mock( - side_effect=exception.InvalidShare(reason='fake')) - self._driver.delete_share(self._context, self.share, self.share_server) - - def test_allow_access(self): - mount_path = self._get_mount_path(self.share) - self._helper_nfs.allow_access(mount_path, - self.share['name'], - self.access['access_type'], - self.access['access_to']) - self._driver.allow_access(self._context, self.share, self.access, - self.share_server) - - def test_deny_access(self): - mount_path = self._get_mount_path(self.share) - self._helper_nfs.deny_access(mount_path, - self.share['name'], - self.access['access_type'], - self.access['access_to']) - self._driver.deny_access(self._context, self.share, self.access, - self.share_server) - - def test_mount_device(self): - mount_path = self._get_mount_path(self.share) - ret = self._driver._mount_device(self.share, 'fakedevice') - expected_exec = [ - "mkdir -p %s" % (mount_path,), - "mount fakedevice %s" % (mount_path,), - "chmod 777 %s" % (mount_path,), - ] - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - self.assertEqual(ret, mount_path) - - def test_mount_device_already(self): - def exec_runner(*ignore_args, **ignore_kwargs): - raise exception.ProcessExecutionError(stderr='already mounted') - - mount_path = self._get_mount_path(self.share) - expected_exec = [ - "mkdir -p %s" % (mount_path,), - "mount fakedevice %s" % (mount_path,), - ] - fake_utils.fake_execute_set_repliers([(expected_exec[1], exec_runner)]) - ret = self._driver._mount_device(self.share, 'fakedevice') - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - self.assertEqual(ret, mount_path) - - def test_mount_device_error(self): - def exec_runner(*ignore_args, **ignore_kwargs): - raise exception.ProcessExecutionError(stderr='fake error') - - mount_path = self._get_mount_path(self.share) - expected_exec = [ - "mkdir -p %s" % (mount_path,), - "mount fakedevice %s" % (mount_path,), - ] - fake_utils.fake_execute_set_repliers([(expected_exec[1], exec_runner)]) - self.assertRaises(exception.ProcessExecutionError, - self._driver._mount_device, self.share, 'fakedevice') - - def test_get_helper(self): - share_cifs = fake_share(share_proto='CIFS') - share_nfs = fake_share(share_proto='NFS') - share_fake = fake_share(share_proto='FAKE') - self.assertEqual(self._driver._get_helper(share_cifs), - self._helper_cifs) - self.assertEqual(self._driver._get_helper(share_nfs), - self._helper_nfs) - self.assertRaises(exception.InvalidShare, self._driver._get_helper, - fake_share(share_proto='FAKE')) - - def _get_mount_path(self, share): - return os.path.join(CONF.share_export_root, share['name']) - - -class NFSHelperTestCase(test.TestCase): - """Test case for NFS driver.""" - - def setUp(self): - super(NFSHelperTestCase, self).setUp() - fake_utils.stub_out_utils_execute(self.stubs) - CONF.set_default('share_export_ip', '127.0.0.1') - self._execute = fake_utils.fake_execute - self.fake_conf = Configuration(None) - self._helper = lvm.NFSHelper(self._execute, self.fake_conf) - fake_utils.fake_execute_clear_log() - - def tearDown(self): - super(NFSHelperTestCase, self).tearDown() - fake_utils.fake_execute_set_repliers([]) - fake_utils.fake_execute_clear_log() - - def test_failed_init(self): - self._execute = mock.Mock(side_effect=exception.ProcessExecutionError) - self.assertRaises(exception.Error, lvm.NFSHelper.__init__, - self._helper, self._execute, self.fake_conf) - - def test_create_export(self): - ret = self._helper.create_export('/opt/nfs', 'volume-00001') - expected_location = '%s:/opt/nfs' % CONF.share_export_ip - self.assertEqual(ret, expected_location) - - def test_remove_export(self): - self._helper.remove_export('/opt/nfs', 'volume-00001') - - def test_allow_access(self): - self._helper.allow_access('/opt/nfs', 'volume-00001', 'ip', '10.0.0.*') - - export_string = '10.0.0.*:/opt/nfs' - expected_exec = [ - 'exportfs', - 'exportfs -o rw,no_subtree_check %s' % export_string, - ] - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - def test_allow_access_no_ip(self): - self.assertRaises(exception.InvalidShareAccess, - self._helper.allow_access, '/opt/nfs', 'share0', - 'fake', 'fakerule') - - def test_allow_access_negative(self): - def exec_runner(*ignore_args, **ignore_kwargs): - return '\n/opt/nfs\t\t10.0.0.*\n', '' - - fake_utils.fake_execute_set_repliers([('exportfs', exec_runner)]) - self.assertRaises(exception.ShareAccessExists, - self._helper.allow_access, - '/opt/nfs', 'volume-00001', 'ip', '10.0.0.*') - - def test_deny_access(self): - self._helper.deny_access('/opt/nfs', 'volume-00001', 'ip', '10.0.0.*') - export_string = '10.0.0.*:/opt/nfs' - expected_exec = ['exportfs -u %s' % export_string] - self.assertEqual(fake_utils.fake_execute_get_log(), expected_exec) - - -class CIFSNetConfHelperTestCase(test.TestCase): - """Test case for CIFS driver with net conf management.""" - - def setUp(self): - super(CIFSNetConfHelperTestCase, self).setUp() - fake_utils.stub_out_utils_execute(self.stubs) - CONF.set_default('share_export_ip', '127.0.0.1') - self.share = fake_share() - self._execute = fake_utils.fake_execute - self.fake_conf = Configuration(None) - self._helper = lvm.CIFSNetConfHelper(self._execute, self.fake_conf) - fake_utils.fake_execute_clear_log() - - def tearDown(self): - super(CIFSNetConfHelperTestCase, self).tearDown() - fake_utils.fake_execute_set_repliers([]) - fake_utils.fake_execute_clear_log() - - def test_create_export(self): - share_name = self.share['name'] - self._helper._execute = mock.Mock() - parameters = { - 'browseable': 'yes', - 'create mask': '0755', - 'hosts deny': '0.0.0.0/0', - 'hosts allow': '127.0.0.1', - } - ret = self._helper.create_export('fakelocalpath', share_name) - calls = [mock.call('net', 'conf', 'addshare', share_name, - 'fakelocalpath', 'writeable=y', 'guest_ok=y', - run_as_root=True)] - for name, value in parameters.items(): - calls.append(mock.call('net', 'conf', 'setparm', share_name, name, - value, run_as_root=True)) - self._helper._execute.assert_has_calls(calls) - expected_ret = "//127.0.0.1/%s" % (share_name,) - self.assertEqual(ret, expected_ret) - - def test_create_export_already_exists(self): - def exec_runner(*ignore_args, **ignore_kwargs): - raise exception.ProcessExecutionError(stderr='already exists') - - expected_exec = [ - "net conf addshare %s %s writeable=y guest_ok=y" % ( - self.share['name'], - 'fakelocalpath', - ), - ] - fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)]) - self.assertRaises(exception.ShareBackendException, - self._helper.create_export, 'fakelocalpath', - self.share['name']) - - def test_create_export_recreate(self): - share_name = self.share['name'] - - def raise_exec_error(): - raise exception.ProcessExecutionError(stderr="already exists") - execute_return_values = [raise_exec_error, ''] - parameters = { - 'browseable': 'yes', - 'create mask': '0755', - 'hosts deny': '0.0.0.0/0', - 'hosts allow': '127.0.0.1', - } - execute_return_values.extend([''] * len(parameters)) - self._helper._execute = mock.Mock(side_effect=execute_return_values) - ret = self._helper.create_export('fakelocalpath', share_name, - recreate=True) - expected_ret = "//127.0.0.1/%s" % (share_name,) - calls = [mock.call('net', 'conf', 'setparm', share_name, name, - value, run_as_root=True) for - name, value in parameters.items()] - self._helper._execute.assert_has_calls(calls) - self.assertEqual(ret, expected_ret) - - def test_create_export_error(self): - share_name = self.share['name'] - - def raise_exec_error(*args, **kwargs): - raise exception.ProcessExecutionError(stderr="fake_stderr") - - self._helper._execute = mock.Mock( - side_effect=raise_exec_error) - self.assertRaises(exception.ProcessExecutionError, - self._helper.create_export, 'fakelocalpath', - share_name) - - def test_remove_export(self): - share_name = self.share['name'] - self._helper._execute = mock.Mock() - self._helper.remove_export('fakelocalpath', share_name) - self._helper._execute.assert_called_with('smbcontrol', 'all', - 'close-share', share_name, - run_as_root=True) - - def test_remove_export_no_such_service(self): - share_name = self.share['name'] - - def exec_return(*args, **kwargs): - if 'net' in args: - raise exception.ProcessExecutionError( - stderr='SBC_ERR_NO_SUCH_SERVICE') - - self._helper._execute = mock.Mock(side_effect=exec_return) - self._helper.remove_export('fakelocalpath', share_name) - self._helper._execute.assert_called_with( - 'smbcontrol', 'all', 'close-share', share_name, run_as_root=True) - - def test_remove_export_error(self): - share_name = self.share['name'] - - def raise_exec_error(*args, **kwargs): - raise exception.ProcessExecutionError(stderr="fake_stderr") - self._helper._execute = mock.Mock( - side_effect=raise_exec_error) - - self.assertRaises(exception.ProcessExecutionError, - self._helper.remove_export, 'fakelocalpath', - share_name) - - def test_allow_access(self): - share_name = self.share['name'] - self._helper._get_allow_hosts = mock.Mock(return_value=['127.0.0.1', - '10.0.0.1']) - self._helper._set_allow_hosts = mock.Mock() - self._helper.allow_access('fakelocalpath', share_name, 'ip', - '10.0.0.2') - self._helper._set_allow_hosts.assert_called_with( - ['127.0.0.1', '10.0.0.1', '10.0.0.2'], share_name) - - def test_allow_access_exists(self): - share_name = self.share['name'] - self._helper._get_allow_hosts = mock.Mock(return_value=['127.0.0.1', - '10.0.0.1']) - self.assertRaises(exception.ShareAccessExists, - self._helper.allow_access, 'fakelocalpath', - share_name, 'ip', '10.0.0.1') - - def test_allow_access_wrong_type(self): - share_name = self.share['name'] - self.assertRaises(exception.InvalidShareAccess, - self._helper.allow_access, 'fakelocalpath', - share_name, 'fake', 'fake access') - - def test_deny_access(self): - share_name = self.share['name'] - self._helper._get_allow_hosts = mock.Mock(return_value=['127.0.0.1', - '10.0.0.1']) - self._helper._set_allow_hosts = mock.Mock() - self._helper.deny_access('fakelocalpath', share_name, 'ip', - '10.0.0.1') - self._helper._set_allow_hosts.assert_called_with( - ['127.0.0.1'], share_name) - - def test_deny_access_not_exists(self): - share_name = self.share['name'] - - def raise_exec_error(*args, **kwargs): - raise exception.ProcessExecutionError(stdout="does not exist") - - self._helper._get_allow_hosts = mock.Mock(side_effect=raise_exec_error) - self.assertRaises(exception.ProcessExecutionError, - self._helper.deny_access, 'fakelocalpath', - share_name, 'ip', '10.0.0.1') - - def test_deny_access_not_exists_force(self): - share_name = self.share['name'] - - def raise_exec_error(*args, **kwargs): - raise exception.ProcessExecutionError(stdout="does not exist") - - self._helper._get_allow_hosts = mock.Mock(side_effect=raise_exec_error) - self._helper.deny_access('fakelocalpath', share_name, 'ip', '10.0.0.1', - force=True) - - def test_deny_access_error(self): - share_name = self.share['name'] - - def raise_exec_error(*args, **kwargs): - raise exception.ProcessExecutionError(stdout="fake out") - - self._helper._get_allow_hosts = mock.Mock(side_effect=raise_exec_error) - self.assertRaises(exception.ProcessExecutionError, - self._helper.deny_access, 'fakelocalpath', - share_name, 'ip', '10.0.0.1') - - def test_get_allow_hosts(self): - share_name = self.share['name'] - self._helper._execute = mock.Mock(return_value=( - '127.0.0.1 10.0.0.1', '')) - ret = self._helper._get_allow_hosts(share_name) - self.assertEqual(ret, ['127.0.0.1', '10.0.0.1']) - - def test_set_allow_hosts(self): - share_name = self.share['name'] - self._helper._execute = mock.Mock() - self._helper._set_allow_hosts(['127.0.0.1', '10.0.0.1'], share_name) - self._helper._execute.assert_called_with( - 'net', 'conf', 'setparm', share_name, 'hosts allow', - '127.0.0.1 10.0.0.1', run_as_root=True)