From dd3ecbcf38a8a75b934fc507c65673f261ada8ca Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 26 Aug 2025 16:30:55 +0200 Subject: [PATCH] CI: Harmonize exec_script() API Instead of making the function a class member, make it an instance member because (1) it is more aligned with other functions, notably, run(), and (2) especially for RemoteCmd() it can be faster, as successive exec_script() calls do not require a new SSH connection. --- ci-scripts/cls_cluster.py | 3 ++- ci-scripts/cls_cmd.py | 20 +++++--------------- ci-scripts/cls_containerize.py | 3 ++- ci-scripts/cls_corenetwork.py | 3 ++- ci-scripts/cls_oaicitest.py | 3 ++- ci-scripts/tests/cmd.py | 18 ++++++++++++------ 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ci-scripts/cls_cluster.py b/ci-scripts/cls_cluster.py index ae4e718ecf..2ffd94802e 100644 --- a/ci-scripts/cls_cluster.py +++ b/ci-scripts/cls_cluster.py @@ -426,7 +426,8 @@ class Cluster: logging.debug(f'Running physims from server: {node}') script = "scripts/oc-deploy-physims.sh" options = f"oaicicd-core-for-ci-ran {oc_release} {image_tag} {self.eNBSourceCodePath}" - ret = cls_cmd.runScript(node, script, 600, options) + with cls_cmd.getConnection(node) as c: + ret = c.exec_script(script, 600, options) logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}') with cls_cmd.getConnection(node) as ssh: details_json = archiveArtifact(ssh, ctx, f'{self.eNBSourceCodePath}/ci-scripts/{oc_release}-tests.json') diff --git a/ci-scripts/cls_cmd.py b/ci-scripts/cls_cmd.py index fb074bdfce..9916049c11 100644 --- a/ci-scripts/cls_cmd.py +++ b/ci-scripts/cls_cmd.py @@ -45,12 +45,6 @@ def getConnection(host, d=None): else: return RemoteCmd(host, d=d) -def runScript(host, path, timeout, parameters=None, redirect=None, silent=False): - if is_local(host): - return LocalCmd.exec_script(path, timeout, parameters, redirect, silent) - else: - return RemoteCmd.exec_script(host, path, timeout, parameters, redirect, silent) - # provides a partial interface for the legacy SSHconnection class (getBefore(), command()) class Cmd(metaclass=abc.ABCMeta): def cd(self, d, silent=False): @@ -111,12 +105,12 @@ class LocalCmd(Cmd): logging.debug(f'Working dir is {self.cwd}') self.cp = sp.CompletedProcess(args='', returncode=0, stdout='') - def exec_script(path, timeout, parameters=None, redirect=None, silent=False): + def exec_script(self, path, timeout, parameters=None, redirect=None, silent=False): if redirect and not redirect.startswith("/"): raise ValueError(f"redirect must be absolute, but is {redirect}") c = f"{path} {parameters}" if parameters else path if not redirect: - ret = sp.run(c, shell=True, timeout=timeout, stdout=sp.PIPE, stderr=sp.STDOUT) + ret = sp.run(c, shell=True, cwd=self.cwd, timeout=timeout, stdout=sp.PIPE, stderr=sp.STDOUT) ret.stdout = ret.stdout.decode('utf-8').strip() else: with open(redirect, "w") as f: @@ -238,18 +232,15 @@ class RemoteCmd(Cmd): cfg['sock'] = paramiko.ProxyCommand(ucfg['proxycommand']) return cfg - def exec_script(host, path, timeout, parameters=None, redirect=None, silent=False): + def exec_script(self, path, timeout, parameters=None, redirect=None, silent=False): if redirect and not redirect.startswith("/"): raise ValueError(f"redirect must be absolute, but is {redirect}") p = parameters if parameters else "" r = f"> {redirect}" if redirect else "" if not silent: - logging.debug(f"local> ssh {host} bash -s {p} < {path} {r} # {path} from localhost") - client = RemoteCmd._ssh_init() - cfg = RemoteCmd._lookup_ssh_config(host) - client.connect(**cfg) + logging.debug(f"local> ssh {self.hostname} bash -s {p} < {path} {r}") bash_opt = 'BASH_XTRACEFD=1' # write bash set -x output to stdout, see bash(1) - stdin, stdout, stderr = client.exec_command(f"{bash_opt} bash -s {p} {r}", timeout=timeout) + stdin, stdout, stderr = self.client.exec_command(f"{bash_opt} bash -s {p} {r}", timeout=timeout) # open() the file f at path, read() it and write() it into the stdin of the bash -s cmd with open(path) as f: stdin.write(f.read()) @@ -259,7 +250,6 @@ class RemoteCmd(Cmd): if redirect: cmd += f" &> {redirect}" ret = sp.CompletedProcess(args=cmd, returncode=stdout.channel.recv_exit_status(), stdout=stdout.read(size=None) + stderr.read(size=None)) ret.stdout = ret.stdout.decode('utf-8').strip() - client.close() return ret def run(self, line, timeout=300, silent=False, reportNonZero=True): diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 33dc39e3a4..8b5a817ac6 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -66,7 +66,8 @@ def CreateWorkspace(host, sourcePath, ranRepository, ranCommitID, ranTargetBranc ranTargetBranch = 'develop' options += f" {ranTargetBranch}" logging.info(f'execute "{script}" with options "{options}" on node {host}') - ret = cls_cmd.runScript(host, script, 90, options) + with cls_cmd.getConnection(host) as c: + ret = c.exec_script(script, 90, options) logging.debug(f'"{script}" finished with code {ret.returncode}, output:\n{ret.stdout}') return ret.returncode == 0 diff --git a/ci-scripts/cls_corenetwork.py b/ci-scripts/cls_corenetwork.py index ad83eae622..bfdaa77481 100644 --- a/ci-scripts/cls_corenetwork.py +++ b/ci-scripts/cls_corenetwork.py @@ -77,7 +77,8 @@ class CoreNetwork: words = line[1:].strip().split(" ") script_name = words[0] options = " ".join(words[1:]) - ret = cls_cmd.runScript(host, script_name, 300, parameters=options, silent=silent) + with cls_cmd.getConnection(host) as c: + ret = c.exec_script(script_name, 300, parameters=options, silent=silent) return ret def _command(self, cmd_list, must_succeed=False, silent=False): diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 18631134c0..623bf3e5bf 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -234,7 +234,8 @@ def Custom_Command(HTML, node, command): def Custom_Script(HTML, node, script): logging.info(f"Executing custom script on {node}") - ret = cls_cmd.runScript(node, script, 90) + with cls_cmd.getConnection(node) as c: + ret = c.exec_script(script, 90) logging.debug(f"Custom_Script: {script} on node: {node} - return code {ret.returncode}, output:\n{ret.stdout}") status = 'OK' message = [ret.stdout] diff --git a/ci-scripts/tests/cmd.py b/ci-scripts/tests/cmd.py index 4d7aedfa0e..b9cbb6f51e 100644 --- a/ci-scripts/tests/cmd.py +++ b/ci-scripts/tests/cmd.py @@ -36,17 +36,20 @@ class TestCmd(unittest.TestCase): self.assertEqual(ret.stdout, "test") def test_local_script(self): - ret = cls_cmd.runScript("localhost", "tests/scripts/hello-world.sh", 1) + with cls_cmd.getConnection("localhost") as ssh: + ret = ssh.exec_script("tests/scripts/hello-world.sh", 1) self.assertEqual(ret.args, "tests/scripts/hello-world.sh") self.assertEqual(ret.returncode, 0) self.assertEqual(ret.stdout, "+ echo hello, world\nhello, world") - ret = cls_cmd.runScript("localhost", "tests/scripts/hello-fail.sh", 1, "TESTFAIL") + with cls_cmd.getConnection("localhost") as ssh: + ret = ssh.exec_script("tests/scripts/hello-fail.sh", 1, "TESTFAIL") self.assertEqual(ret.args, "tests/scripts/hello-fail.sh TESTFAIL") self.assertEqual(ret.returncode, 1) self.assertEqual(ret.stdout, "TESTFAIL") - ret = cls_cmd.runScript("localhost", "tests/scripts/hello-fail.sh", 1, "TESTFAIL2", "/tmp/result") + with cls_cmd.getConnection("localhost") as ssh: + ret = ssh.exec_script("tests/scripts/hello-fail.sh", 1, "TESTFAIL2", "/tmp/result") self.assertEqual(ret.args, "tests/scripts/hello-fail.sh TESTFAIL2 &> /tmp/result") self.assertEqual(ret.returncode, 1) self.assertEqual(ret.stdout, "") @@ -75,17 +78,20 @@ class TestCmd(unittest.TestCase): @unittest.skip("need to be able to passwordlessly SSH to localhost, also disable stty -ixon") def test_remote_script(self): - ret = cls_cmd.runScript("127.0.0.1", "tests/scripts/hello-world.sh", 1) + with cls_cmd.getConnection("127.0.0.1") as ssh: + ret = ssh.exec_script("tests/scripts/hello-world.sh", 1) self.assertEqual(ret.args, "tests/scripts/hello-world.sh") self.assertEqual(ret.returncode, 0) self.assertEqual(ret.stdout, "+ echo hello, world\nhello, world") - ret = cls_cmd.runScript("127.0.0.1", "tests/scripts/hello-fail.sh", 1, "TESTFAIL") + with cls_cmd.getConnection("127.0.0.1") as ssh: + ret = ssh.exec_script("tests/scripts/hello-fail.sh", 1, "TESTFAIL") self.assertEqual(ret.args, "tests/scripts/hello-fail.sh TESTFAIL") self.assertEqual(ret.returncode, 1) self.assertEqual(ret.stdout, "TESTFAIL") - ret = cls_cmd.runScript("127.0.0.1", "tests/scripts/hello-fail.sh", 1, "TESTFAIL2", "/tmp/result") + with cls_cmd.getConnection("127.0.0.1") as ssh: + ret = ssh.exec_script("tests/scripts/hello-fail.sh", 1, "TESTFAIL2", "/tmp/result") self.assertEqual(ret.args, "tests/scripts/hello-fail.sh TESTFAIL2 &> /tmp/result") self.assertEqual(ret.returncode, 1) self.assertEqual(ret.stdout, "")