Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2023-03-21 17:41:18 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/crmsh (Old) and /work/SRC/openSUSE:Factory/.crmsh.new.31432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh" Tue Mar 21 17:41:18 2023 rev:286 rq:1073057 version:4.5.0+20230320.5e777809 Changes: -------- --- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2023-03-09 17:46:52.759271772 +0100 +++ /work/SRC/openSUSE:Factory/.crmsh.new.31432/crmsh.changes 2023-03-21 17:41:22.765787616 +0100 @@ -1,0 +2,16 @@ +Mon Mar 20 08:53:40 UTC 2023 - xli...@suse.com + +- Update to version 4.5.0+20230320.5e777809: + * Dev: unittest: Adjust unit test for previous changes + * Fix: validate ssh session when the users is determined by guessing (bsc#1209193) + +------------------------------------------------------------------- +Tue Mar 14 06:42:32 UTC 2023 - xli...@suse.com + +- Update to version 4.5.0+20230314.c7422396: + * Dev: unittest: Adjust unit test for previous changes + * Fix: parallax: Use 'sudo bash -c' when executing commands via sudoer (bsc#1209192) + * Dev: qdevice: Add more debug messages for running commands + * Dev: log: For the log_only_to_file method, show debug log in debug mode + +------------------------------------------------------------------- Old: ---- crmsh-4.5.0+20230309.a4c4192d.tar.bz2 New: ---- crmsh-4.5.0+20230320.5e777809.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ crmsh.spec ++++++ --- /var/tmp/diff_new_pack.ucqFnk/_old 2023-03-21 17:41:23.509791175 +0100 +++ /var/tmp/diff_new_pack.ucqFnk/_new 2023-03-21 17:41:23.513791194 +0100 @@ -36,7 +36,7 @@ Summary: High Availability cluster command-line interface License: GPL-2.0-or-later Group: %{pkg_group} -Version: 4.5.0+20230309.a4c4192d +Version: 4.5.0+20230320.5e777809 Release: 0 URL: http://crmsh.github.io Source0: %{name}-%{version}.tar.bz2 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.ucqFnk/_old 2023-03-21 17:41:23.593791577 +0100 +++ /var/tmp/diff_new_pack.ucqFnk/_new 2023-03-21 17:41:23.597791596 +0100 @@ -9,7 +9,7 @@ </service> <service name="tar_scm"> <param name="url">https://github.com/ClusterLabs/crmsh.git</param> - <param name="changesrevision">1615c26abe860045bf4e15442899e992cc29455c</param> + <param name="changesrevision">4697fb58457de1acf83640116e145612d97e593f</param> </service> </servicedata> (No newline at EOF) ++++++ crmsh-4.5.0+20230309.a4c4192d.tar.bz2 -> crmsh-4.5.0+20230320.5e777809.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/crmsh/corosync.py new/crmsh-4.5.0+20230320.5e777809/crmsh/corosync.py --- old/crmsh-4.5.0+20230309.a4c4192d/crmsh/corosync.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/crmsh/corosync.py 2023-03-20 08:49:02.000000000 +0100 @@ -107,11 +107,7 @@ raise ValueError("host for qnetd not configured!") # Configure ssh passwordless to qnetd if detect password is needed - local_user = utils.user_of(utils.this_node()) - try: - remote_user = utils.user_of(qnetd_addr) - except ValueError: - remote_user = 'root' + local_user, remote_user = utils.user_pair_for_ssh(qnetd_addr) if utils.check_ssh_passwd_need(local_user, remote_user, qnetd_addr): utils.ssh_copy_id(local_user, remote_user, qnetd_addr) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/crmsh/healthcheck.py new/crmsh-4.5.0+20230320.5e777809/crmsh/healthcheck.py --- old/crmsh-4.5.0+20230309.a4c4192d/crmsh/healthcheck.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/crmsh/healthcheck.py 2023-03-20 08:49:02.000000000 +0100 @@ -145,8 +145,8 @@ remote_nodes = set(nodes) remote_nodes.remove(local_node) remote_nodes = list(remote_nodes) - local_user = crmsh.utils.user_of(local_node) - remote_users = [crmsh.utils.user_of(node) for node in remote_nodes] + local_user = crmsh.utils.user_pair_for_ssh(remote_nodes[0])[0] + remote_users = [crmsh.utils.user_pair_for_ssh(node)[1] for node in remote_nodes] crmsh.bootstrap.init_ssh_impl(local_user, remote_nodes, remote_users) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/crmsh/log.py new/crmsh-4.5.0+20230320.5e777809/crmsh/log.py --- old/crmsh-4.5.0+20230309.a4c4192d/crmsh/log.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/crmsh/log.py 2023-03-20 08:49:02.000000000 +0100 @@ -273,8 +273,12 @@ self.logger.addHandler(console_handler) def log_only_to_file(self, msg, level=logging.INFO): - with self.only_file(): - self.logger.log(level, msg) + from .config import core + if core.debug: + self.logger.log(logging.DEBUG, msg) + else: + with self.only_file(): + self.logger.log(level, msg) @contextmanager def buffer(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/crmsh/parallax.py new/crmsh-4.5.0+20230320.5e777809/crmsh/parallax.py --- old/crmsh-4.5.0+20230309.a4c4192d/crmsh/parallax.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/crmsh/parallax.py 2023-03-20 08:49:02.000000000 +0100 @@ -43,7 +43,7 @@ self.ssh_options = ['StrictHostKeyChecking=no', 'ConnectTimeout=10', 'LogLevel=error'] - sudoer = crmsh.utils.user_of(crmsh.utils.this_node()) + sudoer, _ = crmsh.utils.user_pair_for_ssh(self.nodes[0]) if sudoer is not None: # FIXME: this is really unreliable self.ssh_options.append('IdentityFile={}/.ssh/id_rsa'.format(userdir.gethomedir(sudoer))) @@ -65,9 +65,12 @@ def call(self): host_port_user = [] for host in self.nodes: - host_port_user.append([host, None, crmsh.utils.user_of(host)]) + _, remote_user = crmsh.utils.user_pair_for_ssh(host) + host_port_user.append([host, None, remote_user]) # FIXME: this is really unreliable - results = parallax.call(host_port_user, 'sudo {}'.format(self.cmd), self.opts) + sudoer = userdir.get_sudoer() + cmd = f'sudo bash -c "{self.cmd}"' if sudoer else self.cmd + results = parallax.call(host_port_user, cmd, self.opts) return self.handle(list(results.items())) def slurp(self): @@ -79,11 +82,13 @@ results = parallax.copy(self.nodes, self.src, self.dst, self.opts) return self.handle(list(results.items())) def run(self): - return parallax.run( - [[node, None, crmsh.utils.user_of(node)] for node in self.nodes], - 'sudo {}'.format(self.cmd), - self.opts, - ) + sudoer = userdir.get_sudoer() + cmd = f'sudo bash -c "{self.cmd}"' if sudoer else self.cmd + host_port_user = [] + for host in self.nodes: + _, remote_user = crmsh.utils.user_pair_for_ssh(host) + host_port_user.append([host, None, remote_user]) + return parallax.run(host_port_user, cmd, self.opts) def parallax_call(nodes, cmd, askpass=False, ssh_options=None, strict=True): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/crmsh/qdevice.py new/crmsh-4.5.0+20230320.5e777809/crmsh/qdevice.py --- old/crmsh-4.5.0+20230309.a4c4192d/crmsh/qdevice.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/crmsh/qdevice.py 2023-03-20 08:49:02.000000000 +0100 @@ -358,7 +358,7 @@ cmd = "corosync-qnetd-certutil -i" desc = "Step 1: Initialize database on {}".format(self.qnetd_addr) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc, cmd) parallax.parallax_call([self.qnetd_addr], cmd) def fetch_qnetd_crt_from_qnetd(self): @@ -371,7 +371,7 @@ return desc = "Step 2: Fetch {} from {}".format(self.qnetd_cacert_filename, self.qnetd_addr) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc) self._fetch_file_to_local(self.qnetd_addr, self.qnetd_cacert_on_qnetd, '{}/{}'.format(self.qdevice_path, self.qnetd_addr)) def copy_qnetd_crt_to_cluster(self): @@ -385,19 +385,19 @@ return desc = "Step 3: Copy exported {} to {}".format(self.qnetd_cacert_filename, node_list) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc) self._copy_file_to_remote_hosts(os.path.dirname(self.qnetd_cacert_on_local), node_list, self.qdevice_path) @classmethod def _copy_file_to_remote_host(cls, local_file, remote_host: str, remote_path): - remote_user = utils.user_of(remote_host) + local_user, remote_user = utils.user_pair_for_ssh(remote_host) with tempfile.NamedTemporaryFile('w', encoding='utf-8') as tmp: tmp.write("put -pr '{}' '{}'\n".format(local_file, remote_path)) tmp.flush() # we can not su to a non-root user, since reading the source file may need privilege. cmd = "sftp {} -o IdentityFile=~{}/.ssh/id_rsa -o BatchMode=yes -s 'sudo PATH=/usr/lib/ssh:/usr/libexec/ssh /bin/sh -c \"exec sftp-server\"' -b {} {}@{}".format( constants.SSH_OPTION, - utils.user_of(utils.this_node()), + local_user, # FIXME: sftp-server is not always in /usr/lib/ssh tmp.name, remote_user, cls._enclose_inet6_addr(remote_host), @@ -433,7 +433,7 @@ node_list = utils.list_cluster_nodes() cmd = "corosync-qdevice-net-certutil -i -c {}".format(self.qnetd_cacert_on_local) desc = "Step 4: Initialize database on {}".format(node_list) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc, cmd) parallax.parallax_call(node_list, cmd) def create_ca_request(self): @@ -444,8 +444,8 @@ /usr/sbin/corosync-qdevice-net-certutil -r -n Cluster (Cluster name must match cluster_name key in the corosync.conf) """ - logger_utils.log_only_to_file("Step 5: Generate certificate request {}".format(self.qdevice_crq_filename)) cmd = "corosync-qdevice-net-certutil -r -n {}".format(self.cluster_name) + QDevice.log_only_to_file("Step 5: Generate certificate request {}".format(self.qdevice_crq_filename), cmd) utils.get_stdout_or_raise_error(cmd) def copy_crq_to_qnetd(self): @@ -455,7 +455,7 @@ Copy exported CRQ to QNetd server """ desc = "Step 6: Copy {} to {}".format(self.qdevice_crq_filename, self.qnetd_addr) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc) self._copy_file_to_remote_host(self.qdevice_crq_on_local, self.qnetd_addr, self.qdevice_crq_on_qnetd) def sign_crq_on_qnetd(self): @@ -466,9 +466,9 @@ corosync-qnetd-certutil -s -c qdevice-net-node.crq -n Cluster """ desc = "Step 7: Sign and export cluster certificate on {}".format(self.qnetd_addr) - logger_utils.log_only_to_file(desc) cmd = "corosync-qnetd-certutil -s -c {} -n {}".\ format(self.qdevice_crq_on_qnetd, self.cluster_name) + QDevice.log_only_to_file(desc, cmd) parallax.parallax_call([self.qnetd_addr], cmd) def fetch_cluster_crt_from_qnetd(self): @@ -478,7 +478,7 @@ Copy exported CRT to node where certificate request was created """ desc = "Step 8: Fetch {} from {}".format(os.path.basename(self.qnetd_cluster_crt_on_qnetd), self.qnetd_addr) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc) self._fetch_file_to_local(self.qnetd_addr, self.qnetd_cluster_crt_on_qnetd, '{}/{}'.format(self.qdevice_path, self.qnetd_addr)) @classmethod @@ -502,8 +502,10 @@ Import certificate on node where certificate request was created by running /usr/sbin/corosync-qdevice-net-certutil -M -c cluster-Cluster.crt """ - logger_utils.log_only_to_file("Step 9: Import certificate file {} on local".format(os.path.basename(self.qnetd_cluster_crt_on_local))) cmd = "corosync-qdevice-net-certutil -M -c {}".format(self.qnetd_cluster_crt_on_local) + QDevice.log_only_to_file( + "Step 9: Import certificate file {} on local".format(os.path.basename(self.qnetd_cluster_crt_on_local)), + cmd) utils.get_stdout_or_raise_error(cmd) def copy_p12_to_cluster(self): @@ -517,7 +519,7 @@ return desc = "Step 10: Copy {} to {}".format(self.qdevice_p12_filename, node_list) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc) self._copy_file_to_remote_hosts(self.qdevice_p12_on_local, node_list, self.qdevice_p12_on_local) def import_p12_on_cluster(self): @@ -532,8 +534,8 @@ return desc = "Step 11: Import {} on {}".format(self.qdevice_p12_filename, node_list) - logger_utils.log_only_to_file(desc) cmd = "corosync-qdevice-net-certutil -m -c {}".format(self.qdevice_p12_on_local) + QDevice.log_only_to_file(desc, cmd) parallax.parallax_call(node_list, cmd) def certificate_process_on_init(self): @@ -562,7 +564,7 @@ return desc = "Step 1: Fetch {} from {}".format(self.qnetd_cacert_filename, self.cluster_node) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc) self._fetch_file_to_local(self.cluster_node, self.qnetd_cacert_on_local, '{}/{}'.format(self.qdevice_path, self.cluster_node)) def init_db_on_local(self): @@ -575,8 +577,8 @@ if os.path.exists(self.qdevice_db_path): utils.rmdir_r(self.qdevice_db_path) - logger_utils.log_only_to_file("Step 2: Initialize database on local") cmd = "corosync-qdevice-net-certutil -i -c {}".format(self.qnetd_cacert_on_cluster) + QDevice.log_only_to_file("Step 2: Initialize database on local", cmd) utils.get_stdout_or_raise_error(cmd) def fetch_p12_from_cluster(self): @@ -589,7 +591,7 @@ return desc = "Step 3: Fetch {} from {}".format(self.qdevice_p12_filename, self.cluster_node) - logger_utils.log_only_to_file(desc) + QDevice.log_only_to_file(desc) self._fetch_file_to_local(self.cluster_node, self.qdevice_p12_on_local, '{}/{}'.format(self.qdevice_path, self.cluster_node)) def import_p12_on_local(self): @@ -598,8 +600,8 @@ Step 4 Import cluster certificate and key """ - logger_utils.log_only_to_file("Step 4: Import cluster certificate and key") cmd = "corosync-qdevice-net-certutil -m -c {}".format(self.qdevice_p12_on_cluster) + QDevice.log_only_to_file("Step 4: Import cluster certificate and key", cmd) utils.get_stdout_or_raise_error(cmd) def certificate_process_on_join(self): @@ -653,6 +655,7 @@ return node_list = addr_list if addr_list else utils.list_cluster_nodes() cmd = "rm -rf {}/*".format(QDevice.qdevice_path) + QDevice.log_only_to_file("Remove qdevice database", cmd) parallax.parallax_call(node_list, cmd) @classmethod @@ -742,3 +745,9 @@ if res: qnetd_host = corosync.get_value('quorum.device.net.host') logger.warning("Qdevice's vote is 0, which simply means Qdevice can't talk to Qnetd({}) for various reasons.".format(qnetd_host)) + + @staticmethod + def log_only_to_file(desc, cmd=None): + logger_utils.log_only_to_file(desc) + if cmd: + logger_utils.log_only_to_file(f"Run: {cmd}") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/crmsh/utils.py new/crmsh-4.5.0+20230320.5e777809/crmsh/utils.py --- old/crmsh-4.5.0+20230309.a4c4192d/crmsh/utils.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/crmsh/utils.py 2023-03-20 08:49:02.000000000 +0100 @@ -109,27 +109,46 @@ class UserOfHost: + class UserNotFoundError(Exception): + pass + def __init__(self): - self._cache = dict() + self._user_cache = dict() + self._user_pair_cache = dict() def user_of(self, host): - cached = self._cache.get(host) + cached = self._user_cache.get(host) if cached is None: - ret = self._user_of_impl(host) + ret = self._get_user_of_host_from_config(host) if ret is None: - user = userdir.get_sudoer() - if user: - return user - else: - return userdir.getuser() + raise self.UserNotFoundError else: - self._cache[host] = ret + self._user_cache[host] = ret return ret else: return cached + def user_pair_for_ssh(self, host: str) -> typing.Tuple[str, str]: + """Return (local_user, remote_user) pair for ssh connection""" + try: + local_user = self.user_of(this_node()) + remote_user = self.user_of(host) + return local_user, remote_user + except self.UserNotFoundError: + cached = self._user_pair_cache.get(host) + if cached is None: + ret = self._guess_user_for_ssh(host) + if ret is None: + raise ValueError('Can not create ssh session from {} to {}.'.format(this_node(), host)) + else: + self._user_pair_cache[host] = ret + return ret + else: + return cached + + @staticmethod - def _user_of_impl(host): + def _get_user_of_host_from_config(host): try: canonical, aliases, _ = socket.gethostbyaddr(host) aliases = set(aliases) @@ -151,6 +170,35 @@ logger.debug('Failed to get the user of host %s (aliases: %s). Known hosts are %s', host, aliases, hosts) return None + @staticmethod + def _guess_user_for_ssh(host: str) -> typing.Tuple[str, str]: + args = ['ssh'] + args.extend(constants.SSH_OPTION_ARGS) + args.extend(['-o', 'BatchMode=yes', host, 'true']) + rc = subprocess.call( + args, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + if rc == 0: + user = userdir.getuser() + return user, user + sudoer = userdir.get_sudoer() + if sudoer is None: + return None + result = su_subprocess_run( + sudoer, + 'ssh {} -o BatchMode=yes {}@{} sudo -u {} true'.format(constants.SSH_OPTION, sudoer, host, sudoer), + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + if result.returncode == 0: + return sudoer, sudoer + return None + + _user_of_host_instance = UserOfHost() @@ -159,6 +207,10 @@ return _user_of_host_instance.user_of(host) +def user_pair_for_ssh(host): + return _user_of_host_instance.user_pair_for_ssh(host) + + def ssh_copy_id(local_user, remote_user, remote_node): if check_ssh_passwd_need(local_user, remote_user, remote_node): logger.info("Configuring SSH passwordless with {}@{}".format(remote_user, remote_node)) @@ -1039,7 +1091,11 @@ def get_stdout_stderr_as_local_sudoer(cmd, input_s=None, raw=False): - return su_get_stdout_stderr(user_of(this_node()), cmd, input_s, raw) + try: + user = user_of(this_node()) + except UserOfHost.UserNotFoundError: + user = 'root' + return su_get_stdout_stderr(user, cmd, input_s, raw) def get_stdout_stderr_auto_ssh_no_input(host, cmd, raw=False): @@ -2307,8 +2363,6 @@ Check whether access to host need password """ ssh_options = "-o StrictHostKeyChecking=no -o EscapeChar=none -o ConnectTimeout=15" - if remote_user is None: - remote_user = user_of(host) ssh_cmd = "ssh {} -T -o Batchmode=yes {}@{} true".format(ssh_options, remote_user, host) rc, _, _ = su_get_stdout_stderr(local_user, ssh_cmd) return rc != 0 @@ -2880,13 +2934,12 @@ **kwargs, ) else: - remote_sudoer = user_of(remote) - local_user = user_of(this_node()) + local_user, remote_user = user_pair_for_ssh(remote) if user is None: user = 'root' return su_subprocess_run( local_user, - 'ssh {} {}@{} sudo -H -u {} /bin/sh'.format(constants.SSH_OPTION, remote_sudoer, remote, user), + 'ssh {} {}@{} sudo -H -u {} /bin/sh'.format(constants.SSH_OPTION, remote_user, remote, user), input=cmd.encode('utf-8'), **kwargs, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_corosync.py new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_corosync.py --- old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_corosync.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_corosync.py 2023-03-20 08:49:02.000000000 +0100 @@ -177,18 +177,15 @@ ]) -@mock.patch('crmsh.utils.user_of') +@mock.patch('crmsh.utils.user_pair_for_ssh') @mock.patch("crmsh.parallax.parallax_call") @mock.patch("crmsh.utils.ssh_copy_id") @mock.patch("crmsh.utils.check_ssh_passwd_need") @mock.patch("crmsh.corosync.get_value") @mock.patch("crmsh.utils.is_qdevice_configured") def test_query_qnetd_status_copy_id_failed(mock_qdevice_configured, - mock_get_value, mock_check_passwd, mock_ssh_copy_id, mock_parallax_call, mock_userof): - mock_userof.side_effect = [ - "alice", - "root", - ] + mock_get_value, mock_check_passwd, mock_ssh_copy_id, mock_parallax_call, mock_user_pair_for_ssh): + mock_user_pair_for_ssh.return_value = "alice", "root" mock_parallax_call.side_effect = ValueError("Failed on 10.10.10.123: foo") mock_qdevice_configured.return_value = True mock_get_value.side_effect = ["hacluster", "10.10.10.123"] @@ -205,7 +202,7 @@ mock_ssh_copy_id.assert_called_once_with('alice', 'root', '10.10.10.123') -@mock.patch('crmsh.utils.user_of') +@mock.patch('crmsh.utils.user_pair_for_ssh') @mock.patch("crmsh.utils.print_cluster_nodes") @mock.patch("crmsh.parallax.parallax_call") @mock.patch("crmsh.utils.ssh_copy_id") @@ -214,11 +211,8 @@ @mock.patch("crmsh.utils.is_qdevice_configured") def test_query_qnetd_status_copy(mock_qdevice_configured, mock_get_value, mock_check_passwd, mock_ssh_copy_id, mock_parallax_call, mock_print_nodes, - mock_userof): - mock_userof.side_effect = [ - "alice", - "root", - ] + mock_user_pair_for_ssh): + mock_user_pair_for_ssh.return_value = "alice", "root" mock_qdevice_configured.return_value = True mock_get_value.side_effect = ["hacluster", "10.10.10.123"] mock_check_passwd.return_value = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_parallax.py new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_parallax.py --- old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_parallax.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_parallax.py 2023-03-20 08:49:02.000000000 +0100 @@ -13,121 +13,124 @@ class TestParallax(unittest.TestCase): - @classmethod - def setUpClass(cls): - """ - Global setUp. - """ - def setUp(self): """ Test setUp. """ # Use the setup to create a fresh instance for each test - self.parallax_call_instance = cparallax.Parallax(["node1"], cmd="ls") - self.parallax_slurp_instance = cparallax.Parallax(["node1"], localdir="/opt", filename="/opt/file.c") - self.parallax_copy_instance = cparallax.Parallax(["node1", "node2"], src="/opt/file.c", dst="/tmp") - - def tearDown(self): - """ - Test tearDown. - """ - - @classmethod - def tearDownClass(cls): - """ - Global tearDown. - """ - - @mock.patch("crmsh.utils.user_of") + @mock.patch("crmsh.utils.user_pair_for_ssh") @mock.patch("parallax.call") @mock.patch("crmsh.parallax.Parallax.handle") - def test_call(self, mock_handle, mock_call, mock_userof): + def test_call(self, mock_handle, mock_call, mock_user_pair_for_ssh): + mock_user_pair_for_ssh.return_value = "alice", "alice" + parallax_call_instance = cparallax.Parallax(["node1"], cmd="ls") mock_call.return_value = {"node1": (0, None, None)} - mock_userof.return_value = "alice" mock_handle.return_value = [("node1", (0, None, None))] - result = self.parallax_call_instance.call() + result = parallax_call_instance.call() self.assertEqual(result, mock_handle.return_value) - mock_userof.assert_called_once_with("node1") - mock_call.assert_called_once_with([["node1", None, "alice"]], "sudo ls", self.parallax_call_instance.opts) + mock_user_pair_for_ssh.assert_has_calls([ + mock.call("node1"), + mock.call("node1"), + ]) + mock_call.assert_called_once_with([["node1", None, "alice"]], "ls", parallax_call_instance.opts) mock_handle.assert_called_once_with(list(mock_call.return_value.items())) @mock.patch("parallax.Error") - @mock.patch("crmsh.utils.user_of") + @mock.patch("crmsh.utils.user_pair_for_ssh") @mock.patch("parallax.call") @mock.patch("crmsh.parallax.Parallax.handle") - def test_call_exception(self, mock_handle, mock_call, mock_userof, mock_error): + def test_call_exception(self, mock_handle, mock_call, mock_user_pair_for_ssh, mock_error): + mock_user_pair_for_ssh.return_value = "alice", "alice" + parallax_call_instance = cparallax.Parallax(["node1"], cmd="ls") mock_error = mock.Mock() mock_call.return_value = {"node1": mock_error} - mock_userof.return_value = "alice" mock_handle.side_effect = ValueError("error happen") with self.assertRaises(ValueError) as err: - self.parallax_call_instance.call() + parallax_call_instance.call() self.assertEqual("error happen", str(err.exception)) - mock_userof.assert_called_once_with("node1") - mock_call.assert_called_once_with([["node1", None, "alice"]], "sudo ls", self.parallax_call_instance.opts) + mock_user_pair_for_ssh.assert_has_calls([ + mock.call("node1"), + mock.call("node1"), + ]) + mock_call.assert_called_once_with([["node1", None, "alice"]], "ls", parallax_call_instance.opts) mock_handle.assert_called_once_with(list(mock_call.return_value.items())) + @mock.patch("crmsh.utils.user_pair_for_ssh") @mock.patch("crmsh.parallax.Parallax.handle") @mock.patch("parallax.slurp") @mock.patch("os.path.basename") - def test_slurp(self, mock_basename, mock_slurp, mock_handle): + def test_slurp(self, mock_basename, mock_slurp, mock_handle, mock_user_pair_for_ssh): + mock_user_pair_for_ssh.return_value = "alice", "alice" + parallax_slurp_instance = cparallax.Parallax(["node1"], localdir="/opt", filename="/opt/file.c") mock_basename.return_value = "file.c" mock_slurp.return_value = {"node1": (0, None, None, "/opt")} mock_handle.return_value = [("node1", (0, None, None, "/opt"))] - result = self.parallax_slurp_instance.slurp() + result = parallax_slurp_instance.slurp() self.assertEqual(result, mock_handle.return_value) mock_basename.assert_called_once_with("/opt/file.c") - mock_slurp.assert_called_once_with(["node1"], "/opt/file.c", "file.c", self.parallax_slurp_instance.opts) + mock_slurp.assert_called_once_with(["node1"], "/opt/file.c", "file.c", parallax_slurp_instance.opts) mock_handle.assert_called_once_with(list(mock_slurp.return_value.items())) + mock_user_pair_for_ssh.assert_called_once_with("node1") + @mock.patch("crmsh.utils.user_pair_for_ssh") @mock.patch("parallax.Error") @mock.patch("crmsh.parallax.Parallax.handle") @mock.patch("parallax.slurp") @mock.patch("os.path.basename") - def test_slurp_exception(self, mock_basename, mock_slurp, mock_handle, mock_error): + def test_slurp_exception(self, mock_basename, mock_slurp, mock_handle, mock_error, mock_user_pair_for_ssh): + mock_user_pair_for_ssh.return_value = "alice", "alice" + parallax_slurp_instance = cparallax.Parallax(["node1"], localdir="/opt", filename="/opt/file.c") mock_basename.return_value = "file.c" mock_error = mock.Mock() mock_slurp.return_value = {"node1": mock_error} mock_handle.side_effect = ValueError("error happen") with self.assertRaises(ValueError) as err: - self.parallax_slurp_instance.slurp() + parallax_slurp_instance.slurp() self.assertEqual("error happen", str(err.exception)) mock_basename.assert_called_once_with("/opt/file.c") - mock_slurp.assert_called_once_with(["node1"], "/opt/file.c", "file.c", self.parallax_slurp_instance.opts) + mock_slurp.assert_called_once_with(["node1"], "/opt/file.c", "file.c", parallax_slurp_instance.opts) mock_handle.assert_called_once_with(list(mock_slurp.return_value.items())) + mock_user_pair_for_ssh.assert_called_once_with("node1") + @mock.patch("crmsh.utils.user_pair_for_ssh") @mock.patch("parallax.copy") @mock.patch("crmsh.parallax.Parallax.handle") - def test_copy(self, mock_handle, mock_copy): + def test_copy(self, mock_handle, mock_copy, mock_user_pair_for_ssh): + mock_user_pair_for_ssh.return_value = "alice", "alice" + parallax_copy_instance = cparallax.Parallax(["node1", "node2"], src="/opt/file.c", dst="/tmp") mock_copy.return_value = {"node1": (0, None, None), "node2": (0, None, None)} mock_handle.return_value = [("node1", (0, None, None)), ("node2", (0, None, None))] - result = self.parallax_copy_instance.copy() + result = parallax_copy_instance.copy() self.assertEqual(result, mock_handle.return_value) - mock_copy.assert_called_once_with(["node1", "node2"], "/opt/file.c", "/tmp", self.parallax_copy_instance.opts) + mock_copy.assert_called_once_with(["node1", "node2"], "/opt/file.c", "/tmp", parallax_copy_instance.opts) mock_handle.assert_called_once_with(list(mock_copy.return_value.items())) + mock_user_pair_for_ssh.assert_called_once_with("node1") + @mock.patch("crmsh.utils.user_pair_for_ssh") @mock.patch("parallax.Error") @mock.patch("parallax.copy") @mock.patch("crmsh.parallax.Parallax.handle") - def test_copy_exception(self, mock_handle, mock_copy, mock_error): + def test_copy_exception(self, mock_handle, mock_copy, mock_error, mock_user_pair_for_ssh): + mock_user_pair_for_ssh.return_value = "alice", "alice" + parallax_copy_instance = cparallax.Parallax(["node1", "node2"], src="/opt/file.c", dst="/tmp") mock_error = mock.Mock() mock_copy.return_value = {"node1": mock_error, "node2": (0, None, None)} mock_handle.side_effect = ValueError("error happen") with self.assertRaises(ValueError) as err: - self.parallax_copy_instance.copy() + parallax_copy_instance.copy() self.assertEqual("error happen", str(err.exception)) - mock_copy.assert_called_once_with(["node1", "node2"], "/opt/file.c", "/tmp", self.parallax_copy_instance.opts) + mock_copy.assert_called_once_with(["node1", "node2"], "/opt/file.c", "/tmp", parallax_copy_instance.opts) mock_handle.assert_called_once_with(list(mock_copy.return_value.items())) + mock_user_pair_for_ssh.assert_called_once_with("node1") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_qdevice.py new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_qdevice.py --- old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_qdevice.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_qdevice.py 2023-03-20 08:49:02.000000000 +0100 @@ -391,7 +391,7 @@ self.qdevice_with_ip.stop_qnetd() mock_stop.assert_called_once_with("corosync-qnetd.service", remote_addr="10.10.10.123") - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.parallax.parallax_call") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_qnetd", new_callable=mock.PropertyMock) def test_init_db_on_qnetd_already_exists(self, mock_qnetd_cacert, mock_call, mock_log): @@ -403,7 +403,7 @@ mock_qnetd_cacert.assert_called_once_with() mock_log.assert_not_called() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.parallax.parallax_call") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_qnetd", new_callable=mock.PropertyMock) def test_init_db_on_qnetd(self, mock_qnetd_cacert, mock_call, mock_log): @@ -418,9 +418,10 @@ mock.call(["10.10.10.123"], "corosync-qnetd-certutil -i") ]) mock_qnetd_cacert.assert_called_once_with() - mock_log.assert_called_once_with("Step 1: Initialize database on 10.10.10.123") + mock_log.assert_called_once_with("Step 1: Initialize database on 10.10.10.123", + 'corosync-qnetd-certutil -i') - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("os.path.exists") @mock.patch("crmsh.qdevice.QDevice._fetch_file_to_local") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_local", new_callable=mock.PropertyMock) @@ -436,7 +437,7 @@ mock_fetch.assert_not_called() mock_log.assert_not_called() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("os.path.exists") @mock.patch("crmsh.qdevice.QDevice._fetch_file_to_local") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_local", new_callable=mock.PropertyMock) @@ -452,7 +453,7 @@ mock_log.assert_called_once_with("Step 2: Fetch qnetd-cacert.crt from 10.10.10.123") mock_fetch.assert_called_once_with("10.10.10.123", "/etc/corosync/qnetd/nssdb/qnetd-cacert.crt", "/etc/corosync/qdevice/net/10.10.10.123") - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.list_cluster_nodes") @mock.patch("crmsh.utils.this_node") @mock.patch("crmsh.qdevice.QDevice._copy_file_to_remote_host") @@ -467,7 +468,7 @@ mock_copy.assert_not_called() mock_log.assert_not_called() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.list_cluster_nodes") @mock.patch("crmsh.utils.this_node") @mock.patch("crmsh.qdevice.QDevice._copy_file_to_remote_host") @@ -488,7 +489,7 @@ mock_copy.assert_called_once_with(mock_dirname.return_value, "node2.com", "/etc/corosync/qdevice/net") - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.parallax.parallax_call") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_local", new_callable=mock.PropertyMock) @mock.patch("crmsh.utils.list_cluster_nodes") @@ -501,21 +502,23 @@ mock_list_nodes.assert_called_once_with() mock_qnetd_cacert_local.assert_called_once_with() - mock_log.assert_called_once_with("Step 4: Initialize database on ['node1', 'node2']") + mock_log.assert_called_once_with("Step 4: Initialize database on ['node1', 'node2']", + 'corosync-qdevice-net-certutil -i -c /etc/corosync/qdevice/net/10.10.10.123/qnetd-cacert.crt') mock_call.assert_called_once_with(mock_list_nodes.return_value, "corosync-qdevice-net-certutil -i -c {}".format(mock_qnetd_cacert_local.return_value)) - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.get_stdout_or_raise_error") def test_create_ca_request(self, mock_stdout_stderr, mock_log): mock_stdout_stderr.return_value = (0, None, None) self.qdevice_with_cluster_name.create_ca_request() - mock_log.assert_called_once_with("Step 5: Generate certificate request qdevice-net-node.crq") + mock_log.assert_called_once_with("Step 5: Generate certificate request qdevice-net-node.crq", + 'corosync-qdevice-net-certutil -r -n hacluster1') mock_stdout_stderr.assert_called_once_with("corosync-qdevice-net-certutil -r -n hacluster1") - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.qdevice.QDevice.qdevice_crq_on_qnetd", new_callable=mock.PropertyMock) @mock.patch("crmsh.qdevice.QDevice.qdevice_crq_on_local", new_callable=mock.PropertyMock) @mock.patch("crmsh.qdevice.QDevice._copy_file_to_remote_host") @@ -532,7 +535,7 @@ mock_qdevice_crq_local.assert_called_once_with() mock_qdevice_crq_qnetd.assert_called_once_with() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.parallax.parallax_call") @mock.patch("crmsh.qdevice.QDevice.qdevice_crq_on_qnetd", new_callable=mock.PropertyMock) def test_sign_crq_on_qnetd(self, mock_qdevice_crq_qnetd, mock_call, mock_log): @@ -542,12 +545,13 @@ self.qdevice_with_ip.cluster_name = "hacluster" self.qdevice_with_ip.sign_crq_on_qnetd() - mock_log.assert_called_once_with("Step 7: Sign and export cluster certificate on 10.10.10.123") + mock_log.assert_called_once_with("Step 7: Sign and export cluster certificate on 10.10.10.123", + 'corosync-qnetd-certutil -s -c /etc/corosync/qnetd/nssdb/qdevice-net-node.crq -n hacluster') mock_qdevice_crq_qnetd.assert_called_once_with() mock_call.assert_called_once_with(["10.10.10.123"], "corosync-qnetd-certutil -s -c {} -n hacluster".format(mock_qdevice_crq_qnetd.return_value)) - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.qdevice.QDevice.qnetd_cluster_crt_on_qnetd", new_callable=mock.PropertyMock) @mock.patch("crmsh.qdevice.QDevice._fetch_file_to_local") def test_fetch_cluster_crt_from_qnetd(self, mock_fetch, mock_crt_on_qnetd, mock_log): @@ -560,7 +564,7 @@ mock_crt_on_qnetd.assert_has_calls([mock.call(), mock.call()]) mock_fetch.assert_called_once_with("10.10.10.123", mock_crt_on_qnetd.return_value, "/etc/corosync/qdevice/net/10.10.10.123",) - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.get_stdout_or_raise_error") @mock.patch("crmsh.qdevice.QDevice.qnetd_cluster_crt_on_local", new_callable=mock.PropertyMock) def test_import_cluster_crt(self, mock_crt_on_local, mock_stdout_stderr, mock_log): @@ -568,11 +572,12 @@ self.qdevice_with_ip.import_cluster_crt() - mock_log.assert_called_once_with("Step 9: Import certificate file cluster-hacluster.crt on local") + mock_log.assert_called_once_with("Step 9: Import certificate file cluster-hacluster.crt on local", + 'corosync-qdevice-net-certutil -M -c /etc/corosync/qdevice/net/10.10.10.123/cluster-hacluster.crt') mock_crt_on_local.assert_has_calls([mock.call(), mock.call()]) mock_stdout_stderr.assert_called_once_with("corosync-qdevice-net-certutil -M -c {}".format(mock_crt_on_local.return_value)) - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.list_cluster_nodes") @mock.patch("crmsh.utils.this_node") @mock.patch("crmsh.qdevice.QDevice._copy_file_to_remote_host") @@ -587,7 +592,7 @@ mock_list_nodes.assert_called_once_with() mock_copy.assert_not_called() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.list_cluster_nodes") @mock.patch("crmsh.utils.this_node") @mock.patch("crmsh.qdevice.QDevice._copy_file_to_remote_hosts") @@ -607,7 +612,7 @@ mock_p12_on_local.return_value) mock_p12_on_local.assert_has_calls([mock.call(), mock.call()]) - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.parallax.parallax_call") @mock.patch("crmsh.utils.list_cluster_nodes_except_me") def test_import_p12_on_cluster_one_node(self, mock_list_nodes, mock_call, mock_log): @@ -620,7 +625,7 @@ mock_call.assert_not_called() @mock.patch("crmsh.parallax.parallax_call") - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.qdevice.QDevice.qdevice_p12_on_local", new_callable=mock.PropertyMock) @mock.patch("crmsh.utils.list_cluster_nodes_except_me") def test_import_p12_on_cluster(self, mock_list_nodes, mock_p12_on_local, mock_log, mock_call): @@ -630,7 +635,8 @@ self.qdevice_with_ip.import_p12_on_cluster() - mock_log.assert_called_once_with("Step 11: Import qdevice-net-node.p12 on ['node2', 'node3']") + mock_log.assert_called_once_with("Step 11: Import qdevice-net-node.p12 on ['node2', 'node3']", + 'corosync-qdevice-net-certutil -m -c /etc/corosync/qdevice/net/nssdb/qdevice-net-node.p12') mock_list_nodes.assert_called_once_with() mock_call.assert_called_once_with( ["node2", "node3"], @@ -666,7 +672,7 @@ mock_copy_p12_to_cluster.assert_called_once_with() mock_import_p12_on_cluster.assert_called_once_with() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("os.path.exists") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_cluster", new_callable=mock.PropertyMock) @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_local", new_callable=mock.PropertyMock) @@ -684,7 +690,7 @@ mock_qnetd_cacert_local.assert_not_called() mock_fetch.assert_not_called() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("os.path.exists") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_cluster", new_callable=mock.PropertyMock) @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_local", new_callable=mock.PropertyMock) @@ -703,7 +709,7 @@ mock_qnetd_cacert_local.assert_called_once_with() mock_fetch.assert_called_once_with("node1.com", mock_qnetd_cacert_local.return_value, '/etc/corosync/qdevice/net/node1.com') - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.get_stdout_or_raise_error") @mock.patch("crmsh.qdevice.QDevice.qnetd_cacert_on_cluster", new_callable=mock.PropertyMock) def test_init_db_on_local(self, mock_qnetd_cacert_cluster, mock_stdout_stderr, mock_log): @@ -712,11 +718,12 @@ self.qdevice_with_ip_cluster_node.init_db_on_local() - mock_log.assert_called_once_with("Step 2: Initialize database on local") + mock_log.assert_called_once_with("Step 2: Initialize database on local", + 'corosync-qdevice-net-certutil -i -c /etc/corosync/qdevice/net/node1.com/qnetd-cacert.crt') mock_qnetd_cacert_cluster.assert_called_once_with() mock_stdout_stderr.assert_called_once_with("corosync-qdevice-net-certutil -i -c {}".format(mock_qnetd_cacert_cluster.return_value)) - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("os.path.exists") @mock.patch("crmsh.qdevice.QDevice.qdevice_p12_on_cluster", new_callable=mock.PropertyMock) @mock.patch("crmsh.qdevice.QDevice.qdevice_p12_on_local", new_callable=mock.PropertyMock) @@ -734,7 +741,7 @@ mock_p12_on_local.assert_not_called() mock_fetch.assert_not_called() - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("os.path.exists") @mock.patch("crmsh.qdevice.QDevice.qdevice_p12_on_cluster", new_callable=mock.PropertyMock) @mock.patch("crmsh.qdevice.QDevice.qdevice_p12_on_local", new_callable=mock.PropertyMock) @@ -753,7 +760,7 @@ mock_p12_on_local.assert_called_once_with() mock_fetch.assert_called_once_with("node1.com", mock_p12_on_local.return_value, "/etc/corosync/qdevice/net/node1.com") - @mock.patch("crmsh.log.LoggerUtils.log_only_to_file") + @mock.patch("crmsh.qdevice.QDevice.log_only_to_file") @mock.patch("crmsh.utils.get_stdout_or_raise_error") @mock.patch("crmsh.qdevice.QDevice.qdevice_p12_on_cluster", new_callable=mock.PropertyMock) def test_import_p12_on_local(self, mock_p12_on_cluster, mock_stdout_stderr, mock_log): @@ -761,7 +768,8 @@ self.qdevice_with_ip_cluster_node.import_p12_on_local() - mock_log.assert_called_once_with("Step 4: Import cluster certificate and key") + mock_log.assert_called_once_with("Step 4: Import cluster certificate and key", + 'corosync-qdevice-net-certutil -m -c /etc/corosync/qdevice/net/node1.com/qdevice-net-node.p12') mock_p12_on_cluster.assert_called_once_with() mock_stdout_stderr.assert_called_once_with("corosync-qdevice-net-certutil -m -c {}".format(mock_p12_on_cluster.return_value)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_utils.py new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_utils.py --- old/crmsh-4.5.0+20230309.a4c4192d/test/unittests/test_utils.py 2023-03-09 10:55:42.000000000 +0100 +++ new/crmsh-4.5.0+20230320.5e777809/test/unittests/test_utils.py 2023-03-20 08:49:02.000000000 +0100 @@ -1227,28 +1227,25 @@ self.assertEqual(b'bar', result.stdout) @mock.patch("crmsh.utils.this_node") - @mock.patch("crmsh.utils.user_of") + @mock.patch('crmsh.utils.user_pair_for_ssh') @mock.patch("crmsh.utils.su_subprocess_run") @mock.patch("subprocess.run") def test_subprocess_run_auto_ssh_no_input_remote_no_user( self, mock_subprocess_run: mock.MagicMock, mock_su_subprocess_run: mock.MagicMock, - mock_user_of: mock.MagicMock, + mock_user_pair_for_ssh: mock.MagicMock, mock_this_node: mock.MagicMock, ): mock_this_node.return_value = 'node1' - mock_user_of.return_value = 'alice' + mock_user_pair_for_ssh.return_value = "alice", "bob" mock_su_subprocess_run.return_value = mock.Mock(returncode=0, stdout=b'bar', stderr=b'') result = utils.subprocess_run_auto_ssh_no_input("foo", "node2", stderr=subprocess.DEVNULL) - mock_user_of.assert_has_calls([ - mock.call('node1'), - mock.call('node2'), - ], any_order=True) + mock_user_pair_for_ssh.assert_called_once_with('node2') mock_subprocess_run.assert_not_called() mock_su_subprocess_run.assert_called_once_with( 'alice', - 'ssh -o StrictHostKeyChecking=no alice@node2 sudo -H -u root /bin/sh', + 'ssh -o StrictHostKeyChecking=no bob@node2 sudo -H -u root /bin/sh', input=b'foo', stderr=subprocess.DEVNULL, )