Implement update_access() in generic driver + LVM
Add update_access() method to generic and LVM drivers, to NFSHelper and CIFSHelper. Remove allow_access() and deny_access() from helpers. Partially implements bp new-share-access-driver-interface Co-Authored-By: Rodrigo Barbieri <rodrigo.barbieri@fit-tecnologia.org.br> Change-Id: Ie34c92f8b9dc95d6af5cb150427503bf66543ab6
This commit is contained in:
parent
21cc6fea34
commit
fbfd65b338
|
@ -652,7 +652,7 @@ class ShareDriver(object):
|
|||
|
||||
Drivers should support 2 different cases in this method:
|
||||
1. Recovery after error - 'access_rules' contains all access_rules,
|
||||
'add_rules' and 'delete_rules' are None. Driver should clear any
|
||||
'add_rules' and 'delete_rules' shall be None. Driver should clear any
|
||||
existent access rules and apply all access rules for given share.
|
||||
This recovery is made at driver start up.
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
|||
service_instance.ServiceInstanceManager(
|
||||
driver_config=self.configuration))
|
||||
|
||||
def _ssh_exec(self, server, command):
|
||||
def _ssh_exec(self, server, command, check_exit_code=True):
|
||||
connection = self.ssh_connections.get(server['instance_id'])
|
||||
ssh_conn_timeout = self.configuration.ssh_conn_timeout
|
||||
if not connection:
|
||||
|
@ -163,7 +163,8 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
|||
ssh_pool.remove(ssh)
|
||||
ssh = ssh_pool.create()
|
||||
self.ssh_connections[server['instance_id']] = (ssh_pool, ssh)
|
||||
return processutils.ssh_execute(ssh, ' '.join(command))
|
||||
return processutils.ssh_execute(ssh, ' '.join(command),
|
||||
check_exit_code=check_exit_code)
|
||||
|
||||
def check_for_setup_error(self):
|
||||
"""Returns an error if prerequisites aren't met."""
|
||||
|
@ -821,23 +822,34 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
|||
share_server['backend_details'], share['name'], recreate=True)
|
||||
|
||||
@ensure_server
|
||||
def allow_access(self, context, share, access, share_server=None):
|
||||
"""Allow access to the share."""
|
||||
def update_access(self, context, share, access_rules, add_rules=None,
|
||||
delete_rules=None, share_server=None):
|
||||
"""Update access rules for given share.
|
||||
|
||||
# NOTE(vponomaryov): use direct verification for case some additional
|
||||
# level is added.
|
||||
access_level = access['access_level']
|
||||
if access_level not in (const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO):
|
||||
raise exception.InvalidShareAccessLevel(level=access_level)
|
||||
self._get_helper(share).allow_access(
|
||||
share_server['backend_details'], share['name'],
|
||||
access['access_type'], access['access_level'], access['access_to'])
|
||||
This driver has two different behaviors according to parameters:
|
||||
1. Recovery after error - 'access_rules' contains all access_rules,
|
||||
'add_rules' and 'delete_rules' shall be None. Previously existing
|
||||
access rules are cleared and then added back according
|
||||
to 'access_rules'.
|
||||
|
||||
@ensure_server
|
||||
def deny_access(self, context, share, access, share_server=None):
|
||||
"""Deny access to the share."""
|
||||
self._get_helper(share).deny_access(
|
||||
share_server['backend_details'], share['name'], access)
|
||||
2. Adding/Deleting of several access rules - 'access_rules' contains
|
||||
all access_rules, 'add_rules' and 'delete_rules' contain rules which
|
||||
should be added/deleted. Rules in 'access_rules' are ignored and
|
||||
only rules from 'add_rules' and 'delete_rules' are applied.
|
||||
|
||||
:param context: Current context
|
||||
:param share: Share model with share data.
|
||||
:param access_rules: All access rules for given share
|
||||
:param add_rules: None or List of access rules which should be added
|
||||
access_rules already contains these rules.
|
||||
:param delete_rules: None or List of access rules which should be
|
||||
removed. access_rules doesn't contain these rules.
|
||||
:param share_server: None or Share server model
|
||||
"""
|
||||
self._get_helper(share).update_access(share_server['backend_details'],
|
||||
share['name'], access_rules,
|
||||
add_rules=add_rules,
|
||||
delete_rules=delete_rules)
|
||||
|
||||
def _get_helper(self, share):
|
||||
helper = self._helpers.get(share['share_proto'])
|
||||
|
|
|
@ -17,7 +17,6 @@ import os
|
|||
import re
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
|
||||
from manila.common import constants as const
|
||||
from manila import exception
|
||||
|
@ -51,13 +50,29 @@ class NASHelperBase(object):
|
|||
"""Configure server before allowing access."""
|
||||
pass
|
||||
|
||||
def allow_access(self, server, share_name, access_type, access_level,
|
||||
access_to):
|
||||
"""Allow access to the host."""
|
||||
raise NotImplementedError()
|
||||
def update_access(self, server, share_name, access_rules, add_rules=None,
|
||||
delete_rules=None):
|
||||
"""Update access rules for given share.
|
||||
|
||||
def deny_access(self, server, share_name, access, force=False):
|
||||
"""Deny access to the host."""
|
||||
This driver has two different behaviors according to parameters:
|
||||
1. Recovery after error - 'access_rules' contains all access_rules,
|
||||
'add_rules' and 'delete_rules' shall be None. Previously existing
|
||||
access rules are cleared and then added back according
|
||||
to 'access_rules'.
|
||||
|
||||
2. Adding/Deleting of several access rules - 'access_rules' contains
|
||||
all access_rules, 'add_rules' and 'delete_rules' contain rules which
|
||||
should be added/deleted. Rules in 'access_rules' are ignored and
|
||||
only rules from 'add_rules' and 'delete_rules' are applied.
|
||||
|
||||
:param server: None or Share server's backend details
|
||||
:param share_name: Share's path according to id.
|
||||
:param access_rules: All access rules for given share
|
||||
:param add_rules: None or List of access rules which should be added
|
||||
access_rules already contains these rules.
|
||||
:param delete_rules: None or List of access rules which should be
|
||||
removed. access_rules doesn't contain these rules.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
|
@ -80,6 +95,24 @@ class NASHelperBase(object):
|
|||
def restore_access_after_maintenance(self, server, share_name):
|
||||
"""Enables access to share after maintenance operations were done."""
|
||||
|
||||
@staticmethod
|
||||
def validate_access_rules(access_rules, allowed_types, allowed_levels):
|
||||
"""Validates access rules according to access_type and access_level.
|
||||
|
||||
:param access_rules: List of access rules to be validated.
|
||||
:param allowed_types: tuple of allowed type values.
|
||||
:param allowed_levels: tuple of allowed level values.
|
||||
"""
|
||||
for access in (access_rules or []):
|
||||
access_type = access['access_type']
|
||||
access_level = access['access_level']
|
||||
if access_type not in allowed_types:
|
||||
reason = _("Only %s access type allowed.") % (
|
||||
', '.join(tuple(["'%s'" % x for x in allowed_types])))
|
||||
raise exception.InvalidShareAccess(reason=reason)
|
||||
if access_level not in allowed_levels:
|
||||
raise exception.InvalidShareAccessLevel(level=access_level)
|
||||
|
||||
def _get_maintenance_file_path(self, share_name):
|
||||
return os.path.join(self.configuration.share_mount_path,
|
||||
"%s.maintenance" % share_name)
|
||||
|
@ -88,7 +121,7 @@ class NASHelperBase(object):
|
|||
def nfs_synchronized(f):
|
||||
|
||||
def wrapped_func(self, *args, **kwargs):
|
||||
key = "nfs-%s" % args[0]["instance_id"]
|
||||
key = "nfs-%s" % args[0]["public_address"]
|
||||
|
||||
@utils.synchronized(key)
|
||||
def source_func(self, *args, **kwargs):
|
||||
|
@ -104,9 +137,9 @@ class NFSHelper(NASHelperBase):
|
|||
|
||||
def create_export(self, server, share_name, recreate=False):
|
||||
"""Create new export, delete old one if exists."""
|
||||
return ':'.join([server['public_address'],
|
||||
return ':'.join((server['public_address'],
|
||||
os.path.join(
|
||||
self.configuration.share_mount_path, share_name)])
|
||||
self.configuration.share_mount_path, share_name)))
|
||||
|
||||
def init_helper(self, server):
|
||||
try:
|
||||
|
@ -122,36 +155,90 @@ class NFSHelper(NASHelperBase):
|
|||
"""Remove export."""
|
||||
|
||||
@nfs_synchronized
|
||||
def allow_access(self, server, share_name, access_type, access_level,
|
||||
access_to):
|
||||
"""Allow access to the host."""
|
||||
def update_access(self, server, share_name, access_rules, add_rules=None,
|
||||
delete_rules=None):
|
||||
"""Update access rules for given share.
|
||||
|
||||
Please refer to base class for a more in-depth description.
|
||||
"""
|
||||
local_path = os.path.join(self.configuration.share_mount_path,
|
||||
share_name)
|
||||
if access_type != 'ip':
|
||||
msg = _('only ip access type allowed')
|
||||
raise exception.InvalidShareAccess(reason=msg)
|
||||
|
||||
# check if presents in export
|
||||
out, err = self._ssh_exec(server, ['sudo', 'exportfs'])
|
||||
out = re.search(
|
||||
re.escape(local_path) + '[\s\n]*' + re.escape(access_to), out)
|
||||
if out is not None:
|
||||
raise exception.ShareAccessExists(access_type=access_type,
|
||||
access=access_to)
|
||||
self._ssh_exec(
|
||||
server,
|
||||
['sudo', 'exportfs', '-o', '%s,no_subtree_check' % access_level,
|
||||
':'.join([access_to, local_path])])
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
# Recovery mode
|
||||
if not (add_rules or delete_rules):
|
||||
|
||||
@nfs_synchronized
|
||||
def deny_access(self, server, share_name, access, force=False):
|
||||
"""Deny access to the host."""
|
||||
local_path = os.path.join(self.configuration.share_mount_path,
|
||||
share_name)
|
||||
self._ssh_exec(server, ['sudo', 'exportfs', '-u',
|
||||
':'.join([access['access_to'], local_path])])
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
self.validate_access_rules(
|
||||
access_rules, ('ip',),
|
||||
(const.ACCESS_LEVEL_RO, const.ACCESS_LEVEL_RW))
|
||||
|
||||
hosts = self._get_host_list(out, local_path)
|
||||
for host in hosts:
|
||||
self._ssh_exec(server, ['sudo', 'exportfs', '-u',
|
||||
':'.join((host, local_path))])
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
for access in access_rules:
|
||||
self._ssh_exec(
|
||||
server,
|
||||
['sudo', 'exportfs', '-o',
|
||||
'%s,no_subtree_check' % access['access_level'],
|
||||
':'.join((access['access_to'], local_path))])
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
# Adding/Deleting specific rules
|
||||
else:
|
||||
|
||||
self.validate_access_rules(
|
||||
add_rules, ('ip',),
|
||||
(const.ACCESS_LEVEL_RO, const.ACCESS_LEVEL_RW))
|
||||
|
||||
for access in (delete_rules or []):
|
||||
try:
|
||||
self.validate_access_rules(
|
||||
[access], ('ip',),
|
||||
(const.ACCESS_LEVEL_RO, const.ACCESS_LEVEL_RW))
|
||||
except (exception.InvalidShareAccess,
|
||||
exception.InvalidShareAccessLevel):
|
||||
LOG.warning(_LW(
|
||||
"Unsupported access level %(level)s or access type "
|
||||
"%(type)s, skipping removal of access rule to "
|
||||
"%(to)s.") % {'level': access['access_level'],
|
||||
'type': access['access_type'],
|
||||
'to': access['access_to']})
|
||||
continue
|
||||
self._ssh_exec(server, ['sudo', 'exportfs', '-u',
|
||||
':'.join((access['access_to'], local_path))])
|
||||
if delete_rules:
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
for access in (add_rules or []):
|
||||
access_to, access_type = (access['access_to'],
|
||||
access['access_type'])
|
||||
found_item = re.search(
|
||||
re.escape(local_path) + '[\s\n]*' + re.escape(access_to),
|
||||
out)
|
||||
if found_item is not None:
|
||||
LOG.warning(_LW("Access rule %(type)s:%(to)s already "
|
||||
"exists for share %(name)s") % {
|
||||
'to': access_to,
|
||||
'type': access_type,
|
||||
'name': share_name
|
||||
})
|
||||
else:
|
||||
self._ssh_exec(
|
||||
server,
|
||||
['sudo', 'exportfs', '-o',
|
||||
'%s,no_subtree_check' % access['access_level'],
|
||||
':'.join((access['access_to'], local_path))])
|
||||
if add_rules:
|
||||
self._sync_nfs_temp_and_perm_files(server)
|
||||
|
||||
def _get_host_list(self, output, local_path):
|
||||
entries = []
|
||||
output = output.replace('\n\t\t', ' ')
|
||||
lines = output.split('\n')
|
||||
for line in lines:
|
||||
items = line.split(' ')
|
||||
if local_path == items[0]:
|
||||
entries.append(items[1])
|
||||
return entries
|
||||
|
||||
def _sync_nfs_temp_and_perm_files(self, server):
|
||||
"""Sync changes of exports with permanent NFS config file.
|
||||
|
@ -164,11 +251,17 @@ class NFSHelper(NASHelperBase):
|
|||
]
|
||||
self._ssh_exec(server, sync_cmd)
|
||||
self._ssh_exec(server, ['sudo', 'exportfs', '-a'])
|
||||
out, _ = self._ssh_exec(
|
||||
server, ['sudo', 'service', 'nfs-kernel-server', 'status'],
|
||||
check_exit_code=False)
|
||||
if "not" in out:
|
||||
self._ssh_exec(
|
||||
server, ['sudo', 'service', 'nfs-kernel-server', 'restart'])
|
||||
|
||||
def get_exports_for_share(self, server, old_export_location):
|
||||
self._verify_server_has_public_address(server)
|
||||
path = old_export_location.split(':')[-1]
|
||||
return [':'.join([server['public_address'], path])]
|
||||
return [':'.join((server['public_address'], path))]
|
||||
|
||||
def get_share_path_by_export_location(self, server, export_location):
|
||||
return export_location.split(':')[-1]
|
||||
|
@ -234,21 +327,27 @@ class CIFSHelperIPAccess(NASHelperBase):
|
|||
try:
|
||||
self._ssh_exec(
|
||||
server, ['sudo', 'net', 'conf', 'showshare', share_name, ])
|
||||
except exception.ProcessExecutionError as parent_e:
|
||||
except exception.ProcessExecutionError:
|
||||
# Share does not exist, create it
|
||||
try:
|
||||
self._ssh_exec(server, create_cmd)
|
||||
except Exception:
|
||||
# If we get here, then it will be useful
|
||||
# to log parent exception too.
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(parent_e)
|
||||
except Exception as child_e:
|
||||
msg = _("Could not create CIFS export %s.") % share_name
|
||||
LOG.exception(child_e)
|
||||
LOG.error(msg)
|
||||
raise exception.ManilaException(reason=msg)
|
||||
else:
|
||||
# Share exists
|
||||
if recreate:
|
||||
self._ssh_exec(
|
||||
server, ['sudo', 'net', 'conf', 'delshare', share_name, ])
|
||||
self._ssh_exec(server, create_cmd)
|
||||
try:
|
||||
self._ssh_exec(server, create_cmd)
|
||||
except Exception as e:
|
||||
msg = _("Could not create CIFS export %s.") % share_name
|
||||
LOG.exception(e)
|
||||
LOG.error(msg)
|
||||
raise exception.ManilaException(reason=msg)
|
||||
else:
|
||||
msg = _('Share section %s already defined.') % share_name
|
||||
raise exception.ShareBackendException(msg=msg)
|
||||
|
@ -270,38 +369,23 @@ class CIFSHelperIPAccess(NASHelperBase):
|
|||
self._ssh_exec(server, ['sudo', 'smbcontrol', 'all', 'close-share',
|
||||
share_name])
|
||||
|
||||
def allow_access(self, server, share_name, access_type, access_level,
|
||||
access_to):
|
||||
"""Add access for share."""
|
||||
if access_type != 'ip':
|
||||
reason = _('Only ip access type allowed.')
|
||||
raise exception.InvalidShareAccess(reason=reason)
|
||||
if access_level != const.ACCESS_LEVEL_RW:
|
||||
raise exception.InvalidShareAccessLevel(level=access_level)
|
||||
def update_access(self, server, share_name, access_rules, add_rules=None,
|
||||
delete_rules=None):
|
||||
"""Update access rules for given share.
|
||||
|
||||
hosts = self._get_allow_hosts(server, share_name)
|
||||
if access_to in hosts:
|
||||
raise exception.ShareAccessExists(
|
||||
access_type=access_type, access=access_to)
|
||||
hosts.append(access_to)
|
||||
Please refer to base class for a more in-depth description. For this
|
||||
specific implementation, add_rules and delete_rules parameters are not
|
||||
used.
|
||||
"""
|
||||
hosts = []
|
||||
|
||||
self.validate_access_rules(
|
||||
access_rules, ('ip',), (const.ACCESS_LEVEL_RW,))
|
||||
|
||||
for access in access_rules:
|
||||
hosts.append(access['access_to'])
|
||||
self._set_allow_hosts(server, hosts, share_name)
|
||||
|
||||
def deny_access(self, server, share_name, access, force=False):
|
||||
"""Remove access for share."""
|
||||
access_to, access_level = access['access_to'], access['access_level']
|
||||
if access_level != const.ACCESS_LEVEL_RW:
|
||||
return
|
||||
try:
|
||||
hosts = self._get_allow_hosts(server, share_name)
|
||||
if access_to in hosts:
|
||||
# Access rule can be in error state, if so
|
||||
# it can be absent in rules, hence - skip removal.
|
||||
hosts.remove(access_to)
|
||||
self._set_allow_hosts(server, hosts, share_name)
|
||||
except exception.ProcessExecutionError:
|
||||
if not force:
|
||||
raise
|
||||
|
||||
def _get_allow_hosts(self, server, share_name):
|
||||
(out, _) = self._ssh_exec(server, ['sudo', 'net', 'conf', 'getparm',
|
||||
share_name, '\"hosts allow\"'])
|
||||
|
@ -378,63 +462,35 @@ class CIFSHelperUserAccess(CIFSHelperIPAccess):
|
|||
'read only': 'no',
|
||||
}
|
||||
|
||||
def allow_access(self, server, share_name, access_type, access_level,
|
||||
access_to):
|
||||
"""Add to allow hosts additional access rule."""
|
||||
if access_type != 'user':
|
||||
reason = _('Only user access type allowed.')
|
||||
raise exception.InvalidShareAccess(reason=reason)
|
||||
all_users = self._get_valid_users(server, share_name)
|
||||
def update_access(self, server, share_name, access_rules, add_rules=None,
|
||||
delete_rules=None):
|
||||
"""Update access rules for given share.
|
||||
|
||||
if access_to in all_users:
|
||||
raise exception.ShareAccessExists(
|
||||
access_type=access_type, access=access_to)
|
||||
Please refer to base class for a more in-depth description. For this
|
||||
specific implementation, add_rules and delete_rules parameters are not
|
||||
used.
|
||||
"""
|
||||
all_users_rw = []
|
||||
all_users_ro = []
|
||||
|
||||
user_list = self._get_valid_users(server, share_name, access_level)
|
||||
user_list.append(access_to)
|
||||
self._set_valid_users(server, user_list, share_name, access_level)
|
||||
self.validate_access_rules(
|
||||
access_rules, ('user',),
|
||||
(const.ACCESS_LEVEL_RO, const.ACCESS_LEVEL_RW))
|
||||
|
||||
def deny_access(self, server, share_name, access, force=False):
|
||||
"""Remove from allow hosts permit rule."""
|
||||
access_to, access_level = access['access_to'], access['access_level']
|
||||
users = self._get_valid_users(server, share_name, access_level,
|
||||
force=force)
|
||||
if access_to in users:
|
||||
users.remove(access_to)
|
||||
self._set_valid_users(server, users, share_name, access_level)
|
||||
|
||||
def _get_valid_users(self, server, share_name, access_level=None,
|
||||
force=True):
|
||||
if not access_level:
|
||||
all_users_list = []
|
||||
for param in ['valid users', 'read list']:
|
||||
out = ""
|
||||
try:
|
||||
(out, _) = self._ssh_exec(server, ['sudo', 'net', 'conf',
|
||||
'getparm', share_name,
|
||||
param])
|
||||
out = out.replace("\"", "")
|
||||
except exception.ProcessExecutionError:
|
||||
if not force:
|
||||
raise
|
||||
all_users_list += out.split()
|
||||
return all_users_list
|
||||
|
||||
param = self._get_conf_param(access_level)
|
||||
try:
|
||||
(out, _) = self._ssh_exec(server, ['sudo', 'net', 'conf',
|
||||
'getparm', share_name, param])
|
||||
out = out.replace("\"", "")
|
||||
return out.split()
|
||||
except exception.ProcessExecutionError:
|
||||
if not force:
|
||||
raise
|
||||
return []
|
||||
for access in access_rules:
|
||||
if access['access_level'] == const.ACCESS_LEVEL_RW:
|
||||
all_users_rw.append(access['access_to'])
|
||||
else:
|
||||
all_users_ro.append(access['access_to'])
|
||||
self._set_valid_users(
|
||||
server, all_users_rw, share_name, const.ACCESS_LEVEL_RW)
|
||||
self._set_valid_users(
|
||||
server, all_users_ro, share_name, const.ACCESS_LEVEL_RO)
|
||||
|
||||
def _get_conf_param(self, access_level):
|
||||
if access_level == const.ACCESS_LEVEL_RW:
|
||||
return 'valid users'
|
||||
if access_level == const.ACCESS_LEVEL_RO:
|
||||
else:
|
||||
return 'read list'
|
||||
|
||||
def _set_valid_users(self, server, users, share_name, access_level):
|
||||
|
|
|
@ -35,7 +35,6 @@ from manila.i18n import _LW
|
|||
from manila.share import driver
|
||||
from manila.share.drivers import generic
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
share_opts = [
|
||||
|
@ -146,11 +145,12 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
|||
'instance_id': self.backend_name,
|
||||
}
|
||||
|
||||
def _ssh_exec_as_root(self, server, command):
|
||||
def _ssh_exec_as_root(self, server, command, check_exit_code=True):
|
||||
kwargs = {}
|
||||
if 'sudo' in command:
|
||||
kwargs['run_as_root'] = True
|
||||
command.remove('sudo')
|
||||
kwargs['check_exit_code'] = check_exit_code
|
||||
return self._execute(*command, **kwargs)
|
||||
|
||||
def do_setup(self, context):
|
||||
|
@ -265,17 +265,34 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
|||
except exception.InvalidShare as exc:
|
||||
LOG.warning(exc.message)
|
||||
|
||||
def allow_access(self, ctx, share, access, share_server=None):
|
||||
"""Allow access to the share."""
|
||||
self._get_helper(share).allow_access(self.share_server, share['name'],
|
||||
access['access_type'],
|
||||
access['access_level'],
|
||||
access['access_to'])
|
||||
def update_access(self, context, share, access_rules, add_rules=None,
|
||||
delete_rules=None, share_server=None):
|
||||
"""Update access rules for given share.
|
||||
|
||||
def deny_access(self, ctx, share, access, share_server=None):
|
||||
"""Deny access to the share."""
|
||||
self._get_helper(share).deny_access(self.share_server, share['name'],
|
||||
access)
|
||||
This driver has two different behaviors according to parameters:
|
||||
1. Recovery after error - 'access_rules' contains all access_rules,
|
||||
'add_rules' and 'delete_rules' shall be None. Previously existing
|
||||
access rules are cleared and then added back according
|
||||
to 'access_rules'.
|
||||
|
||||
2. Adding/Deleting of several access rules - 'access_rules' contains
|
||||
all access_rules, 'add_rules' and 'delete_rules' contain rules which
|
||||
should be added/deleted. Rules in 'access_rules' are ignored and
|
||||
only rules from 'add_rules' and 'delete_rules' are applied.
|
||||
|
||||
:param context: Current context
|
||||
:param share: Share model with share data.
|
||||
:param access_rules: All access rules for given share
|
||||
:param add_rules: None or List of access rules which should be added
|
||||
access_rules already contains these rules.
|
||||
:param delete_rules: None or List of access rules which should be
|
||||
removed. access_rules doesn't contain these rules.
|
||||
:param share_server: None or Share server model
|
||||
"""
|
||||
self._get_helper(share).update_access(self.share_server,
|
||||
share['name'], access_rules,
|
||||
add_rules=add_rules,
|
||||
delete_rules=delete_rules)
|
||||
|
||||
def _get_helper(self, share):
|
||||
if share['share_proto'].lower().startswith('nfs'):
|
||||
|
|
|
@ -146,6 +146,14 @@ def get_fake_collated_cg_snap_info():
|
|||
return fake_collated_cg_snap_info
|
||||
|
||||
|
||||
def get_fake_access_rule(access_to, access_level, access_type='ip'):
|
||||
return {
|
||||
'access_type': access_type,
|
||||
'access_to': access_to,
|
||||
'access_level': access_level,
|
||||
}
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class GenericShareDriverTestCase(test.TestCase):
|
||||
"""Tests GenericShareDriver."""
|
||||
|
@ -1208,38 +1216,25 @@ class GenericShareDriverTestCase(test.TestCase):
|
|||
self._context, self.share, share_server=self.server)
|
||||
|
||||
@ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO)
|
||||
def test_allow_access(self, access_level):
|
||||
access = {
|
||||
'access_type': 'ip',
|
||||
'access_to': 'fake_dest',
|
||||
'access_level': access_level,
|
||||
}
|
||||
self._driver.allow_access(
|
||||
self._context, self.share, access, share_server=self.server)
|
||||
def test_update_access(self, access_level):
|
||||
|
||||
# fakes
|
||||
access_rules = [get_fake_access_rule('1.1.1.1', access_level),
|
||||
get_fake_access_rule('2.2.2.2', access_level)]
|
||||
add_rules = [get_fake_access_rule('2.2.2.2', access_level), ]
|
||||
delete_rules = [get_fake_access_rule('3.3.3.3', access_level), ]
|
||||
|
||||
# run
|
||||
self._driver.update_access(self._context, self.share, access_rules,
|
||||
add_rules=add_rules,
|
||||
delete_rules=delete_rules,
|
||||
share_server=self.server)
|
||||
|
||||
# asserts
|
||||
self._driver._helpers[self.share['share_proto']].\
|
||||
allow_access.assert_called_once_with(
|
||||
update_access.assert_called_once_with(
|
||||
self.server['backend_details'], self.share['name'],
|
||||
access['access_type'], access['access_level'],
|
||||
access['access_to'])
|
||||
|
||||
def test_allow_access_unsupported(self):
|
||||
access = {
|
||||
'access_type': 'ip',
|
||||
'access_to': 'fake_dest',
|
||||
'access_level': 'fakefoobar',
|
||||
}
|
||||
self.assertRaises(
|
||||
exception.InvalidShareAccessLevel,
|
||||
self._driver.allow_access,
|
||||
self._context, self.share, access, share_server=self.server)
|
||||
|
||||
def test_deny_access(self):
|
||||
access = 'fake_access'
|
||||
self._driver.deny_access(
|
||||
self._context, self.share, access, share_server=self.server)
|
||||
self._driver._helpers[
|
||||
self.share['share_proto']].deny_access.assert_called_once_with(
|
||||
self.server['backend_details'], self.share['name'], access)
|
||||
access_rules, add_rules=add_rules, delete_rules=delete_rules)
|
||||
|
||||
@ddt.data(fake_share.fake_share(),
|
||||
fake_share.fake_share(share_proto='NFSBOGUS'),
|
||||
|
@ -1306,7 +1301,8 @@ class GenericShareDriverTestCase(test.TestCase):
|
|||
self.server['ip'], 22, ssh_conn_timeout, self.server['username'],
|
||||
self.server['password'], self.server['pk_path'], max_size=1)
|
||||
ssh_pool.create.assert_called_once_with()
|
||||
processutils.ssh_execute.assert_called_once_with(ssh, 'fake command')
|
||||
processutils.ssh_execute.assert_called_once_with(
|
||||
ssh, 'fake command', check_exit_code=True)
|
||||
ssh.get_transport().is_active.assert_called_once_with()
|
||||
self.assertEqual(
|
||||
self._driver.ssh_connections,
|
||||
|
@ -1329,7 +1325,8 @@ class GenericShareDriverTestCase(test.TestCase):
|
|||
|
||||
result = self._driver._ssh_exec(self.server, cmd)
|
||||
|
||||
processutils.ssh_execute.assert_called_once_with(ssh, 'fake command')
|
||||
processutils.ssh_execute.assert_called_once_with(
|
||||
ssh, 'fake command', check_exit_code=True)
|
||||
ssh.get_transport().is_active.assert_called_once_with()
|
||||
self.assertEqual(
|
||||
self._driver.ssh_connections,
|
||||
|
@ -1354,7 +1351,8 @@ class GenericShareDriverTestCase(test.TestCase):
|
|||
|
||||
result = self._driver._ssh_exec(self.server, cmd)
|
||||
|
||||
processutils.ssh_execute.assert_called_once_with(ssh, 'fake command')
|
||||
processutils.ssh_execute.assert_called_once_with(
|
||||
ssh, 'fake command', check_exit_code=True)
|
||||
ssh.get_transport().is_active.assert_called_once_with()
|
||||
ssh_pool.create.assert_called_once_with()
|
||||
ssh_pool.remove.assert_called_once_with(ssh)
|
||||
|
|
|
@ -26,6 +26,7 @@ from manila.share.drivers import helpers
|
|||
from manila import test
|
||||
from manila.tests import fake_compute
|
||||
from manila.tests import fake_utils
|
||||
from manila.tests.share.drivers import test_generic
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -48,6 +49,37 @@ class NFSHelperTestCase(test.TestCase):
|
|||
ip=ip, public_address=ip, instance_id='fake_instance_id')
|
||||
self.share_name = 'fake_share_name'
|
||||
|
||||
def test_init_helper(self):
|
||||
|
||||
# mocks
|
||||
self.mock_object(
|
||||
self._helper, '_ssh_exec',
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError(
|
||||
stderr='command not found')))
|
||||
|
||||
# run
|
||||
self.assertRaises(exception.ManilaException,
|
||||
self._helper.init_helper, self.server)
|
||||
|
||||
# asserts
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server, ['sudo', 'exportfs'])
|
||||
|
||||
def test_init_helper_log(self):
|
||||
|
||||
# mocks
|
||||
self.mock_object(
|
||||
self._helper, '_ssh_exec',
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError(
|
||||
stderr='fake')))
|
||||
|
||||
# run
|
||||
self._helper.init_helper(self.server)
|
||||
|
||||
# asserts
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server, ['sudo', 'exportfs'])
|
||||
|
||||
def test_create_export(self):
|
||||
ret = self._helper.create_export(self.server, self.share_name)
|
||||
expected_location = ':'.join([self.server['public_address'],
|
||||
|
@ -56,38 +88,88 @@ class NFSHelperTestCase(test.TestCase):
|
|||
self.assertEqual(expected_location, ret)
|
||||
|
||||
@ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO)
|
||||
def test_allow_access(self, data):
|
||||
def test_update_access(self, access_level):
|
||||
self.mock_object(self._helper, '_sync_nfs_temp_and_perm_files')
|
||||
self._helper.allow_access(
|
||||
self.server, self.share_name, 'ip', data, '10.0.0.2')
|
||||
local_path = os.path.join(CONF.share_mount_path, self.share_name)
|
||||
exec_result = ' '.join([local_path, '2.2.2.3'])
|
||||
self.mock_object(self._helper, '_ssh_exec',
|
||||
mock.Mock(return_value=(exec_result, '')))
|
||||
access_rules = [
|
||||
test_generic.get_fake_access_rule('1.1.1.1', access_level),
|
||||
test_generic.get_fake_access_rule('2.2.2.2', access_level),
|
||||
test_generic.get_fake_access_rule('2.2.2.3', access_level)]
|
||||
add_rules = [
|
||||
test_generic.get_fake_access_rule('2.2.2.2', access_level),
|
||||
test_generic.get_fake_access_rule('2.2.2.3', access_level)]
|
||||
delete_rules = [
|
||||
test_generic.get_fake_access_rule('3.3.3.3', access_level),
|
||||
test_generic.get_fake_access_rule('4.4.4.4', access_level, 'user')]
|
||||
self._helper.update_access(self.server, self.share_name, access_rules,
|
||||
add_rules=add_rules,
|
||||
delete_rules=delete_rules)
|
||||
local_path = os.path.join(CONF.share_mount_path, self.share_name)
|
||||
self._helper._ssh_exec.assert_has_calls([
|
||||
mock.call(self.server, ['sudo', 'exportfs']),
|
||||
mock.call(self.server, ['sudo', 'exportfs', '-u',
|
||||
':'.join(['3.3.3.3', local_path])]),
|
||||
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
||||
'%s,no_subtree_check' % access_level,
|
||||
':'.join(['2.2.2.2', local_path])]),
|
||||
])
|
||||
self._helper._sync_nfs_temp_and_perm_files.assert_has_calls([
|
||||
mock.call(self.server), mock.call(self.server)])
|
||||
|
||||
def test_update_access_invalid_type(self):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'2.2.2.2', const.ACCESS_LEVEL_RW, access_type='fake'), ]
|
||||
self.assertRaises(
|
||||
exception.InvalidShareAccess,
|
||||
self._helper.update_access,
|
||||
self.server,
|
||||
self.share_name,
|
||||
access_rules)
|
||||
|
||||
def test_update_access_invalid_level(self):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'2.2.2.2', 'fake_level', access_type='ip'), ]
|
||||
self.assertRaises(
|
||||
exception.InvalidShareAccessLevel,
|
||||
self._helper.update_access,
|
||||
self.server,
|
||||
self.share_name,
|
||||
access_rules)
|
||||
|
||||
def test_get_host_list(self):
|
||||
fake_exportfs = ('/shares/share-1\n\t\t20.0.0.3\n'
|
||||
'/shares/share-1\n\t\t20.0.0.6\n'
|
||||
'/shares/share-2\n\t\t10.0.0.2\n'
|
||||
'/shares/share-2\n\t\t10.0.0.5\n'
|
||||
'/shares/share-3\n\t\t30.0.0.4\n'
|
||||
'/shares/share-3\n\t\t30.0.0.7\n')
|
||||
expected = ['20.0.0.3', '20.0.0.6']
|
||||
result = self._helper._get_host_list(fake_exportfs, '/shares/share-1')
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO)
|
||||
def test_update_access_recovery_mode(self, access_level):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'1.1.1.1', access_level), ]
|
||||
self.mock_object(self._helper, '_sync_nfs_temp_and_perm_files')
|
||||
self.mock_object(self._helper, '_get_host_list',
|
||||
mock.Mock(return_value=['1.1.1.1']))
|
||||
self._helper.update_access(self.server, self.share_name, access_rules)
|
||||
local_path = os.path.join(CONF.share_mount_path, self.share_name)
|
||||
self._ssh_exec.assert_has_calls([
|
||||
mock.call(self.server, ['sudo', 'exportfs']),
|
||||
mock.call(
|
||||
self.server, ['sudo', 'exportfs', '-u',
|
||||
':'.join([access_rules[0]['access_to'],
|
||||
local_path])]),
|
||||
mock.call(self.server, ['sudo', 'exportfs', '-o',
|
||||
'%s,no_subtree_check' % data,
|
||||
':'.join(['10.0.0.2', local_path])])
|
||||
'%s,no_subtree_check' % access_level,
|
||||
':'.join(['1.1.1.1', local_path])]),
|
||||
])
|
||||
self._helper._sync_nfs_temp_and_perm_files.assert_called_once_with(
|
||||
self.server)
|
||||
|
||||
def test_allow_access_no_ip(self):
|
||||
self.assertRaises(
|
||||
exception.InvalidShareAccess,
|
||||
self._helper.allow_access,
|
||||
self.server, self.share_name,
|
||||
'fake_type', 'fake_level', 'fake_rule')
|
||||
|
||||
@ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO)
|
||||
def test_deny_access(self, data):
|
||||
self.mock_object(self._helper, '_sync_nfs_temp_and_perm_files')
|
||||
local_path = os.path.join(CONF.share_mount_path, self.share_name)
|
||||
access = dict(
|
||||
access_to='10.0.0.2', access_type='ip', access_level=data)
|
||||
self._helper.deny_access(self.server, self.share_name, access)
|
||||
export_string = ':'.join(['10.0.0.2', local_path])
|
||||
expected_exec = ['sudo', 'exportfs', '-u', export_string]
|
||||
self._ssh_exec.assert_called_once_with(self.server, expected_exec)
|
||||
self._helper._sync_nfs_temp_and_perm_files.assert_called_once_with(
|
||||
self._helper._sync_nfs_temp_and_perm_files.assert_called_with(
|
||||
self.server)
|
||||
|
||||
def test_sync_nfs_temp_and_perm_files(self):
|
||||
|
@ -198,7 +280,7 @@ class CIFSHelperIPAccessTestCase(test.TestCase):
|
|||
if 'showshare' in args[1]:
|
||||
raise exception.ProcessExecutionError()
|
||||
else:
|
||||
return ('', '')
|
||||
return '', ''
|
||||
|
||||
self.mock_object(self._helper, '_ssh_exec',
|
||||
mock.Mock(side_effect=fake_ssh_exec))
|
||||
|
@ -222,8 +304,21 @@ class CIFSHelperIPAccessTestCase(test.TestCase):
|
|||
share_path, 'writeable=y', 'guest_ok=y',
|
||||
]
|
||||
),
|
||||
mock.call(self.server_details, mock.ANY),
|
||||
])
|
||||
|
||||
def test_create_export_share_does_not_exist_exception(self):
|
||||
|
||||
self.mock_object(self._helper, '_ssh_exec',
|
||||
mock.Mock(
|
||||
side_effect=[exception.ProcessExecutionError(),
|
||||
Exception('')]
|
||||
))
|
||||
|
||||
self.assertRaises(
|
||||
exception.ManilaException, self._helper.create_export,
|
||||
self.server_details, self.share_name)
|
||||
|
||||
def test_create_export_share_exist_recreate_true(self):
|
||||
ret = self._helper.create_export(self.server_details, self.share_name,
|
||||
recreate=True)
|
||||
|
@ -249,6 +344,7 @@ class CIFSHelperIPAccessTestCase(test.TestCase):
|
|||
share_path, 'writeable=y', 'guest_ok=y',
|
||||
]
|
||||
),
|
||||
mock.call(self.server_details, mock.ANY),
|
||||
])
|
||||
|
||||
def test_create_export_share_exist_recreate_false(self):
|
||||
|
@ -298,122 +394,49 @@ class CIFSHelperIPAccessTestCase(test.TestCase):
|
|||
),
|
||||
])
|
||||
|
||||
def test_allow_access_ip_exist(self):
|
||||
hosts = [self.access['access_to'], ]
|
||||
self.mock_object(self._helper, '_get_allow_hosts',
|
||||
mock.Mock(return_value=hosts))
|
||||
self.mock_object(self._helper, '_set_allow_hosts')
|
||||
|
||||
self.assertRaises(
|
||||
exception.ShareAccessExists,
|
||||
self._helper.allow_access,
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
self.access['access_type'],
|
||||
self.access['access_level'],
|
||||
self.access['access_to'])
|
||||
|
||||
self._helper._get_allow_hosts.assert_called_once_with(
|
||||
self.server_details, self.share_name)
|
||||
self._helper._set_allow_hosts.assert_has_calls([])
|
||||
|
||||
def test_allow_access_ip_does_not_exist(self):
|
||||
hosts = []
|
||||
self.mock_object(self._helper, '_get_allow_hosts',
|
||||
mock.Mock(return_value=hosts))
|
||||
self.mock_object(self._helper, '_set_allow_hosts')
|
||||
|
||||
self._helper.allow_access(
|
||||
self.server_details, self.share_name,
|
||||
self.access['access_type'], self.access['access_level'],
|
||||
self.access['access_to'])
|
||||
|
||||
self._helper._get_allow_hosts.assert_called_once_with(
|
||||
self.server_details, self.share_name)
|
||||
self._helper._set_allow_hosts.assert_called_once_with(
|
||||
self.server_details, hosts, self.share_name)
|
||||
|
||||
def test_allow_access_wrong_type(self):
|
||||
self.assertRaises(
|
||||
exception.InvalidShareAccess,
|
||||
self._helper.allow_access,
|
||||
self.server_details,
|
||||
self.share_name, 'fake', const.ACCESS_LEVEL_RW, '1.1.1.1')
|
||||
|
||||
@ddt.data(const.ACCESS_LEVEL_RO, 'fake')
|
||||
def test_allow_access_wrong_access_level(self, data):
|
||||
def test_update_access_wrong_access_level(self):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'2.2.2.2', const.ACCESS_LEVEL_RO), ]
|
||||
self.assertRaises(
|
||||
exception.InvalidShareAccessLevel,
|
||||
self._helper.allow_access,
|
||||
self._helper.update_access,
|
||||
self.server_details,
|
||||
self.share_name, 'ip', data, '1.1.1.1')
|
||||
self.share_name,
|
||||
access_rules)
|
||||
|
||||
@ddt.data(const.ACCESS_LEVEL_RO, 'fake')
|
||||
def test_deny_access_unsupported_access_level(self, data):
|
||||
access = dict(access_to='1.1.1.1', access_level=data)
|
||||
self.mock_object(self._helper, '_get_allow_hosts')
|
||||
self.mock_object(self._helper, '_set_allow_hosts')
|
||||
|
||||
self._helper.deny_access(self.server_details, self.share_name, access)
|
||||
|
||||
self.assertFalse(self._helper._get_allow_hosts.called)
|
||||
self.assertFalse(self._helper._set_allow_hosts.called)
|
||||
|
||||
def test_deny_access_list_has_value(self):
|
||||
hosts = [self.access['access_to'], ]
|
||||
self.mock_object(self._helper, '_get_allow_hosts',
|
||||
mock.Mock(return_value=hosts))
|
||||
self.mock_object(self._helper, '_set_allow_hosts')
|
||||
|
||||
self._helper.deny_access(
|
||||
self.server_details, self.share_name, self.access)
|
||||
self._helper._get_allow_hosts.assert_called_once_with(
|
||||
self.server_details, self.share_name)
|
||||
self._helper._set_allow_hosts.assert_called_once_with(
|
||||
self.server_details, [], self.share_name)
|
||||
|
||||
def test_deny_access_list_does_not_have_value(self):
|
||||
hosts = []
|
||||
self.mock_object(self._helper, '_get_allow_hosts',
|
||||
mock.Mock(return_value=hosts))
|
||||
self.mock_object(self._helper, '_set_allow_hosts')
|
||||
|
||||
self._helper.deny_access(
|
||||
self.server_details, self.share_name, self.access)
|
||||
|
||||
self._helper._get_allow_hosts.assert_called_once_with(
|
||||
self.server_details, self.share_name)
|
||||
self._helper._set_allow_hosts.assert_has_calls([])
|
||||
|
||||
def test_deny_access_force(self):
|
||||
self.mock_object(
|
||||
self._helper,
|
||||
'_get_allow_hosts',
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError()),
|
||||
)
|
||||
self.mock_object(self._helper, '_set_allow_hosts')
|
||||
|
||||
self._helper.deny_access(
|
||||
self.server_details, self.share_name, self.access, force=True)
|
||||
|
||||
self._helper._get_allow_hosts.assert_called_once_with(
|
||||
self.server_details, self.share_name)
|
||||
self._helper._set_allow_hosts.assert_has_calls([])
|
||||
|
||||
def test_deny_access_not_force(self):
|
||||
def raise_process_execution_error(*args, **kwargs):
|
||||
raise exception.ProcessExecutionError()
|
||||
|
||||
self.mock_object(self._helper, '_get_allow_hosts',
|
||||
mock.Mock(side_effect=raise_process_execution_error))
|
||||
self.mock_object(self._helper, '_set_allow_hosts')
|
||||
def test_update_access_wrong_access_type(self):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'2.2.2.2', const.ACCESS_LEVEL_RW, access_type='fake'), ]
|
||||
self.assertRaises(
|
||||
exception.ProcessExecutionError,
|
||||
self._helper.deny_access,
|
||||
self.server_details, self.share_name, self.access)
|
||||
self._helper._get_allow_hosts.assert_called_once_with(
|
||||
exception.InvalidShareAccess,
|
||||
self._helper.update_access,
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access_rules)
|
||||
|
||||
def test_update_access(self):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'1.1.1.1', const.ACCESS_LEVEL_RW), ]
|
||||
|
||||
self._helper.update_access(self.server_details, self.share_name,
|
||||
access_rules)
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server_details, ['sudo', 'net', 'conf', 'setparm',
|
||||
self.share_name, '"hosts allow"',
|
||||
'"1.1.1.1"'])
|
||||
|
||||
def test_get_allow_hosts(self):
|
||||
self.mock_object(self._helper, '_ssh_exec',
|
||||
mock.Mock(
|
||||
return_value=('1.1.1.1 2.2.2.2 3.3.3.3', '')))
|
||||
expected = ['1.1.1.1', '2.2.2.2', '3.3.3.3']
|
||||
result = self._helper._get_allow_hosts(
|
||||
self.server_details, self.share_name)
|
||||
self._helper._set_allow_hosts.assert_has_calls([])
|
||||
self.assertEqual(expected, result)
|
||||
cmd = ['sudo', 'net', 'conf', 'getparm', self.share_name,
|
||||
'\"hosts allow\"']
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server_details, cmd)
|
||||
|
||||
@ddt.data(
|
||||
'', '1.2.3.4:/nfs/like/export', '/1.2.3.4/foo', '\\1.2.3.4\\foo',
|
||||
|
@ -534,232 +557,36 @@ class CIFSHelperUserAccessTestCase(test.TestCase):
|
|||
self._helper = helpers.CIFSHelperUserAccess(
|
||||
self._execute, self._ssh_exec, self.fake_conf)
|
||||
|
||||
@ddt.data('ip', 'cert', 'fake')
|
||||
def test_allow_access_wrong_type(self, wrong_access_type):
|
||||
def test_update_access_exception_type(self):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'user1', const.ACCESS_LEVEL_RW, access_type='ip')]
|
||||
self.assertRaises(exception.InvalidShareAccess,
|
||||
self._helper.update_access, self.server_details,
|
||||
self.share_name, access_rules, None, None)
|
||||
|
||||
def test_update_access(self):
|
||||
access_list = [test_generic.get_fake_access_rule(
|
||||
'user1', const.ACCESS_LEVEL_RW, access_type='user'),
|
||||
test_generic.get_fake_access_rule(
|
||||
'user2', const.ACCESS_LEVEL_RO, access_type='user')]
|
||||
self._helper.update_access(self.server_details, self.share_name,
|
||||
access_list, None, None)
|
||||
|
||||
self._helper._ssh_exec.assert_has_calls([
|
||||
mock.call(self.server_details,
|
||||
['sudo', 'net', 'conf', 'setparm', self.share_name,
|
||||
'valid users', '"user1"']),
|
||||
mock.call(self.server_details,
|
||||
['sudo', 'net', 'conf', 'setparm', self.share_name,
|
||||
'read list', '"user2"'])
|
||||
])
|
||||
|
||||
def test_update_access_exception_level(self):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'user1', 'fake_level', access_type='user'), ]
|
||||
self.assertRaises(
|
||||
exception.InvalidShareAccess,
|
||||
self._helper.allow_access,
|
||||
exception.InvalidShareAccessLevel,
|
||||
self._helper.update_access,
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
wrong_access_type,
|
||||
const.ACCESS_LEVEL_RW,
|
||||
'1.1.1.1')
|
||||
|
||||
@ddt.data(access_rw, access_ro)
|
||||
def test_allow_access_ro_rule_does_not_exist(self, access):
|
||||
users = ['user1', 'user2']
|
||||
self.mock_object(self._helper, '_get_valid_users',
|
||||
mock.Mock(return_value=users))
|
||||
self.mock_object(self._helper, '_set_valid_users')
|
||||
|
||||
self._helper.allow_access(
|
||||
self.server_details, self.share_name,
|
||||
access['access_type'], access['access_level'],
|
||||
access['access_to'])
|
||||
self.assertEqual(
|
||||
[mock.call(self.server_details, self.share_name),
|
||||
mock.call(self.server_details, self.share_name,
|
||||
access['access_level'])],
|
||||
self._helper._get_valid_users.call_args_list)
|
||||
self._helper._set_valid_users.assert_called_once_with(
|
||||
self.server_details,
|
||||
users,
|
||||
self.share_name,
|
||||
access['access_level'])
|
||||
|
||||
@ddt.data(access_rw, access_ro)
|
||||
def test_allow_access_ro_rule_exists(self, access):
|
||||
users = ['user1', 'user2', 'manila-user']
|
||||
self.mock_object(self._helper, '_get_valid_users',
|
||||
mock.Mock(return_value=users))
|
||||
|
||||
self.assertRaises(
|
||||
exception.ShareAccessExists,
|
||||
self._helper.allow_access,
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access['access_type'],
|
||||
access['access_level'],
|
||||
access['access_to'])
|
||||
|
||||
@ddt.data(access_rw, access_ro)
|
||||
def test_deny_access_list_has_value(self, access):
|
||||
users = ['user1', 'user2', 'manila-user']
|
||||
self.mock_object(self._helper, '_get_valid_users',
|
||||
mock.Mock(return_value=users))
|
||||
self.mock_object(self._helper, '_set_valid_users')
|
||||
|
||||
self._helper.deny_access(
|
||||
self.server_details, self.share_name, access)
|
||||
self._helper._get_valid_users.assert_called_once_with(
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access['access_level'],
|
||||
force=False)
|
||||
self._helper._set_valid_users.assert_called_once_with(
|
||||
self.server_details, ['user1', 'user2'], self.share_name,
|
||||
access['access_level'])
|
||||
|
||||
@ddt.data(access_rw, access_ro)
|
||||
def test_deny_access_list_does_not_have_value(self, access):
|
||||
users = []
|
||||
self.mock_object(self._helper, '_get_valid_users',
|
||||
mock.Mock(return_value=users))
|
||||
self.mock_object(self._helper, '_set_valid_users')
|
||||
|
||||
self._helper.deny_access(
|
||||
self.server_details, self.share_name, access)
|
||||
self._helper._get_valid_users.assert_called_once_with(
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access['access_level'],
|
||||
force=False)
|
||||
self._helper._set_valid_users.assert_has_calls([])
|
||||
|
||||
@ddt.data(access_rw, access_ro)
|
||||
def test_deny_access_force_access_exists(self, access):
|
||||
users = ['user1', 'user2', 'manila-user']
|
||||
self.mock_object(self._helper, '_get_valid_users',
|
||||
mock.Mock(return_value=users))
|
||||
self.mock_object(self._helper, '_set_valid_users')
|
||||
|
||||
self._helper.deny_access(
|
||||
self.server_details, self.share_name, access, force=True)
|
||||
self._helper._get_valid_users.assert_called_once_with(
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access['access_level'],
|
||||
force=True)
|
||||
self._helper._set_valid_users.assert_called_once_with(
|
||||
self.server_details, ['user1', 'user2'], self.share_name,
|
||||
access['access_level'])
|
||||
|
||||
@ddt.data(access_rw, access_ro)
|
||||
def test_deny_access_force_access_does_not_exist(self, access):
|
||||
self.mock_object(
|
||||
self._helper,
|
||||
'_get_valid_users',
|
||||
mock.Mock(return_value=[]),
|
||||
)
|
||||
self.mock_object(self._helper, '_set_valid_users')
|
||||
|
||||
self._helper.deny_access(
|
||||
self.server_details, self.share_name, access, force=True)
|
||||
|
||||
self._helper._get_valid_users.assert_called_once_with(
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access['access_level'],
|
||||
force=True)
|
||||
self._helper._set_valid_users.assert_has_calls([])
|
||||
|
||||
@ddt.data(access_rw, access_ro)
|
||||
def test_deny_access_force_exc(self, access):
|
||||
self.mock_object(
|
||||
self._helper,
|
||||
'_get_valid_users',
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError()),
|
||||
)
|
||||
self.mock_object(self._helper, '_set_valid_users')
|
||||
|
||||
self.assertRaises(exception.ProcessExecutionError,
|
||||
self._helper.deny_access,
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access,
|
||||
force=True)
|
||||
self._helper._get_valid_users.assert_called_once_with(
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
access['access_level'],
|
||||
force=True)
|
||||
|
||||
def test_get_conf_param_rw(self):
|
||||
result = self._helper._get_conf_param(const.ACCESS_LEVEL_RW)
|
||||
self.assertEqual('valid users', result)
|
||||
|
||||
def test_get_conf_param_ro(self):
|
||||
result = self._helper._get_conf_param(const.ACCESS_LEVEL_RO)
|
||||
self.assertEqual('read list', result)
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_get_valid_users(self, force):
|
||||
users = ("\"manila-user\" \"user1\" \"user2\"", None)
|
||||
self.mock_object(self._helper, '_ssh_exec',
|
||||
mock.Mock(return_value=users))
|
||||
result = self._helper._get_valid_users(self.server_details,
|
||||
self.share_name,
|
||||
const.ACCESS_LEVEL_RW,
|
||||
force=force)
|
||||
self.assertEqual(['manila-user', 'user1', 'user2'], result)
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server_details,
|
||||
['sudo', 'net', 'conf', 'getparm', self.share_name, 'valid users'])
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_get_valid_users_access_level_none(self, force):
|
||||
def fake_ssh_exec(*args, **kwargs):
|
||||
if 'valid users' in args[1]:
|
||||
return ("\"user1\"", '')
|
||||
else:
|
||||
return ("\"user2\"", '')
|
||||
|
||||
self.mock_object(self._helper, '_ssh_exec',
|
||||
mock.Mock(side_effect=fake_ssh_exec))
|
||||
|
||||
result = self._helper._get_valid_users(self.server_details,
|
||||
self.share_name,
|
||||
force=force)
|
||||
self.assertEqual(['user1', 'user2'], result)
|
||||
for param in ['read list', 'valid users']:
|
||||
self._helper._ssh_exec.assert_any_call(
|
||||
self.server_details,
|
||||
['sudo', 'net', 'conf', 'getparm', self.share_name, param])
|
||||
|
||||
def test_get_valid_users_access_level_none_with_exc(self):
|
||||
self.mock_object(
|
||||
self._helper,
|
||||
'_ssh_exec',
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError()))
|
||||
self.assertRaises(exception.ProcessExecutionError,
|
||||
self._helper._get_valid_users,
|
||||
self.server_details,
|
||||
self.share_name,
|
||||
force=False)
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server_details,
|
||||
['sudo', 'net', 'conf', 'getparm', self.share_name, 'valid users'])
|
||||
|
||||
def test_get_valid_users_force_with_exc(self):
|
||||
self.mock_object(
|
||||
self._helper,
|
||||
'_ssh_exec',
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError()))
|
||||
result = self._helper._get_valid_users(self.server_details,
|
||||
self.share_name,
|
||||
const.ACCESS_LEVEL_RW)
|
||||
self.assertEqual([], result)
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server_details,
|
||||
['sudo', 'net', 'conf', 'getparm', self.share_name, 'valid users'])
|
||||
|
||||
def test_get_valid_users_not_force_with_exc(self):
|
||||
self.mock_object(
|
||||
self._helper,
|
||||
'_ssh_exec',
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError()))
|
||||
self.assertRaises(exception.ProcessExecutionError,
|
||||
self._helper._get_valid_users, self.server_details,
|
||||
self.share_name, const.ACCESS_LEVEL_RW, force=False)
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server_details,
|
||||
['sudo', 'net', 'conf', 'getparm', self.share_name, 'valid users'])
|
||||
|
||||
def test_set_valid_users(self):
|
||||
self.mock_object(self._helper, '_ssh_exec', mock.Mock())
|
||||
self._helper._set_valid_users(self.server_details, ['user1', 'user2'],
|
||||
self.share_name, const.ACCESS_LEVEL_RW)
|
||||
self._helper._ssh_exec.assert_called_once_with(
|
||||
self.server_details,
|
||||
['sudo', 'net', 'conf', 'setparm', self.share_name,
|
||||
'valid users', '"user1 user2"'])
|
||||
access_rules)
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
import os
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from manila.common import constants as const
|
||||
from manila import context
|
||||
from manila import exception
|
||||
from manila.share import configuration
|
||||
|
@ -26,6 +28,7 @@ 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
|
||||
from manila.tests.share.drivers import test_generic
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -69,6 +72,7 @@ def fake_access(**kwargs):
|
|||
return db_fakes.FakeModel(access)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class LVMShareDriverTestCase(test.TestCase):
|
||||
"""Tests LVMShareDriver."""
|
||||
|
||||
|
@ -358,23 +362,22 @@ class LVMShareDriverTestCase(test.TestCase):
|
|||
self.server,
|
||||
self.share['name'])
|
||||
|
||||
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)
|
||||
@ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO)
|
||||
def test_update_access(self, access_level):
|
||||
access_rules = [test_generic.get_fake_access_rule(
|
||||
'1.1.1.1', access_level), ]
|
||||
add_rules = [test_generic.get_fake_access_rule(
|
||||
'2.2.2.2', access_level), ]
|
||||
delete_rules = [test_generic.get_fake_access_rule(
|
||||
'3.3.3.3', access_level), ]
|
||||
self._driver.update_access(self._context, self.share, access_rules,
|
||||
add_rules=add_rules,
|
||||
delete_rules=delete_rules,
|
||||
share_server=self.server)
|
||||
(self._driver._helpers[self.share['share_proto']].
|
||||
update_access.assert_called_once_with(
|
||||
self.server, self.share['name'],
|
||||
access_rules, add_rules=add_rules, delete_rules=delete_rules))
|
||||
|
||||
def test_mount_device(self):
|
||||
mount_path = self._get_mount_path(self.share)
|
||||
|
@ -448,14 +451,15 @@ class LVMShareDriverTestCase(test.TestCase):
|
|||
command = ['fake_command']
|
||||
self.mock_object(self._driver, '_execute')
|
||||
self._driver._ssh_exec_as_root('fake_server', command)
|
||||
self._driver._execute.assert_called_once_with('fake_command')
|
||||
self._driver._execute.assert_called_once_with('fake_command',
|
||||
check_exit_code=True)
|
||||
|
||||
def test_ssh_exec_as_root_with_sudo(self):
|
||||
command = ['sudo', 'fake_command']
|
||||
self.mock_object(self._driver, '_execute')
|
||||
self._driver._ssh_exec_as_root('fake_server', command)
|
||||
self._driver._execute.assert_called_once_with('fake_command',
|
||||
run_as_root=True)
|
||||
self._driver._execute.assert_called_once_with(
|
||||
'fake_command', run_as_root=True, check_exit_code=True)
|
||||
|
||||
def test_extend_container(self):
|
||||
self.mock_object(self._driver, '_try_execute')
|
||||
|
|
Loading…
Reference in New Issue