diff --git a/manila/share/drivers/hp/hp_3par_driver.py b/manila/share/drivers/hp/hp_3par_driver.py index 0534709b53..b020f7e43b 100644 --- a/manila/share/drivers/hp/hp_3par_driver.py +++ b/manila/share/drivers/hp/hp_3par_driver.py @@ -59,6 +59,9 @@ HP3PAR_OPTS = [ cfg.StrOpt('hp3par_share_ip_address', default='', help="The IP address for shares not using a share server"), + cfg.BoolOpt('hp3par_fstore_per_share', + default=False, + help="Use one filestore per share"), cfg.BoolOpt('hp3par_debug', default=False, help="Enable HTTP debugging to 3PAR"), @@ -105,6 +108,7 @@ class HP3ParShareDriver(driver.ShareDriver): hp3par_san_login=self.configuration.hp3par_san_login, hp3par_san_password=self.configuration.hp3par_san_password, hp3par_san_ssh_port=self.configuration.hp3par_san_ssh_port, + hp3par_fstore_per_share=self.configuration.hp3par_fstore_per_share, ssh_conn_timeout=self.configuration.ssh_conn_timeout, ) @@ -170,6 +174,7 @@ class HP3ParShareDriver(driver.ShareDriver): protocol = share['share_proto'] path = self._hp3par.create_share( + share['project_id'], share['id'], protocol, self.fpg, self.vfs, @@ -188,7 +193,9 @@ class HP3ParShareDriver(driver.ShareDriver): path = self._hp3par.create_share_from_snapshot( share['id'], protocol, + snapshot['share']['project_id'], snapshot['share']['id'], + snapshot['share']['share_proto'], snapshot['id'], self.fpg, self.vfs @@ -199,7 +206,8 @@ class HP3ParShareDriver(driver.ShareDriver): def delete_share(self, context, share, share_server=None): """Deletes share and its fstore.""" - self._hp3par.delete_share(share['id'], + self._hp3par.delete_share(share['project_id'], + share['id'], share['share_proto'], self.fpg, self.vfs) @@ -207,7 +215,9 @@ class HP3ParShareDriver(driver.ShareDriver): def create_snapshot(self, context, snapshot, share_server=None): """Creates a snapshot of a share.""" - self._hp3par.create_snapshot(snapshot['share']['id'], + self._hp3par.create_snapshot(snapshot['share']['project_id'], + snapshot['share']['id'], + snapshot['share']['share_proto'], snapshot['id'], self.fpg, self.vfs) @@ -215,7 +225,9 @@ class HP3ParShareDriver(driver.ShareDriver): def delete_snapshot(self, context, snapshot, share_server=None): """Deletes a snapshot of a share.""" - self._hp3par.delete_snapshot(snapshot['share']['id'], + self._hp3par.delete_snapshot(snapshot['share']['project_id'], + snapshot['share']['id'], + snapshot['share']['share_proto'], snapshot['id'], self.fpg, self.vfs) @@ -225,7 +237,8 @@ class HP3ParShareDriver(driver.ShareDriver): def allow_access(self, context, share, access, share_server=None): """Allow access to the share.""" - self._hp3par.allow_access(share['id'], + self._hp3par.allow_access(share['project_id'], + share['id'], share['share_proto'], access['access_type'], access['access_to'], @@ -234,7 +247,8 @@ class HP3ParShareDriver(driver.ShareDriver): def deny_access(self, context, share, access, share_server=None): """Deny access to the share.""" - self._hp3par.deny_access(share['id'], + self._hp3par.deny_access(share['project_id'], + share['id'], share['share_proto'], access['access_type'], access['access_to'], diff --git a/manila/share/drivers/hp/hp_3par_mediator.py b/manila/share/drivers/hp/hp_3par_mediator.py index 81412f461f..f05c2c82d7 100644 --- a/manila/share/drivers/hp/hp_3par_mediator.py +++ b/manila/share/drivers/hp/hp_3par_mediator.py @@ -24,7 +24,6 @@ import six from manila import exception from manila.i18n import _ -from manila.i18n import _LI from manila.openstack.common import log as logging hp3parclient = importutils.try_import("hp3parclient") @@ -51,6 +50,7 @@ class HP3ParMediator(object): self.hp3par_san_password = kwargs.get('hp3par_san_password') self.hp3par_san_ssh_port = kwargs.get('hp3par_san_ssh_port') self.hp3par_san_private_key = kwargs.get('hp3par_san_private_key') + self.hp3par_fstore_per_share = kwargs.get('hp3par_fstore_per_share') self.ssh_conn_timeout = kwargs.get('ssh_conn_timeout') self._client = None @@ -132,13 +132,22 @@ class HP3ParMediator(object): return protocol @staticmethod - def ensure_prefix(id): - if id.startswith('osf-'): - return id - else: - return 'osf-%s' % id + def other_protocol(share_proto): + """Given 'nfs' or 'smb' (or equivalent) return the other one.""" + protocol = HP3ParMediator.ensure_supported_protocol(share_proto) + return 'nfs' if protocol == 'smb' else 'smb' - def create_share(self, share_id, share_proto, fpg, vfs, + @staticmethod + def ensure_prefix(uid, protocol=None): + if uid.startswith('osf-'): + return uid + elif protocol: + return 'osf-%s-%s' % ( + HP3ParMediator.ensure_supported_protocol(protocol), uid) + else: + return 'osf-%s' % uid + + def create_share(self, project_id, share_id, share_proto, fpg, vfs, fstore=None, sharedir=None, readonly=False, size=None): """Create the share and return its path. @@ -146,6 +155,7 @@ class HP3ParMediator(object): called locally from create_share_from_snapshot(). The optional parameters allow re-use. + :param project_id: The tenant ID. :param share_id: The share-id with or without osf- prefix. :param share_proto: The protocol (to map to smb or nfs) :param fpg: The file provisioning group @@ -162,7 +172,11 @@ class HP3ParMediator(object): share_name = self.ensure_prefix(share_id) if not fstore: - fstore = share_name + if self.hp3par_fstore_per_share: + fstore = share_name + else: + fstore = self.ensure_prefix(project_id, protocol) + try: result = self._client.createfstore( vfs, fstore, fpg=fpg, @@ -175,11 +189,26 @@ class HP3ParMediator(object): raise exception.ShareBackendException(msg) if size: + if self.hp3par_fstore_per_share: + hcapacity = six.text_type(size * units.Ki) + scapacity = hcapacity + else: + hard_size_mb = size * units.Ki + soft_size_mb = hard_size_mb + result = self._client.getfsquota( + fpg=fpg, vfs=vfs, fstore=fstore) + LOG.debug("getfsquota result=%s", result) + quotas = result['members'] + if len(quotas) == 1: + hard_size_mb += int(quotas[0].get('hardBlock', '0')) + soft_size_mb += int(quotas[0].get('softBlock', '0')) + hcapacity = six.text_type(hard_size_mb) + scapacity = six.text_type(soft_size_mb) + try: - size_str = six.text_type(size) result = self._client.setfsquota( vfs, fpg=fpg, fstore=fstore, - scapacity=size_str, hcapacity=size_str) + scapacity=scapacity, hcapacity=hcapacity) LOG.debug("setfsquota result=%s", result) except Exception as e: msg = (_('Failed to setfsquota on %(fstore)s: %(e)s') % @@ -187,6 +216,9 @@ class HP3ParMediator(object): LOG.exception(msg) raise exception.ShareBackendException(msg) + if not (sharedir or self.hp3par_fstore_per_share): + sharedir = share_name + try: if protocol == 'nfs': if readonly: @@ -239,30 +271,45 @@ class HP3ParMediator(object): else: return result['members'][0]['shareName'] - def create_share_from_snapshot(self, share_id, share_proto, orig_share_id, + def create_share_from_snapshot(self, share_id, share_proto, + orig_project_id, orig_share_id, orig_proto, snapshot_id, fpg, vfs): - share_name = self.ensure_prefix(share_id) - orig_share_name = self.ensure_prefix(orig_share_id) - fstore = orig_share_name + protocol = self.ensure_supported_protocol(share_proto) snapshot_tag = self.ensure_prefix(snapshot_id) - snapshots = self.get_snapshots(fstore, snapshot_tag, fpg, vfs) + orig_share_name = self.ensure_prefix(orig_share_id) - if len(snapshots) != 1: + snapshot = self._find_fsnap(orig_project_id, + orig_share_name, + orig_proto, + snapshot_tag, + fpg, + vfs) + + if not snapshot: msg = (_('Failed to create share from snapshot for ' - 'FPG/VFS/fstore/tag %(fpg)s/%(vfs)s/%(fstore)s/%(tag)s.' - ' Expected to find 1 snapshot, found %(count)s.') % - {'fpg': fpg, 'vfs': vfs, 'fstore': fstore, - 'tag': snapshot_tag, 'count': len(snapshots)}) + 'FPG/VFS/tag %(fpg)s/%(vfs)s/%(tag)s. ' + 'Snapshot not found.') % + { + 'fpg': fpg, + 'vfs': vfs, + 'tag': snapshot_tag}) LOG.exception(msg) raise exception.ShareBackendException(msg) - snapshot = snapshots[0] - sharedir = '.snapshot/%s' % snapshot['snapName'] + fstore = snapshot['fstoreName'] + share_name = self.ensure_prefix(share_id) + if fstore == orig_share_name: + # No subdir for original share created with fstore_per_share + sharedir = '.snapshot/%s' % snapshot['snapName'] + else: + sharedir = '.snapshot/%s/%s' % (snapshot['snapName'], + orig_share_name) return self.create_share( + orig_project_id, share_name, - share_proto, + protocol, fpg, vfs, fstore=fstore, @@ -270,11 +317,12 @@ class HP3ParMediator(object): readonly=True, ) - def delete_share(self, share_id, share_proto, fpg, vfs): + def delete_share(self, project_id, share_id, share_proto, fpg, vfs): - share_name = self.ensure_prefix(share_id) - fstore = self.get_fstore(share_id, share_proto, fpg, vfs) protocol = self.ensure_supported_protocol(share_proto) + share_name = self.ensure_prefix(share_id) + fstore = self._find_fstore(project_id, share_name, protocol, fpg, vfs, + allow_cross_protocol=True) if not fstore: # Share does not exist. @@ -289,13 +337,14 @@ class HP3ParMediator(object): LOG.exception(msg) raise exception.ShareBackendException(message=msg) - try: - self._client.removefstore(vfs, fstore, fpg=fpg) - except Exception as e: - msg = (_('Failed to remove fstore %(fstore)s: %(e)s') % - {'fstore': fstore, 'e': six.text_type(e)}) - LOG.exception(msg) - raise exception.ShareBackendException(message=msg) + if fstore == share_name: + try: + self._client.removefstore(vfs, fstore, fpg=fpg) + except Exception as e: + msg = (_('Failed to remove fstore %(fstore)s: %(e)s') % + {'fstore': fstore, 'e': six.text_type(e)}) + LOG.exception(msg) + raise exception.ShareBackendException(message=msg) def get_vfs_name(self, fpg): return self.get_vfs(fpg)['vfsname'] @@ -331,10 +380,33 @@ class HP3ParMediator(object): return result['members'][0] - def create_snapshot(self, orig_share_id, snapshot_id, fpg, vfs): + def create_snapshot(self, orig_project_id, orig_share_id, orig_share_proto, + snapshot_id, fpg, vfs): """Creates a snapshot of a share.""" - fstore = self.ensure_prefix(orig_share_id) + fshare = self._find_fshare(orig_project_id, + orig_share_id, + orig_share_proto, + fpg, + vfs) + + if not fshare: + msg = (_('Failed to create snapshot for FPG/VFS/fshare ' + '%(fpg)s/%(vfs)s/%(fshare)s: Failed to find fshare.') % + {'fpg': fpg, 'vfs': vfs, 'fshare': orig_share_id}) + LOG.exception(msg) + raise exception.ShareBackendException(msg) + + sharedir = fshare.get('shareDir') + if sharedir and sharedir.startswith('.snapshot'): + msg = (_('Failed to create snapshot for FPG/VFS/fshare ' + '%(fpg)s/%(vfs)s/%(fshare)s: Share is a read-only ' + 'share of an existing snapshot.') % + {'fpg': fpg, 'vfs': vfs, 'fshare': orig_share_id}) + LOG.exception(msg) + raise exception.ShareBackendException(msg) + + fstore = fshare.get('fstoreName') snapshot_tag = self.ensure_prefix(snapshot_id) try: result = self._client.createfsnap( @@ -350,41 +422,20 @@ class HP3ParMediator(object): LOG.exception(msg) raise exception.ShareBackendException(msg) - def get_snapshots(self, orig_share_id, snapshot_tag, fpg, vfs): - fstore = self.ensure_prefix(orig_share_id) - try: - pattern = '*_%s' % snapshot_tag - result = self._client.getfsnap( - pattern, fpg=fpg, vfs=vfs, fstore=fstore, pat=True) - - LOG.debug("getfsnap result=%s", result) - - except Exception as e: - msg = (_('Failed to get snapshot for FPG/VFS/fstore/tag ' - '%(fpg)s/%(vfs)s/%(fstore)s/%(tag)s: %(e)s') % - {'fpg': fpg, 'vfs': vfs, 'fstore': fstore, - 'tag': snapshot_tag, 'e': six.text_type(e)}) - LOG.exception(msg) - raise exception.ShareBackendException(msg) - - if result['total'] == 0: - LOG.info((_LI('Found zero snapshots for FPG/VFS/fstore/tag ' - '%(fpg)s/%(vfs)s/%(fstore)s/%(tag)s.') % - {'fpg': fpg, 'vfs': vfs, 'fstore': fstore, - 'tag': snapshot_tag})) - - return result['members'] - - def delete_snapshot(self, orig_share_id, snapshot_id, fpg, vfs): + def delete_snapshot(self, orig_project_id, orig_share_id, orig_proto, + snapshot_id, fpg, vfs): """Deletes a snapshot of a share.""" - fstore = self.ensure_prefix(orig_share_id) snapshot_tag = self.ensure_prefix(snapshot_id) - snapshots = self.get_snapshots(fstore, snapshot_tag, fpg, vfs) - if not snapshots: + snapshot = self._find_fsnap(orig_project_id, orig_share_id, orig_proto, + snapshot_tag, fpg, vfs) + + if not snapshot: return + fstore = snapshot.get('fstoreName') + for protocol in ('nfs', 'smb'): try: shares = self._client.getfshare(protocol, @@ -417,23 +468,24 @@ class HP3ParMediator(object): 'dependent share.')) raise exception.Invalid(msg) - # Tag should be unique enough to only return one, but this method - # doesn't really need to know that. So just loop. - for snapshot in snapshots: - try: - snapname = snapshot['snapName'] - result = self._client.removefsnap( - vfs, fstore, snapname=snapname, fpg=fpg) + snapname = snapshot['snapName'] + try: + result = self._client.removefsnap( + vfs, fstore, snapname=snapname, fpg=fpg) - LOG.debug("removefsnap result=%s", result) + LOG.debug("removefsnap result=%s", result) - except Exception as e: - msg = (_('Failed to delete snapshot for FPG/VFS/fstore ' - '%(fpg)s/%(vfs)s/%(fstore)s: %(e)s') % - {'fpg': fpg, 'vfs': vfs, 'fstore': fstore, - 'e': six.text_type(e)}) - LOG.exception(msg) - raise exception.ShareBackendException(msg) + except Exception as e: + msg = (_('Failed to delete snapshot for FPG/VFS/fstore/snapshot ' + '%(fpg)s/%(vfs)s/%(fstore)s/%(snapname)s: %(e)s') % + { + 'fpg': fpg, + 'vfs': vfs, + 'fstore': fstore, + 'snapname': snapname, + 'e': six.text_type(e)}) + LOG.exception(msg) + raise exception.ShareBackendException(msg) # Try to reclaim the space try: @@ -461,7 +513,7 @@ class HP3ParMediator(object): return protocol - def _change_access(self, plus_or_minus, fstore, share_id, share_proto, + def _change_access(self, plus_or_minus, project_id, share_id, share_proto, access_type, access_to, fpg, vfs): """Allow or deny access to a share. @@ -469,12 +521,13 @@ class HP3ParMediator(object): allow list (-). """ - share_name = self.ensure_prefix(share_id) - fstore = self.ensure_prefix(fstore) - protocol = self.ensure_supported_protocol(share_proto) self.validate_access_type(protocol, access_type) + share_name = self.ensure_prefix(share_id) + fstore = self._find_fstore(project_id, share_id, protocol, fpg, vfs, + allow_cross_protocol=True) + try: if protocol == 'nfs': result = self._client.setfshare( @@ -507,37 +560,80 @@ class HP3ParMediator(object): LOG.exception(msg) raise exception.ShareBackendException(msg) - def get_fstore(self, share_id, share_proto, fpg, vfs): + def _find_fstore(self, project_id, share_id, share_proto, fpg, vfs, + allow_cross_protocol=False): + + share = self._find_fshare(project_id, share_id, share_proto, fpg, vfs) + + if not share and allow_cross_protocol: + share = self._find_fshare(project_id, + share_id, + self.other_protocol(share_proto), + fpg, + vfs) + + return share.get('fstoreName') if share else None + + def _find_fshare(self, project_id, share_id, share_proto, fpg, vfs): protocol = self.ensure_supported_protocol(share_proto) share_name = self.ensure_prefix(share_id) + + project_fstore = self.ensure_prefix(project_id, share_proto) + search_order = [ + {'fpg': fpg, 'vfs': vfs, 'fstore': project_fstore}, + {'fpg': fpg, 'vfs': vfs, 'fstore': share_name}, + {'fpg': fpg}, + {} + ] + try: - shares = self._client.getfshare(protocol, - share_name, - fpg=fpg, - vfs=vfs) + for search_params in search_order: + result = self._client.getfshare(protocol, share_name, + **search_params) + shares = result.get('members', []) + if len(shares) == 1: + return shares[0] except Exception as e: msg = (_('Unexpected exception while getting share list: %s') % six.text_type(e)) raise exception.ShareBackendException(msg) - members = shares['members'] - if members: - return members[0].get('fstoreName') + def _find_fsnap(self, project_id, share_id, orig_proto, snapshot_tag, + fpg, vfs): - def allow_access(self, share_id, share_proto, access_type, access_to, - fpg, vfs): + share_name = self.ensure_prefix(share_id) + osf_project_id = self.ensure_prefix(project_id, orig_proto) + pattern = '*_%s' % self.ensure_prefix(snapshot_tag) + + search_order = [ + {'pat': True, 'fpg': fpg, 'vfs': vfs, 'fstore': osf_project_id}, + {'pat': True, 'fpg': fpg, 'vfs': vfs, 'fstore': share_name}, + {'pat': True, 'fpg': fpg}, + {'pat': True}, + ] + + try: + for search_params in search_order: + result = self._client.getfsnap(pattern, **search_params) + snapshots = result.get('members', []) + if len(snapshots) == 1: + return snapshots[0] + except Exception as e: + msg = (_('Unexpected exception while getting snapshots: %s') % + six.text_type(e)) + raise exception.ShareBackendException(msg) + + def allow_access(self, project_id, share_id, share_proto, access_type, + access_to, fpg, vfs): """Grant access to a share.""" - fstore = self.get_fstore(share_id, share_proto, fpg, vfs) - self._change_access(ALLOW, fstore, share_id, share_proto, + self._change_access(ALLOW, project_id, share_id, share_proto, access_type, access_to, fpg, vfs) - def deny_access(self, share_id, share_proto, access_type, access_to, - fpg, vfs): + def deny_access(self, project_id, share_id, share_proto, access_type, + access_to, fpg, vfs): """Deny access to a share.""" - fstore = self.get_fstore(share_id, share_proto, fpg, vfs) - if fstore: - self._change_access(DENY, fstore, share_id, share_proto, - access_type, access_to, fpg, vfs) + self._change_access(DENY, project_id, share_id, share_proto, + access_type, access_to, fpg, vfs) diff --git a/manila/tests/share/drivers/hp/test_hp_3par_constants.py b/manila/tests/share/drivers/hp/test_hp_3par_constants.py index f4b69a23e6..f5e0009bd3 100644 --- a/manila/tests/share/drivers/hp/test_hp_3par_constants.py +++ b/manila/tests/share/drivers/hp/test_hp_3par_constants.py @@ -29,6 +29,7 @@ PORT = 22 EXPECTED_IP_10203040 = '10.20.30.40' EXPECTED_IP_1234 = '1.2.3.4' EXPECTED_IP_127 = '127.0.0.1' +EXPECTED_PROJECT_ID = 'osf-nfs-project-id' EXPECTED_SHARE_ID = 'osf-share-id' EXPECTED_SHARE_NAME = 'share-name' EXPECTED_SHARE_PATH = '/anyfpg/anyvfs/anyfstore' @@ -38,11 +39,12 @@ EXPECTED_SNAP_NAME = 'osf-snap-name' EXPECTED_SNAP_ID = 'osf-snap-id' EXPECTED_STATS = {'test': 'stats'} EXPECTED_FPG = 'FPG_1' -EXPECTED_FSTORE = 'osf-test_fstore' +EXPECTED_FSTORE = EXPECTED_PROJECT_ID EXPECTED_VFS = 'test_vfs' EXPECTED_HP_DEBUG = True NFS_SHARE_INFO = { + 'project_id': EXPECTED_PROJECT_ID, 'id': EXPECTED_SHARE_ID, 'share_proto': NFS, } @@ -55,5 +57,9 @@ ACCESS_INFO = { SNAPSHOT_INFO = { 'name': EXPECTED_SNAP_NAME, 'id': EXPECTED_SNAP_ID, - 'share': {'id': EXPECTED_SHARE_ID}, + 'share': { + 'project_id': EXPECTED_PROJECT_ID, + 'id': EXPECTED_SHARE_ID, + 'share_proto': NFS, + }, } diff --git a/manila/tests/share/drivers/hp/test_hp_3par_driver.py b/manila/tests/share/drivers/hp/test_hp_3par_driver.py index 297e9ed9a7..21ba2a1888 100644 --- a/manila/tests/share/drivers/hp/test_hp_3par_driver.py +++ b/manila/tests/share/drivers/hp/test_hp_3par_driver.py @@ -39,11 +39,12 @@ class HP3ParDriverTestCase(test.TestCase): self.conf.hp3par_api_url = constants.API_URL self.conf.hp3par_san_login = constants.SAN_LOGIN self.conf.hp3par_san_password = constants.SAN_PASSWORD - self.conf.hp3par.san_ip = constants.EXPECTED_IP_1234 + self.conf.hp3par_san_ip = constants.EXPECTED_IP_1234 self.conf.hp3par_fpg = constants.EXPECTED_FPG self.conf.hp3par_san_ssh_port = constants.PORT self.conf.ssh_conn_timeout = constants.TIMEOUT self.conf.hp3par_share_ip_address = constants.EXPECTED_IP_10203040 + self.conf.hp3par_fstore_per_share = False self.conf.network_config_group = 'test_network_config_group' def safe_get(attr): @@ -66,20 +67,22 @@ class HP3ParDriverTestCase(test.TestCase): self.mock_mediator.get_vfs_name.return_value = constants.EXPECTED_VFS self.driver.do_setup(None) + conf = self.conf self.mock_mediator_constructor.assert_has_calls([ - mock.call(hp3par_san_ssh_port=self.conf.hp3par_san_ssh_port, - hp3par_san_password=self.conf.hp3par_san_password, - hp3par_username=self.conf.hp3par_username, - hp3par_san_login=self.conf.hp3par_san_login, - hp3par_debug=self.conf.hp3par_debug, - hp3par_api_url=self.conf.hp3par_api_url, - hp3par_password=self.conf.hp3par_password, - hp3par_san_ip=self.conf.hp3par_san_ip, - ssh_conn_timeout=self.conf.ssh_conn_timeout)]) + mock.call(hp3par_san_ssh_port=conf.hp3par_san_ssh_port, + hp3par_san_password=conf.hp3par_san_password, + hp3par_username=conf.hp3par_username, + hp3par_san_login=conf.hp3par_san_login, + hp3par_debug=conf.hp3par_debug, + hp3par_api_url=conf.hp3par_api_url, + hp3par_password=conf.hp3par_password, + hp3par_san_ip=conf.hp3par_san_ip, + hp3par_fstore_per_share=conf.hp3par_fstore_per_share, + ssh_conn_timeout=conf.ssh_conn_timeout)]) self.mock_mediator.assert_has_calls([ mock.call.do_setup(), - mock.call.get_vfs_name(self.conf.hp3par_fpg)]) + mock.call.get_vfs_name(conf.hp3par_fpg)]) self.assertEqual(constants.EXPECTED_VFS, self.driver.vfs) @@ -92,16 +95,18 @@ class HP3ParDriverTestCase(test.TestCase): self.assertRaises(exception.ShareBackendException, self.driver.do_setup, None) + conf = self.conf self.mock_mediator_constructor.assert_has_calls([ - mock.call(hp3par_san_ssh_port=self.conf.hp3par_san_ssh_port, - hp3par_san_password=self.conf.hp3par_san_password, - hp3par_username=self.conf.hp3par_username, - hp3par_san_login=self.conf.hp3par_san_login, - hp3par_debug=self.conf.hp3par_debug, - hp3par_api_url=self.conf.hp3par_api_url, - hp3par_password=self.conf.hp3par_password, - hp3par_san_ip=self.conf.hp3par_san_ip, - ssh_conn_timeout=self.conf.ssh_conn_timeout)]) + mock.call(hp3par_san_ssh_port=conf.hp3par_san_ssh_port, + hp3par_san_password=conf.hp3par_san_password, + hp3par_username=conf.hp3par_username, + hp3par_san_login=conf.hp3par_san_login, + hp3par_debug=conf.hp3par_debug, + hp3par_api_url=conf.hp3par_api_url, + hp3par_password=conf.hp3par_password, + hp3par_san_ip=conf.hp3par_san_ip, + hp3par_fstore_per_share=conf.hp3par_fstore_per_share, + ssh_conn_timeout=conf.ssh_conn_timeout)]) self.mock_mediator.assert_has_calls([mock.call.do_setup()]) @@ -114,20 +119,22 @@ class HP3ParDriverTestCase(test.TestCase): self.assertRaises(exception.ShareBackendException, self.driver.do_setup, None) + conf = self.conf self.mock_mediator_constructor.assert_has_calls([ - mock.call(hp3par_san_ssh_port=self.conf.hp3par_san_ssh_port, - hp3par_san_password=self.conf.hp3par_san_password, - hp3par_username=self.conf.hp3par_username, - hp3par_san_login=self.conf.hp3par_san_login, - hp3par_debug=self.conf.hp3par_debug, - hp3par_api_url=self.conf.hp3par_api_url, - hp3par_password=self.conf.hp3par_password, - hp3par_san_ip=self.conf.hp3par_san_ip, - ssh_conn_timeout=self.conf.ssh_conn_timeout)]) + mock.call(hp3par_san_ssh_port=conf.hp3par_san_ssh_port, + hp3par_san_password=conf.hp3par_san_password, + hp3par_username=conf.hp3par_username, + hp3par_san_login=conf.hp3par_san_login, + hp3par_debug=conf.hp3par_debug, + hp3par_api_url=conf.hp3par_api_url, + hp3par_password=conf.hp3par_password, + hp3par_san_ip=conf.hp3par_san_ip, + hp3par_fstore_per_share=conf.hp3par_fstore_per_share, + ssh_conn_timeout=conf.ssh_conn_timeout)]) self.mock_mediator.assert_has_calls([ mock.call.do_setup(), - mock.call.get_vfs_name(self.conf.hp3par_fpg)]) + mock.call.get_vfs_name(conf.hp3par_fpg)]) def init_driver(self): """Simple driver setup for re-use with tests that need one.""" @@ -137,11 +144,13 @@ class HP3ParDriverTestCase(test.TestCase): self.driver.fpg = constants.EXPECTED_FPG self.driver.share_ip_address = self.conf.hp3par_share_ip_address - def do_create_share(self, protocol, expected_share_id, expected_size): + def do_create_share(self, protocol, expected_project_id, expected_share_id, + expected_size): """Re-usable code for create share.""" context = None share_server = None share = { + 'project_id': expected_project_id, 'id': expected_share_id, 'share_proto': protocol, 'size': expected_size, @@ -188,11 +197,13 @@ class HP3ParDriverTestCase(test.TestCase): constants.EXPECTED_SHARE_NAME) location = self.do_create_share(constants.CIFS, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, constants.EXPECTED_SIZE_2) self.assertEqual(expected_location, location) expected_calls = [mock.call.create_share( + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, constants.CIFS, constants.EXPECTED_FPG, @@ -210,12 +221,14 @@ class HP3ParDriverTestCase(test.TestCase): constants.EXPECTED_SHARE_PATH) location = self.do_create_share(constants.NFS, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, constants.EXPECTED_SIZE_1) self.assertEqual(expected_location, location) expected_calls = [ - mock.call.create_share(constants.EXPECTED_SHARE_ID, + mock.call.create_share(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.NFS, constants.EXPECTED_FPG, constants.EXPECTED_VFS, @@ -242,7 +255,9 @@ class HP3ParDriverTestCase(test.TestCase): expected_calls = [ mock.call.create_share_from_snapshot(constants.EXPECTED_SHARE_ID, constants.CIFS, + constants.EXPECTED_FSTORE, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_ID, constants.EXPECTED_FPG, constants.EXPECTED_VFS)] @@ -267,7 +282,9 @@ class HP3ParDriverTestCase(test.TestCase): expected_calls = [ mock.call.create_share_from_snapshot(constants.EXPECTED_SHARE_ID, constants.NFS, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_ID, constants.EXPECTED_FPG, constants.EXPECTED_VFS)] @@ -280,6 +297,7 @@ class HP3ParDriverTestCase(test.TestCase): context = None share_server = None share = { + 'project_id': constants.EXPECTED_PROJECT_ID, 'id': constants.EXPECTED_SHARE_ID, 'share_proto': constants.CIFS, 'size': constants.EXPECTED_SIZE_1, @@ -288,7 +306,8 @@ class HP3ParDriverTestCase(test.TestCase): self.driver.delete_share(context, share, share_server) expected_calls = [ - mock.call.delete_share(constants.EXPECTED_SHARE_ID, + mock.call.delete_share(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.CIFS, constants.EXPECTED_FPG, constants.EXPECTED_VFS)] @@ -305,7 +324,9 @@ class HP3ParDriverTestCase(test.TestCase): share_server) expected_calls = [ - mock.call.create_snapshot(constants.EXPECTED_SHARE_ID, + mock.call.create_snapshot(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_ID, constants.EXPECTED_FPG, constants.EXPECTED_VFS)] @@ -321,7 +342,9 @@ class HP3ParDriverTestCase(test.TestCase): share_server) expected_calls = [ - mock.call.delete_snapshot(constants.EXPECTED_SHARE_ID, + mock.call.delete_snapshot(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_ID, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -337,7 +360,8 @@ class HP3ParDriverTestCase(test.TestCase): constants.ACCESS_INFO) expected_calls = [ - mock.call.allow_access(constants.EXPECTED_SHARE_ID, + mock.call.allow_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.NFS, constants.IP, constants.EXPECTED_IP_1234, @@ -355,7 +379,8 @@ class HP3ParDriverTestCase(test.TestCase): constants.ACCESS_INFO) expected_calls = [ - mock.call.deny_access(constants.EXPECTED_SHARE_ID, + mock.call.deny_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.NFS, constants.IP, constants.EXPECTED_IP_1234, diff --git a/manila/tests/share/drivers/hp/test_hp_3par_mediator.py b/manila/tests/share/drivers/hp/test_hp_3par_mediator.py index aea1aac4d1..b8a8bac08b 100644 --- a/manila/tests/share/drivers/hp/test_hp_3par_mediator.py +++ b/manila/tests/share/drivers/hp/test_hp_3par_mediator.py @@ -14,10 +14,10 @@ import sys +import ddt import mock if 'hp3parclient' not in sys.modules: sys.modules['hp3parclient'] = mock.Mock() -import six from manila import exception from manila.share.drivers.hp import hp_3par_mediator as hp3parmediator @@ -27,6 +27,7 @@ from manila.tests.share.drivers.hp import test_hp_3par_constants as constants from oslo_utils import units +@ddt.ddt class HP3ParMediatorTestCase(test.TestCase): def setUp(self): @@ -143,20 +144,23 @@ class HP3ParMediatorTestCase(test.TestCase): expected_fpg, expected_vfsname, expected_protocol, - expected_share_id, - expected_size): - size = six.text_type(expected_size) - expected_sharedir = None + expected_project_id, + expected_share_id): + expected_sharedir = expected_share_id if expected_protocol == constants.NFS_LOWER: expected_calls = [ - mock.call.createfstore(expected_vfsname, expected_share_id, + mock.call.createfstore(expected_vfsname, expected_project_id, comment='OpenStack Manila fstore', fpg=expected_fpg), - mock.call.setfsquota(expected_vfsname, fpg=expected_fpg, - hcapacity=size, - scapacity=size, - fstore=expected_share_id), + mock.call.getfsquota(fpg=expected_fpg, + vfs=expected_vfsname, + fstore=expected_project_id), + mock.call.setfsquota(expected_vfsname, + fpg=expected_fpg, + hcapacity='2048', + scapacity='2048', + fstore=expected_project_id), mock.call.createfshare(expected_protocol, expected_vfsname, expected_share_id, comment='OpenStack Manila fshare', @@ -164,29 +168,33 @@ class HP3ParMediatorTestCase(test.TestCase): sharedir=expected_sharedir, clientip='127.0.0.1', options='rw,no_root_squash,insecure', - fstore=expected_share_id), + fstore=expected_project_id), mock.call.getfshare(expected_protocol, expected_share_id, fpg=expected_fpg, vfs=expected_vfsname, - fstore=expected_share_id)] + fstore=expected_project_id)] else: expected_calls = [ - mock.call.createfstore(expected_vfsname, expected_share_id, + mock.call.createfstore(expected_vfsname, expected_project_id, comment='OpenStack Manila fstore', fpg=expected_fpg), - mock.call.setfsquota(expected_vfsname, fpg=expected_fpg, - hcapacity=size, - scapacity=size, - fstore=expected_share_id), + mock.call.getfsquota(fpg=expected_fpg, + vfs=expected_vfsname, + fstore=expected_project_id), + mock.call.setfsquota(expected_vfsname, + fpg=expected_fpg, + hcapacity='2048', + scapacity='2048', + fstore=expected_project_id), mock.call.createfshare(expected_protocol, expected_vfsname, expected_share_id, comment='OpenStack Manila fshare', fpg=expected_fpg, sharedir=expected_sharedir, allowip='127.0.0.1', - fstore=expected_share_id), + fstore=expected_project_id), mock.call.getfshare(expected_protocol, expected_share_id, fpg=expected_fpg, vfs=expected_vfsname, - fstore=expected_share_id)] + fstore=expected_project_id)] return expected_calls def test_mediator_create_cifs_share(self): @@ -198,11 +206,18 @@ class HP3ParMediatorTestCase(test.TestCase): 'members': [{'shareName': constants.EXPECTED_SHARE_NAME}] } - location = self.mediator.create_share(constants.EXPECTED_SHARE_ID, + self.mock_client.getfsquota.return_value = { + 'message': None, + 'total': 1, + 'members': [{'hardBlock': '1024', 'softBlock': '1024'}] + } + + location = self.mediator.create_share(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.CIFS, constants.EXPECTED_FPG, constants.EXPECTED_VFS, - size=constants.EXPECTED_SIZE_2) + size=constants.EXPECTED_SIZE_1) self.assertEqual(constants.EXPECTED_SHARE_NAME, location) @@ -210,8 +225,8 @@ class HP3ParMediatorTestCase(test.TestCase): constants.EXPECTED_FPG, constants.EXPECTED_VFS, constants.SMB_LOWER, - constants.EXPECTED_SHARE_ID, - constants.EXPECTED_SIZE_2) + constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID) self.mock_client.assert_has_calls(expected_calls) @@ -224,7 +239,14 @@ class HP3ParMediatorTestCase(test.TestCase): 'members': [{'sharePath': constants.EXPECTED_SHARE_PATH}] } - location = self.mediator.create_share(constants.EXPECTED_SHARE_ID, + self.mock_client.getfsquota.return_value = { + 'message': None, + 'total': 1, + 'members': [{'hardBlock': '1024', 'softBlock': '1024'}] + } + + location = self.mediator.create_share(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.NFS.lower(), constants.EXPECTED_FPG, constants.EXPECTED_VFS, @@ -233,10 +255,11 @@ class HP3ParMediatorTestCase(test.TestCase): self.assertEqual(constants.EXPECTED_SHARE_PATH, location) expected_calls = self.get_expected_calls_for_create_share( - constants.EXPECTED_FPG, constants.EXPECTED_VFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS, constants.NFS.lower(), - constants.EXPECTED_SHARE_ID, - constants.EXPECTED_SIZE_1) + constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID) self.mock_client.assert_has_calls(expected_calls) @@ -246,13 +269,16 @@ class HP3ParMediatorTestCase(test.TestCase): self.mock_client.getfsnap.return_value = { 'message': None, 'total': 1, - 'members': [{'snapName': constants.EXPECTED_SNAP_ID}] + 'members': [{'snapName': constants.EXPECTED_SNAP_ID, + 'fstoreName': constants.EXPECTED_FSTORE}] } location = self.mediator.create_share_from_snapshot( constants.EXPECTED_SHARE_ID, constants.CIFS, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_ID, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -264,21 +290,22 @@ class HP3ParMediatorTestCase(test.TestCase): vfs=constants.EXPECTED_VFS, fpg=constants.EXPECTED_FPG, pat=True, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_FSTORE), mock.call.createfshare(constants.SMB_LOWER, constants.EXPECTED_VFS, constants.EXPECTED_SHARE_ID, comment=mock.ANY, fpg=constants.EXPECTED_FPG, - sharedir='.snapshot/%s' % + sharedir='.snapshot/%s/%s' % ( constants.EXPECTED_SNAP_ID, - fstore=constants.EXPECTED_SHARE_ID, + constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_FSTORE, allowip=constants.EXPECTED_IP_127), mock.call.getfshare(constants.SMB_LOWER, constants.EXPECTED_SHARE_ID, fpg=constants.EXPECTED_FPG, vfs=constants.EXPECTED_VFS, - fstore=constants.EXPECTED_SHARE_ID)] + fstore=constants.EXPECTED_FSTORE)] self.mock_client.assert_has_calls(expected_calls) @@ -288,13 +315,16 @@ class HP3ParMediatorTestCase(test.TestCase): self.mock_client.getfsnap.return_value = { 'message': None, 'total': 1, - 'members': [{'snapName': constants.EXPECTED_SNAP_ID}] + 'members': [{'snapName': constants.EXPECTED_SNAP_ID, + 'fstoreName': constants.EXPECTED_FSTORE}] } location = self.mediator.create_share_from_snapshot( constants.EXPECTED_SHARE_ID, constants.NFS, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_ID, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -306,22 +336,23 @@ class HP3ParMediatorTestCase(test.TestCase): vfs=constants.EXPECTED_VFS, fpg=constants.EXPECTED_FPG, pat=True, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_FSTORE), mock.call.createfshare(constants.NFS_LOWER, constants.EXPECTED_VFS, constants.EXPECTED_SHARE_ID, comment=mock.ANY, fpg=constants.EXPECTED_FPG, - sharedir='.snapshot/%s' % - constants.EXPECTED_SNAP_ID, - fstore=constants.EXPECTED_SHARE_ID, + sharedir='.snapshot/%s/%s' % + (constants.EXPECTED_SNAP_ID, + constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_FSTORE, clientip=constants.EXPECTED_IP_127, options='ro,no_root_squash,insecure'), mock.call.getfshare(constants.NFS_LOWER, constants.EXPECTED_SHARE_ID, fpg=constants.EXPECTED_FPG, vfs=constants.EXPECTED_VFS, - fstore=constants.EXPECTED_SHARE_ID)] + fstore=constants.EXPECTED_FSTORE)] self.mock_client.assert_has_calls(expected_calls) @@ -338,7 +369,9 @@ class HP3ParMediatorTestCase(test.TestCase): self.mediator.create_share_from_snapshot, constants.EXPECTED_SHARE_ID, constants.NFS, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_ID, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -346,7 +379,12 @@ class HP3ParMediatorTestCase(test.TestCase): def test_mediator_delete_share(self): self.init_mediator() - self.mediator.delete_share(constants.EXPECTED_SHARE_ID, + self.mock_object(self.mediator, + '_find_fstore', + mock.Mock(return_value=constants.EXPECTED_SHARE_ID)) + + self.mediator.delete_share(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.CIFS, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -356,10 +394,10 @@ class HP3ParMediatorTestCase(test.TestCase): constants.EXPECTED_VFS, constants.EXPECTED_SHARE_ID, fpg=constants.EXPECTED_FPG, - fstore=constants.EXPECTED_FSTORE), + fstore=constants.EXPECTED_SHARE_ID), mock.call.removefstore(constants.EXPECTED_VFS, - constants.EXPECTED_FSTORE, - fpg=constants.EXPECTED_FPG) + constants.EXPECTED_SHARE_ID, + fpg=constants.EXPECTED_FPG), ] self.mock_client.assert_has_calls(expected_calls) @@ -367,14 +405,16 @@ class HP3ParMediatorTestCase(test.TestCase): def test_mediator_create_snapshot(self): self.init_mediator() - self.mediator.create_snapshot(constants.EXPECTED_SHARE_ID, + self.mediator.create_snapshot(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) expected_calls = [ mock.call.createfsnap(constants.EXPECTED_VFS, - constants.EXPECTED_SHARE_ID, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SNAP_NAME, fpg=constants.EXPECTED_FPG) ] @@ -389,7 +429,9 @@ class HP3ParMediatorTestCase(test.TestCase): self.assertRaises(exception.ShareBackendException, self.mediator.create_snapshot, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -401,7 +443,13 @@ class HP3ParMediatorTestCase(test.TestCase): self.mock_client.getfsnap.return_value = { 'total': 1, - 'members': [{'snapName': expected_name_from_array}] + 'members': [ + { + 'snapName': expected_name_from_array, + 'fstoreName': constants.EXPECTED_PROJECT_ID, + } + ], + 'message': None } self.mock_client.getfshare.side_effect = [ @@ -416,7 +464,9 @@ class HP3ParMediatorTestCase(test.TestCase): } ] - self.mediator.delete_snapshot(constants.EXPECTED_SHARE_ID, + self.mediator.delete_snapshot(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -426,17 +476,17 @@ class HP3ParMediatorTestCase(test.TestCase): vfs=constants.EXPECTED_VFS, fpg=constants.EXPECTED_FPG, pat=True, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_PROJECT_ID), mock.call.getfshare(constants.NFS_LOWER, fpg=constants.EXPECTED_FPG, vfs=constants.EXPECTED_VFS, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_PROJECT_ID), mock.call.getfshare(constants.SMB_LOWER, fpg=constants.EXPECTED_FPG, vfs=constants.EXPECTED_VFS, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_PROJECT_ID), mock.call.removefsnap(constants.EXPECTED_VFS, - constants.EXPECTED_SHARE_ID, + constants.EXPECTED_PROJECT_ID, fpg=constants.EXPECTED_FPG, snapname=expected_name_from_array), mock.call.startfsnapclean(constants.EXPECTED_FPG, @@ -452,7 +502,9 @@ class HP3ParMediatorTestCase(test.TestCase): 'members': [], } - self.mediator.delete_snapshot(constants.EXPECTED_SHARE_ID, + self.mediator.delete_snapshot(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -497,7 +549,9 @@ class HP3ParMediatorTestCase(test.TestCase): self.assertRaises(exception.Invalid, self.mediator.delete_snapshot, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -527,7 +581,9 @@ class HP3ParMediatorTestCase(test.TestCase): self.assertRaises(exception.Invalid, self.mediator.delete_snapshot, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -535,7 +591,9 @@ class HP3ParMediatorTestCase(test.TestCase): def _assert_delete_snapshot_raises(self): self.assertRaises(exception.ShareBackendException, self.mediator.delete_snapshot, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -551,7 +609,8 @@ class HP3ParMediatorTestCase(test.TestCase): self.mock_client.getfsnap.side_effect = None self.mock_client.getfsnap.return_value = { 'total': 1, - 'members': [{'snapName': constants.EXPECTED_SNAP_NAME}] + 'members': [{'snapName': constants.EXPECTED_SNAP_NAME, + 'fstoreName': constants.EXPECTED_FSTORE}] } # getfshare exception @@ -563,12 +622,14 @@ class HP3ParMediatorTestCase(test.TestCase): if args[0] == constants.NFS_LOWER: return { 'total': 1, - 'members': [{'sharePath': '/anyfpg/anyvfs/anyfstore'}] + 'members': [{'sharePath': '/anyfpg/anyvfs/anyfstore', + 'fstoreName': constants.EXPECTED_FSTORE}] } else: return { 'total': 1, - 'members': [{'shareDir': []}] + 'members': [{'shareDir': [], + 'fstoreName': constants.EXPECTED_FSTORE}] } self.mock_client.getfshare.side_effect = mock_fshare @@ -587,7 +648,9 @@ class HP3ParMediatorTestCase(test.TestCase): 'startfsnapclean fail.') mock_log = self.mock_object(hp3parmediator, 'LOG') - self.mediator.delete_snapshot(constants.EXPECTED_SHARE_ID, + self.mediator.delete_snapshot(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, constants.EXPECTED_SNAP_NAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) @@ -597,17 +660,17 @@ class HP3ParMediatorTestCase(test.TestCase): vfs=constants.EXPECTED_VFS, fpg=constants.EXPECTED_FPG, pat=True, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_FSTORE), mock.call.getfshare(constants.NFS_LOWER, fpg=constants.EXPECTED_FPG, vfs=constants.EXPECTED_VFS, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_FSTORE), mock.call.getfshare(constants.SMB_LOWER, fpg=constants.EXPECTED_FPG, vfs=constants.EXPECTED_VFS, - fstore=constants.EXPECTED_SHARE_ID), + fstore=constants.EXPECTED_FSTORE), mock.call.removefsnap(constants.EXPECTED_VFS, - constants.EXPECTED_SHARE_ID, + constants.EXPECTED_FSTORE, fpg=constants.EXPECTED_FPG, snapname=constants.EXPECTED_SNAP_NAME), mock.call.startfsnapclean(constants.EXPECTED_FPG, @@ -651,7 +714,8 @@ class HP3ParMediatorTestCase(test.TestCase): expected_allowperm = '+%s:fullcontrol' % constants.USERNAME - self.mediator.allow_access(constants.EXPECTED_SHARE_ID, + self.mediator.allow_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.CIFS, constants.USER, constants.USERNAME, @@ -675,7 +739,8 @@ class HP3ParMediatorTestCase(test.TestCase): expected_denyperm = '-%s:fullcontrol' % constants.USERNAME - self.mediator.deny_access(constants.EXPECTED_SHARE_ID, + self.mediator.deny_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.CIFS, constants.USER, constants.USERNAME, @@ -699,7 +764,8 @@ class HP3ParMediatorTestCase(test.TestCase): expected_allowip = '+%s' % constants.EXPECTED_IP_1234 - self.mediator.allow_access(constants.EXPECTED_SHARE_ID, + self.mediator.allow_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.CIFS, constants.IP, constants.EXPECTED_IP_1234, @@ -722,7 +788,8 @@ class HP3ParMediatorTestCase(test.TestCase): expected_denyip = '-%s' % constants.EXPECTED_IP_1234 - self.mediator.deny_access(constants.EXPECTED_SHARE_ID, + self.mediator.deny_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.CIFS, constants.IP, constants.EXPECTED_IP_1234, @@ -745,7 +812,8 @@ class HP3ParMediatorTestCase(test.TestCase): expected_clientip = '+%s' % constants.EXPECTED_IP_1234 - self.mediator.allow_access(constants.EXPECTED_SHARE_ID, + self.mediator.allow_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.NFS, constants.IP, constants.EXPECTED_IP_1234, @@ -768,7 +836,8 @@ class HP3ParMediatorTestCase(test.TestCase): expected_clientip = '-%s' % constants.EXPECTED_IP_1234 - self.mediator.deny_access(constants.EXPECTED_SHARE_ID, + self.mediator.deny_access(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, constants.NFS, constants.IP, constants.EXPECTED_IP_1234, @@ -791,6 +860,7 @@ class HP3ParMediatorTestCase(test.TestCase): self.assertRaises(exception.HP3ParInvalid, self.mediator.allow_access, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, constants.NFS, constants.USER, @@ -804,6 +874,7 @@ class HP3ParMediatorTestCase(test.TestCase): self.assertRaises(exception.InvalidInput, self.mediator.allow_access, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, 'unsupported_other_protocol', constants.USER, @@ -817,9 +888,241 @@ class HP3ParMediatorTestCase(test.TestCase): self.assertRaises(exception.InvalidInput, self.mediator.allow_access, + constants.EXPECTED_PROJECT_ID, constants.EXPECTED_SHARE_ID, constants.CIFS, 'unsupported_other_type', constants.USERNAME, constants.EXPECTED_FPG, constants.EXPECTED_VFS) + + @ddt.data((('nfs', 'NFS', 'nFs'), 'smb'), + (('smb', 'SMB', 'SmB', 'CIFS', 'cifs', 'CiFs'), 'nfs')) + @ddt.unpack + def test_other_protocol(self, protocols, expected_other): + for protocol in protocols: + self.assertEqual(expected_other, + hp3parmediator.HP3ParMediator().other_protocol( + protocol)) + + @ddt.data('', 'bogus') + def test_other_protocol_exception(self, protocol): + self.assertRaises(exception.InvalidInput, + hp3parmediator.HP3ParMediator().other_protocol, + protocol) + + @ddt.data(('osf-uid', None, 'osf-uid'), + ('uid', None, 'osf-uid'), + ('uid', 'smb', 'osf-smb-uid'), + ('uid', 'smb', 'osf-smb-uid')) + @ddt.unpack + def test_ensure_prefix(self, uid, protocol, expected): + self.assertEqual(expected, + hp3parmediator.HP3ParMediator().ensure_prefix( + uid, protocol=protocol)) + + def test_find_fstore_search(self): + self.init_mediator() + + mock_find_fshare = self.mock_object(self.mediator, + '_find_fshare', + mock.Mock(return_value=None)) + + result = self.mediator._find_fstore(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + + mock_find_fshare.assert_called_once_with(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + self.assertIsNone(result) + + def test_find_fstore_search_xproto(self): + self.init_mediator() + + mock_find_fshare = self.mock_object(self.mediator, + '_find_fshare', + mock.Mock(return_value=None)) + + result = self.mediator._find_fstore(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS, + allow_cross_protocol=True) + + expected_calls = [ + mock.call(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS), + mock.call(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.SMB_LOWER, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS), + ] + mock_find_fshare.assert_has_calls(expected_calls) + self.assertIsNone(result) + + def test_find_fshare_search(self): + self.init_mediator() + + self.mock_client.getfshare.return_value = {} + + result = self.mediator._find_fshare(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + + expected_calls = [ + mock.call.getfshare(constants.NFS_LOWER, + constants.EXPECTED_SHARE_ID, + fpg=constants.EXPECTED_FPG, + vfs=constants.EXPECTED_VFS, + fstore=constants.EXPECTED_PROJECT_ID), + mock.call.getfshare(constants.NFS_LOWER, + constants.EXPECTED_SHARE_ID, + fpg=constants.EXPECTED_FPG, + vfs=constants.EXPECTED_VFS, + fstore=constants.EXPECTED_SHARE_ID), + mock.call.getfshare(constants.NFS_LOWER, + constants.EXPECTED_SHARE_ID, + fpg=constants.EXPECTED_FPG), + mock.call.getfshare(constants.NFS_LOWER, + constants.EXPECTED_SHARE_ID), + ] + self.mock_client.assert_has_calls(expected_calls) + self.assertIsNone(result) + + def test_find_fshare_exception(self): + self.init_mediator() + + self.mock_client.getfshare.side_effect = Exception('test unexpected') + + self.assertRaises(exception.ShareBackendException, + self.mediator._find_fshare, + constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + + self.mock_client.getfshare.assert_called_once_with( + constants.NFS_LOWER, + constants.EXPECTED_SHARE_ID, + fpg=constants.EXPECTED_FPG, + vfs=constants.EXPECTED_VFS, + fstore=constants.EXPECTED_PROJECT_ID) + + def test_find_fshare_hit(self): + self.init_mediator() + + expected_result = {'shareName': 'hit'} + self.mock_client.getfshare.return_value = { + 'total': 1, + 'members': [expected_result] + } + + result = self.mediator._find_fshare(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + + self.mock_client.getfshare.assert_called_once_with( + constants.NFS_LOWER, + constants.EXPECTED_SHARE_ID, + fpg=constants.EXPECTED_FPG, + vfs=constants.EXPECTED_VFS, + fstore=constants.EXPECTED_PROJECT_ID), + self.assertEqual(expected_result, result) + + def test_find_fsnap_search(self): + self.init_mediator() + + self.mock_client.getfsnap.return_value = {} + + result = self.mediator._find_fsnap(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_SNAP_ID, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + + expected_snap_pattern = '*_%s' % constants.EXPECTED_SNAP_ID + + expected_calls = [ + mock.call.getfsnap(expected_snap_pattern, + vfs=constants.EXPECTED_VFS, + fpg=constants.EXPECTED_FPG, + pat=True, + fstore=constants.EXPECTED_PROJECT_ID), + mock.call.getfsnap(expected_snap_pattern, + vfs=constants.EXPECTED_VFS, + fpg=constants.EXPECTED_FPG, + pat=True, + fstore=constants.EXPECTED_SHARE_ID), + mock.call.getfsnap(expected_snap_pattern, + fpg=constants.EXPECTED_FPG, + pat=True), + mock.call.getfsnap(expected_snap_pattern, pat=True), + ] + self.mock_client.assert_has_calls(expected_calls) + self.assertIsNone(result) + + def test_find_fsnap_exception(self): + self.init_mediator() + + self.mock_client.getfsnap.side_effect = Exception('test unexpected') + + self.assertRaises(exception.ShareBackendException, + self.mediator._find_fsnap, + constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_SNAP_ID, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + + expected_snap_pattern = '*_%s' % constants.EXPECTED_SNAP_ID + + self.mock_client.getfsnap.assert_called_once_with( + expected_snap_pattern, + vfs=constants.EXPECTED_VFS, + fpg=constants.EXPECTED_FPG, + pat=True, + fstore=constants.EXPECTED_PROJECT_ID) + + def test_find_fsnap_hit(self): + self.init_mediator() + + expected_result = {'snapName': 'hit'} + self.mock_client.getfsnap.return_value = { + 'total': 1, + 'members': [expected_result] + } + + result = self.mediator._find_fsnap(constants.EXPECTED_PROJECT_ID, + constants.EXPECTED_SHARE_ID, + constants.NFS, + constants.EXPECTED_SNAP_ID, + constants.EXPECTED_FPG, + constants.EXPECTED_VFS) + + expected_snap_pattern = '*_%s' % constants.EXPECTED_SNAP_ID + + self.mock_client.getfsnap.assert_called_once_with( + expected_snap_pattern, + vfs=constants.EXPECTED_VFS, + fpg=constants.EXPECTED_FPG, + pat=True, + fstore=constants.EXPECTED_PROJECT_ID) + + self.assertEqual(expected_result, result)