Remove LVM driver

Support of LVM driver is planned to be dropped as it is duplicates
functionality from Cinder. Default driver is Generic driver, that uses Cinder.

Changes:
- removed LVM driver
- removed related stuff to LVM driver

Partially implements blueprint remove-lvm-driver
Change-Id: If2df592db082fdc5d609a5ce644106fee7d0583d
This commit is contained in:
vponomaryov 2014-08-05 17:57:35 +03:00
parent b8f511e8c9
commit dd4bd5c487
10 changed files with 21 additions and 1391 deletions

View File

@ -2,39 +2,6 @@
# This file should be owned by (and only-writeable by) the root user # This file should be owned by (and only-writeable by) the root user
[Filters] [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' # manila/share/drivers/glusterfs.py: 'mkdir', '%s'
mkdir: CommandFilter, /usr/bin/mkdir, root mkdir: CommandFilter, /usr/bin/mkdir, root

View File

@ -28,10 +28,6 @@ lvdisplay: CommandFilter, lvdisplay, root
# manila/volume/driver.py: 'iscsiadm', '-m', 'node', '-T', ... # manila/volume/driver.py: 'iscsiadm', '-m', 'node', '-T', ...
iscsiadm: CommandFilter, iscsiadm, root 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), ... #manila/volume/.py: utils.temporary_chown(path, 0), ...
chown: CommandFilter, chown, root chown: CommandFilter, chown, root

View File

@ -1505,7 +1505,7 @@ msgstr ""
msgid "Error in gluster volume set: %s" msgid "Error in gluster volume set: %s"
msgstr "" msgstr ""
#: manila/share/drivers/glusterfs.py:144 manila/share/drivers/lvm.py:306 #: manila/share/drivers/glusterfs.py:144
#, python-format #, python-format
msgid "%s is already mounted" msgid "%s is already mounted"
msgstr "" msgstr ""
@ -1524,39 +1524,6 @@ msgstr ""
msgid "GlusterFS control mount is not available" msgid "GlusterFS control mount is not available"
msgstr "" 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 #: manila/share/drivers/service_instance.py:130
msgid "Service instance user is not specified" msgid "Service instance user is not specified"
msgstr "" msgstr ""

View File

@ -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 or from a particular CONF group, to be able to set multiple configurations
for a given set of values. 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] [generic1]
volume_group=lvm-group-1 share_backend_name=generic-backend-1
... ...
[lvm2] [generic2]
volume_group=lvm-group-2 share_backend_name=generic-backend-2
... ...
And the configuration group name will be passed in so that all calls to And the configuration group name will be passed in so that all calls to

View File

@ -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)

View File

