diff options
Diffstat (limited to 'import-layers/yocto-poky/meta/lib/oeqa/utils')
12 files changed, 372 insertions, 91 deletions
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py index 48f644129..5cd0f7477 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py @@ -41,7 +41,7 @@ class Command(object): self.data = data self.options = dict(self.defaultopts) - if isinstance(self.cmd, basestring): + if isinstance(self.cmd, str): self.options["shell"] = True if self.data: self.options['stdin'] = subprocess.PIPE @@ -78,7 +78,10 @@ class Command(object): self.process.kill() self.thread.join() - self.output = self.output.rstrip() + if not self.output: + self.output = "" + else: + self.output = self.output.decode("utf-8", errors='replace').rstrip() self.status = self.process.poll() self.log.debug("Command '%s' returned %d as exit code." % (self.cmd, self.status)) @@ -103,6 +106,7 @@ def runCmd(command, ignore_status=False, timeout=None, assert_error=True, **opti result.command = command result.status = cmd.status result.output = cmd.output + result.error = cmd.error result.pid = cmd.process.pid if result.status and not ignore_status: @@ -123,7 +127,7 @@ def bitbake(command, ignore_status=False, timeout=None, postconfig=None, **optio else: extra_args = "" - if isinstance(command, basestring): + if isinstance(command, str): cmd = "bitbake " + extra_args + " " + command else: cmd = [ "bitbake" ] + [a for a in (command + extra_args.split(" ")) if a not in [""]] @@ -141,22 +145,45 @@ def get_bb_env(target=None, postconfig=None): else: return bitbake("-e", postconfig=postconfig).output -def get_bb_var(var, target=None, postconfig=None): - val = None +def get_bb_vars(variables=None, target=None, postconfig=None): + """Get values of multiple bitbake variables""" bbenv = get_bb_env(target, postconfig=postconfig) + + var_re = re.compile(r'^(export )?(?P<var>\w+)="(?P<value>.*)"$') + unset_re = re.compile(r'^unset (?P<var>\w+)$') lastline = None + values = {} for line in bbenv.splitlines(): - if re.search("^(export )?%s=" % var, line): - val = line.split('=', 1)[1] - val = val.strip('\"') - break - elif re.match("unset %s$" % var, line): - # Handle [unexport] variables - if lastline.startswith('# "'): - val = lastline.split('\"')[1] - break + match = var_re.match(line) + val = None + if match: + val = match.group('value') + else: + match = unset_re.match(line) + if match: + # Handle [unexport] variables + if lastline.startswith('# "'): + val = lastline.split('"')[1] + if val: + var = match.group('var') + if variables is None: + values[var] = val + else: + if var in variables: + values[var] = val + variables.remove(var) + # Stop after all required variables have been found + if not variables: + break lastline = line - return val + if variables: + # Fill in missing values + for var in variables: + values[var] = None + return values + +def get_bb_var(var, target=None, postconfig=None): + return get_bb_vars([var], target, postconfig)[var] def get_test_layer(): layers = get_bb_var("BBLAYERS").split() @@ -196,7 +223,7 @@ def runqemu(pn, ssh=True): tinfoil.config_data.setVar("TEST_QEMUBOOT_TIMEOUT", "1000") import oe.recipeutils recipefile = oe.recipeutils.pn_to_recipe(tinfoil.cooker, pn) - recipedata = oe.recipeutils.parse_recipe(recipefile, [], tinfoil.config_data) + recipedata = oe.recipeutils.parse_recipe(tinfoil.cooker, recipefile, []) # The QemuRunner log is saved out, but we need to ensure it is at the right # log level (and then ensure that since it's a child of the BitBake logger, @@ -237,3 +264,15 @@ def runqemu(pn, ssh=True): qemu.stop() except: pass + +def updateEnv(env_file): + """ + Source a file and update environment. + """ + + cmd = ". %s; env -0" % env_file + result = runCmd(cmd) + + for line in result.output.split("\0"): + (key, _, value) = line.partition("=") + os.environ[key] = value diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py index 0d79223a2..25f9c54e6 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py @@ -57,6 +57,7 @@ class skipIfFailure(object): self.testcase = testcase def __call__(self,f): + @wraps(f) def wrapped_f(*args, **kwargs): res = getResults() if self.testcase in (res.getFailList() or res.getErrorList()): @@ -71,6 +72,7 @@ class skipIfSkipped(object): self.testcase = testcase def __call__(self,f): + @wraps(f) def wrapped_f(*args, **kwargs): res = getResults() if self.testcase in res.getSkipList(): @@ -85,6 +87,7 @@ class skipUnlessPassed(object): self.testcase = testcase def __call__(self,f): + @wraps(f) def wrapped_f(*args, **kwargs): res = getResults() if self.testcase in res.getSkipList() or \ @@ -97,11 +100,11 @@ class skipUnlessPassed(object): return wrapped_f class testcase(object): - def __init__(self, test_case): self.test_case = test_case def __call__(self, func): + @wraps(func) def wrapped_f(*args, **kwargs): return func(*args, **kwargs) wrapped_f.test_case = self.test_case @@ -112,6 +115,8 @@ class NoParsingFilter(logging.Filter): def filter(self, record): return record.levelno == 100 +import inspect + def LogResults(original_class): orig_method = original_class.run @@ -121,6 +126,19 @@ def LogResults(original_class): logfile = os.path.join(os.getcwd(),'results-'+caller+'.'+timestamp+'.log') linkfile = os.path.join(os.getcwd(),'results-'+caller+'.log') + def get_class_that_defined_method(meth): + if inspect.ismethod(meth): + for cls in inspect.getmro(meth.__self__.__class__): + if cls.__dict__.get(meth.__name__) is meth: + return cls + meth = meth.__func__ # fallback to __qualname__ parsing + if inspect.isfunction(meth): + cls = getattr(inspect.getmodule(meth), + meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0]) + if isinstance(cls, type): + return cls + return None + #rewrite the run method of unittest.TestCase to add testcase logging def run(self, result, *args, **kws): orig_method(self, result, *args, **kws) @@ -132,7 +150,7 @@ def LogResults(original_class): except AttributeError: test_case = self._testMethodName - class_name = str(testMethod.im_class).split("'")[1] + class_name = str(get_class_that_defined_method(testMethod)).split("'")[1] #create custom logging level for filtering. custom_log_level = 100 @@ -171,10 +189,24 @@ def LogResults(original_class): if passed: local_log.results("Testcase "+str(test_case)+": PASSED") + # XXX: In order to avoid race condition when test if exists the linkfile + # use bb.utils.lock, the best solution is to create a unique name for the + # link file. + try: + import bb + has_bb = True + lockfilename = linkfile + '.lock' + except ImportError: + has_bb = False + + if has_bb: + lf = bb.utils.lockfile(lockfilename, block=True) # Create symlink to the current log - if os.path.exists(linkfile): + if os.path.lexists(linkfile): os.remove(linkfile) os.symlink(logfile, linkfile) + if has_bb: + bb.utils.unlockfile(lf) original_class.run = run @@ -212,7 +244,7 @@ def tag(*args, **kwargs): def wrap_ob(ob): for name in args: setattr(ob, __tag_prefix + name, True) - for name, value in kwargs.iteritems(): + for name, value in kwargs.items(): setattr(ob, __tag_prefix + name, value) return ob return wrap_ob diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py index 63a591d36..71422a9ae 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py @@ -3,7 +3,7 @@ import sys import errno import datetime import itertools -from commands import runCmd +from .commands import runCmd def get_host_dumper(d): cmds = d.getVar("testimage_dump_host", True) diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py new file mode 100644 index 000000000..ae85d2766 --- /dev/null +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py @@ -0,0 +1,68 @@ +# +# Copyright (C) 2016 Intel Corporation +# +# Released under the MIT license (see COPYING.MIT) +# +"""Git repository interactions""" +import os + +from oeqa.utils.commands import runCmd + + +class GitError(Exception): + """Git error handling""" + pass + +class GitRepo(object): + """Class representing a Git repository clone""" + def __init__(self, path, is_topdir=False): + self.top_dir = self._run_git_cmd_at(['rev-parse', '--show-toplevel'], + path) + realpath = os.path.realpath(path) + if is_topdir and realpath != self.top_dir: + raise GitError("{} is not a Git top directory".format(realpath)) + + @staticmethod + def _run_git_cmd_at(git_args, cwd, **kwargs): + """Run git command at a specified directory""" + git_cmd = 'git ' if isinstance(git_args, str) else ['git'] + git_cmd += git_args + ret = runCmd(git_cmd, ignore_status=True, cwd=cwd, **kwargs) + if ret.status: + cmd_str = git_cmd if isinstance(git_cmd, str) \ + else ' '.join(git_cmd) + raise GitError("'{}' failed with exit code {}: {}".format( + cmd_str, ret.status, ret.output)) + return ret.output.strip() + + @staticmethod + def init(path): + """Initialize a new Git repository""" + GitRepo._run_git_cmd_at('init', cwd=path) + return GitRepo(path, is_topdir=True) + + def run_cmd(self, git_args, env_update=None): + """Run Git command""" + env = None + if env_update: + env = os.environ.copy() + env.update(env_update) + return self._run_git_cmd_at(git_args, self.top_dir, env=env) + + def rev_parse(self, revision): + """Do git rev-parse""" + try: + return self.run_cmd(['rev-parse', revision]) + except GitError: + # Revision does not exist + return None + + def get_current_branch(self): + """Get current branch""" + try: + # Strip 11 chars, i.e. 'refs/heads' from the beginning + return self.run_cmd(['symbolic-ref', 'HEAD'])[11:] + except GitError: + return None + + diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py index 76518d8ef..7d1233145 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py @@ -1,8 +1,9 @@ -import SimpleHTTPServer +import http.server import multiprocessing import os +from socketserver import ThreadingMixIn -class HTTPServer(SimpleHTTPServer.BaseHTTPServer.HTTPServer): +class HTTPServer(ThreadingMixIn, http.server.HTTPServer): def server_start(self, root_dir): import signal @@ -10,7 +11,7 @@ class HTTPServer(SimpleHTTPServer.BaseHTTPServer.HTTPServer): os.chdir(root_dir) self.serve_forever() -class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): +class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler): def log_message(self, format_str, *args): pass diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py index 87b50354c..b377dcd27 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py @@ -3,7 +3,7 @@ import sys import os import re -import ftools +from . import ftools # A parser that can be used to identify weather a line is a test result or a section statement. diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py new file mode 100644 index 000000000..099ecc972 --- /dev/null +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py @@ -0,0 +1,29 @@ +def get_package_manager(d, root_path): + """ + Returns an OE package manager that can install packages in root_path. + """ + from oe.package_manager import RpmPM, OpkgPM, DpkgPM + + pkg_class = d.getVar("IMAGE_PKGTYPE", True) + if pkg_class == "rpm": + pm = RpmPM(d, + root_path, + d.getVar('TARGET_VENDOR', True)) + pm.create_configs() + + elif pkg_class == "ipk": + pm = OpkgPM(d, + root_path, + d.getVar("IPKGCONF_TARGET", True), + d.getVar("ALL_MULTILIB_PACKAGE_ARCHS", True)) + + elif pkg_class == "deb": + pm = DpkgPM(d, + root_path, + d.getVar('PACKAGE_ARCHS', True), + d.getVar('DPKG_ARCH', True)) + + pm.write_index() + pm.update() + + return pm diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py index 784cf964f..8f1b5b980 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py @@ -20,16 +20,17 @@ from oeqa.utils.dump import HostDumper import logging logger = logging.getLogger("BitBake.QemuRunner") +logger.addHandler(logging.StreamHandler()) # Get Unicode non printable control chars -control_range = range(0,32)+range(127,160) -control_chars = [unichr(x) for x in control_range - if unichr(x) not in string.printable] +control_range = list(range(0,32))+list(range(127,160)) +control_chars = [chr(x) for x in control_range + if chr(x) not in string.printable] re_control_char = re.compile('[%s]' % re.escape("".join(control_chars))) class QemuRunner: - def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds): + def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds, use_kvm): # Popen object for runqemu self.runqemu = None @@ -49,6 +50,7 @@ class QemuRunner: self.boottime = boottime self.logged = False self.thread = None + self.use_kvm = use_kvm self.runqemutime = 60 self.host_dumper = HostDumper(dump_host_cmds, dump_dir) @@ -71,7 +73,8 @@ class QemuRunner: if self.logfile: # It is needed to sanitize the data received from qemu # because is possible to have control characters - msg = re_control_char.sub('', unicode(msg, 'utf-8')) + msg = msg.decode("utf-8") + msg = re_control_char.sub('', msg) with codecs.open(self.logfile, "a", encoding="utf-8") as f: f.write("%s" % msg) @@ -79,7 +82,7 @@ class QemuRunner: import fcntl fl = fcntl.fcntl(o, fcntl.F_GETFL) fcntl.fcntl(o, fcntl.F_SETFL, fl | os.O_NONBLOCK) - return os.read(o.fileno(), 1000000) + return os.read(o.fileno(), 1000000).decode("utf-8") def handleSIGCHLD(self, signum, frame): @@ -91,7 +94,7 @@ class QemuRunner: self._dump_host() raise SystemExit - def start(self, qemuparams = None, get_ip = True): + def start(self, qemuparams = None, get_ip = True, extra_bootparams = None): if self.display: os.environ["DISPLAY"] = self.display # Set this flag so that Qemu doesn't do any grabs as SDL grabs @@ -114,12 +117,16 @@ class QemuRunner: try: threadsock, threadport = self.create_socket() self.server_socket, self.serverport = self.create_socket() - except socket.error, msg: + except socket.error as msg: logger.error("Failed to create listening socket: %s" % msg[1]) return False - self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8 printk.time=1" qemuparams="-serial tcp:127.0.0.1:{}"'.format(threadport) + bootparams = 'console=tty1 console=ttyS0,115200n8 printk.time=1' + if extra_bootparams: + bootparams = bootparams + ' ' + extra_bootparams + + self.qemuparams = 'bootparams="{0}" qemuparams="-serial tcp:127.0.0.1:{1}"'.format(bootparams, threadport) if not self.display: self.qemuparams = 'nographic ' + self.qemuparams if qemuparams: @@ -128,7 +135,15 @@ class QemuRunner: self.origchldhandler = signal.getsignal(signal.SIGCHLD) signal.signal(signal.SIGCHLD, self.handleSIGCHLD) - launch_cmd = 'runqemu tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams) + launch_cmd = 'runqemu snapshot ' + if self.use_kvm: + logger.info('Using kvm for runqemu') + launch_cmd += 'kvm ' + else: + logger.info('Not using kvm for runqemu') + launch_cmd += 'tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams) + logger.info('launchcmd=%s'%(launch_cmd)) + # FIXME: We pass in stdin=subprocess.PIPE here to work around stty # blocking at the end of the runqemu script when using this within # oe-selftest (this makes stty error out immediately). There ought @@ -137,12 +152,12 @@ class QemuRunner: output = self.runqemu.stdout # - # We need the preexec_fn above so that all runqemu processes can easily be killed + # We need the preexec_fn above so that all runqemu processes can easily be killed # (by killing their process group). This presents a problem if this controlling - # process itself is killed however since those processes don't notice the death + # process itself is killed however since those processes don't notice the death # of the parent and merrily continue on. # - # Rather than hack runqemu to deal with this, we add something here instead. + # Rather than hack runqemu to deal with this, we add something here instead. # Basically we fork off another process which holds an open pipe to the parent # and also is setpgrp. If/when the pipe sees EOF from the parent dieing, it kills # the process group. This is like pctrl's PDEATHSIG but for a process group @@ -192,7 +207,7 @@ class QemuRunner: else: self.ip = ips[0] self.server_ip = ips[1] - except IndexError, ValueError: + except (IndexError, ValueError): logger.info("Couldn't get ip from qemu process arguments! Here is the qemu command line used:\n%s\nand output from runqemu:\n%s" % (cmdline, self.getOutput(output))) self._dump_host() self.stop() @@ -219,6 +234,7 @@ class QemuRunner: stopread = False qemusock = None bootlog = '' + data = b'' while time.time() < endtime and not stopread: sread, swrite, serror = select.select(socklist, [], [], 5) for sock in sread: @@ -229,14 +245,19 @@ class QemuRunner: socklist.remove(self.server_socket) logger.info("Connection from %s:%s" % addr) else: - data = sock.recv(1024) + data = data + sock.recv(1024) if data: - bootlog += data - if re.search(".* login:", bootlog): - self.server_socket = qemusock - stopread = True - reachedlogin = True - logger.info("Reached login banner") + try: + data = data.decode("utf-8", errors="surrogateescape") + bootlog += data + data = b'' + if re.search(".* login:", bootlog): + self.server_socket = qemusock + stopread = True + reachedlogin = True + logger.info("Reached login banner") + except UnicodeDecodeError: + continue else: socklist.remove(sock) sock.close() @@ -277,13 +298,14 @@ class QemuRunner: if hasattr(self, "origchldhandler"): signal.signal(signal.SIGCHLD, self.origchldhandler) if self.runqemu: - os.kill(self.monitorpid, signal.SIGKILL) - logger.info("Sending SIGTERM to runqemu") - try: - os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM) - except OSError as e: - if e.errno != errno.ESRCH: - raise + if hasattr(self, "monitorpid"): + os.kill(self.monitorpid, signal.SIGKILL) + logger.info("Sending SIGTERM to runqemu") + try: + os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM) + except OSError as e: + if e.errno != errno.ESRCH: + raise endtime = time.time() + self.runqemutime while self.runqemu.poll() is None and time.time() < endtime: time.sleep(1) @@ -325,7 +347,7 @@ class QemuRunner: # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd] # ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0] - processes = ps.split('\n') + processes = ps.decode("utf-8").split('\n') nfields = len(processes[0].split()) - 1 pids = {} commands = {} @@ -354,9 +376,9 @@ class QemuRunner: if p not in parents: parents.append(p) newparents = next - #print "Children matching %s:" % str(parents) + #print("Children matching %s:" % str(parents)) for p in parents: - # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx" + # Need to be careful here since runqemu runs "ldd qemu-system-xxxx" # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx" basecmd = commands[p].split()[0] basecmd = os.path.basename(basecmd) @@ -370,14 +392,14 @@ class QemuRunner: data = '' status = 0 - self.server_socket.sendall(command) + self.server_socket.sendall(command.encode('utf-8')) keepreading = True while keepreading: sread, _, _ = select.select([self.server_socket],[],[],5) if sread: answer = self.server_socket.recv(1024) if answer: - data += answer + data += answer.decode('utf-8') # Search the prompt to stop if re.search("[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data): keepreading = False @@ -442,7 +464,7 @@ class LoggingThread(threading.Thread): def stop(self): self.logger.info("Stopping logging thread") if self.running: - os.write(self.writepipe, "stop") + os.write(self.writepipe, bytes("stop", "utf-8")) def teardown(self): self.logger.info("Tearing down logging thread") diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py index 4f95101f3..d554f0dbc 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py @@ -13,7 +13,7 @@ import re import socket import select import bb -from qemurunner import QemuRunner +from .qemurunner import QemuRunner class QemuTinyRunner(QemuRunner): @@ -50,7 +50,7 @@ class QemuTinyRunner(QemuRunner): self.server_socket.connect(self.socketfile) bb.note("Created listening socket for qemu serial console.") tries = 0 - except socket.error, msg: + except socket.error as msg: self.server_socket.close() bb.fatal("Failed to create listening socket.") tries -= 1 @@ -60,7 +60,7 @@ class QemuTinyRunner(QemuRunner): with open(self.logfile, "a") as f: f.write("%s" % msg) - def start(self, qemuparams = None): + def start(self, qemuparams = None, ssh=True, extra_bootparams=None): if self.display: os.environ["DISPLAY"] = self.display @@ -102,7 +102,7 @@ class QemuTinyRunner(QemuRunner): bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime) output = self.runqemu.stdout self.stop() - bb.note("Output from runqemu:\n%s" % output.read()) + bb.note("Output from runqemu:\n%s" % output.read().decode("utf-8")) return False return self.is_alive() @@ -131,7 +131,7 @@ class QemuTinyRunner(QemuRunner): # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd] # ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0] - processes = ps.split('\n') + processes = ps.decode("utf-8").split('\n') nfields = len(processes[0].split()) - 1 pids = {} commands = {} @@ -160,11 +160,11 @@ class QemuTinyRunner(QemuRunner): if p not in parents: parents.append(p) newparents = next - #print "Children matching %s:" % str(parents) + #print("Children matching %s:" % str(parents)) for p in parents: - # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx" + # Need to be careful here since runqemu runs "ldd qemu-system-xxxx" # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx" basecmd = commands[p].split()[0] basecmd = os.path.basename(basecmd) if "qemu-system" in basecmd and "-serial unix" in commands[p]: - return [int(p),commands[p]]
\ No newline at end of file + return [int(p),commands[p]] diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py index 165874416..05d650255 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Copyright (C) 2013 Intel Corporation # # Released under the MIT license (see COPYING.MIT) @@ -51,16 +52,19 @@ class SSHProcess(object): 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 - + try: + 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: + data = data.decode("utf-8") + output += data + self.log(data) + endtime = time.time() + timeout + except InterruptedError: + continue # process hasn't returned yet if not eof: @@ -145,9 +149,97 @@ class SSHControl(object): 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) + if os.path.islink(localpath): + link = os.readlink(localpath) + dst_dir, dst_base = os.path.split(remotepath) + return self.run("cd %s; ln -s %s %s" % (dst_dir, link, dst_base)) + else: + 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) + + def copy_dir_to(self, localpath, remotepath): + """ + Copy recursively localpath directory to remotepath in target. + """ + + for root, dirs, files in os.walk(localpath): + # Create directories in the target as needed + for d in dirs: + tmp_dir = os.path.join(root, d).replace(localpath, "") + new_dir = os.path.join(remotepath, tmp_dir.lstrip("/")) + cmd = "mkdir -p %s" % new_dir + self.run(cmd) + + # Copy files into the target + for f in files: + tmp_file = os.path.join(root, f).replace(localpath, "") + dst_file = os.path.join(remotepath, tmp_file.lstrip("/")) + src_file = os.path.join(root, f) + self.copy_to(src_file, dst_file) + + + def delete_files(self, remotepath, files): + """ + Delete files in target's remote path. + """ + + cmd = "rm" + if not isinstance(files, list): + files = [files] + + for f in files: + cmd = "%s %s" % (cmd, os.path.join(remotepath, f)) + + self.run(cmd) + + + def delete_dir(self, remotepath): + """ + Delete remotepath directory in target. + """ + + cmd = "rmdir %s" % remotepath + self.run(cmd) + + + def delete_dir_structure(self, localpath, remotepath): + """ + Delete recursively localpath structure directory in target's remotepath. + + This function is very usefult to delete a package that is installed in + the DUT and the host running the test has such package extracted in tmp + directory. + + Example: + pwd: /home/user/tmp + tree: . + └── work + ├── dir1 + │ └── file1 + └── dir2 + + localpath = "/home/user/tmp" and remotepath = "/home/user" + + With the above variables this function will try to delete the + directory in the DUT in this order: + /home/user/work/dir1/file1 + /home/user/work/dir1 (if dir is empty) + /home/user/work/dir2 (if dir is empty) + /home/user/work (if dir is empty) + """ + + for root, dirs, files in os.walk(localpath, topdown=False): + # Delete files first + tmpdir = os.path.join(root).replace(localpath, "") + remotedir = os.path.join(remotepath, tmpdir.lstrip("/")) + self.delete_files(remotedir, files) + + # Remove dirs if empty + for d in dirs: + tmpdir = os.path.join(root, d).replace(localpath, "") + remotedir = os.path.join(remotepath, tmpdir.lstrip("/")) + self.delete_dir(remotepath) diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py index f850d78df..59593f5ef 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py @@ -10,18 +10,17 @@ import bb.utils import subprocess from abc import ABCMeta, abstractmethod -class BuildProject(): - - __metaclass__ = ABCMeta +class BuildProject(metaclass=ABCMeta): def __init__(self, d, uri, foldername=None, tmpdir="/tmp/"): self.d = d self.uri = uri self.archive = os.path.basename(uri) self.localarchive = os.path.join(tmpdir,self.archive) - self.fname = re.sub(r'.tar.bz2|tar.gz$', '', self.archive) if foldername: self.fname = foldername + else: + self.fname = re.sub(r'\.tar\.bz2$|\.tar\.gz$|\.tar\.xz$', '', self.archive) # Download self.archive to self.localarchive def _download_archive(self): @@ -118,10 +117,10 @@ class SDKBuildProject(BuildProject): subprocess.check_call(cmd, shell=True) #Change targetdir to project folder - self.targetdir = self.targetdir + self.fname + self.targetdir = os.path.join(self.targetdir, self.fname) - def run_configure(self, configure_args=''): - return super(SDKBuildProject, self).run_configure(configure_args=(configure_args or '$CONFIGURE_FLAGS'), extra_cmds=' gnu-configize; ') + def run_configure(self, configure_args='', extra_cmds=' gnu-configize; '): + return super(SDKBuildProject, self).run_configure(configure_args=(configure_args or '$CONFIGURE_FLAGS'), extra_cmds=extra_cmds) def run_install(self, install_args=''): return super(SDKBuildProject, self).run_install(install_args=(install_args or "DESTDIR=%s/../install" % self.targetdir)) @@ -134,4 +133,3 @@ class SDKBuildProject(BuildProject): def _run(self, cmd): self.log("Running . %s; " % self.sdkenv + cmd) return subprocess.call(". %s; " % self.sdkenv + cmd, shell=True) - diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py index 243463bc1..57be2ca44 100644 --- a/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py +++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py @@ -6,7 +6,7 @@ import os, re, glob as g, shutil as sh,sys from time import sleep -from commands import runCmd +from .commands import runCmd from difflib import SequenceMatcher as SM try: @@ -17,13 +17,13 @@ except ImportError: pass def plain(self, msg): if msg: - print msg + print(msg) def warn(self, msg): if msg: - print "WARNING: " + msg + print("WARNING: " + msg) def fatal(self, msg): if msg: - print "FATAL:" + msg + print("FATAL:" + msg) sys.exit(1) bb = my_log() |