From a338bc6331d0ba279126877368bf31bfd7170e05 Mon Sep 17 00:00:00 2001 From: nicholasyang Date: Thu, 2 Jan 2025 08:52:06 +0800 Subject: [PATCH] Dev: add pre-migration checks for pacemaker version (jsc#PED-11808) --- crmsh/migration.py | 48 ++++++++++++++++++++++++-------- test/unittests/test_migration.py | 28 +++++++++++++++++++ 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/crmsh/migration.py b/crmsh/migration.py index 3f602f663..1e80aafde 100644 --- a/crmsh/migration.py +++ b/crmsh/migration.py @@ -226,21 +226,47 @@ def check_dependency_version(handler: CheckResultHandler): handler.log_info('Checking dependency version...') shell = sh.LocalShell() out = shell.get_stdout_or_raise_error(None, 'corosync -v') - match = re.search(r"version\s+'(\d+(?:\.\d+)*)'", out) - corosync_supported = True + _check_version_range( + handler, + 'Corosync', (2, 4, 6), (3,), + re.compile(r"version\s+'(\d+(?:\.\d+)*)'"), + shell.get_stdout_or_raise_error(None, 'corosync -v'), + ) + _check_version_range( + handler, + 'Pacemaker', (2, 1, 7), (3,), + re.compile(r"^Pacemaker\s+(\d+(?:\.\d+)*)"), + shell.get_stdout_or_raise_error(None, 'pacemakerd --version'), + ) + + +def _check_version_range( + handler: CheckResultHandler, component_name: str, + minimum: tuple, maximum: tuple, + pattern, + text: str, +): + match = pattern.search(text) if not match: - corosync_supported = False - else: - version = tuple(int(x) for x in match.group(1).split('.')) - if not (2, 4, 6) <= version < (3,): - corosync_supported = False - if not corosync_supported: handler.handle_problem( - False, 'Corosync version not supported', [ - 'Supported version: 2.4.6 <= corosync < 3', - f'Actual version: corosync == {match.group(1)}', + False, f'{component_name} version not supported', [ + 'Unknown version:', + text, ], ) + else: + version = tuple(int(x) for x in match.group(1).split('.')) + if not minimum <= version < maximum: + handler.handle_problem( + False, f'{component_name} version not supported', [ + 'Supported version: {} <= {} < {}'.format( + '.'.join(str(x) for x in minimum), + component_name, + '.'.join(str(x) for x in maximum) + ), + f'Actual version: {component_name} == {match.group(1)}', + ], + ) def check_service_status(handler: CheckResultHandler): diff --git a/test/unittests/test_migration.py b/test/unittests/test_migration.py index 76f212871..65a6c9ccc 100644 --- a/test/unittests/test_migration.py +++ b/test/unittests/test_migration.py @@ -1,3 +1,4 @@ +import re import unittest from unittest import mock @@ -13,3 +14,30 @@ def test_load_supported_resource_agents(self): self.assertIn(cibquery.ResourceAgent('ocf', 'heartbeat', 'IPaddr2'), s) self.assertIn(cibquery.ResourceAgent('stonith', None, 'fence_sbd'), s) self.assertNotIn(cibquery.ResourceAgent('foo', None, 'bar'), s) + + def test_check_version_range(self): + def check_fn(x): + migration._check_version_range( + self._handler, + 'foo', + (0, 2,), + (1,), + re.compile(r'^foo\s+(\d+(?:.\d+)*)'), + x, + ) + check_fn('foo 0.2') + self._handler.handle_problem.assert_not_called() + check_fn('foo 0.2.1') + self._handler.handle_problem.assert_not_called() + check_fn('foo 0.9') + self._handler.handle_problem.assert_not_called() + check_fn('foo 0.9.99') + self._handler.handle_problem.assert_not_called() + check_fn('foo 1') + self._handler.handle_problem.assert_called() + self._handler.handle_problem.reset_mock() + check_fn('foo 1.0') + self._handler.handle_problem.assert_called() + self._handler.handle_problem.reset_mock() + check_fn('foo 1.0.0') + self._handler.handle_problem.assert_called()