@ -16,8 +16,7 @@
**Related Flags** **Related Flags**
:share_driver: Used by :class:`ShareManager`. Defaults to :share_driver: Used by :class:`ShareManager`.
:class:`manila.share.drivers.lvm.LVMShareDriver`.
""" """
from manila.common import constants from manila.common import constants
@ -39,7 +38,7 @@ LOG = logging.getLogger(__name__)
share_manager_opts = [ share_manager_opts = [
cfg.StrOpt('share_driver', cfg.StrOpt('share_driver',
default='manila.share.drivers.lvm.LVMShareDriver', default='manila.share.drivers.generic.GenericShareDriver',
help='Driver to use for share creation'), help='Driver to use for share creation'),
cfg.BoolOpt('delete_share_server_with_last_share', cfg.BoolOpt('delete_share_server_with_last_share',
default=False, default=False,

View File

@ -31,7 +31,6 @@ def set_defaults(conf):
_safe_set_of_opts(conf, 'connection', "sqlite://", group='database') _safe_set_of_opts(conf, 'connection', "sqlite://", group='database')
_safe_set_of_opts(conf, 'sqlite_synchronous', False) _safe_set_of_opts(conf, 'sqlite_synchronous', False)
_safe_set_of_opts(conf, 'policy_file', _POLICY_PATH) _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, 'service_instance_user', 'fake_user')
_safe_set_of_opts(conf, 'api_paste_config', _API_PASTE_PATH) _safe_set_of_opts(conf, 'api_paste_config', _API_PASTE_PATH)
_safe_set_of_opts(conf, 'share_driver', _safe_set_of_opts(conf, 'share_driver',

View File

@ -13,17 +13,21 @@
# under the License. # under the License.
from manila.openstack.common import log as logging from manila.openstack.common import log as logging
from manila.share.drivers import lvm from manila.share.drivers import generic
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class FakeShareDriver(lvm.LVMShareDriver): class FakeShareDriver(generic.GenericShareDriver):
"""Logs calls instead of executing.""" """Logs calls instead of executing."""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(FakeShareDriver, self).__init__(execute=self.fake_execute, super(FakeShareDriver, self).__init__(execute=self.fake_execute,
*args, **kwargs) *args, **kwargs)
def do_setup(self, context):
"""Fake setup of Generic driver."""
pass
def check_for_setup_error(self): def check_for_setup_error(self):
"""No setup necessary in fake mode.""" """No setup necessary in fake mode."""
pass pass

View File

@ -36,7 +36,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
fake_context = context.RequestContext('user', 'project') fake_context = context.RequestContext('user', 'project')
request_spec = { request_spec = {
'share_properties': {'project_id': 1, 'size': 1}, 'share_properties': {'project_id': 1, 'size': 1},
'share_type': {'name': 'LVM_NFS'}, 'share_type': {'name': 'NFS'},
'share_id': ['fake-id1'], 'share_id': ['fake-id1'],
} }
self.assertRaises(exception.NoValidHost, sched.schedule_create_share, self.assertRaises(exception.NoValidHost, sched.schedule_create_share,
@ -60,7 +60,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
fake_context = context.RequestContext('user', 'project') fake_context = context.RequestContext('user', 'project')
request_spec = { request_spec = {
'share_properties': {'project_id': 1, 'size': 1}, 'share_properties': {'project_id': 1, 'size': 1},
'share_type': {'name': 'LVM_NFS'}, 'share_type': {'name': 'NFS'},
'share_id': ['fake-id1'], 'share_id': ['fake-id1'],
} }
self.assertRaises(exception.NoValidHost, sched.schedule_create_share, self.assertRaises(exception.NoValidHost, sched.schedule_create_share,
@ -77,7 +77,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
is_admin=True) is_admin=True)
fakes.mock_host_manager_db_calls(_mock_service_get_all_by_topic) fakes.mock_host_manager_db_calls(_mock_service_get_all_by_topic)
request_spec = { request_spec = {
'share_type': {'name': 'LVM_NFS'}, 'share_type': {'name': 'NFS'},
'share_properties': {'project_id': 1, 'size': 1}, 'share_properties': {'project_id': 1, 'size': 1},
} }
weighed_host = sched._schedule_share(fake_context, request_spec, {}) weighed_host = sched._schedule_share(fake_context, request_spec, {})
@ -99,7 +99,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
self.flags(scheduler_max_attempts=1) self.flags(scheduler_max_attempts=1)
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
request_spec = { request_spec = {
'volume_type': {'name': 'LVM_iSCSI'}, 'volume_type': {'name': 'iSCSI'},
'share_properties': {'project_id': 1, 'size': 1}, 'share_properties': {'project_id': 1, 'size': 1},
} }
filter_properties = {} filter_properties = {}
@ -113,7 +113,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
self.flags(scheduler_max_attempts=2) self.flags(scheduler_max_attempts=2)
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
request_spec = { request_spec = {
'volume_type': {'name': 'LVM_iSCSI'}, 'volume_type': {'name': 'iSCSI'},
'share_properties': {'project_id': 1, 'size': 1}, 'share_properties': {'project_id': 1, 'size': 1},
} }
filter_properties = {} filter_properties = {}
@ -127,7 +127,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
self.flags(scheduler_max_attempts=2) self.flags(scheduler_max_attempts=2)
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
request_spec = { request_spec = {
'volume_type': {'name': 'LVM_iSCSI'}, 'volume_type': {'name': 'iSCSI'},
'share_properties': {'project_id': 1, 'size': 1}, 'share_properties': {'project_id': 1, 'size': 1},
} }
retry = dict(num_attempts=1) retry = dict(num_attempts=1)
@ -142,7 +142,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
self.flags(scheduler_max_attempts=2) self.flags(scheduler_max_attempts=2)
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
request_spec = { request_spec = {
'volume_type': {'name': 'LVM_iSCSI'}, 'volume_type': {'name': 'iSCSI'},
'share_properties': {'project_id': 1, 'size': 1}, 'share_properties': {'project_id': 1, 'size': 1},
} }
retry = dict(num_attempts=2) retry = dict(num_attempts=2)

View File

@ -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)