Merge "HPE 3PAR driver pool support"

This commit is contained in:
Jenkins 2016-09-01 01:35:45 +00:00 committed by Gerrit Code Review
commit 07563cff3d
9 changed files with 724 additions and 254 deletions

View File

@ -769,6 +769,8 @@ class ShareDriver(object):
Redefine it within share driver when it is going to handle share
servers.
:param metadata: a dictionary, for now containing a key 'request_host'
"""
raise NotImplementedError()

View File

@ -21,6 +21,7 @@ import os
import re
from oslo_config import cfg
from oslo_config import types
from oslo_log import log
import six
@ -31,8 +32,90 @@ from manila.i18n import _LI
from manila.share import driver
from manila.share.drivers.hpe import hpe_3par_mediator
from manila.share import share_types
from manila.share import utils as share_utils
from manila import utils
LOG = log.getLogger(__name__)
class FPG(types.String, types.IPAddress):
"""FPG type.
Used to represent multiple pools per backend values.
Converts configuration value to an FPGs value.
FPGs value format:
FPG name, IP address 1, IP address 2, ..., IP address 4
where FPG name is a string value,
IP address is of type types.IPAddress
Optionally doing range checking.
If value is whitespace or empty string will raise error
:param min_ip: Optional check that number of min IP address of VFS.
:param max_ip: Optional check that number of max IP address of VFS.
:param type_name: Type name to be used in the sample config file.
"""
MAX_SUPPORTED_IP_PER_VFS = 4
def __init__(self, min_ip=0, max_ip=MAX_SUPPORTED_IP_PER_VFS,
type_name='FPG'):
types.String.__init__(self, type_name=type_name)
types.IPAddress.__init__(self, type_name=type_name)
if max_ip < min_ip:
msg = _("Pool's max acceptable IP cannot be less than min.")
raise exception.HPE3ParInvalid(err=msg)
if min_ip < 0:
msg = _("Pools must be configured with zero or more IPs.")
raise exception.HPE3ParInvalid(err=msg)
if max_ip > FPG.MAX_SUPPORTED_IP_PER_VFS:
msg = (_("Pool's max acceptable IP cannot be greater than "
"supported value=%s.") % FPG.MAX_SUPPORTED_IP_PER_VFS)
raise exception.HPE3ParInvalid(err=msg)
self.min_ip = min_ip
self.max_ip = max_ip
def __call__(self, value):
if value is None or value.strip(' ') is '':
message = _("Invalid configuration. hpe3par_fpg must be set.")
LOG.error(message)
raise exception.HPE3ParInvalid(err=message)
ips = []
values = value.split(",")
# Extract pool name
pool_name = values.pop(0).strip()
# values will now be ['ip1', ...]
if len(values) < self.min_ip:
msg = (_("Require at least %s IPs configured per "
"pool") % self.min_ip)
raise exception.HPE3ParInvalid(err=msg)
if len(values) > self.max_ip:
msg = (_("Cannot configure IPs more than max supported "
"%s IPs per pool") % self.max_ip)
raise exception.HPE3ParInvalid(err=msg)
for ip_addr in values:
ip_addr = types.String.__call__(self, ip_addr.strip())
try:
ips.append(types.IPAddress.__call__(self, ip_addr))
except ValueError as verror:
raise exception.HPE3ParInvalid(err=verror)
fpg = {pool_name: ips}
return fpg
def __repr__(self):
return 'FPG'
def _formatter(self, value):
return six.text_type(value)
HPE3PAR_OPTS = [
cfg.StrOpt('hpe3par_api_url',
default='',
@ -65,14 +148,10 @@ HPE3PAR_OPTS = [
default=22,
help='SSH port to use with SAN',
deprecated_name='hp3par_san_ssh_port'),
cfg.StrOpt('hpe3par_fpg',
default="OpenStack",
help="The File Provisioning Group (FPG) to use",
deprecated_name='hp3par_fpg'),
cfg.StrOpt('hpe3par_share_ip_address',
default='',
help="The IP address for shares not using a share server",
deprecated_name='hp3par_share_ip_address'),
cfg.MultiOpt('hpe3par_fpg',
item_type=FPG(min_ip=0, max_ip=FPG.MAX_SUPPORTED_IP_PER_VFS),
help="The File Provisioning Group (FPG) to use",
deprecated_name='hp3par_fpg'),
cfg.BoolOpt('hpe3par_fstore_per_share',
default=False,
help="Use one filestore per share",
@ -107,7 +186,13 @@ HPE3PAR_OPTS = [
CONF = cfg.CONF
CONF.register_opts(HPE3PAR_OPTS)
LOG = log.getLogger(__name__)
def to_list(var):
"""Convert var to list type if not"""
if isinstance(var, six.string_types):
return [var]
else:
return var
class HPE3ParShareDriver(driver.ShareDriver):
@ -126,10 +211,11 @@ class HPE3ParShareDriver(driver.ShareDriver):
2.0.4 - Reduce the fsquota by share size
when a share is deleted #1582931
2.0.5 - Add update_access support
2.0.6 - Multi pool support per backend
"""
VERSION = "2.0.5"
VERSION = "2.0.6"
def __init__(self, *args, **kwargs):
super(HPE3ParShareDriver, self).__init__((True, False),
@ -140,9 +226,7 @@ class HPE3ParShareDriver(driver.ShareDriver):
self.configuration.append_config_values(HPE3PAR_OPTS)
self.configuration.append_config_values(driver.ssh_opts)
self.configuration.append_config_values(config.global_opts)
self.fpg = None
self.vfs = None
self.share_ip_address = None
self.fpgs = {}
self._hpe3par = None # mediator between driver and client
def do_setup(self, context):
@ -152,14 +236,6 @@ class HPE3ParShareDriver(driver.ShareDriver):
{'driver_name': self.__class__.__name__,
'version': self.VERSION})
if not self.driver_handles_share_servers:
self.share_ip_address = self.configuration.hpe3par_share_ip_address
if not self.share_ip_address:
raise exception.HPE3ParInvalid(
_("Unsupported configuration. "
"hpe3par_share_ip_address must be set when "
"driver_handles_share_servers is False."))
mediator = hpe_3par_mediator.HPE3ParMediator(
hpe3par_username=self.configuration.hpe3par_username,
hpe3par_password=self.configuration.hpe3par_password,
@ -172,8 +248,6 @@ class HPE3ParShareDriver(driver.ShareDriver):
hpe3par_fstore_per_share=(self.configuration
.hpe3par_fstore_per_share),
hpe3par_require_cifs_ip=self.configuration.hpe3par_require_cifs_ip,
hpe3par_share_ip_address=(
self.configuration.hpe3par_share_ip_address),
hpe3par_cifs_admin_access_username=(
self.configuration.hpe3par_cifs_admin_access_username),
hpe3par_cifs_admin_access_password=(
@ -188,15 +262,94 @@ class HPE3ParShareDriver(driver.ShareDriver):
mediator.do_setup()
# FPG must be configured and must exist.
self.fpg = self.configuration.safe_get('hpe3par_fpg')
# Validate the FPG and discover the VFS
# This also validates the client, connection, firmware, WSAPI, FPG...
self.vfs = mediator.get_vfs_name(self.fpg)
def _validate_pool_ips(addresses, conf_pool_ips):
# Pool configured IP addresses should be subset of IP addresses
# retured from vfs
addresses = to_list(addresses)
if not set(conf_pool_ips) <= set(addresses):
msg = _("Incorrect configuration. "
"Configuration pool IP address did not match with "
"IP addresses at 3par array")
raise exception.HPE3ParInvalid(err=msg)
def _construct_fpg():
# FPG must be configured and must exist.
# self.configuration.safe_get('hpe3par_fpg') will have value in
# following format:
# [ {'pool_name':['ip_addr', 'ip_addr', ...]}, ... ]
for fpg in self.configuration.safe_get('hpe3par_fpg'):
pool_name = list(fpg)[0]
conf_pool_ips = fpg[pool_name]
# Validate the FPG and discover the VFS
# This also validates the client, connection, firmware, WSAPI,
# FPG...
vfs_info = mediator.get_vfs(pool_name)
if self.driver_handles_share_servers:
# Use discovered IP(s) from array
vfs_info['vfsip']['address'] = to_list(
vfs_info['vfsip']['address'])
self.fpgs[pool_name] = {
vfs_info['vfsname']: vfs_info['vfsip']['address']}
elif conf_pool_ips == []:
# not DHSS and IPs not configured in manila.conf.
if not vfs_info['vfsip']['address']:
msg = _("Unsupported configuration. "
"hpe3par_fpg must have IP address "
"or be discoverable at 3PAR")
LOG.error(msg)
raise exception.HPE3ParInvalid(err=msg)
else:
# Use discovered pool ips
vfs_info['vfsip']['address'] = to_list(
vfs_info['vfsip']['address'])
self.fpgs[pool_name] = {
vfs_info['vfsname']: vfs_info['vfsip']['address']}
else:
# not DHSS and IPs configured in manila.conf
_validate_pool_ips(vfs_info['vfsip']['address'],
conf_pool_ips)
self.fpgs[pool_name] = {
vfs_info['vfsname']: conf_pool_ips}
_construct_fpg()
# Don't set _hpe3par until it is ready. Otherwise _update_stats fails.
self._hpe3par = mediator
def _get_pool_location_from_share_host(self, share_instance_host):
# Return pool name, vfs, IPs for a pool from share instance host
pool_name = share_utils.extract_host(share_instance_host, level='pool')
if not pool_name:
message = (_("Pool is not available in the share host %s.") %
share_instance_host)
raise exception.InvalidHost(reason=message)
if pool_name not in self.fpgs:
message = (_("Pool location lookup failed. "
"Could not find pool %s") %
pool_name)
raise exception.InvalidHost(reason=message)
vfs = list(self.fpgs[pool_name])[0]
ips = self.fpgs[pool_name][vfs]
return (pool_name, vfs, ips)
def _get_pool_location(self, share, share_server=None):
# Return pool name, vfs, IPs for a pool from share host field
# Use share_server if provided, instead of self.fpgs
if share_server is not None:
# When DHSS
ips = share_server['backend_details'].get('ip')
ips = to_list(ips)
vfs = share_server['backend_details'].get('vfs')
pool_name = share_server['backend_details'].get('fpg')
return (pool_name, vfs, ips)
else:
# When DHSS = false
return self._get_pool_location_from_share_host(share['host'])
def check_for_setup_error(self):
try:
@ -230,6 +383,31 @@ class HPE3ParShareDriver(driver.ShareDriver):
def get_network_allocations_number(self):
return 1
def choose_share_server_compatible_with_share(self, context, share_servers,
share, snapshot=None,
consistency_group=None):
"""Method that allows driver to choose share server for provided share.
If compatible share-server is not found, method should return None.
:param context: Current context
:param share_servers: list with share-server models
:param share: share model
:param snapshot: snapshot model
:param consistency_group: ConsistencyGroup model with shares
:returns: share-server or None
"""
# If creating in a consistency group, raise exception
if consistency_group:
msg = _("HPE 3PAR driver does not support consistency group")
raise exception.InvalidRequest(message=msg)
pool_name = share_utils.extract_host(share['host'], level='pool')
for share_server in share_servers:
if share_server['backend_details'].get('fpg') == pool_name:
return share_server
return None
@staticmethod
def _validate_network_type(network_type):
if network_type not in ('flat', 'vlan', None):
@ -238,37 +416,53 @@ class HPE3ParShareDriver(driver.ShareDriver):
raise exception.NetworkBadConfigurationException(
reason=reason % network_type)
def _create_share_server(self, network_info, request_host=None):
"""Is called to create/setup share server"""
# Return pool name, vfs, IPs for a pool
pool_name, vfs, ips = self._get_pool_location_from_share_host(
request_host)
ip = network_info['network_allocations'][0]['ip_address']
if ip not in ips:
# Besides DHSS, admin could have setup IP to VFS directly on array
if len(ips) > (FPG.MAX_SUPPORTED_IP_PER_VFS - 1):
message = (_("Pool %s has exceeded 3PAR's "
"max supported VFS IP address") % pool_name)
LOG.error(message)
raise exception.Invalid(message)
subnet = utils.cidr_to_netmask(network_info['cidr'])
vlantag = network_info['segmentation_id']
self._hpe3par.create_fsip(ip, subnet, vlantag, pool_name, vfs)
# Update in global saved config, self.fpgs[pool_name]
ips.append(ip)
return {'share_server_name': network_info['server_id'],
'share_server_id': network_info['server_id'],
'ip': ip,
'subnet': subnet,
'vlantag': vlantag if vlantag else 0,
'fpg': pool_name,
'vfs': vfs}
def _setup_server(self, network_info, metadata=None):
LOG.debug("begin _setup_server with %s", network_info)
self._validate_network_type(network_info['network_type'])
ip = network_info['network_allocations'][0]['ip_address']
subnet = utils.cidr_to_netmask(network_info['cidr'])
vlantag = network_info['segmentation_id']
self._hpe3par.create_fsip(ip, subnet, vlantag, self.fpg, self.vfs)
return {
'share_server_name': network_info['server_id'],
'share_server_id': network_info['server_id'],
'ip': ip,
'subnet': subnet,
'vlantag': vlantag if vlantag else 0,
'fpg': self.fpg,
'vfs': self.vfs,
}
if metadata is not None and metadata['request_host'] is not None:
return self._create_share_server(network_info,
metadata['request_host'])
def _teardown_server(self, server_details, security_services=None):
LOG.debug("begin _teardown_server with %s", server_details)
self._hpe3par.remove_fsip(server_details.get('ip'),
server_details.get('fpg'),
server_details.get('vfs'))
def _get_share_ip(self, share_server):
return share_server['backend_details'].get('ip') if share_server else (
self.share_ip_address)
fpg = server_details.get('fpg')
vfs = server_details.get('vfs')
ip = server_details.get('ip')
self._hpe3par.remove_fsip(ip, fpg, vfs)
if ip in self.fpgs[fpg][vfs]:
self.fpgs[fpg][vfs].remove(ip)
@staticmethod
def build_share_comment(share):
@ -289,7 +483,7 @@ class HPE3ParShareDriver(driver.ShareDriver):
def create_share(self, context, share, share_server=None):
"""Is called to create share."""
ip = self._get_share_ip(share_server)
fpg, vfs, ips = self._get_pool_location(share, share_server)
protocol = share['share_proto']
extra_specs = share_types.get_extra_specs_from_share(share)
@ -299,18 +493,18 @@ class HPE3ParShareDriver(driver.ShareDriver):
share['id'],
protocol,
extra_specs,
self.fpg, self.vfs,
fpg, vfs,
size=share['size'],
comment=self.build_share_comment(share)
)
return self._hpe3par.build_export_location(protocol, ip, path)
return self._hpe3par.build_export_locations(protocol, ips, path)
def create_share_from_snapshot(self, context, share, snapshot,
share_server=None):
"""Is called to create share from snapshot."""
ip = self._get_share_ip(share_server)
fpg, vfs, ips = self._get_pool_location(share, share_server)
protocol = share['share_proto']
extra_specs = share_types.get_extra_specs_from_share(share)
@ -322,43 +516,50 @@ class HPE3ParShareDriver(driver.ShareDriver):
share['project_id'],
snapshot['share_id'],
snapshot['id'],
self.fpg,
self.vfs,
fpg,
vfs,
ips,
size=share['size'],
comment=self.build_share_comment(share)
)
return self._hpe3par.build_export_location(protocol, ip, path)
return self._hpe3par.build_export_locations(protocol, ips, path)
def delete_share(self, context, share, share_server=None):
"""Deletes share and its fstore."""
fpg, vfs, ips = self._get_pool_location(share, share_server)
self._hpe3par.delete_share(share['project_id'],
share['id'],
share['size'],
share['share_proto'],
self.fpg,
self.vfs)
fpg,
vfs,
ips[0])
def create_snapshot(self, context, snapshot, share_server=None):
"""Creates a snapshot of a share."""
fpg, vfs, ips = self._get_pool_location(snapshot['share'],
share_server)
self._hpe3par.create_snapshot(snapshot['share']['project_id'],
snapshot['share']['id'],
snapshot['share']['share_proto'],
snapshot['id'],
self.fpg,
self.vfs)
fpg,
vfs)
def delete_snapshot(self, context, snapshot, share_server=None):
"""Deletes a snapshot of a share."""
fpg, vfs, ips = self._get_pool_location(snapshot['share'],
share_server)
self._hpe3par.delete_snapshot(snapshot['share']['project_id'],
snapshot['share']['id'],
snapshot['share']['share_proto'],
snapshot['id'],
self.fpg,
self.vfs)
fpg,
vfs)
def ensure_share(self, context, share, share_server=None):
pass
@ -370,6 +571,7 @@ class HPE3ParShareDriver(driver.ShareDriver):
if 'NFS' == share['share_proto']: # Avoiding DB call otherwise
extra_specs = share_types.get_extra_specs_from_share(share)
fpg, vfs, ips = self._get_pool_location(share, share_server)
self._hpe3par.update_access(share['project_id'],
share['id'],
share['share_proto'],
@ -377,28 +579,32 @@ class HPE3ParShareDriver(driver.ShareDriver):
access_rules,
add_rules,
delete_rules,
self.fpg,
self.vfs)
fpg,
vfs)
def extend_share(self, share, new_size, share_server=None):
"""Extends size of existing share."""
fpg, vfs, ips = self._get_pool_location(share, share_server)
self._hpe3par.resize_share(share['project_id'],
share['id'],
share['share_proto'],
new_size,
share['size'],
self.fpg,
self.vfs)
fpg,
vfs)
def shrink_share(self, share, new_size, share_server=None):
"""Shrinks size of existing share."""
fpg, vfs, ips = self._get_pool_location(share, share_server)
self._hpe3par.resize_share(share['project_id'],
share['id'],
share['share_proto'],
new_size,
share['size'],
self.fpg,
self.vfs)
fpg,
vfs)
def _update_share_stats(self):
"""Retrieve stats info from share group."""
@ -434,8 +640,10 @@ class HPE3ParShareDriver(driver.ShareDriver):
_LI("Skipping capacity and capabilities update. Setup has not "
"completed."))
else:
fpg_status = self._hpe3par.get_fpg_status(self.fpg)
LOG.debug("FPG status = %s.", fpg_status)
stats.update(fpg_status)
for fpg in self.fpgs:
fpg_status = self._hpe3par.get_fpg_status(fpg)
fpg_status['reserved_percentage'] = reserved_share_percentage
LOG.debug("FPG status = %s.", fpg_status)
stats.setdefault('pools', []).append(fpg_status)
super(HPE3ParShareDriver, self)._update_share_stats(stats)

