Introduce RemoteHost and refactor code to use it
Let's move code related to coping stuff to remote hosts and managing
remote processes under a class where relevant information is stored.
This simplifies parameters being passed all over and allows to reuse
more code.
Change-Id: Ifff5ded8fdb28e8ef267cebe6c5f30a910cae11a
diff --git a/src/osmo_gsm_tester/bts_oc2g.py b/src/osmo_gsm_tester/bts_oc2g.py
index ec4ed3d..7b46fc8 100644
--- a/src/osmo_gsm_tester/bts_oc2g.py
+++ b/src/osmo_gsm_tester/bts_oc2g.py
@@ -41,14 +41,6 @@
def _direct_pcu_enabled(self):
return util.str2bool(self.conf.get('direct_pcu'))
- def launch_remote(self, name, popen_args, remote_cwd=None, keepalive=False):
- run_dir = self.run_dir.new_dir(name)
- proc = process.RemoteProcess(name, run_dir, self.remote_user, self.remote_addr(), remote_cwd,
- popen_args)
- self.suite_run.remember_to_stop(proc, keepalive)
- proc.launch()
- return proc
-
def create_pcu(self):
return pcu_oc2g.OsmoPcuOC2G(self.suite_run, self, self.conf)
@@ -116,15 +108,19 @@
if not self.inst.isfile('bin', OsmoBtsOC2G.BTS_OC2G_BIN):
raise log.Error('No osmo-bts-oc2g binary in', self.inst)
- remote_run_dir = util.Dir(OsmoBtsOC2G.REMOTE_DIR)
-
- self.remote_inst = process.copy_inst_ssh(self.run_dir, self.inst, remote_run_dir, self.remote_user,
- self.remote_addr(), OsmoBtsOC2G.BTS_OC2G_BIN, self.config_file)
-
+ rem_host = remote.RemoteHost(self.run_dir, self.remote_user, self.remote_addr())
+ remote_prefix_dir = util.Dir(OsmoBtsOC2G.REMOTE_DIR)
+ self.remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst))))
+ remote_run_dir = util.Dir(remote_prefix_dir.child(OsmoBtsOC2G.BTS_OC2G_BIN))
remote_config_file = remote_run_dir.child(OsmoBtsOC2G.BTS_OC2G_CFG)
- remote_lib = self.remote_inst.child('lib')
- remote_binary = self.remote_inst.child('bin', 'osmo-bts-oc2g')
+ rem_host.recreate_remote_dir(self.remote_inst)
+ rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir)
+ rem_host.create_remote_dir(remote_run_dir)
+ rem_host.scp('scp-cfg-to-remote', self.config_file, remote_config_file)
+
+ remote_lib = self.remote_inst.child('lib')
+ remote_binary = self.remote_inst.child('bin', OsmoBtsOC2G.BTS_OC2G_BIN)
args = ('LD_LIBRARY_PATH=%s' % remote_lib,
remote_binary, '-c', remote_config_file, '-r', '1',
'-i', self.bsc.addr())
@@ -132,6 +128,7 @@
if self._direct_pcu_enabled():
args += ('-M',)
- self.proc_bts = self.launch_remote('osmo-bts-oc2g', args, remote_cwd=remote_run_dir, keepalive=keepalive)
-
+ proc = rem_host.RemoteProcess(OsmoBtsOC2G.BTS_OC2G_BIN, args)
+ self.suite_run.remember_to_stop(proc, keepalive)
+ proc.launch()
# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/bts_osmotrx.py b/src/osmo_gsm_tester/bts_osmotrx.py
index 9110d8a..78c17cc 100644
--- a/src/osmo_gsm_tester/bts_osmotrx.py
+++ b/src/osmo_gsm_tester/bts_osmotrx.py
@@ -18,10 +18,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import stat
import pprint
from abc import ABCMeta, abstractmethod
-from . import log, config, util, template, process, bts_osmo
+from . import log, config, util, template, process, remote, bts_osmo
from . import powersupply
from .event_loop import MainLoop
@@ -263,72 +262,41 @@
proc.launch()
return proc
- def launch_process_remote(self, name, popen_args, remote_cwd=None, keepalive=False):
- run_dir = self.run_dir.new_dir(name)
- proc = process.RemoteProcess(name, run_dir, self.remote_user, self.listen_ip, remote_cwd,
- popen_args)
- self.suite_run.remember_to_stop(proc, keepalive)
- proc.launch()
- return proc
-
- def generate_wrapper_script(self):
- wrapper_script = self.run_dir.new_file(OsmoTrx.WRAPPER_SCRIPT)
- with open(wrapper_script, 'w') as f:
- r = """#!/bin/bash
- mypid=0
- sign_handler() {
- sig=$1
- echo "received signal handler $sig, killing $mypid"
- kill $mypid
- }
- trap 'sign_handler SIGTERM' SIGTERM
- trap 'sign_handler SIGINT' SIGINT
- trap 'sign_handler SIGHUP' SIGHUP
- "$@" &
- mypid=$!
- echo "waiting for $mypid"
- wait $mypid
- echo "process $mypid finished"
- """
- f.write(r)
- st = os.stat(wrapper_script)
- os.chmod(wrapper_script, st.st_mode | stat.S_IEXEC)
- return wrapper_script
-
- def inst_compatible_for_remote(self):
- proc = process.run_remote_sync(self.run_dir, self.remote_user, self.listen_ip, 'uname-m', ('uname', '-m'))
- if "x86_64" in (proc.get_stdout() or ''):
- return True
- return False
-
def start_remotely(self, keepalive):
# Run remotely through ssh. We need to run osmo-trx under a wrapper
# script since osmo-trx ignores SIGHUP and will keep running after
# we close local ssh session. The wrapper script catches SIGHUP and
# sends SIGINT to it.
- remote_run_dir = util.Dir(OsmoTrx.REMOTE_DIR)
+
+ rem_host = remote.RemoteHost(self.run_dir, self.remote_user, self.listen_ip)
+
+ remote_prefix_dir = util.Dir(OsmoTrx.REMOTE_DIR)
+ remote_run_dir = util.Dir(remote_prefix_dir.child(self.binary_name()))
remote_config_file = remote_run_dir.child(OsmoTrx.CONF_OSMO_TRX)
- have_inst = self.inst_compatible_for_remote()
+ have_inst = rem_host.inst_compatible_for_remote()
if have_inst:
self.inst = util.Dir(os.path.abspath(self.suite_run.trial.get_inst('osmo-trx')))
- # if self.inst is None, we still want to copy config file, create remote run dir, etc.
- self.remote_inst = process.copy_inst_ssh(self.run_dir, self.inst, remote_run_dir, self.remote_user,
- self.listen_ip, self.binary_name(), self.config_file)
-
- wrapper_script = self.generate_wrapper_script()
- remote_wrapper_script = remote_run_dir.child(OsmoTrx.WRAPPER_SCRIPT)
- process.scp(self.run_dir, self.remote_user, self.listen_ip, 'scp-wrapper-to-remote', wrapper_script, remote_wrapper_script)
+ rem_host.recreate_remote_dir(remote_prefix_dir)
+ if have_inst:
+ self.remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst))))
+ rem_host.create_remote_dir(self.remote_inst)
+ rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir)
+ rem_host.create_remote_dir(remote_run_dir)
+ rem_host.scp('scp-cfg-to-remote', self.config_file, remote_config_file)
if have_inst:
remote_lib = self.remote_inst.child('lib')
remote_binary = self.remote_inst.child('bin', self.binary_name())
- args = ('LD_LIBRARY_PATH=%s' % remote_lib, remote_wrapper_script, remote_binary, '-C', remote_config_file)
+ args = (remote_binary, '-C', remote_config_file)
else: # Use whatever is available i nremote system PATH:
- args = (remote_wrapper_script, self.binary_name(), '-C', remote_config_file)
-
- self.proc_trx = self.launch_process_remote(self.binary_name(), args, remote_cwd=remote_run_dir, keepalive=keepalive)
+ remote_lib = None
+ remote_binary = self.binary_name()
+ args = (remote_binary, '-C', remote_config_file)
+ self.proc_trx = rem_host.RemoteProcessFixIgnoreSIGHUP(self.binary_name(), remote_run_dir, args, prepend_ldlibpath=remote_lib)
+ self.suite_run.remember_to_stop(self.proc_trx, keepalive)
+ self.proc_trx.launch()
##############
# PUBLIC (test API included)
diff --git a/src/osmo_gsm_tester/bts_sysmo.py b/src/osmo_gsm_tester/bts_sysmo.py
index 66d305a..d616e29 100644
--- a/src/osmo_gsm_tester/bts_sysmo.py
+++ b/src/osmo_gsm_tester/bts_sysmo.py
@@ -19,7 +19,7 @@
import os
import pprint
-from . import log, config, util, template, process, pcu_sysmo, bts_osmo
+from . import log, config, util, template, process, remote, pcu_sysmo, bts_osmo
class SysmoBts(bts_osmo.OsmoBts):
##############
@@ -36,19 +36,12 @@
self.inst = None
self.remote_inst = None
self.remote_dir = None
+ self.proc_bts = None
self.remote_user = 'root'
def _direct_pcu_enabled(self):
return util.str2bool(self.conf.get('direct_pcu'))
- def launch_remote(self, name, popen_args, remote_cwd=None, keepalive=False):
- run_dir = self.run_dir.new_dir(name)
- proc = process.RemoteProcess(name, run_dir, self.remote_user, self.remote_addr(), remote_cwd,
- popen_args)
- self.suite_run.remember_to_stop(proc, keepalive)
- proc.launch()
- return proc
-
def create_pcu(self):
return pcu_sysmo.OsmoPcuSysmo(self.suite_run, self, self.conf)
@@ -110,17 +103,21 @@
if not self.inst.isfile('bin', SysmoBts.BTS_SYSMO_BIN):
raise log.Error('No osmo-bts-sysmo binary in', self.inst)
- remote_run_dir = util.Dir(SysmoBts.REMOTE_DIR)
-
- self.remote_inst = process.copy_inst_ssh(self.run_dir, self.inst, remote_run_dir, self.remote_user,
- self.remote_addr(), SysmoBts.BTS_SYSMO_BIN, self.config_file)
- process.run_remote_sync(self.run_dir, self.remote_user, self.remote_addr(), 'reload-dsp-firmware',
- ('/bin/sh', '-c', '"cat /lib/firmware/sysmobts-v?.bit > /dev/fpgadl_par0 ; cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0"'))
-
+ rem_host = remote.RemoteHost(self.run_dir, self.remote_user, self.remote_addr())
+ remote_prefix_dir = util.Dir(SysmoBts.REMOTE_DIR)
+ self.remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst))))
+ remote_run_dir = util.Dir(remote_prefix_dir.child(SysmoBts.BTS_SYSMO_BIN))
remote_config_file = remote_run_dir.child(SysmoBts.BTS_SYSMO_CFG)
- remote_lib = self.remote_inst.child('lib')
- remote_binary = self.remote_inst.child('bin', 'osmo-bts-sysmo')
+ rem_host.recreate_remote_dir(self.remote_inst)
+ rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir)
+ rem_host.create_remote_dir(remote_run_dir)
+ rem_host.scp('scp-cfg-to-remote', self.config_file, remote_config_file)
+
+ rem_host.run_remote_sync('reload-dsp-firmware', ('/bin/sh', '-c', '"cat /lib/firmware/sysmobts-v?.bit > /dev/fpgadl_par0 ; cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0"'))
+
+ remote_lib = self.remote_inst.child('lib')
+ remote_binary = self.remote_inst.child('bin', SysmoBts.BTS_SYSMO_BIN)
args = ('LD_LIBRARY_PATH=%s' % remote_lib,
remote_binary, '-c', remote_config_file, '-r', '1',
'-i', self.bsc.addr())
@@ -128,6 +125,8 @@
if self._direct_pcu_enabled():
args += ('-M',)
- self.proc_bts = self.launch_remote('osmo-bts-sysmo', args, remote_cwd=remote_run_dir, keepalive=keepalive)
+ self.proc_bts = rem_host.RemoteProcess(SysmoBts.BTS_SYSMO_BIN, args)
+ self.suite_run.remember_to_stop(self.proc_bts, keepalive)
+ self.proc_bts.launch()
# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/process.py b/src/osmo_gsm_tester/process.py
index 06da3bf..1c2f592 100644
--- a/src/osmo_gsm_tester/process.py
+++ b/src/osmo_gsm_tester/process.py
@@ -406,32 +406,4 @@
proc = NetNSProcess(name, run_dir, netns, popen_args)
proc.launch_sync()
return proc
-
-def run_remote_sync(run_dir, remote_user, remote_addr, name, popen_args, remote_cwd=None):
- run_dir = run_dir.new_dir(name)
- proc = RemoteProcess(name, run_dir, remote_user, remote_addr, remote_cwd, popen_args)
- proc.launch_sync()
- return proc
-
-def scp(run_dir, remote_user, remote_addr, name, local_path, remote_path):
- run_local_sync(run_dir, name, ('scp', '-r', local_path, '%s@%s:%s' % (remote_user, remote_addr, remote_path)))
-
-# If no inst binaries copying is required (eg. because binary+libs is already available in distro), inst can be None.
-def copy_inst_ssh(run_dir, inst, remote_dir, remote_user, remote_addr, remote_rundir_append, cfg_file_name):
- remote_dir_str = str(remote_dir)
- run_remote_sync(run_dir, remote_user, remote_addr, 'rm-remote-dir', ('test', '!', '-d', remote_dir_str, '||', 'rm', '-rf', remote_dir_str))
- run_remote_sync(run_dir, remote_user, remote_addr, 'mk-remote-dir', ('mkdir', '-p', remote_dir_str))
- if inst is not None:
- remote_inst = Dir(remote_dir.child(os.path.basename(str(inst))))
- scp(run_dir, remote_user, remote_addr, 'scp-inst-to-remote', str(inst), remote_dir_str)
- else:
- remote_inst = None
-
- remote_run_dir = remote_dir.child(remote_rundir_append)
- run_remote_sync(run_dir, remote_user, remote_addr, 'mk-remote-run-dir', ('mkdir', '-p', remote_run_dir))
-
- remote_config_file = remote_dir.child(os.path.basename(cfg_file_name))
- scp(run_dir, remote_user, remote_addr, 'scp-cfg-to-remote', cfg_file_name, remote_config_file)
- return remote_inst
-
# vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/remote.py b/src/osmo_gsm_tester/remote.py
new file mode 100644
index 0000000..92dd113
--- /dev/null
+++ b/src/osmo_gsm_tester/remote.py
@@ -0,0 +1,117 @@
+# osmo_gsm_tester: specifics for remote nodes
+#
+# Copyright (C) 2020 by sysmocom - s.f.m.c. GmbH
+#
+# Author: Pau Espin Pedrol <pespin@sysmocom.de>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import stat
+import os
+import re
+import pprint
+
+from . import log, util, config, template, process, osmo_ctrl, pcap_recorder
+
+class RemoteHost(log.Origin):
+
+ WRAPPER_SCRIPT = 'ssh_sigkiller.sh'
+
+ def __init__(self, run_dir, remote_user = 'root', remote_host = 'localhost', remote_cwd=None):
+ super().__init__(log.C_RUN, 'host-' + remote_user + '@' + remote_host)
+ self.run_dir = util.Dir(run_dir.new_dir(self.name()))
+ self.remote_user = remote_user
+ self.remote_host = remote_host
+ self.remote_cwd = remote_cwd
+
+ def user(self):
+ return self.remote_user
+
+ def host(self):
+ return self.remote_host
+
+ def cwd(self):
+ return self.remote_cwd
+
+ def RemoteProcess(self, name, popen_args, **popen_kwargs):
+ run_dir = self.run_dir.new_dir(name)
+ return process.RemoteProcess(name, run_dir, self.user(), self.host(), self.cwd(), popen_args, **popen_kwargs)
+
+ def generate_wrapper_script(self):
+ wrapper_script = self.run_dir.new_file(RemoteHost.WRAPPER_SCRIPT)
+ with open(wrapper_script, 'w') as f:
+ r = """#!/bin/bash
+ mypid=0
+ sign_handler() {
+ sig=$1
+ echo "received signal handler $sig, killing $mypid"
+ kill $mypid
+ }
+ trap 'sign_handler SIGTERM' SIGTERM
+ trap 'sign_handler SIGINT' SIGINT
+ trap 'sign_handler SIGHUP' SIGHUP
+ $@ &
+ mypid=$!
+ echo "waiting for $mypid"
+ wait $mypid
+ echo "process $mypid finished"
+ """
+ f.write(r)
+ st = os.stat(wrapper_script)
+ os.chmod(wrapper_script, st.st_mode | stat.S_IEXEC)
+ return wrapper_script
+
+ def RemoteProcessFixIgnoreSIGHUP(self, name, remote_dir, popen_args, prepend_ldlibpath=None, **popen_kwargs):
+ # Run remotely through ssh. We need to run binary under a wrapper
+ # script since osmo-trx ignores SIGHUP and will keep running after
+ # we close local ssh session. The wrapper script catches SIGHUP and
+ # sends SIGINT to it.
+ self.create_remote_dir(remote_dir)
+
+ wrapper_script = self.generate_wrapper_script()
+ remote_wrapper_script = remote_dir.child(RemoteHost.WRAPPER_SCRIPT)
+ self.scp('scp-wrapper-to-remote', wrapper_script, remote_wrapper_script)
+
+ # Used fi to run stuff from an osmo-gsm-tester copied inst
+ if prepend_ldlibpath is not None:
+ args = ('LD_LIBRARY_PATH=%s' % prepend_ldlibpath, remote_wrapper_script,) + popen_args
+ else:
+ args = (remote_wrapper_script,) + popen_args
+ return self.RemoteProcess(name, args, **popen_kwargs)
+
+ def run_remote_sync(self, name, popen_args):
+ proc = self.RemoteProcess(name, popen_args)
+ proc.launch_sync()
+ return proc
+
+ def rm_remote_dir(self, remote_dir):
+ remote_dir_str = str(remote_dir)
+ self.run_remote_sync('rm-remote-dir', ('test', '!', '-d', remote_dir_str, '||', 'rm', '-rf', remote_dir_str))
+
+ def create_remote_dir(self, remote_dir):
+ remote_dir_str = str(remote_dir)
+ self.run_remote_sync('mk-remote-dir', ('mkdir', '-p', remote_dir_str))
+
+ def recreate_remote_dir(self, remote_dir):
+ self.rm_remote_dir(remote_dir)
+ self.create_remote_dir(remote_dir)
+
+ def inst_compatible_for_remote(self):
+ proc = self.run_remote_sync('uname-m', ('uname', '-m'))
+ if "x86_64" in (proc.get_stdout() or ''):
+ return True
+ return False
+
+ def scp(self, name, local_path, remote_path):
+ process.run_local_sync(self.run_dir, name, ('scp', '-r', local_path, '%s@%s:%s' % (self.user(), self.host(), remote_path)))