mirror of
https://git.yoctoproject.org/poky
synced 2026-02-11 11:13:04 +01:00
os.environ is special and copy.copy() doesn't do what we'd expect, changes in the child object change the parent. copy.deepcopy() is also known to have issues with it. Use the dedicated .copy() method which will not influence the parent. This fixes selftest failures where the DISPLAY variable disappears. (From OE-Core rev: 638cd44cc9a9eb435350aac7e8eeec585d74f8db) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
145 lines
5.0 KiB
Python
145 lines
5.0 KiB
Python
# Copyright (C) 2013 Intel Corporation
|
|
#
|
|
# Released under the MIT license (see COPYING.MIT)
|
|
|
|
# Provides a class for setting up ssh connections,
|
|
# running commands and copying files to/from a target.
|
|
# It's used by testimage.bbclass and tests in lib/oeqa/runtime.
|
|
|
|
import subprocess
|
|
import time
|
|
import os
|
|
import select
|
|
|
|
|
|
class SSHProcess(object):
|
|
def __init__(self, **options):
|
|
|
|
self.defaultopts = {
|
|
"stdout": subprocess.PIPE,
|
|
"stderr": subprocess.STDOUT,
|
|
"stdin": None,
|
|
"shell": False,
|
|
"bufsize": -1,
|
|
"preexec_fn": os.setsid,
|
|
}
|
|
self.options = dict(self.defaultopts)
|
|
self.options.update(options)
|
|
self.status = None
|
|
self.output = None
|
|
self.process = None
|
|
self.starttime = None
|
|
self.logfile = None
|
|
|
|
# Unset DISPLAY which means we won't trigger SSH_ASKPASS
|
|
env = os.environ.copy()
|
|
if "DISPLAY" in env:
|
|
del env['DISPLAY']
|
|
self.options['env'] = env
|
|
|
|
def log(self, msg):
|
|
if self.logfile:
|
|
with open(self.logfile, "a") as f:
|
|
f.write("%s" % msg)
|
|
|
|
def run(self, command, timeout=None, logfile=None):
|
|
self.logfile = logfile
|
|
self.starttime = time.time()
|
|
output = ''
|
|
self.process = subprocess.Popen(command, **self.options)
|
|
if timeout:
|
|
endtime = self.starttime + timeout
|
|
eof = False
|
|
while time.time() < endtime and not eof:
|
|
if select.select([self.process.stdout], [], [], 5)[0] != []:
|
|
data = os.read(self.process.stdout.fileno(), 1024)
|
|
if not data:
|
|
self.process.stdout.close()
|
|
eof = True
|
|
else:
|
|
output += data
|
|
self.log(data)
|
|
endtime = time.time() + timeout
|
|
|
|
|
|
# process hasn't returned yet
|
|
if not eof:
|
|
self.process.terminate()
|
|
time.sleep(5)
|
|
try:
|
|
self.process.kill()
|
|
except OSError:
|
|
pass
|
|
lastline = "\nProcess killed - no output for %d seconds. Total running time: %d seconds." % (timeout, time.time() - self.starttime)
|
|
self.log(lastline)
|
|
output += lastline
|
|
else:
|
|
output = self.process.communicate()[0]
|
|
self.log(output.rstrip())
|
|
|
|
self.status = self.process.wait()
|
|
self.output = output.rstrip()
|
|
return (self.status, self.output)
|
|
|
|
|
|
class SSHControl(object):
|
|
def __init__(self, ip, logfile=None, timeout=300, user='root', port=None):
|
|
self.ip = ip
|
|
self.defaulttimeout = timeout
|
|
self.ignore_status = True
|
|
self.logfile = logfile
|
|
self.user = user
|
|
self.ssh_options = [
|
|
'-o', 'UserKnownHostsFile=/dev/null',
|
|
'-o', 'StrictHostKeyChecking=no',
|
|
'-o', 'LogLevel=ERROR'
|
|
]
|
|
self.ssh = ['ssh', '-l', self.user ] + self.ssh_options
|
|
self.scp = ['scp'] + self.ssh_options
|
|
if port:
|
|
self.ssh = self.ssh + [ '-p', port ]
|
|
self.scp = self.scp + [ '-P', port ]
|
|
|
|
def log(self, msg):
|
|
if self.logfile:
|
|
with open(self.logfile, "a") as f:
|
|
f.write("%s\n" % msg)
|
|
|
|
def _internal_run(self, command, timeout=None, ignore_status = True):
|
|
self.log("[Running]$ %s" % " ".join(command))
|
|
|
|
proc = SSHProcess()
|
|
status, output = proc.run(command, timeout, logfile=self.logfile)
|
|
|
|
self.log("[Command returned '%d' after %.2f seconds]" % (status, time.time() - proc.starttime))
|
|
|
|
if status and not ignore_status:
|
|
raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, status, output))
|
|
|
|
return (status, output)
|
|
|
|
def run(self, command, timeout=None):
|
|
"""
|
|
command - ssh command to run
|
|
timeout=<val> - kill command if there is no output after <val> seconds
|
|
timeout=None - kill command if there is no output after a default value seconds
|
|
timeout=0 - no timeout, let command run until it returns
|
|
"""
|
|
|
|
# We need to source /etc/profile for a proper PATH on the target
|
|
command = self.ssh + [self.ip, ' . /etc/profile; ' + command]
|
|
|
|
if timeout is None:
|
|
return self._internal_run(command, self.defaulttimeout, self.ignore_status)
|
|
if timeout == 0:
|
|
return self._internal_run(command, None, self.ignore_status)
|
|
return self._internal_run(command, timeout, self.ignore_status)
|
|
|
|
def copy_to(self, localpath, remotepath):
|
|
command = self.scp + [localpath, '%s@%s:%s' % (self.user, self.ip, remotepath)]
|
|
return self._internal_run(command, ignore_status=False)
|
|
|
|
def copy_from(self, remotepath, localpath):
|
|
command = self.scp + ['%s@%s:%s' % (self.user, self.ip, remotepath), localpath]
|
|
return self._internal_run(command, ignore_status=False)
|