From 0bbf54f84fca528d20974ce05138d554d2c3355d Mon Sep 17 00:00:00 2001 From: Jay Mehta Date: Mon, 13 Jun 2016 17:59:08 -0700 Subject: [PATCH] HPE 3PAR driver pool support 3PAR driver so far did not support multiple pools per backend. This patch aims to allow support for multiple pools per backend. The configuration now accepts multiple pool entries per backend. In manila.conf, hpe3par_fpg was used to represent one pool (FPG in terms of 3PAR) under a backend. Now, hpe3par_fpg can be used multiple times within a backend, each entry will represent a pool (FPG name). Sample configuration to supply pools 'testfpg' and 'sharenetfpg': hpe3par_fpg = sharenetfpg, 10.50.158.8 hpe3par_fpg = testfpg, 10.50.158.7 Note that the configuration now also accepts IP address of share server. 3PAR supports up to 4 IP addresses per share server. So the format to supply multiple share server IP addresses per pool: hpe3par_fpg = fpgname, IPaddress1, IPaddress2, IPaddress3, IPaddress4 If the share server is already setup and configured with IP address, it need not be passed with hpe3par_fpg. When DHSS=True, the format remains same as above, except the IP address of share server is silently ignored if provided. Obsoletes: hpe3par_share_ip_address from config Release note added. Change-Id: Ic08452f3ddf6b7b2f2dc1ba5ee32b4ebe5ae2614 Implements: blueprint hpe3par-pool-support --- manila/share/driver.py | 2 + manila/share/drivers/hpe/hpe_3par_driver.py | 356 ++++++++++++---- manila/share/drivers/hpe/hpe_3par_mediator.py | 75 ++-- manila/share/manager.py | 14 +- .../drivers/hpe/test_hpe_3par_constants.py | 30 +- .../share/drivers/hpe/test_hpe_3par_driver.py | 397 +++++++++++++----- .../drivers/hpe/test_hpe_3par_mediator.py | 86 ++-- manila/tests/share/test_manager.py | 9 +- .../3par-pool-support-fb43b368214c9eda.yaml | 9 + 9 files changed, 724 insertions(+), 254 deletions(-) create mode 100644 releasenotes/notes/3par-pool-support-fb43b368214c9eda.yaml diff --git a/manila/share/driver.py b/manila/share/driver.py index 314fd40e..bc3144d8 100644 --- a/manila/share/driver.py +++ b/manila/share/driver.py @@ -730,6 +730,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() diff --git a/manila/share/drivers/hpe/hpe_3par_driver.py b/manila/share/drivers/hpe/hpe_3par_driver.py index 3332f64d..a936ffa4 100644 --- a/manila/share/drivers/hpe/hpe_3par_driver.py +++ b/manila/share/drivers/hpe/hpe_3par_driver.py @@ -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) diff --git a/manila/share/drivers/hpe/hpe_3par_mediator.py b/manila/share/drivers/hpe/hpe_3par_mediator.py index 67f1f7bd..1d6ef33d 100644 --- a/manila/share/drivers/hpe/hpe_3par_mediator.py +++ b/manila/share/drivers/hpe/hpe_3par_mediator.py @@ -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): diff --git a/manila/share/manager.py b/manila/share/manager.py index 22cf9a56..0ce6e4ee 100644 --- a/manila/share/manager.py +++ b/manila/share/manager.py @@ -476,19 +476,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: " diff --git a/manila/tests/share/drivers/hpe/test_hpe_3par_constants.py b/manila/tests/share/drivers/hpe/test_hpe_3par_constants.py index 724a7187..638b8bbe 100644 --- a/manila/tests/share/drivers/hpe/test_hpe_3par_constants.py +++ b/manila/tests/share/drivers/hpe/test_hpe_3par_constants.py @@ -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, }, } diff --git a/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py b/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py index 00a04d79..690a20eb 100644 --- a/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py +++ b/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py @@ -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) ] diff --git a/manila/tests/share/drivers/hpe/test_hpe_3par_mediator.py b/manila/tests/share/drivers/hpe/test_hpe_3par_mediator.py index ff81bd9f..62c6b000 100644 --- a/manila/tests/share/drivers/hpe/test_hpe_3par_mediator.py +++ b/manila/tests/share/drivers/hpe/test_hpe_3par_mediator.py @@ -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) diff --git a/manila/tests/share/test_manager.py b/manila/tests/share/test_manager.py index 3a1cf591..79faa6d0 100644 --- a/manila/tests/share/test_manager.py +++ b/manila/tests/share/test_manager.py @@ -1714,7 +1714,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']) @@ -1811,7 +1812,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() @@ -1846,7 +1848,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): diff --git a/releasenotes/notes/3par-pool-support-fb43b368214c9eda.yaml b/releasenotes/notes/3par-pool-support-fb43b368214c9eda.yaml new file mode 100644 index 00000000..a4b06113 --- /dev/null +++ b/releasenotes/notes/3par-pool-support-fb43b368214c9eda.yaml @@ -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. \ No newline at end of file