From 1347ea00c1627566d5c9ed3efadb098a52d42c23 Mon Sep 17 00:00:00 2001 From: Felipe Reyes Date: Fri, 6 May 2022 17:33:51 -0400 Subject: [PATCH] Drop the use of 'crm node status' on jammy. The version of crmsh available on jammy doesn't have the 'crm node status' subcommand available since it was removed[0], this change uses the command 'crm node attribute' to figure out if the node is in standby mode when running on ubuntu>=jammy, and 'crm node show' to get the list of nodes. [0] https://github.com/ClusterLabs/crmsh/pull/753 Change-Id: Iafb711be220573cb701527ec84de285edd8942cf Closes-Bug: #1972022 --- hooks/pcmk.py | 20 ++++++++++++++++---- hooks/utils.py | 29 ++++++++++++++++++++--------- unit_tests/test_hacluster_utils.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/hooks/pcmk.py b/hooks/pcmk.py index b7ffce2..a89e4c7 100644 --- a/hooks/pcmk.py +++ b/hooks/pcmk.py @@ -29,6 +29,10 @@ from charmhelpers.core.hookenv import ( DEBUG, WARNING, ) +from charmhelpers.core.host import ( + get_distrib_codename, + CompareHostReleases, +) class ServicesNotUp(Exception): @@ -183,10 +187,18 @@ def crm_res_running_on_node(resource, node): def list_nodes(): """List member nodes.""" - cmd = ['crm', 'node', 'status'] - out = subprocess.check_output(cmd).decode('utf-8') - tree = etree.fromstring(out) - nodes = [n.attrib['uname'] for n in tree.iter('node')] + nodes = [] + if CompareHostReleases(get_distrib_codename()) >= 'jammy': + cmd = ['crm', 'node', 'show'] + out = subprocess.check_output(cmd).decode('utf-8') + for line in out.strip().split('\n'): + nodes.append(line.split('(')[0]) + else: + cmd = ['crm', 'node', 'status'] + out = subprocess.check_output(cmd).decode('utf-8') + tree = etree.fromstring(out) + nodes = [n.attrib['uname'] for n in tree.iter('node')] + return sorted(nodes) diff --git a/hooks/utils.py b/hooks/utils.py index 508815a..32c16f5 100644 --- a/hooks/utils.py +++ b/hooks/utils.py @@ -71,6 +71,7 @@ from charmhelpers.core.host import ( file_hash, lsb_release, init_is_systemd, + get_distrib_codename, CompareHostReleases, ) from charmhelpers.fetch import ( @@ -1178,16 +1179,26 @@ def is_in_standby_mode(node_name): @param node_name: The name of the node to check @returns boolean - True if node_name is in standby mode """ - out = (subprocess - .check_output(['crm', 'node', 'status', node_name]) - .decode('utf-8')) - root = ET.fromstring(out) + if CompareHostReleases(get_distrib_codename()) >= 'jammy': + out = (subprocess.check_output(['crm', 'node', 'attribute', + node_name, 'show', 'standby']) + .decode('utf-8')) + attrs = {} + for item in re.split('[ ]+', out.strip()): + tokens = item.split('=') + attrs[tokens[0]] = tokens[1] + standby_mode = attrs['name'] == 'standby' and attrs['value'] == 'on' + else: + out = (subprocess + .check_output(['crm', 'node', 'status', node_name]) + .decode('utf-8')) + root = ET.fromstring(out) - standby_mode = False - for nvpair in root.iter('nvpair'): - if (nvpair.attrib.get('name') == 'standby' and - nvpair.attrib.get('value') == 'on'): - standby_mode = True + standby_mode = False + for nvpair in root.iter('nvpair'): + if (nvpair.attrib.get('name') == 'standby' and + nvpair.attrib.get('value') == 'on'): + standby_mode = True return standby_mode diff --git a/unit_tests/test_hacluster_utils.py b/unit_tests/test_hacluster_utils.py index 45a4bd6..8d109d6 100644 --- a/unit_tests/test_hacluster_utils.py +++ b/unit_tests/test_hacluster_utils.py @@ -26,6 +26,15 @@ import utils import pcmk +FOCAL_NODE_STATUS_STANDBY = ''' + + + + +''' +JAMMY_NODE_STATUS_STANDBY = 'scope=nodes name=standby value=on\n' + + def write_file(path, content, *args, **kwargs): with open(path, 'wt') as f: f.write(content) @@ -1367,3 +1376,23 @@ class UtilsTestCase(unittest.TestCase): 127, "fake crm command") with self.assertRaises(utils.RemoveCorosyncNodeFailed): utils.update_node_list() + + @mock.patch('subprocess.check_output', autospec=True) + @mock.patch.object(utils, 'get_distrib_codename', autospec=True) + def test_is_in_standby_mode(self, get_distrib_codename, check_output): + node_name = 'juju-3ff82c-focal-1' + get_distrib_codename.return_value = 'focal' + check_output.return_value = FOCAL_NODE_STATUS_STANDBY.encode() + self.assertTrue(utils.is_in_standby_mode(node_name)) + + check_output.assert_called_with(['crm', 'node', 'status', node_name]) + + get_distrib_codename.reset_mock() + check_output.reset_mock() + + get_distrib_codename.return_value = 'jammy' + check_output.return_value = JAMMY_NODE_STATUS_STANDBY.encode() + self.assertTrue(utils.is_in_standby_mode(node_name)) + + check_output.assert_called_with(['crm', 'node', 'attribute', node_name, + 'show', 'standby'])