From b06e4f126601b4761265e06c82ea70d4f7d7e952 Mon Sep 17 00:00:00 2001 From: tallison Date: Tue, 30 Jul 2024 16:56:19 -0400 Subject: [PATCH] Add list unit files integration test Fixes: https://github.com/eclipse-bluechi/bluechi/issues/889 Signed-off-by: tallison --- tests/bluechi_test/bluechictl.py | 18 +++ tests/bluechi_test/systemctl.py | 11 ++ .../main.fmf | 3 + ...st_bluechi_list_unit_files_on_all_nodes.py | 117 ++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/main.fmf create mode 100644 tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/test_bluechi_list_unit_files_on_all_nodes.py diff --git a/tests/bluechi_test/bluechictl.py b/tests/bluechi_test/bluechictl.py index 6c40afecef..b64a3d8f4f 100644 --- a/tests/bluechi_test/bluechictl.py +++ b/tests/bluechi_test/bluechictl.py @@ -55,6 +55,24 @@ def list_units( "Listing units on all nodes", "list-units", check_result, expected_result ) + def list_unit_files( + self, node_name: str = None, check_result: bool = True, expected_result: int = 0 + ) -> Tuple[Optional[int], Union[Iterator[bytes], Any, Tuple[bytes, bytes]]]: + if node_name: + return self._run( + f"Listing unit files on node {node_name}", + f"list-unit-files {node_name}", + check_result, + expected_result, + ) + + return self._run( + "Listing unit files on all nodes", + "list-unit-files", + check_result, + expected_result, + ) + def get_unit_status( self, node_name: str, diff --git a/tests/bluechi_test/systemctl.py b/tests/bluechi_test/systemctl.py index fadf6304af..a1f84a32f8 100644 --- a/tests/bluechi_test/systemctl.py +++ b/tests/bluechi_test/systemctl.py @@ -157,3 +157,14 @@ def list_units( ) -> Tuple[Optional[int], Union[Iterator[bytes], Any, Tuple[bytes, bytes]]]: command = "list-units --legend=false{}".format(" --all" if all_units else "") return self._do_operation(command, check_result, expected_result) + + def list_unit_files( + self, + all_unit_files: bool = False, + check_result: bool = True, + expected_result: int = 0, + ) -> Tuple[Optional[int], Union[Iterator[bytes], Any, Tuple[bytes, bytes]]]: + command = "list-unit-files --legend=false{}".format( + " -all" if all_unit_files else "" + ) + return self._do_operation(command, check_result, expected_result) diff --git a/tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/main.fmf b/tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/main.fmf new file mode 100644 index 0000000000..42aca954e9 --- /dev/null +++ b/tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/main.fmf @@ -0,0 +1,3 @@ +summary: Test if bluechi list-unit-files returns the same list of unit files from all + nodes which can be gathered by running systemctl on each node +id: f3b40b8e-756b-4508-9d37-f21a1fac59ee diff --git a/tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/test_bluechi_list_unit_files_on_all_nodes.py b/tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/test_bluechi_list_unit_files_on_all_nodes.py new file mode 100644 index 0000000000..d79855459d --- /dev/null +++ b/tests/tests/tier0/bluechi-list-unit-files-on-all-nodes/test_bluechi_list_unit_files_on_all_nodes.py @@ -0,0 +1,117 @@ +# +# Copyright Contributors to the Eclipse BlueChi project +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +import re +from typing import Dict + +from bluechi_test.config import BluechiAgentConfig, BluechiControllerConfig +from bluechi_test.machine import BluechiAgentMachine, BluechiControllerMachine +from bluechi_test.test import BluechiTest + +node_foo_name = "node-foo" +node_bar_name = "node-bar" + + +def parse_bluechictl_output(output: str) -> Dict[str, Dict[str, str]]: + line_pat = re.compile( + r"""\s*(?P[\S]+)\s*\| + \s*((?:[^/]*/)*)(?P[\S]+)\s*\| + \s*(?P[\S]+)\s*""", + re.VERBOSE, + ) + result = {} + for line in output.splitlines(): + if line.startswith("NODE ") or line.startswith("===="): + continue + + match = line_pat.match(line) + if not match: + raise Exception( + f"Error parsing bluechictl list-unit-files output, invalid line: '{line}'" + ) + + node_unit_files = result.get(match.group("node_name")) + if not node_unit_files: + node_unit_files = {} + result[match.group("node_name")] = node_unit_files + + if match.group("unit_file_path") in node_unit_files: + raise Exception( + f"Error parsing bluechictl list-unit-files output, unit file already reported, line: '{line}'" + ) + + node_unit_files[match.group("unit_file_path")] = match.group( + "enablement_status" + ) + + return result + + +def verify_unit_files(all_unit_files: Dict[str, str], output: str, node_name: str): + esc_seq = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") + line_pat = re.compile( + r"""\s*(?P\S+) + \s+(?P\S+) + \s+.*$ + """, + re.VERBOSE, + ) + for line in output.splitlines(): + line = esc_seq.sub("", line) + + match = line_pat.match(line) + if not match: + raise Exception( + f"Error parsing systemctl list-unit-files output, invalid line: '{line}'" + ) + + found = all_unit_files.get(match.group("unit_file_path")) + if not found or match.group("enablement_status") != found: + raise Exception( + "Unit file '{}' with enablement status '{}' reported by systemctl" + " on node '{}', but not reported by bluechictl".format( + match.group("unit_file_path"), + match.group("enablement_status"), + node_name, + ) + ) + + +def exec(ctrl: BluechiControllerMachine, nodes: Dict[str, BluechiAgentMachine]): + node_foo = nodes[node_foo_name] + node_bar = nodes[node_bar_name] + + all_res, all_out = ctrl.bluechictl.list_unit_files() + assert all_res == 0 + all_unit_files = parse_bluechictl_output(all_out) + + foo_res, foo_out = node_foo.systemctl.list_unit_files() + assert foo_res == 0 + verify_unit_files(all_unit_files[node_foo_name], foo_out, node_foo_name) + + bar_res, bar_out = node_bar.systemctl.list_unit_files() + assert bar_res == 0 + verify_unit_files(all_unit_files[node_bar_name], bar_out, node_bar_name) + + +def test_bluechi_list_unit_files_on_all_nodes( + bluechi_test: BluechiTest, + bluechi_ctrl_default_config: BluechiControllerConfig, + bluechi_node_default_config: BluechiAgentConfig, +): + + node_foo_cfg = bluechi_node_default_config.deep_copy() + node_foo_cfg.node_name = node_foo_name + + node_bar_cfg = bluechi_node_default_config.deep_copy() + node_bar_cfg.node_name = node_bar_name + + bluechi_ctrl_default_config.allowed_node_names = [node_foo_name, node_bar_name] + + bluechi_test.set_bluechi_controller_config(bluechi_ctrl_default_config) + bluechi_test.add_bluechi_agent_config(node_foo_cfg) + bluechi_test.add_bluechi_agent_config(node_bar_cfg) + + bluechi_test.run(exec)