diff --git a/hooks/pcmk.py b/hooks/pcmk.py index e418f8a..c71dff5 100644 --- a/hooks/pcmk.py +++ b/hooks/pcmk.py @@ -39,6 +39,10 @@ class PropertyNotFound(Exception): pass +class PcmkError(Exception): + pass + + def wait_for_pcmk(retries=12, sleep=10): crm_up = None hostname = socket.gethostname() @@ -85,11 +89,23 @@ def online(node=None): def crm_opt_exists(opt_name): - output = commands.getstatusoutput("crm configure show")[1] - if opt_name in output: - return True + (code, output) = commands.getstatusoutput("crm configure show xml") + if code != 0: + raise PcmkError(code, output) - return False + tree = etree.parse(StringIO(output)) + root = tree.getroot() + resources = root.find('configuration').find('resources') + result = resources.findall(".//*[@id='%s']" % opt_name) + if len(result) == 0: + return False + elif len(result) == 1: + return True + else: + log('crm configure show xml:\n%s' % output, level=DEBUG) + raise PcmkError(len(result), + "Found more %d elements with id '%s'" % (len(result), + opt_name)) def crm_res_running(opt_name): diff --git a/unit_tests/test_hacluster_hooks.py b/unit_tests/test_hacluster_hooks.py index 7f2dc05..b5f9729 100644 --- a/unit_tests/test_hacluster_hooks.py +++ b/unit_tests/test_hacluster_hooks.py @@ -279,6 +279,7 @@ class TestHooks(test_utils.CharmTestCase): @mock.patch.object(hooks, 'maintenance_mode') @mock.patch.object(hooks, 'is_leader') @mock.patch.object(hooks, 'update_nrpe_config') + @mock.patch('pcmk.crm_opt_exists') @mock.patch('pcmk.commit') @mock.patch('pcmk.wait_for_pcmk') @mock.patch.object(hooks, 'configure_corosync') @@ -289,11 +290,11 @@ class TestHooks(test_utils.CharmTestCase): def test_config_changed(self, mock_mkdir, mock_rsync, mock_config, mock_os_mkdir, mock_configure_corosync, mock_wait_for_pcmk, mock_pcmk_commit, - mock_update_nrpe_config, mock_is_leader, - mock_maintenance_mode, - mock_hanode_relation_joined, - mock_relation_ids): + mock_crm_opt_exists, mock_update_nrpe_config, + mock_is_leader, mock_maintenance_mode, + mock_hanode_relation_joined, mock_relation_ids): + mock_crm_opt_exists.return_value = False mock_config.side_effect = self.test_config.get mock_relation_ids.return_value = ['hanode:1'] mock_wait_for_pcmk.return_value = True diff --git a/unit_tests/test_pcmk.py b/unit_tests/test_pcmk.py index 25fb526..728277b 100644 --- a/unit_tests/test_pcmk.py +++ b/unit_tests/test_pcmk.py @@ -73,6 +73,70 @@ CRM_CONFIGURE_SHOW_XML_MAINT_MODE_TRUE = ''' ''' # noqa +CRM_CONFIGURE_SHOW_XML_OPT_EXISTS = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +''' # noqa + class TestPcmk(unittest.TestCase): def setUp(self): @@ -192,3 +256,16 @@ class TestPcmk(unittest.TestCase): self.assertEqual(f.read(), ('primitive res_test IPaddr2 \\\n' '\tparams ip=1.2.3.4 cidr_netmask=255.255.0.0')) + + @mock.patch('commands.getstatusoutput') + def test_crm_opt_exists(self, mock_getstatusoutput): + mock_getstatusoutput.return_value = (0, + CRM_CONFIGURE_SHOW_XML_OPT_EXISTS) + self.assertTrue(pcmk.crm_opt_exists( + 'res_ceph-radosgw_public_hostname')) + self.assertFalse(pcmk.crm_opt_exists('foobar')) + self.assertFalse(pcmk.crm_opt_exists('rsc-options')) + + mock_getstatusoutput.return_value = (1, 'error') + self.assertRaises(pcmk.PcmkError, pcmk.crm_opt_exists, + 'res_ceph-radosgw_public_hostname')