HP 3PAR uses scoped extra-specs to influence share creation options
Add support for share type extra-specs that can be used to influence share creation options that are specific to 3PAR. These extra-specs are scoped (use 'hp_3par:' prefix). CIFS extra-specs include 'smb_abe' (access based enumeration), 'smb_cache' and 'smb_ca' (continuous availability). 'smb_ca' is ignored until hp3parclient > 3.2.1. The NFS extra-spec is 'nfs_options'. This value is a comma-separated string as needed for the 3PAR createfshare -options value in the CLI. The following values are not allowed because the driver uses specific settings (for now): (no_)root_squash, (in)secure. The following values are not allowed per HP 3PAR CLI support requirements: no_subtree_check, fsid. 'rw' and 'ro' are not allowed because read-only access should be controlled by Manila and not by extra-specs. Additional value validation is left to the 3PAR CLI to handle. Implements Blueprint: hp3par-extra-specs Change-Id: Ia311cae718bf3998346b068aebb08a64924195de
This commit is contained in:
parent
a778199f01
commit
8895ea2254
@ -114,6 +114,72 @@ and using the shares. This includes:
|
|||||||
- Configuring the Manila host networking properly for IP forwarding
|
- Configuring the Manila host networking properly for IP forwarding
|
||||||
- Configuring the VFS networking properly for client subnets
|
- Configuring the VFS networking properly for client subnets
|
||||||
|
|
||||||
|
Share Types
|
||||||
|
-----------
|
||||||
|
|
||||||
|
When creating a share, a share type can be specified to determine where and
|
||||||
|
how the share will be created. If a share type is not specified, the
|
||||||
|
`default_share_type` set in the Manila configuration file is used.
|
||||||
|
|
||||||
|
Manila requires that the share type includes the
|
||||||
|
`driver_handles_share_servers` extra-spec. This ensures that the share
|
||||||
|
will be created on a backend that supports the requested
|
||||||
|
driver_handles_share_servers (share networks) capability.
|
||||||
|
For the HP 3PAR driver, this must be set to False.
|
||||||
|
|
||||||
|
Another common Manila extra-spec used to determine where a share is created
|
||||||
|
is `share_backend_name`. When this extra-spec is defined in the share type,
|
||||||
|
the share will be created on a backend with a matching share_backend_name.
|
||||||
|
|
||||||
|
Scoped extra-specs are used to influence vendor-specific implementation
|
||||||
|
details. Scoped extra-specs use a prefix followed by a colon. For HP 3PAR
|
||||||
|
these extra-specs have a prefix of `hp_3par`.
|
||||||
|
|
||||||
|
The following HP 3PAR extra-specs are used when creating CIFS (SMB) shares:
|
||||||
|
|
||||||
|
- `hp3_par:smb_access_based_enum` = true or false
|
||||||
|
- `hp3_par:smb_continuous_avail` = true or false
|
||||||
|
- `hp3_par:smb_cache` = off, manual, optimized or auto
|
||||||
|
|
||||||
|
`smb_access_based_enum` (Access Based Enumeration) specifies if users can see
|
||||||
|
only the files and directories to which they have been allowed access on the
|
||||||
|
shares. The default is `false`.
|
||||||
|
|
||||||
|
`smb_continuous_avail` (Continuous Availability) specifies if SMB3 continuous
|
||||||
|
availability features should be enabled for this share. If not specified,
|
||||||
|
the default is `true`. This setting will be ignored with hp3parclient 3.2.1
|
||||||
|
or earlier.
|
||||||
|
|
||||||
|
`smb_cache` specifies client-side caching for offline files. Valid values are:
|
||||||
|
|
||||||
|
* `off`: The client must not cache any files from this share. The share is
|
||||||
|
configured to disallow caching.
|
||||||
|
* `manual`: The client must allow only manual caching for the files open from
|
||||||
|
this share.
|
||||||
|
* `optimized`: The client may cache every file that it opens from
|
||||||
|
this share. Also, the client may satisfy the file requests from its
|
||||||
|
local cache. The share is configured to allow automatic caching
|
||||||
|
of programs and documents.
|
||||||
|
* `auto`: The client may cache every file that it opens from this
|
||||||
|
share. The share is configured to allow automatic caching of
|
||||||
|
documents.
|
||||||
|
* If this is not specified, the default is `manual`.
|
||||||
|
|
||||||
|
The following HP 3PAR extra-specs are used when creating NFS shares:
|
||||||
|
|
||||||
|
- `hp3_par:nfs_options` = Comma separated list of NFS export options
|
||||||
|
|
||||||
|
The NFS export options have the following limitations:
|
||||||
|
|
||||||
|
* `ro` and `rw` are not allowed (Manila will determine the read-only option)
|
||||||
|
* `no_subtree_check` and `fsid` are not allowed per HP 3PAR CLI support
|
||||||
|
* `(in)secure` and `(no_)root_squash` are not allowed because the HP 3PAR
|
||||||
|
driver controls those settings
|
||||||
|
|
||||||
|
All other NFS options are forwarded to the HP 3PAR as part of share creation.
|
||||||
|
The HP 3PAR will do additional validation at share creation time. Refer to
|
||||||
|
HP 3PAR CLI help for more details.
|
||||||
|
|
||||||
The :mod:`manila.share.drivers.hp.hp_3par_driver` Module
|
The :mod:`manila.share.drivers.hp.hp_3par_driver` Module
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ from manila.i18n import _
|
|||||||
from manila.i18n import _LI
|
from manila.i18n import _LI
|
||||||
from manila.share import driver
|
from manila.share import driver
|
||||||
from manila.share.drivers.hp import hp_3par_mediator
|
from manila.share.drivers.hp import hp_3par_mediator
|
||||||
|
from manila.share import share_types
|
||||||
|
|
||||||
HP3PAR_OPTS = [
|
HP3PAR_OPTS = [
|
||||||
cfg.StrOpt('hp3par_api_url',
|
cfg.StrOpt('hp3par_api_url',
|
||||||
@ -169,10 +170,13 @@ class HP3ParShareDriver(driver.ShareDriver):
|
|||||||
ip = self.share_ip_address
|
ip = self.share_ip_address
|
||||||
|
|
||||||
protocol = share['share_proto']
|
protocol = share['share_proto']
|
||||||
|
extra_specs = share_types.get_extra_specs_from_share(share)
|
||||||
|
|
||||||
path = self._hp3par.create_share(
|
path = self._hp3par.create_share(
|
||||||
share['project_id'],
|
share['project_id'],
|
||||||
share['id'],
|
share['id'],
|
||||||
protocol,
|
protocol,
|
||||||
|
extra_specs,
|
||||||
self.fpg, self.vfs,
|
self.fpg, self.vfs,
|
||||||
size=share['size']
|
size=share['size']
|
||||||
)
|
)
|
||||||
@ -186,9 +190,12 @@ class HP3ParShareDriver(driver.ShareDriver):
|
|||||||
ip = self.share_ip_address
|
ip = self.share_ip_address
|
||||||
|
|
||||||
protocol = share['share_proto']
|
protocol = share['share_proto']
|
||||||
|
extra_specs = share_types.get_extra_specs_from_share(share)
|
||||||
|
|
||||||
path = self._hp3par.create_share_from_snapshot(
|
path = self._hp3par.create_share_from_snapshot(
|
||||||
share['id'],
|
share['id'],
|
||||||
protocol,
|
protocol,
|
||||||
|
extra_specs,
|
||||||
snapshot['share']['project_id'],
|
snapshot['share']['project_id'],
|
||||||
snapshot['share']['id'],
|
snapshot['share']['id'],
|
||||||
snapshot['share']['share_proto'],
|
snapshot['share']['share_proto'],
|
||||||
|
@ -33,9 +33,13 @@ if hp3parclient:
|
|||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
MIN_CLIENT_VERSION = (3, 2, 1)
|
MIN_CLIENT_VERSION = (3, 2, 1)
|
||||||
|
MIN_SMB_CA_VERSION = (3, 2, 2)
|
||||||
DENY = '-'
|
DENY = '-'
|
||||||
ALLOW = '+'
|
ALLOW = '+'
|
||||||
OPEN_STACK_MANILA_FSHARE = 'OpenStack Manila fshare'
|
OPEN_STACK_MANILA_FSHARE = 'OpenStack Manila fshare'
|
||||||
|
CACHE = 'cache'
|
||||||
|
CONTINUOUS_AVAIL = 'continuous_avail'
|
||||||
|
ACCESS_BASED_ENUM = 'access_based_enum'
|
||||||
|
|
||||||
|
|
||||||
class HP3ParMediator(object):
|
class HP3ParMediator(object):
|
||||||
@ -55,6 +59,7 @@ class HP3ParMediator(object):
|
|||||||
|
|
||||||
self.ssh_conn_timeout = kwargs.get('ssh_conn_timeout')
|
self.ssh_conn_timeout = kwargs.get('ssh_conn_timeout')
|
||||||
self._client = None
|
self._client = None
|
||||||
|
self.client_version = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def no_client():
|
def no_client():
|
||||||
@ -68,11 +73,11 @@ class HP3ParMediator(object):
|
|||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
raise exception.HP3ParInvalidClient(message=msg)
|
raise exception.HP3ParInvalidClient(message=msg)
|
||||||
|
|
||||||
client_version = hp3parclient.version_tuple
|
self.client_version = hp3parclient.version_tuple
|
||||||
if client_version < MIN_CLIENT_VERSION:
|
if self.client_version < MIN_CLIENT_VERSION:
|
||||||
msg = (_('Invalid hp3parclient version found (%(found)s). '
|
msg = (_('Invalid hp3parclient version found (%(found)s). '
|
||||||
'Version %(minimum)s or greater required.') %
|
'Version %(minimum)s or greater required.') %
|
||||||
{'found': '.'.join(map(six.text_type, client_version)),
|
{'found': '.'.join(map(six.text_type, self.client_version)),
|
||||||
'minimum': '.'.join(map(six.text_type,
|
'minimum': '.'.join(map(six.text_type,
|
||||||
MIN_CLIENT_VERSION))})
|
MIN_CLIENT_VERSION))})
|
||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
@ -176,7 +181,68 @@ class HP3ParMediator(object):
|
|||||||
else:
|
else:
|
||||||
return 'osf-%s' % uid
|
return 'osf-%s' % uid
|
||||||
|
|
||||||
def create_share(self, project_id, share_id, share_proto, fpg, vfs,
|
@staticmethod
|
||||||
|
def _get_nfs_options(extra_specs, readonly):
|
||||||
|
"""Validate the NFS extra_specs and return the options to use."""
|
||||||
|
|
||||||
|
nfs_options = extra_specs.get('hp_3par:nfs_options')
|
||||||
|
if nfs_options:
|
||||||
|
options = nfs_options.split(',')
|
||||||
|
else:
|
||||||
|
options = []
|
||||||
|
|
||||||
|
# rw, ro, and (no)root_squash (in)secure options are not allowed in
|
||||||
|
# extra_specs because they will be forcibly set below.
|
||||||
|
# no_subtree_check and fsid are not allowed per 3PAR support.
|
||||||
|
# Other strings will be allowed to be sent to the 3PAR which will do
|
||||||
|
# further validation.
|
||||||
|
options_not_allowed = ['ro', 'rw',
|
||||||
|
'no_root_squash', 'root_squash',
|
||||||
|
'secure', 'insecure',
|
||||||
|
'no_subtree_check', 'fsid']
|
||||||
|
|
||||||
|
invalid_options = [
|
||||||
|
option for option in options if option in options_not_allowed
|
||||||
|
]
|
||||||
|
|
||||||
|
if invalid_options:
|
||||||
|
raise exception.InvalidInput(_('Invalid hp3_par:nfs_options in '
|
||||||
|
'extra-specs. The following '
|
||||||
|
'options are not allowed: %s') %
|
||||||
|
invalid_options)
|
||||||
|
|
||||||
|
options.append('ro' if readonly else 'rw')
|
||||||
|
options.append('no_root_squash')
|
||||||
|
options.append('insecure')
|
||||||
|
|
||||||
|
return ','.join(options)
|
||||||
|
|
||||||
|
def _build_createfshare_kwargs(self, protocol, fpg, fstore, readonly,
|
||||||
|
sharedir, extra_specs):
|
||||||
|
createfshare_kwargs = dict(fpg=fpg,
|
||||||
|
fstore=fstore,
|
||||||
|
sharedir=sharedir,
|
||||||
|
comment=OPEN_STACK_MANILA_FSHARE)
|
||||||
|
if protocol == 'nfs':
|
||||||
|
createfshare_kwargs['clientip'] = '127.0.0.1'
|
||||||
|
options = self._get_nfs_options(extra_specs, readonly)
|
||||||
|
createfshare_kwargs['options'] = options
|
||||||
|
else:
|
||||||
|
createfshare_kwargs['allowip'] = '127.0.0.1'
|
||||||
|
|
||||||
|
if self.client_version < MIN_SMB_CA_VERSION:
|
||||||
|
smb_opts = (ACCESS_BASED_ENUM, CACHE)
|
||||||
|
else:
|
||||||
|
smb_opts = (ACCESS_BASED_ENUM, CONTINUOUS_AVAIL, CACHE)
|
||||||
|
|
||||||
|
for smb_opt in smb_opts:
|
||||||
|
opt_value = extra_specs.get('hp_3par:smb_%s' % smb_opt)
|
||||||
|
if opt_value:
|
||||||
|
createfshare_kwargs[smb_opt] = opt_value
|
||||||
|
return createfshare_kwargs
|
||||||
|
|
||||||
|
def create_share(self, project_id, share_id, share_proto, extra_specs,
|
||||||
|
fpg, vfs,
|
||||||
fstore=None, sharedir=None, readonly=False, size=None):
|
fstore=None, sharedir=None, readonly=False, size=None):
|
||||||
"""Create the share and return its path.
|
"""Create the share and return its path.
|
||||||
|
|
||||||
@ -187,6 +253,7 @@ class HP3ParMediator(object):
|
|||||||
:param project_id: The tenant ID.
|
:param project_id: The tenant ID.
|
||||||
:param share_id: The share-id with or without osf- prefix.
|
:param share_id: The share-id with or without osf- prefix.
|
||||||
:param share_proto: The protocol (to map to smb or nfs)
|
:param share_proto: The protocol (to map to smb or nfs)
|
||||||
|
:param extra_specs: The share type extra-specs
|
||||||
:param fpg: The file provisioning group
|
:param fpg: The file provisioning group
|
||||||
:param vfs: The virtual file system
|
:param vfs: The virtual file system
|
||||||
:param fstore: (optional) The file store. When provided, an existing
|
:param fstore: (optional) The file store. When provided, an existing
|
||||||
@ -200,12 +267,27 @@ class HP3ParMediator(object):
|
|||||||
protocol = self.ensure_supported_protocol(share_proto)
|
protocol = self.ensure_supported_protocol(share_proto)
|
||||||
share_name = self.ensure_prefix(share_id)
|
share_name = self.ensure_prefix(share_id)
|
||||||
|
|
||||||
if not fstore:
|
if not (sharedir or self.hp3par_fstore_per_share):
|
||||||
|
sharedir = share_name
|
||||||
|
|
||||||
|
if fstore:
|
||||||
|
use_existing_fstore = True
|
||||||
|
else:
|
||||||
|
use_existing_fstore = False
|
||||||
if self.hp3par_fstore_per_share:
|
if self.hp3par_fstore_per_share:
|
||||||
fstore = share_name
|
fstore = share_name
|
||||||
else:
|
else:
|
||||||
fstore = self.ensure_prefix(project_id, protocol)
|
fstore = self.ensure_prefix(project_id, protocol)
|
||||||
|
|
||||||
|
createfshare_kwargs = self._build_createfshare_kwargs(protocol,
|
||||||
|
fpg,
|
||||||
|
fstore,
|
||||||
|
readonly,
|
||||||
|
sharedir,
|
||||||
|
extra_specs)
|
||||||
|
|
||||||
|
if not use_existing_fstore:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = self._client.createfstore(
|
result = self._client.createfstore(
|
||||||
vfs, fstore, fpg=fpg,
|
vfs, fstore, fpg=fpg,
|
||||||
@ -245,28 +327,13 @@ class HP3ParMediator(object):
|
|||||||
LOG.exception(msg)
|
LOG.exception(msg)
|
||||||
raise exception.ShareBackendException(msg)
|
raise exception.ShareBackendException(msg)
|
||||||
|
|
||||||
if not (sharedir or self.hp3par_fstore_per_share):
|
|
||||||
sharedir = share_name
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if protocol == 'nfs':
|
|
||||||
if readonly:
|
|
||||||
options = 'ro,no_root_squash,insecure'
|
|
||||||
else:
|
|
||||||
options = 'rw,no_root_squash,insecure'
|
|
||||||
|
|
||||||
result = self._client.createfshare(
|
result = self._client.createfshare(protocol,
|
||||||
protocol, vfs, share_name,
|
vfs,
|
||||||
fpg=fpg, fstore=fstore, sharedir=sharedir,
|
share_name,
|
||||||
clientip='127.0.0.1',
|
**createfshare_kwargs)
|
||||||
options=options,
|
|
||||||
comment=OPEN_STACK_MANILA_FSHARE)
|
|
||||||
else:
|
|
||||||
result = self._client.createfshare(
|
|
||||||
protocol, vfs, share_name,
|
|
||||||
fpg=fpg, fstore=fstore, sharedir=sharedir,
|
|
||||||
allowip='127.0.0.1',
|
|
||||||
comment=OPEN_STACK_MANILA_FSHARE)
|
|
||||||
LOG.debug("createfshare result=%s", result)
|
LOG.debug("createfshare result=%s", result)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -300,7 +367,7 @@ class HP3ParMediator(object):
|
|||||||
else:
|
else:
|
||||||
return result['members'][0]['shareName']
|
return result['members'][0]['shareName']
|
||||||
|
|
||||||
def create_share_from_snapshot(self, share_id, share_proto,
|
def create_share_from_snapshot(self, share_id, share_proto, extra_specs,
|
||||||
orig_project_id, orig_share_id, orig_proto,
|
orig_project_id, orig_share_id, orig_proto,
|
||||||
snapshot_id, fpg, vfs):
|
snapshot_id, fpg, vfs):
|
||||||
|
|
||||||
@ -339,6 +406,7 @@ class HP3ParMediator(object):
|
|||||||
orig_project_id,
|
orig_project_id,
|
||||||
share_name,
|
share_name,
|
||||||
protocol,
|
protocol,
|
||||||
|
extra_specs,
|
||||||
fpg,
|
fpg,
|
||||||
vfs,
|
vfs,
|
||||||
fstore=fstore,
|
fstore=fstore,
|
||||||
|
@ -24,6 +24,7 @@ SAN_PASSWORD = 'testpassword4san'
|
|||||||
API_URL = 'https://1.2.3.4:8080/api/v1'
|
API_URL = 'https://1.2.3.4:8080/api/v1'
|
||||||
TIMEOUT = 60
|
TIMEOUT = 60
|
||||||
PORT = 22
|
PORT = 22
|
||||||
|
SHARE_TYPE_ID = 123456789
|
||||||
|
|
||||||
# Constants to use with Mock and expect in results
|
# Constants to use with Mock and expect in results
|
||||||
EXPECTED_IP_10203040 = '10.20.30.40'
|
EXPECTED_IP_10203040 = '10.20.30.40'
|
||||||
@ -42,6 +43,11 @@ EXPECTED_FPG = 'FPG_1'
|
|||||||
EXPECTED_FSTORE = EXPECTED_PROJECT_ID
|
EXPECTED_FSTORE = EXPECTED_PROJECT_ID
|
||||||
EXPECTED_VFS = 'test_vfs'
|
EXPECTED_VFS = 'test_vfs'
|
||||||
EXPECTED_HP_DEBUG = True
|
EXPECTED_HP_DEBUG = True
|
||||||
|
EXPECTED_EXTRA_SPECS = {}
|
||||||
|
|
||||||
|
GET_FSQUOTA = {'message': None,
|
||||||
|
'total': 1,
|
||||||
|
'members': [{'hardBlock': '1024', 'softBlock': '1024'}]}
|
||||||
|
|
||||||
NFS_SHARE_INFO = {
|
NFS_SHARE_INFO = {
|
||||||
'project_id': EXPECTED_PROJECT_ID,
|
'project_id': EXPECTED_PROJECT_ID,
|
||||||
@ -63,3 +69,9 @@ SNAPSHOT_INFO = {
|
|||||||
'share_proto': NFS,
|
'share_proto': NFS,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FakeException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
FAKE_EXCEPTION = FakeException("Fake exception for testing.")
|
||||||
|
@ -136,9 +136,12 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
self.driver.vfs = constants.EXPECTED_VFS
|
self.driver.vfs = constants.EXPECTED_VFS
|
||||||
self.driver.fpg = constants.EXPECTED_FPG
|
self.driver.fpg = constants.EXPECTED_FPG
|
||||||
self.driver.share_ip_address = self.conf.hp3par_share_ip_address
|
self.driver.share_ip_address = self.conf.hp3par_share_ip_address
|
||||||
|
self.mock_object(hp3pardriver, 'share_types')
|
||||||
|
get_extra_specs = hp3pardriver.share_types.get_extra_specs_from_share
|
||||||
|
get_extra_specs.return_value = constants.EXPECTED_EXTRA_SPECS
|
||||||
|
|
||||||
def do_create_share(self, protocol, expected_project_id, expected_share_id,
|
def do_create_share(self, protocol, share_type_id, expected_project_id,
|
||||||
expected_size):
|
expected_share_id, expected_size):
|
||||||
"""Re-usable code for create share."""
|
"""Re-usable code for create share."""
|
||||||
context = None
|
context = None
|
||||||
share_server = None
|
share_server = None
|
||||||
@ -146,6 +149,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
'project_id': expected_project_id,
|
'project_id': expected_project_id,
|
||||||
'id': expected_share_id,
|
'id': expected_share_id,
|
||||||
'share_proto': protocol,
|
'share_proto': protocol,
|
||||||
|
'share_type_id': share_type_id,
|
||||||
'size': expected_size,
|
'size': expected_size,
|
||||||
}
|
}
|
||||||
location = self.driver.create_share(context, share, share_server)
|
location = self.driver.create_share(context, share, share_server)
|
||||||
@ -153,6 +157,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
|
|
||||||
def do_create_share_from_snapshot(self,
|
def do_create_share_from_snapshot(self,
|
||||||
protocol,
|
protocol,
|
||||||
|
share_type_id,
|
||||||
snapshot_id,
|
snapshot_id,
|
||||||
expected_share_id,
|
expected_share_id,
|
||||||
expected_size):
|
expected_size):
|
||||||
@ -162,6 +167,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
share = {
|
share = {
|
||||||
'id': expected_share_id,
|
'id': expected_share_id,
|
||||||
'share_proto': protocol,
|
'share_proto': protocol,
|
||||||
|
'share_type_id': share_type_id,
|
||||||
'size': expected_size,
|
'size': expected_size,
|
||||||
}
|
}
|
||||||
location = self.driver.create_share_from_snapshot(context,
|
location = self.driver.create_share_from_snapshot(context,
|
||||||
@ -208,6 +214,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
constants.EXPECTED_SHARE_NAME)
|
constants.EXPECTED_SHARE_NAME)
|
||||||
|
|
||||||
location = self.do_create_share(constants.CIFS,
|
location = self.do_create_share(constants.CIFS,
|
||||||
|
constants.SHARE_TYPE_ID,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_SIZE_2)
|
constants.EXPECTED_SIZE_2)
|
||||||
@ -217,6 +224,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.CIFS,
|
constants.CIFS,
|
||||||
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
constants.EXPECTED_FPG,
|
constants.EXPECTED_FPG,
|
||||||
constants.EXPECTED_VFS,
|
constants.EXPECTED_VFS,
|
||||||
size=constants.EXPECTED_SIZE_2)]
|
size=constants.EXPECTED_SIZE_2)]
|
||||||
@ -232,6 +240,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
constants.EXPECTED_SHARE_PATH)
|
constants.EXPECTED_SHARE_PATH)
|
||||||
|
|
||||||
location = self.do_create_share(constants.NFS,
|
location = self.do_create_share(constants.NFS,
|
||||||
|
constants.SHARE_TYPE_ID,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_SIZE_1)
|
constants.EXPECTED_SIZE_1)
|
||||||
@ -241,6 +250,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
mock.call.create_share(constants.EXPECTED_PROJECT_ID,
|
mock.call.create_share(constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.NFS,
|
constants.NFS,
|
||||||
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
constants.EXPECTED_FPG,
|
constants.EXPECTED_FPG,
|
||||||
constants.EXPECTED_VFS,
|
constants.EXPECTED_VFS,
|
||||||
size=constants.EXPECTED_SIZE_1)]
|
size=constants.EXPECTED_SIZE_1)]
|
||||||
@ -258,20 +268,24 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
|
|
||||||
location = self.do_create_share_from_snapshot(
|
location = self.do_create_share_from_snapshot(
|
||||||
constants.CIFS,
|
constants.CIFS,
|
||||||
|
constants.SHARE_TYPE_ID,
|
||||||
constants.SNAPSHOT_INFO,
|
constants.SNAPSHOT_INFO,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_SIZE_2)
|
constants.EXPECTED_SIZE_2)
|
||||||
|
|
||||||
self.assertEqual(expected_location, location)
|
self.assertEqual(expected_location, location)
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call.create_share_from_snapshot(constants.EXPECTED_SHARE_ID,
|
mock.call.create_share_from_snapshot(
|
||||||
constants.CIFS,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_FSTORE,
|
constants.CIFS,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
constants.NFS,
|
constants.EXPECTED_FSTORE,
|
||||||
constants.EXPECTED_SNAP_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_FPG,
|
constants.NFS,
|
||||||
constants.EXPECTED_VFS)]
|
constants.EXPECTED_SNAP_ID,
|
||||||
|
constants.EXPECTED_FPG,
|
||||||
|
constants.EXPECTED_VFS),
|
||||||
|
]
|
||||||
self.mock_mediator.assert_has_calls(expected_calls)
|
self.mock_mediator.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
def test_driver_create_nfs_share_from_snapshot(self):
|
def test_driver_create_nfs_share_from_snapshot(self):
|
||||||
@ -285,20 +299,24 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||||||
|
|
||||||
location = self.do_create_share_from_snapshot(
|
location = self.do_create_share_from_snapshot(
|
||||||
constants.NFS,
|
constants.NFS,
|
||||||
|
constants.SHARE_TYPE_ID,
|
||||||
constants.SNAPSHOT_INFO,
|
constants.SNAPSHOT_INFO,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_SIZE_1)
|
constants.EXPECTED_SIZE_1)
|
||||||
|
|
||||||
self.assertEqual(expected_location, location)
|
self.assertEqual(expected_location, location)
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call.create_share_from_snapshot(constants.EXPECTED_SHARE_ID,
|
mock.call.create_share_from_snapshot(
|
||||||
constants.NFS,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.NFS,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
constants.NFS,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SNAP_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.EXPECTED_FPG,
|
constants.NFS,
|
||||||
constants.EXPECTED_VFS)]
|
constants.EXPECTED_SNAP_ID,
|
||||||
|
constants.EXPECTED_FPG,
|
||||||
|
constants.EXPECTED_VFS)
|
||||||
|
]
|
||||||
|
|
||||||
self.mock_mediator.assert_has_calls(expected_calls)
|
self.mock_mediator.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
|
@ -188,10 +188,7 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
def test_mediator_client_version_exception(self):
|
def test_mediator_client_version_exception(self):
|
||||||
"""Test the getWsApiVersion exception handling."""
|
"""Test the getWsApiVersion exception handling."""
|
||||||
|
|
||||||
class FakeException(Exception):
|
self.mock_client.getWsApiVersion.side_effect = constants.FAKE_EXCEPTION
|
||||||
pass
|
|
||||||
|
|
||||||
self.mock_client.getWsApiVersion.side_effect = FakeException()
|
|
||||||
self.assertRaises(exception.ShareBackendException,
|
self.assertRaises(exception.ShareBackendException,
|
||||||
self.init_mediator)
|
self.init_mediator)
|
||||||
|
|
||||||
@ -204,14 +201,35 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
self.mediator.do_setup)
|
self.mediator.do_setup)
|
||||||
|
|
||||||
def get_expected_calls_for_create_share(self,
|
def get_expected_calls_for_create_share(self,
|
||||||
|
client_version,
|
||||||
expected_fpg,
|
expected_fpg,
|
||||||
expected_vfsname,
|
expected_vfsname,
|
||||||
expected_protocol,
|
expected_protocol,
|
||||||
|
extra_specs,
|
||||||
expected_project_id,
|
expected_project_id,
|
||||||
expected_share_id):
|
expected_share_id):
|
||||||
expected_sharedir = expected_share_id
|
expected_sharedir = expected_share_id
|
||||||
|
|
||||||
|
createfshare_kwargs = dict(comment='OpenStack Manila fshare',
|
||||||
|
fpg=expected_fpg,
|
||||||
|
sharedir=expected_sharedir,
|
||||||
|
fstore=expected_project_id)
|
||||||
|
|
||||||
if expected_protocol == constants.NFS_LOWER:
|
if expected_protocol == constants.NFS_LOWER:
|
||||||
|
|
||||||
|
createfshare_kwargs['clientip'] = '127.0.0.1'
|
||||||
|
|
||||||
|
# Options from extra-specs.
|
||||||
|
opt_string = extra_specs.get('hp_3par:nfs_options', [])
|
||||||
|
opt_list = opt_string.split(',')
|
||||||
|
# Options that the mediator adds.
|
||||||
|
nfs_options = ['rw', 'no_root_squash', 'insecure']
|
||||||
|
nfs_options += opt_list
|
||||||
|
expected_options = ','.join(nfs_options)
|
||||||
|
|
||||||
|
createfshare_kwargs['options'] = OptionMatcher(
|
||||||
|
self.assertListEqual, expected_options)
|
||||||
|
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call.createfstore(expected_vfsname, expected_project_id,
|
mock.call.createfstore(expected_vfsname, expected_project_id,
|
||||||
comment='OpenStack Manila fstore',
|
comment='OpenStack Manila fstore',
|
||||||
@ -226,16 +244,26 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
fstore=expected_project_id),
|
fstore=expected_project_id),
|
||||||
mock.call.createfshare(expected_protocol, expected_vfsname,
|
mock.call.createfshare(expected_protocol, expected_vfsname,
|
||||||
expected_share_id,
|
expected_share_id,
|
||||||
comment='OpenStack Manila fshare',
|
**createfshare_kwargs),
|
||||||
fpg=expected_fpg,
|
|
||||||
sharedir=expected_sharedir,
|
|
||||||
clientip='127.0.0.1',
|
|
||||||
options='rw,no_root_squash,insecure',
|
|
||||||
fstore=expected_project_id),
|
|
||||||
mock.call.getfshare(expected_protocol, expected_share_id,
|
mock.call.getfshare(expected_protocol, expected_share_id,
|
||||||
fpg=expected_fpg, vfs=expected_vfsname,
|
fpg=expected_fpg, vfs=expected_vfsname,
|
||||||
fstore=expected_project_id)]
|
fstore=expected_project_id)]
|
||||||
else:
|
else:
|
||||||
|
createfshare_kwargs['allowip'] = '127.0.0.1'
|
||||||
|
|
||||||
|
if client_version < hp3parmediator.MIN_SMB_CA_VERSION:
|
||||||
|
smb_opts = (hp3parmediator.ACCESS_BASED_ENUM,
|
||||||
|
hp3parmediator.CACHE)
|
||||||
|
else:
|
||||||
|
smb_opts = (hp3parmediator.ACCESS_BASED_ENUM,
|
||||||
|
hp3parmediator.CONTINUOUS_AVAIL,
|
||||||
|
hp3parmediator.CACHE)
|
||||||
|
|
||||||
|
for smb_opt in smb_opts:
|
||||||
|
opt_value = extra_specs.get('hp_3par:smb_%s' % smb_opt)
|
||||||
|
if opt_value:
|
||||||
|
createfshare_kwargs[smb_opt] = opt_value
|
||||||
|
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call.createfstore(expected_vfsname, expected_project_id,
|
mock.call.createfstore(expected_vfsname, expected_project_id,
|
||||||
comment='OpenStack Manila fstore',
|
comment='OpenStack Manila fstore',
|
||||||
@ -250,17 +278,35 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
fstore=expected_project_id),
|
fstore=expected_project_id),
|
||||||
mock.call.createfshare(expected_protocol, expected_vfsname,
|
mock.call.createfshare(expected_protocol, expected_vfsname,
|
||||||
expected_share_id,
|
expected_share_id,
|
||||||
comment='OpenStack Manila fshare',
|
**createfshare_kwargs),
|
||||||
fpg=expected_fpg,
|
|
||||||
sharedir=expected_sharedir,
|
|
||||||
allowip='127.0.0.1',
|
|
||||||
fstore=expected_project_id),
|
|
||||||
mock.call.getfshare(expected_protocol, expected_share_id,
|
mock.call.getfshare(expected_protocol, expected_share_id,
|
||||||
fpg=expected_fpg, vfs=expected_vfsname,
|
fpg=expected_fpg, vfs=expected_vfsname,
|
||||||
fstore=expected_project_id)]
|
fstore=expected_project_id)]
|
||||||
return expected_calls
|
return expected_calls
|
||||||
|
|
||||||
def test_mediator_create_cifs_share(self):
|
@staticmethod
|
||||||
|
def _build_smb_extra_specs(**kwargs):
|
||||||
|
extra_specs = {'driver_handles_share_servers': False}
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
extra_specs['hp_3par:smb_%s' % k] = v
|
||||||
|
return extra_specs
|
||||||
|
|
||||||
|
@ddt.data(((3, 2, 1), None, None, None),
|
||||||
|
((3, 2, 1), 'true', None, None),
|
||||||
|
((3, 2, 1), None, 'false', None),
|
||||||
|
((3, 2, 1), None, 'false', None),
|
||||||
|
((3, 2, 1), None, None, 'optimized'),
|
||||||
|
((3, 2, 1), 'true', 'false', 'optimized'),
|
||||||
|
((3, 2, 2), None, None, None),
|
||||||
|
((3, 2, 2), 'true', None, None),
|
||||||
|
((3, 2, 2), None, 'false', None),
|
||||||
|
((3, 2, 2), None, 'false', None),
|
||||||
|
((3, 2, 2), None, None, 'optimized'),
|
||||||
|
((3, 2, 2), 'true', 'false', 'optimized'))
|
||||||
|
@ddt.unpack
|
||||||
|
def test_mediator_create_cifs_share(self, client_version, abe, ca, cache):
|
||||||
|
self.hp3parclient = sys.modules['hp3parclient']
|
||||||
|
self.hp3parclient.version_tuple = client_version
|
||||||
self.init_mediator()
|
self.init_mediator()
|
||||||
|
|
||||||
self.mock_client.getfshare.return_value = {
|
self.mock_client.getfshare.return_value = {
|
||||||
@ -269,15 +315,16 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
'members': [{'shareName': constants.EXPECTED_SHARE_NAME}]
|
'members': [{'shareName': constants.EXPECTED_SHARE_NAME}]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mock_client.getfsquota.return_value = {
|
self.mock_client.getfsquota.return_value = constants.GET_FSQUOTA
|
||||||
'message': None,
|
|
||||||
'total': 1,
|
extra_specs = self._build_smb_extra_specs(access_based_enum=abe,
|
||||||
'members': [{'hardBlock': '1024', 'softBlock': '1024'}]
|
continuous_avail=ca,
|
||||||
}
|
cache=cache)
|
||||||
|
|
||||||
location = self.mediator.create_share(constants.EXPECTED_PROJECT_ID,
|
location = self.mediator.create_share(constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.CIFS,
|
constants.CIFS,
|
||||||
|
extra_specs,
|
||||||
constants.EXPECTED_FPG,
|
constants.EXPECTED_FPG,
|
||||||
constants.EXPECTED_VFS,
|
constants.EXPECTED_VFS,
|
||||||
size=constants.EXPECTED_SIZE_1)
|
size=constants.EXPECTED_SIZE_1)
|
||||||
@ -285,15 +332,43 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
self.assertEqual(constants.EXPECTED_SHARE_NAME, location)
|
self.assertEqual(constants.EXPECTED_SHARE_NAME, location)
|
||||||
|
|
||||||
expected_calls = self.get_expected_calls_for_create_share(
|
expected_calls = self.get_expected_calls_for_create_share(
|
||||||
|
client_version,
|
||||||
constants.EXPECTED_FPG,
|
constants.EXPECTED_FPG,
|
||||||
constants.EXPECTED_VFS,
|
constants.EXPECTED_VFS,
|
||||||
constants.SMB_LOWER,
|
constants.SMB_LOWER,
|
||||||
|
extra_specs,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID)
|
constants.EXPECTED_SHARE_ID)
|
||||||
|
|
||||||
self.mock_client.assert_has_calls(expected_calls)
|
self.mock_client.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
def test_mediator_create_nfs_share(self):
|
@ddt.data('ro',
|
||||||
|
'rw',
|
||||||
|
'no_root_squash',
|
||||||
|
'root_squash',
|
||||||
|
'secure',
|
||||||
|
'insecure',
|
||||||
|
'hide,insecure,no_wdelay,ro,bogus,root_squash,test')
|
||||||
|
def test_mediator_create_nfs_share_bad_options(self, nfs_options):
|
||||||
|
self.init_mediator()
|
||||||
|
|
||||||
|
extra_specs = {'hp_3par:nfs_options': nfs_options}
|
||||||
|
|
||||||
|
self.assertRaises(exception.InvalidInput,
|
||||||
|
self.mediator.create_share,
|
||||||
|
constants.EXPECTED_PROJECT_ID,
|
||||||
|
constants.EXPECTED_SHARE_ID,
|
||||||
|
constants.NFS.lower(),
|
||||||
|
extra_specs,
|
||||||
|
constants.EXPECTED_FPG,
|
||||||
|
constants.EXPECTED_VFS,
|
||||||
|
size=constants.EXPECTED_SIZE_1)
|
||||||
|
|
||||||
|
self.assertFalse(self.mock_client.createfshare.called)
|
||||||
|
|
||||||
|
@ddt.data('sync',
|
||||||
|
'no_wdelay,sec=sys,hide,sync')
|
||||||
|
def test_mediator_create_nfs_share(self, nfs_options):
|
||||||
self.init_mediator()
|
self.init_mediator()
|
||||||
|
|
||||||
self.mock_client.getfshare.return_value = {
|
self.mock_client.getfshare.return_value = {
|
||||||
@ -302,15 +377,14 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
'members': [{'sharePath': constants.EXPECTED_SHARE_PATH}]
|
'members': [{'sharePath': constants.EXPECTED_SHARE_PATH}]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mock_client.getfsquota.return_value = {
|
self.mock_client.getfsquota.return_value = constants.GET_FSQUOTA
|
||||||
'message': None,
|
|
||||||
'total': 1,
|
extra_specs = {'hp_3par:nfs_options': nfs_options}
|
||||||
'members': [{'hardBlock': '1024', 'softBlock': '1024'}]
|
|
||||||
}
|
|
||||||
|
|
||||||
location = self.mediator.create_share(constants.EXPECTED_PROJECT_ID,
|
location = self.mediator.create_share(constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.NFS.lower(),
|
constants.NFS.lower(),
|
||||||
|
extra_specs,
|
||||||
constants.EXPECTED_FPG,
|
constants.EXPECTED_FPG,
|
||||||
constants.EXPECTED_VFS,
|
constants.EXPECTED_VFS,
|
||||||
size=constants.EXPECTED_SIZE_1)
|
size=constants.EXPECTED_SIZE_1)
|
||||||
@ -318,14 +392,49 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
self.assertEqual(constants.EXPECTED_SHARE_PATH, location)
|
self.assertEqual(constants.EXPECTED_SHARE_PATH, location)
|
||||||
|
|
||||||
expected_calls = self.get_expected_calls_for_create_share(
|
expected_calls = self.get_expected_calls_for_create_share(
|
||||||
|
hp3parmediator.MIN_CLIENT_VERSION,
|
||||||
constants.EXPECTED_FPG,
|
constants.EXPECTED_FPG,
|
||||||
constants.EXPECTED_VFS,
|
constants.EXPECTED_VFS,
|
||||||
constants.NFS.lower(),
|
constants.NFS.lower(),
|
||||||
|
extra_specs,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID)
|
constants.EXPECTED_SHARE_ID)
|
||||||
|
|
||||||
self.mock_client.assert_has_calls(expected_calls)
|
self.mock_client.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
|
def test_mediator_create_nfs_share_get_exception(self):
|
||||||
|
self.init_mediator()
|
||||||
|
|
||||||
|
self.mock_client.getfshare.side_effect = constants.FAKE_EXCEPTION
|
||||||
|
self.mock_client.getfsquota.return_value = constants.GET_FSQUOTA
|
||||||
|
|
||||||
|
self.assertRaises(exception.ShareBackendException,
|
||||||
|
self.mediator.create_share,
|
||||||
|
constants.EXPECTED_PROJECT_ID,
|
||||||
|
constants.EXPECTED_SHARE_ID,
|
||||||
|
constants.NFS.lower(),
|
||||||
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
|
constants.EXPECTED_FPG,
|
||||||
|
constants.EXPECTED_VFS,
|
||||||
|
size=constants.EXPECTED_SIZE_1)
|
||||||
|
|
||||||
|
@ddt.data(0, 2)
|
||||||
|
def test_mediator_create_nfs_share_get_fail(self, count):
|
||||||
|
self.init_mediator()
|
||||||
|
|
||||||
|
self.mock_client.getfshare.return_value = {'total': count}
|
||||||
|
self.mock_client.getfsquota.return_value = constants.GET_FSQUOTA
|
||||||
|
|
||||||
|
self.assertRaises(exception.ShareBackendException,
|
||||||
|
self.mediator.create_share,
|
||||||
|
constants.EXPECTED_PROJECT_ID,
|
||||||
|
constants.EXPECTED_SHARE_ID,
|
||||||
|
constants.NFS.lower(),
|
||||||
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
|
constants.EXPECTED_FPG,
|
||||||
|
constants.EXPECTED_VFS,
|
||||||
|
size=constants.EXPECTED_SIZE_1)
|
||||||
|
|
||||||
def test_mediator_create_cifs_share_from_snapshot(self):
|
def test_mediator_create_cifs_share_from_snapshot(self):
|
||||||
self.init_mediator()
|
self.init_mediator()
|
||||||
|
|
||||||
@ -339,6 +448,7 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
location = self.mediator.create_share_from_snapshot(
|
location = self.mediator.create_share_from_snapshot(
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.CIFS,
|
constants.CIFS,
|
||||||
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.NFS,
|
constants.NFS,
|
||||||
@ -385,6 +495,7 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
location = self.mediator.create_share_from_snapshot(
|
location = self.mediator.create_share_from_snapshot(
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.NFS,
|
constants.NFS,
|
||||||
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.NFS,
|
constants.NFS,
|
||||||
@ -432,6 +543,7 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
self.mediator.create_share_from_snapshot,
|
self.mediator.create_share_from_snapshot,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.NFS,
|
constants.NFS,
|
||||||
|
constants.EXPECTED_EXTRA_SPECS,
|
||||||
constants.EXPECTED_PROJECT_ID,
|
constants.EXPECTED_PROJECT_ID,
|
||||||
constants.EXPECTED_SHARE_ID,
|
constants.EXPECTED_SHARE_ID,
|
||||||
constants.NFS,
|
constants.NFS,
|
||||||
@ -1189,3 +1301,16 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||||||
fstore=constants.EXPECTED_PROJECT_ID)
|
fstore=constants.EXPECTED_PROJECT_ID)
|
||||||
|
|
||||||
self.assertEqual(expected_result, result)
|
self.assertEqual(expected_result, result)
|
||||||
|
|
||||||
|
|
||||||
|
class OptionMatcher(object):
|
||||||
|
"""Options string order can vary. Compare as lists."""
|
||||||
|
|
||||||
|
def __init__(self, assert_func, expected_string):
|
||||||
|
self.assert_func = assert_func
|
||||||
|
self.expected = expected_string.split(',')
|
||||||
|
|
||||||
|
def __eq__(self, actual_string):
|
||||||
|
actual = actual_string.split(',')
|
||||||
|
self.assert_func(sorted(self.expected), sorted(actual))
|
||||||
|
return True
|
||||||
|
Loading…
Reference in New Issue
Block a user