From f1ca396453350e1c592463e0d965ddc5d325d186 Mon Sep 17 00:00:00 2001 From: Samir Aguiar Date: Thu, 12 Aug 2021 09:54:14 -0300 Subject: [PATCH] Add a new function to wait for arnied to be ready Usually code depending on arnied checks whether its process is running before proceeding, which can lead to a race condition when it is not yet fully started. The new function makes use of a novel CLI option in the arnied helper that blocks execution until arnied is indeed ready to be used. --- src/arnied_wrapper.py | 42 +++++++++++++++++++++++++++++------------- test/test_arnied_wrapper.py | 9 ++++++++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/arnied_wrapper.py b/src/arnied_wrapper.py index 73b242a..991e2a4 100644 --- a/src/arnied_wrapper.py +++ b/src/arnied_wrapper.py @@ -62,6 +62,8 @@ from . import sysmisc #: default set_cnf binary BIN_SET_CNF = "/usr/intranator/bin/set_cnf" +#: default arnied_helper binary +BIN_ARNIED_HELPER = "/usr/intranator/bin/arnied_helper" #: default location for template configuration files SRC_CONFIG_DIR = "." #: default location for dumped configuration files @@ -102,7 +104,6 @@ def run_cmd(cmd="", ignore_errors=False, vm=None, timeout=60): def verify_running(process='arnied', timeout=60, vm=None): """ Verify if a given process is running via 'pgrep'. - Normally this is used to check if arnied is running. :param str process: process to verify if running :param int timeout: run verification timeout @@ -141,7 +142,7 @@ def accept_licence(vm=None): cmd = 'echo "LICENSE_ACCEPTED,0: \\"1\\"" | set_cnf' result = run_cmd(cmd=cmd, ignore_errors=True, vm=vm) log.debug(result) - cmd = "/usr/intranator/bin/arnied_helper --wait-for-program-end GENERATE" + cmd = f"{BIN_ARNIED_HELPER} --wait-for-program-end GENERATE" run_cmd(cmd=cmd, vm=vm) @@ -287,7 +288,7 @@ def email_transfer(vm=None): :param vm: vm to run on if running on a guest instead of the host :type vm: VM object or None """ - cmd = "/usr/intranator/bin/arnied_helper --transfer-mail" + cmd = f"{BIN_ARNIED_HELPER} --transfer-mail" result = run_cmd(cmd=cmd, vm=vm) log.debug(result) @@ -370,7 +371,7 @@ def wait_for_run(program, timeout=300, retries=10, vm=None): log.info("Waiting for program %s to finish with timeout %i", program, timeout) for i in range(retries): - cmd = "/usr/intranator/bin/arnied_helper --is-scheduled-or-running " \ + cmd = f"{BIN_ARNIED_HELPER} --is-scheduled-or-running " \ + program.upper() check_scheduled = run_cmd(cmd=cmd, ignore_errors=True, vm=vm) if check_scheduled.returncode == 0: @@ -379,8 +380,23 @@ def wait_for_run(program, timeout=300, retries=10, vm=None): else: log.warning("The program %s was not scheduled and is not running", program) return - cmd = "/usr/intranator/bin/arnied_helper --wait-for-program-end " \ - + program.upper() + " --wait-for-program-timeout " + str(timeout) + cmd = f"{BIN_ARNIED_HELPER} --wait-for-program-end " \ + f"{program.upper()} --wait-for-program-timeout {timeout}" + # add one second to make sure arnied_helper is finished when we expire + result = run_cmd(cmd=cmd, vm=vm, timeout=timeout+1) + log.debug(result.stdout) + + +def wait_for_arnied(timeout=60, vm=None): + """ + Wait for arnied socket to be ready. + + :param int timeout: maximum number of seconds to wait + :param vm: vm to run on if running on a guest instead of the host + :type vm: VM object or None + """ + cmd = f"{BIN_ARNIED_HELPER} --wait-for-arnied-socket " \ + f"--wait-for-arnied-socket-timeout {timeout}" # add one second to make sure arnied_helper is finished when we expire result = run_cmd(cmd=cmd, vm=vm, timeout=timeout+1) log.debug(result.stdout) @@ -404,7 +420,7 @@ def get_cnf(cnf_key, cnf_index=1, regex=".*", compact=False, timeout=30, vm=None If `cnf_index` is set to -1, retrieve and perform regex matching on all instances. """ - verify_running(timeout=timeout, vm=vm) + wait_for_arnied(timeout=timeout, vm=vm) platform_str = "" if vm is not None: platform_str = " from %s" % vm.name @@ -428,7 +444,7 @@ def get_cnf_id(cnf_key, value, timeout=30, vm=None): :returns: the cnf id or -1 if no such cnf variable :rtype: int """ - verify_running(timeout=timeout, vm=vm) + wait_for_arnied(timeout=timeout, vm=vm) regex = "%s,(\d+): \"%s\"" % (cnf_key, value) cnf_id = get_cnf(cnf_key, cnf_index=-1, regex=regex, compact=True, vm=vm) if cnf_id is None: @@ -453,7 +469,7 @@ def get_cnfvar(varname=None, instance=None, data=None, timeout=30, vm=None): :returns: the resulting "cnfvar" structure or None if the lookup fails or the result could not be parsed :rtype: cnfvar option """ - verify_running(timeout=timeout, vm=vm) + wait_for_arnied(timeout=timeout, vm=vm) # firstly, build argv for get_cnf cmd = ["get_cnf", "-j"] if varname is not None: @@ -502,7 +518,7 @@ def get_cnfvar_id(varname, data, timeout=30, vm=None): :returns: the cnf id or -1 if no such cnf variable :rtype: int """ - verify_running(timeout=timeout, vm=vm) + wait_for_arnied(timeout=timeout, vm=vm) log.info("Extracting from arnied CNF_VAR %s with data %s", varname, data) cnf = get_cnfvar(varname=varname, data=data, vm=vm) @@ -537,7 +553,7 @@ def unset_cnf(varname="", instance="", timeout=30, vm=None): :param vm: vm to run on if running on a guest instead of the host :type vm: VM object or None """ - verify_running(timeout=timeout, vm=vm) + wait_for_arnied(timeout=timeout, vm=vm) cmd = "get_cnf %s %s | set_cnf -x" % (varname, instance) run_cmd(cmd=cmd, vm=vm) @@ -563,7 +579,7 @@ def set_cnf(config_files, kind="cnf", timeout=30, vm=None): the config files will be copied there as temporary files before applying. """ log.info("Setting arnied configuration") - verify_running(timeout=timeout, vm=vm) + wait_for_arnied(timeout=timeout, vm=vm) config_paths = prep_config_paths(config_files) for config_path in config_paths: @@ -692,7 +708,7 @@ def set_cnf_pipe(cnf, timeout=30, block=False): This is obviously not generic but supposed to be run on the guest. """ log.info("Setting arnied configuration through local pipe") - verify_running(timeout=timeout) + wait_for_arnied(timeout=timeout) st, out, exit = sysmisc.run_cmd_with_pipe([BIN_SET_CNF, "-j"], inp=str(cnf)) diff --git a/test/test_arnied_wrapper.py b/test/test_arnied_wrapper.py index ea41e82..57ab125 100755 --- a/test/test_arnied_wrapper.py +++ b/test/test_arnied_wrapper.py @@ -50,6 +50,9 @@ class DummyCmdOutputMapping(object): {"cmd": 'rm -f /var/intranator/schedule/UPDATE_VIRSCAN_NODIAL*', "stdout": b"", "returncode": 0}, {"cmd": '/usr/intranator/bin/arnied_helper --transfer-mail', "stdout": b"", "returncode": 0}, + + {"cmd": '/usr/intranator/bin/arnied_helper --wait-for-arnied-socket --wait-for-arnied-socket-timeout 10', "stdout": b"", "returncode": 0}, + {"cmd": '/usr/intranator/bin/arnied_helper --wait-for-arnied-socket --wait-for-arnied-socket-timeout 30', "stdout": b"", "returncode": 0}, ] asserted_cmds = [] @@ -88,6 +91,10 @@ class ArniedWrapperTest(unittest.TestCase): with self.assertRaises(RuntimeError): arnied_wrapper.verify_running(timeout=1) + def test_wait_for_arnied(self): + DummyCmdOutputMapping.asserted_cmds = self.cmd_db + arnied_wrapper.wait_for_arnied(timeout=10) + def test_accept_license(self): DummyCmdOutputMapping.asserted_cmds = self.cmd_db[1:3] arnied_wrapper.accept_licence() @@ -101,7 +108,7 @@ class ArniedWrapperTest(unittest.TestCase): arnied_wrapper.go_online(1) def test_disable_virscan(self): - DummyCmdOutputMapping.asserted_cmds = self.cmd_db[6:14] + DummyCmdOutputMapping.asserted_cmds = self.cmd_db[6:] arnied_wrapper.disable_virscan() def test_email_transfer(self): -- 1.7.1