From 768b02bdb73b6a7ac99523f997d31b3f442413cb Mon Sep 17 00:00:00 2001 From: Csaba Henk <chenk@redhat.com> Date: Sun, 7 Feb 2016 04:16:59 +0100 Subject: [PATCH] gluster*: clean up volume option querying GlusterFS has two kind of options: - regular ones, which are a hardcoded set, and option names are verified in "gluster volume get" - user ones, whose name matches user.* -- these are arbitrarily named, are ignored by "gluster volume get" and are listed in "gluster volume info" output So far we used "gluster volume info" universally, but that, apart from being cumbersome for regular options, is also incorrect, as it can't distinguish an unset option name from an undefined one (querying the former should be treated OK, querying the second should be treated as error). - implement querying of regular options with "gluster volume get" (accepting empty response) - implement querying of user options with searching "gluster vol info" data - verify operations on the XML tree, make tacit XML layout assumptions explicit - implement optional Boolean coercion of values Partially implements bp gluster-code-cleanup Change-Id: I9e0843b88cd1a1668fe48c6979029c012dcbaa13 --- manila/share/drivers/glusterfs/__init__.py | 6 +- manila/share/drivers/glusterfs/common.py | 115 ++++++- .../share/drivers/glusterfs/layout_volume.py | 19 +- manila/share/drivers/glusterfs_native.py | 5 +- .../share/drivers/glusterfs/test_common.py | 288 +++++++++++++++++- .../drivers/glusterfs/test_layout_volume.py | 2 +- manila/tests/share/drivers/test_glusterfs.py | 12 +- .../share/drivers/test_glusterfs_native.py | 22 +- 8 files changed, 405 insertions(+), 64 deletions(-) diff --git a/manila/share/drivers/glusterfs/__init__.py b/manila/share/drivers/glusterfs/__init__.py index 36592e0916..1463e18d7d 100644 --- a/manila/share/drivers/glusterfs/__init__.py +++ b/manila/share/drivers/glusterfs/__init__.py @@ -96,10 +96,10 @@ class GlusterfsShareDriver(driver.ExecuteMixin, driver.GaneshaMixin, gluster_manager = share_manager['manager'] # TODO(csaba): This should be refactored into proper dispatch to helper if self.nfs_helper == GlusterNFSHelper and not gluster_manager.path: - # default is 'on' + # default behavior of NFS_EXPORT_VOL is as if it were 'on' export_vol = gluster_manager.get_vol_option( - NFS_EXPORT_VOL) or 'on' - if export_vol.lower() not in ('on', '1', 'true', 'yes', 'enable'): + NFS_EXPORT_VOL, boolean=True) + if export_vol is False: raise exception.GlusterfsException( _("Gluster-NFS with volume layout should be used " "with `nfs.export-volumes = on`")) diff --git a/manila/share/drivers/glusterfs/common.py b/manila/share/drivers/glusterfs/common.py index ff4803f837..8176030509 100644 --- a/manila/share/drivers/glusterfs/common.py +++ b/manila/share/drivers/glusterfs/common.py @@ -61,6 +61,17 @@ def _check_volume_presence(f): return wrapper +def volxml_get(xmlout, path, *default): + """Extract a value by a path from XML.""" + value = xmlout.find(path) + if value is None: + if default: + return default[0] + raise exception.InvalidShare( + _('Xpath %s not found in volume query response XML') % path) + return value.text + + class GlusterManager(object): """Interface with a GlusterFS volume.""" @@ -68,6 +79,13 @@ class GlusterManager(object): '(?P<host>[^:@/]+)' '(?::/(?P<volume>[^/]+)(?P<path>/.*)?)?\Z') + # See this about GlusterFS' convention for Boolean interpretation + # of strings: + # https://github.com/gluster/glusterfs/blob/v3.7.8/ + # libglusterfs/src/common-utils.c#L1680-L1708 + GLUSTERFS_TRUE_VALUES = ('ON', 'YES', 'TRUE', 'ENABLE', '1') + GLUSTERFS_FALSE_VALUES = ('OFF', 'NO', 'FALSE', 'DISABLE', '0') + @classmethod def parse(cls, address): """Parse address string into component dict.""" @@ -174,9 +192,38 @@ class GlusterManager(object): return _gluster_call - @_check_volume_presence - def get_vol_option(self, option): - """Get the value of an option set on a GlusterFS volume.""" + def xml_response_check(self, xmlout, command, countpath=None): + """Sanity check for GlusterFS XML response.""" + commandstr = ' '.join(command) + ret = {} + for e in 'opRet', 'opErrno': + ret[e] = int(volxml_get(xmlout, e)) + if ret == {'opRet': -1, 'opErrno': 0}: + raise exception.GlusterfsException(_( + 'GlusterFS command %(command)s on volume %(volume)s failed' + ) % {'volume': self.volume, 'command': command}) + if list(six.itervalues(ret)) != [0, 0]: + errdct = {'volume': self.volume, 'command': commandstr, + 'opErrstr': volxml_get(xmlout, 'opErrstr', None)} + errdct.update(ret) + raise exception.InvalidShare(_( + 'GlusterFS command %(command)s on volume %(volume)s got ' + 'unexpected response: ' + 'opRet=%(opRet)s, opErrno=%(opErrno)s, opErrstr=%(opErrstr)s' + ) % errdct) + if not countpath: + return + count = volxml_get(xmlout, countpath) + if count != '1': + raise exception.InvalidShare( + _('GlusterFS command %(command)s on volume %(volume)s got ' + 'ambiguous response: ' + '%(count)s records') % { + 'volume': self.volume, 'command': commandstr, + 'count': count}) + + def _get_vol_option_via_info(self, option): + """Get the value of an option set on a GlusterFS volume via volinfo.""" args = ('--xml', 'volume', 'info', self.volume) out, err = self.gluster_call(*args, log=_LE("retrieving volume info")) @@ -186,19 +233,65 @@ class GlusterManager(object): self.volume ) - vix = etree.fromstring(out) - if int(vix.find('./volInfo/volumes/count').text) != 1: - raise exception.InvalidShare('Volume name ambiguity') - for e in vix.findall(".//option"): - o, v = (e.find(a).text for a in ('name', 'value')) + volxml = etree.fromstring(out) + self.xml_response_check(volxml, args[1:], './volInfo/volumes/count') + for e in volxml.findall(".//option"): + o, v = (volxml_get(e, a) for a in ('name', 'value')) if o == option: return v + @_check_volume_presence + def _get_vol_user_option(self, useropt): + """Get the value of an user option set on a GlusterFS volume.""" + option = '.'.join(('user', useropt)) + return self._get_vol_option_via_info(option) + + @_check_volume_presence + def _get_vol_regular_option(self, option): + """Get the value of a regular option set on a GlusterFS volume.""" + args = ('--xml', 'volume', 'get', self.volume, option) + + out, err = self.gluster_call(*args, check_exit_code=False) + + if not out: + # all input is valid, but the option has not been set + # (nb. some options do come by a null value, but some + # don't even have that, see eg. cluster.nufa) + return + + try: + optxml = etree.fromstring(out) + except Exception: + # non-xml output indicates that GlusterFS backend does not support + # 'vol get', we fall back to 'vol info' based retrieval (glusterfs + # < 3.7). + return self._get_vol_option_via_info(option) + + self.xml_response_check(optxml, args[1:], './volGetopts/count') + return volxml_get(optxml, './volGetopts/Value') + + def get_vol_option(self, option, boolean=False): + """Get the value of an option set on a GlusterFS volume.""" + useropt = re.sub('\Auser\.', '', option) + if option == useropt: + value = self._get_vol_regular_option(option) + else: + value = self._get_vol_user_option(useropt) + if not boolean or value is None: + return value + if value.upper() in self.GLUSTERFS_TRUE_VALUES: + return True + if value.upper() in self.GLUSTERFS_FALSE_VALUES: + return False + raise exception.GlusterfsException(_( + "GlusterFS volume option on volume %(volume)s: " + "%(option)s=%(value)s cannot be interpreted as Boolean") % { + 'volume': self.volume, 'option': option, 'value': value}) + @_check_volume_presence def set_vol_option(self, option, value, ignore_failure=False): - if value is True: - value - value = {True: 'on', False: 'off'}.get(value, value) + value = {True: self.GLUSTERFS_TRUE_VALUES[0], + False: self.GLUSTERFS_FALSE_VALUES[0]}.get(value, value) if value is None: args = ('reset', (option,)) else: diff --git a/manila/share/drivers/glusterfs/layout_volume.py b/manila/share/drivers/glusterfs/layout_volume.py index d31d2f7474..622b4ef087 100644 --- a/manila/share/drivers/glusterfs/layout_volume.py +++ b/manila/share/drivers/glusterfs/layout_volume.py @@ -531,9 +531,9 @@ class GlusterfsVolumeMappedLayout(layout.GlusterfsShareLayoutBase): ) outxml = etree.fromstring(out) - opret = int(outxml.find('opRet').text) - operrno = int(outxml.find('opErrno').text) - operrstr = outxml.find('opErrstr').text + opret = int(common.volxml_get(outxml, 'opRet')) + operrno = int(common.volxml_get(outxml, 'opErrno')) + operrstr = common.volxml_get(outxml, 'opErrstr', None) if opret == -1: vers = self.glusterfs_versions[gluster_mgr.host_access] @@ -573,18 +573,7 @@ class GlusterfsVolumeMappedLayout(layout.GlusterfsShareLayoutBase): ) outxml = etree.fromstring(out) - opret = int(outxml.find('opRet').text) - operrno = int(outxml.find('opErrno').text) - operrstr = outxml.find('opErrstr').text - - if opret: - raise exception.GlusterfsException( - _("Deleting snapshot %(snap_id)s of share %(share_id)s failed " - "with %(errno)d: %(errstr)s") % { - 'snap_id': snapshot['id'], - 'share_id': snapshot['share_id'], - 'errno': operrno, - 'errstr': operrstr}) + gluster_mgr.xml_response_check(outxml, args[1:]) def ensure_share(self, context, share, share_server=None): """Invoked to ensure that share is exported.""" diff --git a/manila/share/drivers/glusterfs_native.py b/manila/share/drivers/glusterfs_native.py index 8778e7f57f..239a661bfd 100644 --- a/manila/share/drivers/glusterfs_native.py +++ b/manila/share/drivers/glusterfs_native.py @@ -203,9 +203,8 @@ class GlusterfsNativeShareDriver(driver.ExecuteMixin, ssl_allow_opt = ','.join(ssl_allow) gluster_mgr.set_vol_option(AUTH_SSL_ALLOW, ssl_allow_opt) - dynauth = gluster_mgr.get_vol_option(DYNAMIC_AUTH) or 'off' - # TODO(csaba): boolean option processing shoud be done in common - if dynauth.lower() not in ('on', '1', 'true', 'yes', 'enable'): + dynauth = gluster_mgr.get_vol_option(DYNAMIC_AUTH, boolean=True) + if not dynauth: common._restart_gluster_vol(gluster_mgr) def _update_share_stats(self): diff --git a/manila/tests/share/drivers/glusterfs/test_common.py b/manila/tests/share/drivers/glusterfs/test_common.py index bc4a7d6a32..eb7775bfee 100644 --- a/manila/tests/share/drivers/glusterfs/test_common.py +++ b/manila/tests/share/drivers/glusterfs/test_common.py @@ -73,6 +73,32 @@ class GlusterManagerTestCase(test.TestCase): exception.GlusterfsException, common._check_volume_presence(mock.Mock()), gmgr) + def test_volxml_get(self): + xmlout = mock.Mock() + value = mock.Mock() + value.text = 'foobar' + xmlout.find = mock.Mock(return_value=value) + + ret = common.volxml_get(xmlout, 'some/path') + + self.assertEqual('foobar', ret) + + @ddt.data(None, 'some-value') + def test_volxml_get_notfound_fallback(self, default): + xmlout = mock.Mock() + xmlout.find = mock.Mock(return_value=None) + + ret = common.volxml_get(xmlout, 'some/path', default) + + self.assertEqual(default, ret) + + def test_volxml_get_notfound(self): + xmlout = mock.Mock() + xmlout.find = mock.Mock(return_value=None) + + self.assertRaises(exception.InvalidShare, common.volxml_get, + xmlout, 'some/path') + def test_gluster_manager_init(self): self.assertEqual(fake_gluster_manager_attrs['user'], self._gluster_manager.user) @@ -222,21 +248,104 @@ class GlusterManagerTestCase(test.TestCase): gluster_manager.make_gluster_call(fake_execute), *fake_args, error_policy='foobar') - def test_get_vol_option_empty_volinfo(self): + @ddt.data({}, {'opErrstr': None}, {'opErrstr': 'error'}) + def test_xml_response_check(self, xdict): + fdict = {'opRet': '0', 'opErrno': '0', 'some/count': '1'} + fdict.update(xdict) + + def vxget(x, e, *a): + if a: + return fdict.get(e, a[0]) + else: + return fdict[e] + + xtree = mock.Mock() + command = ['volume', 'command', 'fake'] + + with mock.patch.object(common, 'volxml_get', side_effect=vxget): + self._gluster_manager.xml_response_check(xtree, command, + 'some/count') + + self.assertTrue(common.volxml_get.called) + + @ddt.data('1', '2') + def test_xml_response_check_failure(self, count): + fdict = {'opRet': '-1', 'opErrno': '0', 'some/count': count} + + def vxget(x, e, *a): + if a: + return fdict.get(e, a[0]) + else: + return fdict[e] + + xtree = mock.Mock() + command = ['volume', 'command', 'fake'] + + with mock.patch.object(common, 'volxml_get', side_effect=vxget): + self.assertRaises(exception.GlusterfsException, + self._gluster_manager.xml_response_check, + xtree, command, 'some/count') + + self.assertTrue(common.volxml_get.called) + + @ddt.data({'opRet': '-2', 'opErrno': '0', 'some/count': '1'}, + {'opRet': '0', 'opErrno': '1', 'some/count': '1'}, + {'opRet': '0', 'opErrno': '0', 'some/count': '0'}, + {'opRet': '0', 'opErrno': '0', 'some/count': '2'}) + def test_xml_response_check_invalid(self, fdict): + + def vxget(x, e, *a): + if a: + return fdict.get(e, a[0]) + else: + return fdict[e] + + xtree = mock.Mock() + command = ['volume', 'command', 'fake'] + + with mock.patch.object(common, 'volxml_get', side_effect=vxget): + self.assertRaises(exception.InvalidShare, + self._gluster_manager.xml_response_check, + xtree, command, 'some/count') + + self.assertTrue(common.volxml_get.called) + + @ddt.data({'opRet': '0', 'opErrno': '0'}, + {'opRet': '0', 'opErrno': '0', 'some/count': '2'}) + def test_xml_response_check_count_ignored(self, fdict): + + def vxget(x, e, *a): + if a: + return fdict.get(e, a[0]) + else: + return fdict[e] + + xtree = mock.Mock() + command = ['volume', 'command', 'fake'] + + with mock.patch.object(common, 'volxml_get', side_effect=vxget): + self._gluster_manager.xml_response_check(xtree, command) + + self.assertTrue(common.volxml_get.called) + + def test_get_vol_option_via_info_empty_volinfo(self): args = ('--xml', 'volume', 'info', self._gluster_manager.volume) self.mock_object(self._gluster_manager, 'gluster_call', mock.Mock(return_value=('', {}))) self.assertRaises(exception.GlusterfsException, - self._gluster_manager.get_vol_option, - NFS_EXPORT_DIR) + self._gluster_manager._get_vol_option_via_info, + 'foobar') self._gluster_manager.gluster_call.assert_called_once_with( *args, log=mock.ANY) - def test_get_vol_option_ambiguous_volinfo(self): + def test_get_vol_option_via_info_ambiguous_volinfo(self): def xml_output(*ignore_args, **ignore_kwargs): return """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <cliOutput> + <opRet>0</opRet> + <opErrno>0</opErrno> + <opErrstr/> <volInfo> <volumes> <count>0</count> @@ -248,16 +357,19 @@ class GlusterManagerTestCase(test.TestCase): self.mock_object(self._gluster_manager, 'gluster_call', mock.Mock(side_effect=xml_output)) self.assertRaises(exception.InvalidShare, - self._gluster_manager.get_vol_option, - NFS_EXPORT_DIR) + self._gluster_manager._get_vol_option_via_info, + 'foobar') self._gluster_manager.gluster_call.assert_called_once_with( *args, log=mock.ANY) - def test_get_vol_option_trivial_volinfo(self): + def test_get_vol_option_via_info_trivial_volinfo(self): def xml_output(*ignore_args, **ignore_kwargs): return """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <cliOutput> + <opRet>0</opRet> + <opErrno>0</opErrno> + <opErrstr/> <volInfo> <volumes> <volume> @@ -270,23 +382,26 @@ class GlusterManagerTestCase(test.TestCase): args = ('--xml', 'volume', 'info', self._gluster_manager.volume) self.mock_object(self._gluster_manager, 'gluster_call', mock.Mock(side_effect=xml_output)) - ret = self._gluster_manager.get_vol_option(NFS_EXPORT_DIR) + ret = self._gluster_manager._get_vol_option_via_info('foobar') self.assertIsNone(ret) self._gluster_manager.gluster_call.assert_called_once_with( *args, log=mock.ANY) - def test_get_vol_option(self): + def test_get_vol_option_via_info(self): def xml_output(*ignore_args, **ignore_kwargs): return """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <cliOutput> + <opRet>0</opRet> + <opErrno>0</opErrno> + <opErrstr/> <volInfo> <volumes> <volume> <options> <option> - <name>nfs.export-dir</name> - <value>/foo(10.0.0.1|10.0.0.2),/bar(10.0.0.1)</value> + <name>foobar</name> + <value>FIRE MONKEY!</value> </option> </options> </volume> @@ -298,15 +413,158 @@ class GlusterManagerTestCase(test.TestCase): args = ('--xml', 'volume', 'info', self._gluster_manager.volume) self.mock_object(self._gluster_manager, 'gluster_call', mock.Mock(side_effect=xml_output)) - ret = self._gluster_manager.get_vol_option(NFS_EXPORT_DIR) - self.assertEqual('/foo(10.0.0.1|10.0.0.2),/bar(10.0.0.1)', ret) + ret = self._gluster_manager._get_vol_option_via_info('foobar') + self.assertEqual('FIRE MONKEY!', ret) self._gluster_manager.gluster_call.assert_called_once_with( *args, log=mock.ANY) + def test_get_vol_user_option(self): + self.mock_object(self._gluster_manager, '_get_vol_option_via_info', + mock.Mock(return_value='VALUE')) + + ret = self._gluster_manager._get_vol_user_option('OPT') + + self.assertEqual(ret, 'VALUE') + (self._gluster_manager._get_vol_option_via_info. + assert_called_once_with('user.OPT')) + + def test_get_vol_regular_option_empty_reponse(self): + args = ('--xml', 'volume', 'get', self._gluster_manager.volume, + NFS_EXPORT_DIR) + self.mock_object(self._gluster_manager, 'gluster_call', + mock.Mock(return_value=('', {}))) + + ret = self._gluster_manager._get_vol_regular_option(NFS_EXPORT_DIR) + + self.assertIsNone(ret) + self._gluster_manager.gluster_call.assert_called_once_with( + *args, check_exit_code=False) + + @ddt.data(0, 2) + def test_get_vol_regular_option_ambiguous_volinfo(self, count): + + def xml_output(*ignore_args, **ignore_kwargs): + return """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<cliOutput> + <opRet>0</opRet> + <opErrno>0</opErrno> + <opErrstr/> + <volGetopts> + <count>%d</count> + </volGetopts> +</cliOutput>""" % count, '' + + args = ('--xml', 'volume', 'get', self._gluster_manager.volume, + NFS_EXPORT_DIR) + self.mock_object(self._gluster_manager, 'gluster_call', + mock.Mock(side_effect=xml_output)) + + self.assertRaises(exception.InvalidShare, + self._gluster_manager._get_vol_regular_option, + NFS_EXPORT_DIR) + + self._gluster_manager.gluster_call.assert_called_once_with( + *args, check_exit_code=False) + + def test_get_vol_regular_option(self): + + def xml_output(*ignore_args, **ignore_kwargs): + return """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<cliOutput> + <opRet>0</opRet> + <opErrno>0</opErrno> + <opErrstr/> + <volGetopts> + <count>1</count> + <Option>nfs.export-dir</Option> + <Value>/foo(10.0.0.1|10.0.0.2),/bar(10.0.0.1)</Value> + </volGetopts> +</cliOutput>""", '' + + args = ('--xml', 'volume', 'get', self._gluster_manager.volume, + NFS_EXPORT_DIR) + self.mock_object(self._gluster_manager, 'gluster_call', + mock.Mock(side_effect=xml_output)) + + ret = self._gluster_manager._get_vol_regular_option(NFS_EXPORT_DIR) + + self.assertEqual('/foo(10.0.0.1|10.0.0.2),/bar(10.0.0.1)', ret) + self._gluster_manager.gluster_call.assert_called_once_with( + *args, check_exit_code=False) + + def test_get_vol_regular_option_not_suppored(self): + args = ('--xml', 'volume', 'get', self._gluster_manager.volume, + NFS_EXPORT_DIR) + self.mock_object(self._gluster_manager, 'gluster_call', + mock.Mock(return_value=( + """Ceci n'est pas un XML.""", ''))) + self.mock_object(self._gluster_manager, '_get_vol_option_via_info', + mock.Mock(return_value="VALUE")) + + ret = self._gluster_manager._get_vol_regular_option(NFS_EXPORT_DIR) + + self.assertEqual("VALUE", ret) + self._gluster_manager.gluster_call.assert_called_once_with( + *args, check_exit_code=False) + (self._gluster_manager._get_vol_option_via_info. + assert_called_once_with(NFS_EXPORT_DIR)) + + @ddt.data({'opt': 'some.option', 'opttype': 'regular', + 'lowopt': 'some.option'}, + {'opt': 'user.param', 'opttype': 'user', 'lowopt': 'param'}) + @ddt.unpack + def test_get_vol_option(self, opt, opttype, lowopt): + for t in ('user', 'regular'): + self.mock_object(self._gluster_manager, '_get_vol_%s_option' % t, + mock.Mock(return_value='value-%s' % t)) + + ret = self._gluster_manager.get_vol_option(opt) + + self.assertEqual('value-%s' % opttype, ret) + for t in ('user', 'regular'): + func = getattr(self._gluster_manager, '_get_vol_%s_option' % t) + if opttype == t: + func.assert_called_once_with(lowopt) + else: + self.assertFalse(func.called) + + def test_get_vol_option_unset(self): + self.mock_object(self._gluster_manager, '_get_vol_regular_option', + mock.Mock(return_value=None)) + + ret = self._gluster_manager.get_vol_option('some.option') + + self.assertIsNone(ret) + + @ddt.data({'value': '0', 'boolval': False}, + {'value': 'Off', 'boolval': False}, + {'value': 'no', 'boolval': False}, + {'value': '1', 'boolval': True}, + {'value': 'true', 'boolval': True}, + {'value': 'enAble', 'boolval': True}, + {'value': None, 'boolval': None}) + @ddt.unpack + def test_get_vol_option_boolean(self, value, boolval): + self.mock_object(self._gluster_manager, '_get_vol_regular_option', + mock.Mock(return_value=value)) + + ret = self._gluster_manager.get_vol_option('some.option', + boolean=True) + + self.assertEqual(boolval, ret) + + def test_get_vol_option_boolean_bad(self): + self.mock_object(self._gluster_manager, '_get_vol_regular_option', + mock.Mock(return_value='jabberwocky')) + + self.assertRaises(exception.GlusterfsException, + self._gluster_manager.get_vol_option, + 'some.option', boolean=True) + @ddt.data({'setting': 'some_value', 'args': ('set', 'some_value')}, {'setting': None, 'args': ('reset',)}, - {'setting': True, 'args': ('set', 'on')}, - {'setting': False, 'args': ('set', 'off')}) + {'setting': True, 'args': ('set', 'ON')}, + {'setting': False, 'args': ('set', 'OFF')}) @ddt.unpack def test_set_vol_option(self, setting, args): self.mock_object(self._gluster_manager, 'gluster_call', mock.Mock()) diff --git a/manila/tests/share/drivers/glusterfs/test_layout_volume.py b/manila/tests/share/drivers/glusterfs/test_layout_volume.py index 5a4d326b6c..8c03b065cf 100644 --- a/manila/tests/share/drivers/glusterfs/test_layout_volume.py +++ b/manila/tests/share/drivers/glusterfs/test_layout_volume.py @@ -913,7 +913,7 @@ class GlusterfsVolumeMappedLayoutTestCase(test.TestCase): (self._layout._find_actual_backend_snapshot_name. assert_called_once_with(gmgr1, snapshot)) - @ddt.data({'side_effect': (glusterXMLOut(ret=-1, errno=2),), + @ddt.data({'side_effect': (glusterXMLOut(ret=-1, errno=0),), '_exception': exception.GlusterfsException}, {'side_effect': (('', ''),), '_exception': exception.GlusterfsException}) diff --git a/manila/tests/share/drivers/test_glusterfs.py b/manila/tests/share/drivers/test_glusterfs.py index 39368ee5a5..f57fa213a0 100644 --- a/manila/tests/share/drivers/test_glusterfs.py +++ b/manila/tests/share/drivers/test_glusterfs.py @@ -116,31 +116,31 @@ class GlusterfsShareDriverTestCase(test.TestCase): helpercls = mock.Mock(return_value=helper) self._driver.nfs_helper = helpercls if helpercls == glusterfs.GlusterNFSHelper and path is None: - gmgr.get_vol_option = mock.Mock(return_value='on') + gmgr.get_vol_option = mock.Mock(return_value=True) self._driver._setup_via_manager( {'manager': gmgr, 'share': self.share}) if helpercls == glusterfs.GlusterNFSHelper and path is None: gmgr.get_vol_option.assert_called_once_with( - NFS_EXPORT_VOL) + NFS_EXPORT_VOL, boolean=True) args = (NFS_RPC_AUTH_REJECT, '*') else: args = (NFS_EXPORT_VOL, False) gmgr.set_vol_option.assert_called_once_with(*args) - @ddt.data('off', 'no', '0', 'false', 'disable', 'foobarbaz') - def test_setup_via_manager_export_volumes_on(self, export_vol): + def test_setup_via_manager_export_volumes_off(self): gmgr = mock.Mock() gmgr.path = None - gmgr.get_vol_option = mock.Mock(return_value=export_vol) + gmgr.get_vol_option = mock.Mock(return_value=False) self._driver.nfs_helper = glusterfs.GlusterNFSHelper self.assertRaises(exception.GlusterfsException, self._driver._setup_via_manager, {'manager': gmgr, 'share': self.share}) - gmgr.get_vol_option.assert_called_once_with(NFS_EXPORT_VOL) + gmgr.get_vol_option.assert_called_once_with(NFS_EXPORT_VOL, + boolean=True) def test_check_for_setup_error(self): self._driver.check_for_setup_error() diff --git a/manila/tests/share/drivers/test_glusterfs_native.py b/manila/tests/share/drivers/test_glusterfs_native.py index 731f06ae92..13f11ea35a 100644 --- a/manila/tests/share/drivers/test_glusterfs_native.py +++ b/manila/tests/share/drivers/test_glusterfs_native.py @@ -227,18 +227,17 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase): self.assertEqual(expected_exec, fake_utils.fake_execute_get_log()) - @ddt.data('on', '1', 'Yes', 'TRUE', 'enable') - def test_deny_access_via_manager(self, trueish): + def test_deny_access_via_manager(self): self.mock_object(common, '_restart_gluster_vol', mock.Mock()) access = {'access_type': 'cert', 'access_to': 'client.example.com'} gmgr1 = common.GlusterManager(self.glusterfs_target1, self._execute, None, None) - def _get_vol_option(opt): + def _get_vol_option(opt, **kw): if opt == 'auth.ssl-allow': return('some.common.name,' + access['access_to']) elif opt == 'server.dynamic-auth': - return trueish + return True self.mock_object( gmgr1, 'get_vol_option', @@ -250,23 +249,24 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase): self.share1, access) gmgr1.get_vol_option.assert_has_calls( - [mock.call(a) for a in ('auth.ssl-allow', 'server.dynamic-auth')]) + [mock.call(a, **kw) for a, kw in ( + ('auth.ssl-allow', {}), + ('server.dynamic-auth', {'boolean': True}))]) test_args = ('auth.ssl-allow', 'some.common.name') gmgr1.set_vol_option.assert_called_once_with(*test_args) self.assertFalse(common._restart_gluster_vol.called) - @ddt.data('off', None, 'strangelove') - def test_deny_access_via_manager_no_dyn_auth(self, falseish): + def test_deny_access_via_manager_no_dyn_auth(self): self.mock_object(common, '_restart_gluster_vol', mock.Mock()) access = {'access_type': 'cert', 'access_to': 'client.example.com'} gmgr1 = common.GlusterManager(self.glusterfs_target1, self._execute, None, None) - def _get_vol_option(opt): + def _get_vol_option(opt, **kw): if opt == 'auth.ssl-allow': return('some.common.name,' + access['access_to']) elif opt == 'server.dynamic-auth': - return falseish + return False self.mock_object( gmgr1, 'get_vol_option', @@ -278,7 +278,9 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase): self.share1, access) gmgr1.get_vol_option.assert_has_calls( - [mock.call(a) for a in ('auth.ssl-allow', 'server.dynamic-auth')]) + [mock.call(a, **kw) for a, kw in ( + ('auth.ssl-allow', {}), + ('server.dynamic-auth', {'boolean': True}))]) test_args = ('auth.ssl-allow', 'some.common.name') gmgr1.set_vol_option.assert_called_once_with(*test_args) common._restart_gluster_vol.assert_called_once_with(gmgr1)