View File

@ -77,10 +77,11 @@ class HPE3ParMediator(object):
when a share is deleted #1582931
2.0.6 - Read-write share from snapshot (using driver mount and copy)
2.0.7 - Add update_access support
2.0.8 - Multi pools support per backend
"""
VERSION = "2.0.7"
VERSION = "2.0.8"
def __init__(self, **kwargs):
@ -95,7 +96,6 @@ class HPE3ParMediator(object):
self.hpe3par_san_private_key = kwargs.get('hpe3par_san_private_key')
self.hpe3par_fstore_per_share = kwargs.get('hpe3par_fstore_per_share')
self.hpe3par_require_cifs_ip = kwargs.get('hpe3par_require_cifs_ip')
self.hpe3par_share_ip_address = kwargs.get('hpe3par_share_ip_address')
self.hpe3par_cifs_admin_access_username = (
kwargs.get('hpe3par_cifs_admin_access_username'))
self.hpe3par_cifs_admin_access_password = (
@ -204,9 +204,9 @@ class HPE3ParMediator(object):
# don't raise exception on logout()
@staticmethod
def build_export_location(protocol, ip, path):
def build_export_locations(protocol, ips, path):
if not ip:
if not ips:
message = _('Failed to build export location due to missing IP.')
raise exception.InvalidInput(reason=message)
@ -216,11 +216,9 @@ class HPE3ParMediator(object):
share_proto = HPE3ParMediator.ensure_supported_protocol(protocol)
if share_proto == 'nfs':
location = ':'.join((ip, path))
return ['%s:%s' % (ip, path) for ip in ips]
else:
location = r'\\%s\%s' % (ip, path)
return location
return [r'\\%s\%s' % (ip, path) for ip in ips]
def get_provisioned_gb(self, fpg):
total_mb = 0
@ -288,6 +286,7 @@ class HPE3ParMediator(object):
hpe3par_flash_cache = flash_cache_policy == ENABLED
status = {
'pool_name': fpg,
'total_capacity_gb': total_capacity_gb,
'free_capacity_gb': free_capacity_gb,
'thin_provisioning': thin_provisioning,
@ -310,7 +309,7 @@ class HPE3ParMediator(object):
message = (_('Invalid protocol. Expected nfs or smb. Got %s.') %
protocol)
LOG.error(message)
raise exception.InvalidInput(message)
raise exception.InvalidShareAccess(reason=message)
return protocol
@staticmethod
@ -523,7 +522,7 @@ class HPE3ParMediator(object):
msg = (_('Failed to create fstore %(fstore)s: %(e)s') %
{'fstore': fstore, 'e': six.text_type(e)})
LOG.exception(msg)
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
if size:
self._update_capacity_quotas(fstore, size, 0, fpg, vfs)
@ -545,7 +544,7 @@ class HPE3ParMediator(object):
msg = (_('Failed to create share %(share_name)s: %(e)s') %
{'share_name': share_name, 'e': six.text_type(e)})
LOG.exception(msg)
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
try:
result = self._client.getfshare(
@ -558,14 +557,14 @@ class HPE3ParMediator(object):
'%(e)s') % {'share_name': share_name,
'e': six.text_type(e)})
LOG.exception(msg)
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
if result['total'] != 1:
msg = (_('Failed to get fshare %(share_name)s after creating it. '
'Expected to get 1 fshare. Got %(total)s.') %
{'share_name': share_name, 'total': result['total']})
LOG.error(msg)
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
return result['members'][0]
def create_share(self, project_id, share_id, share_proto, extra_specs,
@ -616,7 +615,7 @@ class HPE3ParMediator(object):
def create_share_from_snapshot(self, share_id, share_proto, extra_specs,
orig_project_id, orig_share_id,
snapshot_id, fpg, vfs,
snapshot_id, fpg, vfs, ips,
size=None,
comment=OPEN_STACK_MANILA):
@ -710,14 +709,13 @@ class HPE3ParMediator(object):
self._grant_admin_smb_access(
protocol, fpg, vfs, fstore, temp, share=ro_share_name)
ip = self.hpe3par_share_ip_address
source_location = self.build_export_location(
protocol, ip, source_path)
dest_location = self.build_export_location(
protocol, ip, dest_path)
source_locations = self.build_export_locations(
protocol, ips, source_path)
dest_locations = self.build_export_locations(
protocol, ips, dest_path)
self._copy_share_data(
share_id, source_location, dest_location, protocol)
share_id, source_locations[0], dest_locations[0], protocol)
# Revoke the admin access that was needed to copy to the dest.
if protocol == 'nfs':
@ -821,7 +819,7 @@ class HPE3ParMediator(object):
return fstore
def delete_share(self, project_id, share_id, share_size, share_proto,
fpg, vfs):
fpg, vfs, share_ip):
protocol = self.ensure_supported_protocol(share_proto)
share_name = self.ensure_prefix(share_id)
@ -857,7 +855,7 @@ class HPE3ParMediator(object):
# reason, we will not treat this as an error_deleting
# issue. We will allow the delete to continue as requested.
self._delete_file_tree(
share_name, protocol, fpg, vfs, fstore)
share_name, protocol, fpg, vfs, fstore, share_ip)
# reduce the fsquota by share size when a tree is deleted.
self._update_capacity_quotas(
fstore, 0, share_size, fpg, vfs)
@ -871,7 +869,8 @@ class HPE3ParMediator(object):
}
LOG.warning(msg, data)
def _delete_file_tree(self, share_name, protocol, fpg, vfs, fstore):
def _delete_file_tree(self, share_name, protocol, fpg, vfs, fstore,
share_ip):
# If the share protocol is CIFS, we need to make sure the admin
# provided the proper config values. If they have not, we can simply
# return out and log a warning.
@ -893,7 +892,8 @@ class HPE3ParMediator(object):
self._create_mount_directory(mount_location)
# Mount the super share.
self._mount_super_share(protocol, mount_location, fpg, vfs, fstore)
self._mount_super_share(protocol, mount_location, fpg, vfs, fstore,
share_ip)
# Delete the share from the super share.
self._delete_share_directory(share_dir)
@ -995,10 +995,11 @@ class HPE3ParMediator(object):
'-o', cred)
utils.execute(*cmd, run_as_root=True)
def _mount_super_share(self, protocol, mount_dir, fpg, vfs, fstore):
def _mount_super_share(self, protocol, mount_dir, fpg, vfs, fstore,
share_ip):
try:
mount_location = self._generate_mount_path(
protocol, fpg, vfs, fstore)
protocol, fpg, vfs, fstore, share_ip)
self._mount_share(protocol, mount_location, mount_dir)
except Exception as err:
message = (_LW("There was an error mounting the super share: "
@ -1027,17 +1028,17 @@ class HPE3ParMediator(object):
six.text_type(err))
LOG.warning(message)
def _generate_mount_path(self, protocol, fpg, vfs, fstore):
def _generate_mount_path(self, protocol, fpg, vfs, fstore, share_ip):
path = None
if protocol == 'nfs':
path = (("%(share_ip)s:/%(fpg)s/%(vfs)s/%(fstore)s/") %
{'share_ip': self.hpe3par_share_ip_address,
{'share_ip': share_ip,
'fpg': fpg,
'vfs': vfs,
'fstore': fstore})
else:
path = (("//%(share_ip)s/%(share_name)s/") %
{'share_ip': self.hpe3par_share_ip_address,
{'share_ip': share_ip,
'share_name': SUPER_SHARE})
return path
@ -1053,7 +1054,7 @@ class HPE3ParMediator(object):
msg = (_('Exception during getvfs %(vfs)s: %(e)s') %
{'vfs': vfs, 'e': six.text_type(e)})
LOG.exception(msg)
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
if result['total'] != 1:
error_msg = result.get('message')
@ -1062,7 +1063,7 @@ class HPE3ParMediator(object):
'(%(fpg)s/%(vfs)s): %(msg)s') %
{'fpg': fpg, 'vfs': vfs, 'msg': error_msg})
LOG.error(message)
raise exception.ShareBackendException(message)
raise exception.ShareBackendException(msg=message)
else:
message = (_('Error while validating FPG/VFS '
'(%(fpg)s/%(vfs)s): Expected 1, '
@ -1071,7 +1072,7 @@ class HPE3ParMediator(object):
'total': result['total']})
LOG.error(message)
raise exception.ShareBackendException(message)
raise exception.ShareBackendException(msg=message)
return result['members'][0]
@ -1151,7 +1152,7 @@ class HPE3ParMediator(object):
'Cannot delete snapshot without checking for '
'dependent shares first: %s') % six.text_type(e))
LOG.exception(msg)
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
for share in shares['members']:
if protocol == 'nfs':
@ -1189,7 +1190,7 @@ class HPE3ParMediator(object):
'snapname': snapname,
'e': six.text_type(e)})
LOG.exception(msg)
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
# Try to reclaim the space
try:
@ -1207,13 +1208,13 @@ class HPE3ParMediator(object):
msg = (_("Invalid access type. Expected 'ip' or 'user'. "
"Actual '%s'.") % access_type)
LOG.error(msg)
raise exception.InvalidInput(msg)
raise exception.InvalidInput(reason=msg)
if protocol == 'nfs' and access_type != 'ip':
msg = (_("Invalid NFS access type. HPE 3PAR NFS supports 'ip'. "
"Actual '%s'.") % access_type)
LOG.error(msg)
raise exception.HPE3ParInvalid(msg)
raise exception.HPE3ParInvalid(err=msg)
return protocol
@ -1496,7 +1497,7 @@ class HPE3ParMediator(object):
except Exception as e:
msg = (_('Unexpected exception while getting snapshots: %s') %
six.text_type(e))
raise exception.ShareBackendException(msg)
raise exception.ShareBackendException(msg=msg)
def update_access(self, project_id, share_id, share_proto, extra_specs,
access_rules, add_rules, delete_rules, fpg, vfs):

View File

@ -479,19 +479,27 @@ class ShareManager(manager.SchedulerDependentManager):
with_share_data=True
)
if create_on_backend:
metadata = {'request_host': share_instance['host']}
compatible_share_server = (
self._create_share_server_in_backend(
context, compatible_share_server))
context, compatible_share_server,
metadata=metadata))
return compatible_share_server, share_instance_ref
return _provide_share_server_for_share()
def _create_share_server_in_backend(self, context, share_server):
def _create_share_server_in_backend(self, context, share_server,
metadata=None):
"""Perform setup_server on backend
:param metadata: A dictionary, to be passed to driver's setup_server()
"""
if share_server['status'] == constants.STATUS_CREATING:
# Create share server on backend with data from db.
share_server = self._setup_server(context, share_server)
share_server = self._setup_server(context, share_server,
metadata=metadata)
LOG.info(_LI("Share server created successfully."))
else:
LOG.info(_LI("Using preexisting share server: "

View File

@ -40,6 +40,7 @@ EXPECTED_IP_127_2 = '127.0.0.2'
EXPECTED_ACCESS_LEVEL = 'foo_access'
EXPECTED_SUBNET = '255.255.255.0' # based on CIDR_PREFIX above
EXPECTED_VLAN_TYPE = 'vlan'
EXPECTED_VXLAN_TYPE = 'vxlan'
EXPECTED_VLAN_TAG = '101'
EXPECTED_SERVER_ID = '1a1a1a1a-2b2b-3c3c-4d4d-5e5e5e5e5e5e'
EXPECTED_PROJECT_ID = 'osf-nfs-project-id'
@ -47,16 +48,25 @@ SHARE_ID = 'share-id'
EXPECTED_SHARE_ID = 'osf-share-id'
EXPECTED_SHARE_ID_RO = 'osf-ro-share-id'
EXPECTED_SHARE_NAME = 'share-name'
EXPECTED_HOST = 'hostname@backend#pool'
EXPECTED_FPG = 'pool'
EXPECTED_HOST = 'hostname@backend#' + EXPECTED_FPG
UNEXPECTED_FPG = 'not_a_pool'
UNEXPECTED_HOST = 'hostname@backend#' + UNEXPECTED_FPG
HOST_WITHOUT_POOL_1 = 'hostname@backend'
HOST_WITHOUT_POOL_2 = 'hostname@backend#'
EXPECTED_SHARE_PATH = '/anyfpg/anyvfs/anyfstore'
EXPECTED_SIZE_1 = 1
EXPECTED_SIZE_2 = 2
EXPECTED_SNAP_NAME = 'osf-snap-name'
EXPECTED_SNAP_ID = 'osf-snap-id'
EXPECTED_STATS = {'test': 'stats'}
EXPECTED_FPG = 'FPG_1'
EXPECTED_FPG_CONF = [{EXPECTED_FPG: [EXPECTED_IP_10203040]}]
EXPECTED_FSTORE = EXPECTED_PROJECT_ID
EXPECTED_VFS = 'test_vfs'
EXPECTED_GET_VFS = {'vfsname': EXPECTED_VFS,
'vfsip': {'address': EXPECTED_IP_10203040}}
EXPECTED_FPG_MAP = {EXPECTED_FPG: {EXPECTED_VFS: [EXPECTED_IP_10203040]}}
EXPECTED_SHARE_IP = '10.50.3.8'
EXPECTED_HPE_DEBUG = True
EXPECTED_COMMENT = "OpenStack Manila - foo-comment"
EXPECTED_EXTRA_SPECS = {}
@ -67,6 +77,14 @@ EXPECTED_SUPER_SHARE_COMMENT = ('OpenStack super share used to delete nested '
EXPECTED_CIFS_DOMAIN = 'LOCAL_CLUSTER'
EXPECTED_MOUNT_PATH = '/mnt/'
SHARE_SERVER = {
'backend_details': {
'ip': EXPECTED_IP_10203040,
'fpg': EXPECTED_FPG,
'vfs': EXPECTED_VFS,
},
}
# Access rules. Allow for overwrites.
ACCESS_RULE_NFS = {
'access_type': IP,
@ -148,12 +166,7 @@ NFS_SHARE_INFO = {
'share_proto': NFS,
'export_location': EXPECTED_LOCATION,
'size': 1234,
}
ACCESS_INFO = {
'access_type': IP,
'access_to': EXPECTED_IP_1234,
'access_level': READ_WRITE,
'host': EXPECTED_HOST,
}
SNAPSHOT_INFO = {
@ -164,6 +177,7 @@ SNAPSHOT_INFO = {
'id': EXPECTED_SHARE_ID,
'share_proto': NFS,
'export_location': EXPECTED_LOCATION,
'host': EXPECTED_HOST,
},
}

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from copy import deepcopy
import sys
import ddt
@ -26,6 +27,43 @@ from manila import test
from manila.tests.share.drivers.hpe import test_hpe_3par_constants as constants
@ddt.ddt
class HPE3ParDriverFPGTestCase(test.TestCase):
def setUp(self):
super(HPE3ParDriverFPGTestCase, self).setUp()
@ddt.data((-1, 4),
(0, 5),
(0, -1))
@ddt.unpack
def test_FPG_init_args_failure(self, min_ip, max_ip):
self.assertRaises(exception.HPE3ParInvalid,
hpe3pardriver.FPG, min_ip, max_ip)
@ddt.data(('invalid_ip_fpg, 10.256.0.1', 0, 4),
(None, 0, 4),
(' ', 0, 4),
('', 0, 4),
('max_ip_fpg, 10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.4, 10.0.0.5',
0, 4),
('min_1_ip_fpg', 1, 4))
@ddt.unpack
def test_FPG_type_failures(self, value, min_ip, max_ip):
fpg_type_obj = hpe3pardriver.FPG(min_ip=min_ip, max_ip=max_ip)
self.assertRaises(exception.HPE3ParInvalid, fpg_type_obj, value)
@ddt.data(('samplefpg, 10.0.0.1', {'samplefpg': ['10.0.0.1']}),
('samplefpg', {'samplefpg': []}),
('samplefpg, 10.0.0.1, 10.0.0.2',
{'samplefpg': ['10.0.0.1', '10.0.0.2']}))
@ddt.unpack
def test_FPG_type_success(self, value, expected_fpg):
fpg_type_obj = hpe3pardriver.FPG()
fpg = fpg_type_obj(value)
self.assertEqual(expected_fpg, fpg)
@ddt.ddt
class HPE3ParDriverTestCase(test.TestCase):
@ -42,13 +80,11 @@ class HPE3ParDriverTestCase(test.TestCase):
self.conf.hpe3par_san_login = constants.SAN_LOGIN
self.conf.hpe3par_san_password = constants.SAN_PASSWORD
self.conf.hpe3par_san_ip = constants.EXPECTED_IP_1234
self.conf.hpe3par_fpg = constants.EXPECTED_FPG
self.conf.hpe3par_fpg = constants.EXPECTED_FPG_CONF
self.conf.hpe3par_san_ssh_port = constants.PORT
self.conf.ssh_conn_timeout = constants.TIMEOUT
self.conf.hpe3par_share_ip_address = None
self.conf.hpe3par_fstore_per_share = False
self.conf.hpe3par_require_cifs_ip = False
self.conf.hpe3par_share_ip_address = constants.EXPECTED_IP_10203040
self.conf.hpe3par_cifs_admin_access_username = constants.USERNAME,
self.conf.hpe3par_cifs_admin_access_password = constants.PASSWORD,
self.conf.hpe3par_cifs_admin_access_domain = (
@ -75,8 +111,8 @@ class HPE3ParDriverTestCase(test.TestCase):
# restore needed static methods
self.mock_mediator.ensure_supported_protocol = (
self.real_hpe_3par_mediator.ensure_supported_protocol)
self.mock_mediator.build_export_location = (
self.real_hpe_3par_mediator.build_export_location)
self.mock_mediator.build_export_locations = (
self.real_hpe_3par_mediator.build_export_locations)
self.driver = hpe3pardriver.HPE3ParShareDriver(
configuration=self.conf)
@ -84,7 +120,7 @@ class HPE3ParDriverTestCase(test.TestCase):
def test_driver_setup_success(self):
"""Driver do_setup without any errors."""
self.mock_mediator.get_vfs_name.return_value = constants.EXPECTED_VFS
self.mock_mediator.get_vfs.return_value = constants.EXPECTED_GET_VFS
self.driver.do_setup(None)
conf = self.conf
@ -99,8 +135,6 @@ class HPE3ParDriverTestCase(test.TestCase):
hpe3par_san_ip=conf.hpe3par_san_ip,
hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
hpe3par_share_ip_address=(
self.conf.hpe3par_share_ip_address),
hpe3par_cifs_admin_access_username=(
conf.hpe3par_cifs_admin_access_username),
hpe3par_cifs_admin_access_password=(
@ -113,27 +147,55 @@ class HPE3ParDriverTestCase(test.TestCase):
self.mock_mediator.assert_has_calls([
mock.call.do_setup(),
mock.call.get_vfs_name(conf.hpe3par_fpg)])
mock.call.get_vfs(constants.EXPECTED_FPG)])
self.assertEqual(constants.EXPECTED_VFS, self.driver.vfs)
def test_driver_setup_dhss_success(self):
"""Driver do_setup without any errors with dhss=True."""
self.test_driver_setup_success()
self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
def test_driver_setup_no_dhss_success(self):
"""Driver do_setup without any errors with dhss=False."""
self.conf.driver_handles_share_servers = False
self.conf.hpe3par_share_ip_address = constants.EXPECTED_IP_10203040
self.test_driver_setup_success()
self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
def test_driver_setup_success_no_dhss_no_conf_ss_ip(self):
"""test driver's do_setup()
Driver do_setup with dhss=False, share server ip not set in config file
but discoverable at 3par array
"""
self.conf.driver_handles_share_servers = False
# ss ip not provided in conf
original_fpg = deepcopy(self.conf.hpe3par_fpg)
self.conf.hpe3par_fpg[0][constants.EXPECTED_FPG] = []
self.test_driver_setup_success()
def test_driver_setup_no_ss_no_ip(self):
self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
self.conf.hpe3par_fpg = original_fpg
def test_driver_setup_failure_no_dhss_no_conf_ss_ip(self):
"""Configured IP address is required for dhss=False."""
self.conf.driver_handles_share_servers = False
self.conf.hpe3par_share_ip_address = None
# ss ip not provided in conf
fpg_without_ss_ip = deepcopy(self.conf.hpe3par_fpg)
self.conf.hpe3par_fpg[0][constants.EXPECTED_FPG] = []
# ss ip not configured on array
vfs_without_ss_ip = deepcopy(constants.EXPECTED_GET_VFS)
vfs_without_ss_ip['vfsip']['address'] = []
self.mock_mediator.get_vfs.return_value = vfs_without_ss_ip
self.assertRaises(exception.HPE3ParInvalid,
self.driver.do_setup, None)
self.conf.hpe3par_fpg = fpg_without_ss_ip
def test_driver_with_setup_error(self):
def test_driver_setup_mediator_error(self):
"""Driver do_setup when the mediator setup fails."""
self.mock_mediator.do_setup.side_effect = (
@ -154,8 +216,6 @@ class HPE3ParDriverTestCase(test.TestCase):
hpe3par_san_ip=conf.hpe3par_san_ip,
hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
hpe3par_share_ip_address=(
self.conf.hpe3par_share_ip_address),
hpe3par_cifs_admin_access_username=(
conf.hpe3par_cifs_admin_access_username),
hpe3par_cifs_admin_access_password=(
@ -168,10 +228,10 @@ class HPE3ParDriverTestCase(test.TestCase):
self.mock_mediator.assert_has_calls([mock.call.do_setup()])
def test_driver_with_vfs_error(self):
"""Driver do_setup when the get_vfs_name fails."""
def test_driver_setup_with_vfs_error(self):
"""Driver do_setup when the get_vfs fails."""
self.mock_mediator.get_vfs_name.side_effect = (
self.mock_mediator.get_vfs.side_effect = (
exception.ShareBackendException('fail'))
self.assertRaises(exception.ShareBackendException,
@ -189,8 +249,6 @@ class HPE3ParDriverTestCase(test.TestCase):
hpe3par_san_ip=conf.hpe3par_san_ip,
hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
hpe3par_share_ip_address=(
self.conf.hpe3par_share_ip_address),
hpe3par_cifs_admin_access_username=(
conf.hpe3par_cifs_admin_access_username),
hpe3par_cifs_admin_access_password=(
@ -203,64 +261,53 @@ class HPE3ParDriverTestCase(test.TestCase):
self.mock_mediator.assert_has_calls([
mock.call.do_setup(),
mock.call.get_vfs_name(conf.hpe3par_fpg)])
mock.call.get_vfs(constants.EXPECTED_FPG)])
def test_driver_setup_conf_ips_validation_fails(self):
"""Driver do_setup when the _validate_pool_ips fails."""
self.conf.driver_handles_share_servers = False
vfs_with_ss_ip = deepcopy(constants.EXPECTED_GET_VFS)
vfs_with_ss_ip['vfsip']['address'] = ['10.100.100.100']
self.mock_mediator.get_vfs.return_value = vfs_with_ss_ip
self.assertRaises(exception.HPE3ParInvalid,
self.driver.do_setup, None)
conf = self.conf
self.mock_mediator_constructor.assert_has_calls([
mock.call(hpe3par_san_ssh_port=conf.hpe3par_san_ssh_port,
hpe3par_san_password=conf.hpe3par_san_password,
hpe3par_username=conf.hpe3par_username,
hpe3par_san_login=conf.hpe3par_san_login,
hpe3par_debug=conf.hpe3par_debug,
hpe3par_api_url=conf.hpe3par_api_url,
hpe3par_password=conf.hpe3par_password,
hpe3par_san_ip=conf.hpe3par_san_ip,
hpe3par_fstore_per_share=conf.hpe3par_fstore_per_share,
hpe3par_require_cifs_ip=conf.hpe3par_require_cifs_ip,
hpe3par_cifs_admin_access_username=(
conf.hpe3par_cifs_admin_access_username),
hpe3par_cifs_admin_access_password=(
conf.hpe3par_cifs_admin_access_password),
hpe3par_cifs_admin_access_domain=(
conf.hpe3par_cifs_admin_access_domain),
hpe3par_share_mount_path=conf.hpe3par_share_mount_path,
my_ip=self.conf.my_ip,
ssh_conn_timeout=conf.ssh_conn_timeout)])
self.mock_mediator.assert_has_calls([
mock.call.do_setup(),
mock.call.get_vfs(constants.EXPECTED_FPG)])
def init_driver(self):
"""Simple driver setup for re-use with tests that need one."""
self.driver._hpe3par = self.mock_mediator
self.driver.vfs = constants.EXPECTED_VFS
self.driver.fpg = constants.EXPECTED_FPG
self.driver.fpgs = constants.EXPECTED_FPG_MAP
self.mock_object(hpe3pardriver, 'share_types')
get_extra_specs = hpe3pardriver.share_types.get_extra_specs_from_share
get_extra_specs.return_value = constants.EXPECTED_EXTRA_SPECS
def do_create_share(self, protocol, share_type_id, expected_project_id,
expected_share_id, expected_size):
"""Re-usable code for create share."""
context = None
share_server = {
'backend_details': {'ip': constants.EXPECTED_IP_10203040}}
share = {
'display_name': constants.EXPECTED_SHARE_NAME,
'host': constants.EXPECTED_HOST,
'project_id': expected_project_id,
'id': expected_share_id,
'share_proto': protocol,
'share_type_id': share_type_id,
'size': expected_size,
}
location = self.driver.create_share(context, share, share_server)
return location
def do_create_share_from_snapshot(self,
protocol,
share_type_id,
snapshot_instance,
expected_share_id,
expected_size):
"""Re-usable code for create share from snapshot."""
context = None
share_server = {
'backend_details': {
'ip': constants.EXPECTED_IP_10203040,
},
}
share = {
'project_id': constants.EXPECTED_PROJECT_ID,
'display_name': constants.EXPECTED_SHARE_NAME,
'host': constants.EXPECTED_HOST,
'id': expected_share_id,
'share_proto': protocol,
'share_type_id': share_type_id,
'size': expected_size,
}
location = self.driver.create_share_from_snapshot(context,
share,
snapshot_instance,
share_server)
return location
def test_driver_check_for_setup_error_success(self):
"""check_for_setup_error when things go well."""
@ -289,6 +336,98 @@ class HPE3ParDriverTestCase(test.TestCase):
]
hpe3pardriver.LOG.assert_has_calls(expected_calls)
@ddt.data(([constants.SHARE_SERVER], constants.SHARE_SERVER),
([], None),)
@ddt.unpack
def test_choose_share_server_compatible_with_share(self, share_servers,
expected_share_sever):
context = None
share_server = self.driver.choose_share_server_compatible_with_share(
context,
share_servers,
constants.NFS_SHARE_INFO,
None,
None)
self.assertEqual(expected_share_sever, share_server)
def test_choose_share_server_compatible_with_share_with_cg(self):
context = None
cg_ref = {'id': 'dummy'}
self.assertRaises(
exception.InvalidRequest,
self.driver.choose_share_server_compatible_with_share,
context,
[constants.SHARE_SERVER],
constants.NFS_SHARE_INFO,
None,
cg_ref)
def do_create_share(self, protocol, share_type_id, expected_project_id,
expected_share_id, expected_size):
"""Re-usable code for create share."""
context = None
share = {
'display_name': constants.EXPECTED_SHARE_NAME,
'host': constants.EXPECTED_HOST,
'project_id': expected_project_id,
'id': expected_share_id,
'share_proto': protocol,
'share_type_id': share_type_id,
'size': expected_size,
}
location = self.driver.create_share(context, share,
constants.SHARE_SERVER)
return location
def do_create_share_from_snapshot(self,
protocol,
share_type_id,
snapshot_instance,
expected_share_id,
expected_size):
"""Re-usable code for create share from snapshot."""
context = None
share = {
'project_id': constants.EXPECTED_PROJECT_ID,
'display_name': constants.EXPECTED_SHARE_NAME,
'host': constants.EXPECTED_HOST,
'id': expected_share_id,
'share_proto': protocol,
'share_type_id': share_type_id,
'size': expected_size,
}
location = self.driver.create_share_from_snapshot(
context,
share,
snapshot_instance,
constants.SHARE_SERVER)
return location
@ddt.data((constants.UNEXPECTED_HOST, exception.InvalidHost),
(constants.HOST_WITHOUT_POOL_1, exception.InvalidHost),
(constants.HOST_WITHOUT_POOL_2, exception.InvalidHost))
@ddt.unpack
def test_driver_create_share_fails_get_pool_location(self, host,
expected_exception):
"""get_pool_location fails to extract pool name from host"""
self.init_driver()
context = None
share_server = None
share = {
'display_name': constants.EXPECTED_SHARE_NAME,
'host': host,
'project_id': constants.EXPECTED_PROJECT_ID,
'id': constants.EXPECTED_SHARE_ID,
'share_proto': constants.CIFS,
'share_type_id': constants.SHARE_TYPE_ID,
'size': constants.EXPECTED_SIZE_2,
}
self.assertRaises(expected_exception,
self.driver.create_share,
context, share, share_server)
def test_driver_create_cifs_share(self):
self.init_driver()
@ -306,7 +445,7 @@ class HPE3ParDriverTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SIZE_2)
self.assertEqual(expected_location, location)
self.assertIn(expected_location, location)
expected_calls = [mock.call.create_share(
constants.EXPECTED_PROJECT_ID,
constants.EXPECTED_SHARE_ID,
@ -334,7 +473,7 @@ class HPE3ParDriverTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SIZE_1)
self.assertEqual(expected_location, location)
self.assertIn(expected_location, location)
expected_calls = [
mock.call.create_share(constants.EXPECTED_PROJECT_ID,
constants.EXPECTED_SHARE_ID,
@ -367,7 +506,7 @@ class HPE3ParDriverTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SIZE_2)
self.assertEqual(expected_location, location)
self.assertIn(expected_location, location)
expected_calls = [
mock.call.create_share_from_snapshot(
constants.EXPECTED_SHARE_ID,
@ -378,6 +517,7 @@ class HPE3ParDriverTestCase(test.TestCase):
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040],
comment=mock.ANY,
size=constants.EXPECTED_SIZE_2),
]
@ -400,7 +540,7 @@ class HPE3ParDriverTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SIZE_1)
self.assertEqual(expected_location, location)
self.assertIn(expected_location, location)
expected_calls = [
mock.call.create_share_from_snapshot(
constants.EXPECTED_SHARE_ID,
@ -411,6 +551,7 @@ class HPE3ParDriverTestCase(test.TestCase):
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040],
comment=mock.ANY,
size=constants.EXPECTED_SIZE_1),
]
@ -427,6 +568,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'id': constants.EXPECTED_SHARE_ID,
'share_proto': constants.CIFS,
'size': constants.EXPECTED_SIZE_1,
'host': constants.EXPECTED_HOST
}
self.driver.delete_share(context, share, share_server)
@ -437,7 +579,8 @@ class HPE3ParDriverTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)]
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)]
self.mock_mediator.assert_has_calls(expected_calls)
@ -488,7 +631,7 @@ class HPE3ParDriverTestCase(test.TestCase):
[constants.ACCESS_RULE_NFS],
[constants.ADD_RULE_IP],
[],
constants.ACCESS_INFO)
constants.SHARE_SERVER)
expected_calls = [
mock.call.update_access(constants.EXPECTED_PROJECT_ID,
@ -513,7 +656,7 @@ class HPE3ParDriverTestCase(test.TestCase):
[constants.ACCESS_RULE_NFS],
[],
[constants.DELETE_RULE_IP],
constants.ACCESS_INFO)
constants.SHARE_SERVER)
expected_calls = [
mock.call.update_access(constants.EXPECTED_PROJECT_ID,
@ -534,7 +677,9 @@ class HPE3ParDriverTestCase(test.TestCase):
old_size = constants.NFS_SHARE_INFO['size']
new_size = old_size * 2
self.driver.extend_share(constants.NFS_SHARE_INFO, new_size)
share_server = None
self.driver.extend_share(constants.NFS_SHARE_INFO,
new_size, share_server)
self.mock_mediator.resize_share.assert_called_once_with(
constants.EXPECTED_PROJECT_ID,
@ -550,8 +695,9 @@ class HPE3ParDriverTestCase(test.TestCase):
old_size = constants.NFS_SHARE_INFO['size']
new_size = old_size / 2
self.driver.shrink_share(constants.NFS_SHARE_INFO, new_size)
share_server = None
self.driver.shrink_share(constants.NFS_SHARE_INFO,
new_size, share_server)
self.mock_mediator.resize_share.assert_called_once_with(
constants.EXPECTED_PROJECT_ID,
@ -616,31 +762,40 @@ class HPE3ParDriverTestCase(test.TestCase):
expected_version = self.driver.VERSION
self.mock_mediator.get_fpg_status.return_value = {
'free_capacity_gb': expected_free,
'pool_name': constants.EXPECTED_FPG,
'total_capacity_gb': expected_capacity,
'free_capacity_gb': expected_free,
'thin_provisioning': True,
'dedupe': False,
'hpe3par_flash_cache': False,
'hp3par_flash_cache': False,
'reserved_percentage': 0,
'provisioned_capacity_gb': expected_capacity
}
expected_result = {
'driver_handles_share_servers': True,
'qos': False,
'share_backend_name': 'HPE_3PAR',
'vendor_name': 'HPE',
'driver_version': expected_version,
'free_capacity_gb': expected_free,
'max_over_subscription_ratio': None,
'pools': None,
'storage_protocol': 'NFS_CIFS',
'driver_handles_share_servers': True,
'total_capacity_gb': 0,
'free_capacity_gb': 0,
'provisioned_capacity_gb': 0,
'reserved_percentage': 0,
'share_backend_name': 'HPE_3PAR',
'storage_protocol': 'NFS_CIFS',
'total_capacity_gb': expected_capacity,
'vendor_name': 'HPE',
'max_over_subscription_ratio': None,
'qos': False,
'thin_provisioning': True,
'dedupe': False,
'hpe3par_flash_cache': False,
'hp3par_flash_cache': False,
'pools': [{
'pool_name': constants.EXPECTED_FPG,
'total_capacity_gb': expected_capacity,
'free_capacity_gb': expected_free,
'thin_provisioning': True,
'dedupe': False,
'hpe3par_flash_cache': False,
'hp3par_flash_cache': False,
'reserved_percentage': 0,
'provisioned_capacity_gb': expected_capacity}],
'snapshot_support': True,
'replication_domain': None,
'filter_function': None,
@ -745,8 +900,8 @@ class HPE3ParDriverTestCase(test.TestCase):
'fpg': constants.EXPECTED_FPG,
'vfs': constants.EXPECTED_VFS,
}
result = self.driver._setup_server(network_info)
metadata = {'request_host': constants.EXPECTED_HOST}
result = self.driver._setup_server(network_info, metadata)
expected_calls = [
mock.call.create_fsip(constants.EXPECTED_IP_1234,
@ -759,12 +914,60 @@ class HPE3ParDriverTestCase(test.TestCase):
self.assertEqual(expected_result, result)
def test_setup_server_fails_for_unsupported_network_type(self):
"""Setup server fails for unsupported network type"""
self.init_driver()
network_info = {
'network_allocations': [
{'ip_address': constants.EXPECTED_IP_1234}],
'cidr': '/'.join((constants.EXPECTED_IP_1234,
constants.CIDR_PREFIX)),
'network_type': constants.EXPECTED_VXLAN_TYPE,
'segmentation_id': constants.EXPECTED_VLAN_TAG,
'server_id': constants.EXPECTED_SERVER_ID,
}
metadata = {'request_host': constants.EXPECTED_HOST}
self.assertRaises(exception.NetworkBadConfigurationException,
self.driver._setup_server,
network_info, metadata)
def test_setup_server_fails_for_exceed_pool_max_supported_ips(self):
"""Setup server fails when the VFS has reached max supported IPs"""
self.init_driver()
network_info = {
'network_allocations': [
{'ip_address': constants.EXPECTED_IP_1234}],
'cidr': '/'.join((constants.EXPECTED_IP_1234,
constants.CIDR_PREFIX)),
'network_type': constants.EXPECTED_VLAN_TYPE,
'segmentation_id': constants.EXPECTED_VLAN_TAG,
'server_id': constants.EXPECTED_SERVER_ID,
}
metadata = {'request_host': constants.EXPECTED_HOST}
expected_vfs = self.driver.fpgs[
constants.EXPECTED_FPG][constants.EXPECTED_VFS]
self.driver.fpgs[constants.EXPECTED_FPG][constants.EXPECTED_VFS] = [
'10.0.0.1', '10.0.0.2', '10.0.0.3', '10.0.0.4']
self.assertRaises(exception.Invalid,
self.driver._setup_server,
network_info, metadata)
self.driver.fpgs[constants.EXPECTED_FPG][constants.EXPECTED_VFS
] = expected_vfs
def test_teardown_server(self):
"""Test tear down server"""
self.init_driver()
server_details = {
'ip': constants.EXPECTED_IP_1234,
'ip': constants.EXPECTED_IP_10203040,
'fpg': constants.EXPECTED_FPG,
'vfs': constants.EXPECTED_VFS,
}
@ -772,7 +975,7 @@ class HPE3ParDriverTestCase(test.TestCase):
self.driver._teardown_server(server_details)
expected_calls = [
mock.call.remove_fsip(constants.EXPECTED_IP_1234,
mock.call.remove_fsip(constants.EXPECTED_IP_10203040,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
]

View File

@ -79,7 +79,6 @@ class HPE3ParMediatorTestCase(test.TestCase):
hpe3par_san_login=constants.SAN_LOGIN,
hpe3par_san_password=constants.SAN_PASSWORD,
hpe3par_san_ssh_port=constants.PORT,
hpe3par_share_ip_address=constants.EXPECTED_IP_10203040,
hpe3par_cifs_admin_access_username=constants.USERNAME,
hpe3par_cifs_admin_access_password=constants.PASSWORD,
hpe3par_cifs_admin_access_domain=constants.EXPECTED_CIFS_DOMAIN,
@ -506,7 +505,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040])
self.assertEqual(constants.EXPECTED_SHARE_ID, location)
@ -603,6 +603,7 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040],
comment=constants.EXPECTED_COMMENT)
self.assertEqual(constants.EXPECTED_SHARE_ID, location)
@ -642,7 +643,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040])
self.assertEqual(constants.EXPECTED_SHARE_PATH, location)
@ -730,7 +732,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040])
self.assertTrue(mock_bad_copy.run.called)
self.assertTrue(mock_bad_copy.get_progress.called)
@ -758,7 +761,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040])
self.assertTrue(mock_bad_copy.run.called)
def test_mediator_create_share_from_snap_not_found(self):
@ -779,7 +783,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SHARE_ID,
constants.EXPECTED_SNAP_ID,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
[constants.EXPECTED_IP_10203040])
def test_mediator_delete_nfs_share(self):
self.init_mediator()
@ -800,7 +805,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.NFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_SHARE_IP)
expected_calls = [
mock.call.removefshare(constants.NFS_LOWER,
@ -836,7 +842,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
self.assertFalse(self.mock_client.removefshare.called)
@ -858,7 +865,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.NFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
self.mock_client.removefshare.assert_called_once_with(
constants.NFS_LOWER,
@ -883,7 +891,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
expected_calls = [
mock.call.removefshare(constants.SMB_LOWER,
@ -912,7 +921,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
expected_calls = [
mock.call.removefshare(constants.SMB_LOWER,
@ -950,7 +960,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
expected_calls = [
mock.call.removefshare(constants.SMB_LOWER,
@ -997,7 +1008,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
expected_calls = [
mock.call.removefshare(constants.SMB_LOWER,
@ -1028,6 +1040,7 @@ class HPE3ParMediatorTestCase(test.TestCase):
expected_mount_path = constants.EXPECTED_MOUNT_PATH + (
constants.EXPECTED_SHARE_ID)
expected_share_path = '/'.join((expected_mount_path,
constants.EXPECTED_SHARE_ID))
self.mediator._create_mount_directory.assert_called_once_with(
@ -1037,7 +1050,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
expected_mount_path,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS,
constants.EXPECTED_FSTORE)
constants.EXPECTED_FSTORE,
constants.EXPECTED_IP_10203040)
self.mediator._delete_share_directory.assert_has_calls([
mock.call(expected_share_path),
mock.call(expected_mount_path),
@ -1065,7 +1079,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
expected_calls = [
mock.call.removefshare(constants.SMB_LOWER,
@ -1111,7 +1126,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_SIZE_1,
constants.CIFS,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS)
constants.EXPECTED_VFS,
constants.EXPECTED_IP_10203040)
expected_calls = [
mock.call.removefshare(constants.SMB_LOWER,
@ -1153,7 +1169,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
expected_mount_path)
self.mediator._mount_super_share.assert_called_with(
constants.SMB_LOWER, expected_mount_path, constants.EXPECTED_FPG,
constants.EXPECTED_VFS, constants.EXPECTED_FSTORE)
constants.EXPECTED_VFS, constants.EXPECTED_FSTORE,
constants.EXPECTED_IP_10203040)
self.mediator._delete_share_directory.assert_called_with(
expected_mount_path)
self.mediator._unmount_share.assert_called_with(
@ -1508,6 +1525,7 @@ class HPE3ParMediatorTestCase(test.TestCase):
'provisioningType': hpe3parmediator.DEDUPE}
expected_result = {
'pool_name': constants.EXPECTED_FPG,
'free_capacity_gb': expected_free,
'hpe3par_flash_cache': False,
'hp3par_flash_cache': False,
@ -2033,7 +2051,7 @@ class HPE3ParMediatorTestCase(test.TestCase):
""""Allow user access to unsupported protocol."""
self.init_mediator()
self.assertRaises(exception.InvalidInput,
self.assertRaises(exception.InvalidShareAccess,
self.mediator.update_access,
constants.EXPECTED_PROJECT_ID,
constants.EXPECTED_SHARE_ID,
@ -2349,7 +2367,7 @@ class HPE3ParMediatorTestCase(test.TestCase):
@ddt.data('', 'bogus')
def test_other_protocol_exception(self, protocol):
self.assertRaises(exception.InvalidInput,
self.assertRaises(exception.InvalidShareAccess,
hpe3parmediator.HPE3ParMediator().other_protocol,
protocol)
@ -2812,7 +2830,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
mount_path = '%s:/%s/%s/%s/' % (constants.EXPECTED_IP_10203040, fpg,
vfs, fstore)
self.mediator._mount_super_share(protocol, mount_location, fpg, vfs,
fstore)
fstore,
constants.EXPECTED_IP_10203040)
utils.execute.assert_called_with('mount', '-t', protocol, mount_path,
mount_location, run_as_root=True)
@ -2825,7 +2844,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.USERNAME, constants.PASSWORD,
constants.EXPECTED_CIFS_DOMAIN)
self.mediator._mount_super_share(protocol, mount_location, fpg, vfs,
fstore)
fstore,
constants.EXPECTED_IP_10203040)
utils.execute.assert_called_with('mount', '-t', 'cifs', mount_path,
mount_location, '-o', user,
@ -2844,7 +2864,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
vfs = 'bar-vfs'
fstore = 'fstore'
self.mediator._mount_super_share(protocol, mount_location, fpg, vfs,
fstore)
fstore,
constants.EXPECTED_IP_10203040)
# Warning is logged (no exception thrown).
self.assertTrue(mock_log.warning.called)
@ -2903,7 +2924,8 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.SMB_LOWER,
constants.EXPECTED_FPG,
constants.EXPECTED_VFS,
constants.EXPECTED_FSTORE)
constants.EXPECTED_FSTORE,
constants.EXPECTED_SHARE_IP)
# Warning is logged (no exception thrown).
self.assertTrue(mock_log.warning.called)
@ -2951,25 +2973,25 @@ class HPE3ParMediatorTestCase(test.TestCase):
constants.EXPECTED_FSTORE,
constants.EXPECTED_COMMENT)
def test_build_export_location_bad_protocol(self):
self.assertRaises(exception.InvalidInput,
self.mediator.build_export_location,
def test_build_export_locations_bad_protocol(self):
self.assertRaises(exception.InvalidShareAccess,
self.mediator.build_export_locations,
"BOGUS",
constants.EXPECTED_IP_1234,
[constants.EXPECTED_IP_1234],
constants.EXPECTED_SHARE_PATH)
def test_build_export_location_bad_ip(self):
def test_build_export_locations_bad_ip(self):
self.assertRaises(exception.InvalidInput,
self.mediator.build_export_location,
self.mediator.build_export_locations,
constants.NFS,
None,
None)
def test_build_export_location_bad_path(self):
def test_build_export_locations_bad_path(self):
self.assertRaises(exception.InvalidInput,
self.mediator.build_export_location,
self.mediator.build_export_locations,
constants.NFS,
constants.EXPECTED_IP_1234,
[constants.EXPECTED_IP_1234],
None)

View File

@ -1692,7 +1692,8 @@ class ShareManagerTestCase(test.TestCase):
)
])
self.share_manager._setup_server.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), fake_server)
utils.IsAMatcher(context.RequestContext), fake_server,
metadata={'request_host': 'fake_host'})
manager.LOG.error.assert_called_with(mock.ANY,
fake_share.instance['id'])
@ -1789,7 +1790,8 @@ class ShareManagerTestCase(test.TestCase):
db.share_server_create.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), mock.ANY)
self.share_manager._setup_server.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), fake_server)
utils.IsAMatcher(context.RequestContext), fake_server,
metadata={'request_host': 'fake_host'})
def test_create_share_instance_update_replica_state(self):
share_net = db_utils.create_share_network()
@ -1824,7 +1826,8 @@ class ShareManagerTestCase(test.TestCase):
db.share_server_create.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), mock.ANY)
self.share_manager._setup_server.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), fake_server)
utils.IsAMatcher(context.RequestContext), fake_server,
metadata={'request_host': 'fake_host'})
@ddt.data(True, False)
def test_create_delete_share_instance_error(self, exception_update_access):

View File

@ -0,0 +1,9 @@
---
features:
- HPE 3PAR driver now supports configuring multiple pools per backend.
upgrade:
- HPE 3PAR driver no longer uses hpe3par_share_ip_address option in
configuration. With pool support, configuration just requires
hpe3par_fpg option or optionally supply share IP address(es) along with
hpe3par_fpg.