diff --git a/cvs/lib/parallel_ssh_lib.py b/cvs/lib/parallel_ssh_lib.py index 71a5a231e..f11e8093f 100755 --- a/cvs/lib/parallel_ssh_lib.py +++ b/cvs/lib/parallel_ssh_lib.py @@ -169,11 +169,25 @@ def exec(self, cmd, timeout=None, print_console=True): Returns a dictionary of host as key and command output as values """ print(f'cmd = {cmd}') + + # Log command execution + if self.log: + if timeout is not None: + self.log.debug(f"Executing command on {len(self.reachable_hosts)} host(s) [timeout={timeout}s]: {cmd}") + else: + self.log.debug(f"Executing command on {len(self.reachable_hosts)} host(s): {cmd}") + if timeout is None: output = self.client.run_command(cmd, stop_on_errors=self.stop_on_errors) else: output = self.client.run_command(cmd, read_timeout=timeout, stop_on_errors=self.stop_on_errors) cmd_output = self._process_output(output, cmd=cmd, print_console=print_console) + + # Log per-host execution completion + if self.log: + for host in cmd_output.keys(): + self.log.debug(f"Command completed on {host}: {cmd}") + return cmd_output def exec_cmd_list(self, cmd_list, timeout=None, print_console=True): @@ -183,6 +197,14 @@ def exec_cmd_list(self, cmd_list, timeout=None, print_console=True): Returns a dictionary of host as key and command output as values """ print(cmd_list) + + # Log command list execution + if self.log: + if timeout is not None: + self.log.debug(f"Executing command list on {len(self.reachable_hosts)} host(s) [timeout={timeout}s]") + else: + self.log.debug(f"Executing command list on {len(self.reachable_hosts)} host(s)") + if timeout is None: output = self.client.run_command('%s', host_args=cmd_list, stop_on_errors=self.stop_on_errors) else: @@ -190,6 +212,12 @@ def exec_cmd_list(self, cmd_list, timeout=None, print_console=True): '%s', host_args=cmd_list, read_timeout=timeout, stop_on_errors=self.stop_on_errors ) cmd_output = self._process_output(output, cmd_list=cmd_list, print_console=print_console) + + # Log per-host command execution + if self.log: + for host, cmd in zip(self.reachable_hosts, cmd_list): + self.log.debug(f"Command on {host}: {cmd}") + return cmd_output def scp_file(self, local_file, remote_file, recurse=False): diff --git a/cvs/lib/unittests/test_parallel_ssh_lib.py b/cvs/lib/unittests/test_parallel_ssh_lib.py index 23b70a3c7..5a5966596 100644 --- a/cvs/lib/unittests/test_parallel_ssh_lib.py +++ b/cvs/lib/unittests/test_parallel_ssh_lib.py @@ -10,7 +10,8 @@ def setUp(self, mock_pssh_client): mock_pssh_client.return_value = self.mock_client self.mock_pssh_client = mock_pssh_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") def test_exec_successful(self): # Test: Execute command successfully on all hosts @@ -89,7 +90,8 @@ def test_exec_with_pruning_unreachable_host(self, mock_check_connectivity, mock_ self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity from pssh.exceptions import ConnectionError @@ -127,7 +129,8 @@ def test_exec_no_pruning_when_reachable(self, mock_check_connectivity, mock_pssh self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity from pssh.exceptions import Timeout @@ -165,7 +168,8 @@ def test_exec_pruning_with_multiple_unreachable_hosts(self, mock_check_connectiv self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2", "host3"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity from pssh.exceptions import ConnectionError @@ -211,7 +215,8 @@ def test_exec_no_pruning_on_timeout_exception_reachable(self, mock_pssh_client, self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False from pssh.exceptions import Timeout @@ -248,7 +253,8 @@ def test_exec_pruning_on_timeout_exception_unreachable(self, mock_pssh_client, m self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False from pssh.exceptions import Timeout @@ -355,7 +361,8 @@ def setUp(self, mock_pssh_client): mock_pssh_client.return_value = self.mock_client self.mock_pssh_client = mock_pssh_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") def test_exec_cmd_list_successful(self): # Test: Execute different commands on different hosts successfully @@ -437,7 +444,8 @@ def test_exec_cmd_list_no_pruning_when_reachable(self, mock_check_connectivity, self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity cmd_list = ["echo success", "echo fail"] @@ -476,7 +484,8 @@ def test_exec_cmd_list_pruning_on_timeout_exception_unreachable(self, mock_check self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity cmd_list = ["echo success", "echo fail"] @@ -516,7 +525,8 @@ def test_exec_cmd_list_with_pruning(self, mock_check_connectivity, mock_pssh_cli self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity cmd_list = ["echo success", "echo fail"] @@ -554,7 +564,8 @@ def test_exec_cmd_list_pruning_with_multiple_unreachable_hosts(self, mock_check_ self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2", "host3"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity cmd_list = ["echo success", "echo fail1", "echo fail2"] @@ -603,7 +614,8 @@ def test_exec_cmd_list_no_pruning_on_connection_error_when_reachable( self.mock_client = MagicMock() mock_pssh_client.return_value = self.mock_client self.host_list = ["host1", "host2"] - self.pssh = Pssh("log", self.host_list, user="user", password="pass") + self.mock_log = MagicMock() + self.pssh = Pssh(self.mock_log, self.host_list, user="user", password="pass") self.pssh.stop_on_errors = False self.pssh.check_connectivity = mock_check_connectivity cmd_list = ["echo success", "echo fail"]