kernel: add a boot validation step post boot
When requesting a boot via a kernel generate a kernel identifier
for that kernel and a command line validator (the time). These
are then checked in a post boot validation stepper "step" which
is inserted before the users next step.
Signed-off-by: Andy Whitcroft <apw@shadowen.org>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@657 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/client/bin/autotest_utils.py b/client/bin/autotest_utils.py
index eca8a66..745791c 100755
--- a/client/bin/autotest_utils.py
+++ b/client/bin/autotest_utils.py
@@ -516,3 +516,48 @@
def pickle_load(filename):
return pickle.load(open(filename, 'r'))
+
+# Return the kernel version and build timestamp.
+def running_os_release():
+ return os.uname()[2:4]
+
+
+def running_os_ident():
+ (version, timestamp) = running_os_release()
+ return version + '::' + timestamp
+
+
+# Check the passed kernel identifier against the command line
+# and the running kernel, abort the job on missmatch.
+def kernel_check_ident(expected_when, expected_id):
+ print "POST BOOT: checking booted kernel mark=%d identity='%s'" \
+ % (expected_when, expected_id)
+
+ running_id = running_os_ident()
+
+ cmdline = read_one_line("/proc/cmdline")
+
+ find_sum = re.compile(r'.*IDENT=(\d+)')
+ m = find_sum.match(cmdline)
+ cmdline_when = -1
+ if m:
+ cmdline_when = int(m.groups()[0])
+
+ # We have all the facts, see if they indicate we
+ # booted the requested kernel or not.
+ bad = False
+ if expected_id != running_id:
+ print "check_kernel_ident: kernel identifier mismatch"
+ bad = True
+ if expected_when != cmdline_when:
+ print "check_kernel_ident: kernel command line mismatch"
+ bad = True
+
+ if bad:
+ print " Expected Ident: " + expected_id
+ print " Running Ident: " + running_id
+ print " Expected Mark: %d" % (expected_when)
+ print "Command Line Mark: %d" % (cmdline_when)
+ print " Command Line: " + cmdline
+
+ raise JobError("boot failure")
diff --git a/client/bin/job.py b/client/bin/job.py
index f9e9137..f103948 100755
--- a/client/bin/job.py
+++ b/client/bin/job.py
@@ -329,6 +329,13 @@
pickle.dump(self.steps, open(self.control + '.state', 'w'))
+ def next_step_prepend(self, step):
+ """Insert a new step, executing first"""
+ step[0] = step[0].__name__
+ self.steps.insert(0, step)
+ pickle.dump(self.steps, open(self.control + '.state', 'w'))
+
+
def step_engine(self):
"""the stepping engine -- if the control file defines
step_init we will be using this engine to drive multiple runs.
diff --git a/client/bin/kernel.py b/client/bin/kernel.py
index 3344bb5..74d7d19 100755
--- a/client/bin/kernel.py
+++ b/client/bin/kernel.py
@@ -1,6 +1,6 @@
__author__ = """Copyright Martin J. Bligh, 2006"""
-import os,os.path,shutil,urllib,copy,pickle,re,glob
+import os,os.path,shutil,urllib,copy,pickle,re,glob,time
from autotest_utils import *
import kernel_config, test, os_dep
@@ -428,11 +428,53 @@
return arch
- def boot(self, args='', once=True):
+ def get_kernel_build_release(self):
+ releasem = re.compile(r'.*UTS_RELEASE\s+"([^"]+)".*');
+ versionm = re.compile(r'.*UTS_VERSION\s+"([^"]+)".*');
+
+ release = None
+ version = None
+
+ for file in [ self.build_dir + "/include/linux/version.h",
+ self.build_dir + "/include/linux/utsrelease.h",
+ self.build_dir + "/include/linux/compile.h" ]:
+ if os.path.exists(file):
+ fd = open(file, 'r')
+ for line in fd.readlines():
+ m = releasem.match(line)
+ if m:
+ release = m.groups()[0]
+ m = versionm.match(line)
+ if m:
+ version = m.groups()[0]
+ fd.close()
+
+ return (release, version)
+
+
+ def get_kernel_build_ident(self):
+ (release, version) = self.get_kernel_build_release()
+
+ if not release or not version:
+ raise JobError('kernel has no identity')
+
+ return release + '::' + version
+
+
+ def boot(self, args='', once=True, ident=1):
""" install and boot this kernel, do not care how
just make it happen.
"""
+ # If we can check the kernel identity do so.
+ if ident:
+ when = int(time.time())
+ ident = self.get_kernel_build_ident()
+ args += " IDENT=%d" % (when)
+
+ self.job.next_step_prepend([kernel_check_ident,
+ when, ident])
+
# Install this kernel.
self.install()
self.add_to_bootloader(args=args, tag='autotest')
diff --git a/tko/create_db b/tko/create_db
index 09e00a4..bbff238 100644
--- a/tko/create_db
+++ b/tko/create_db
@@ -17,9 +17,9 @@
-- One entry per patch used, anywhere
CREATE TABLE patches (
-kversion INTEGER PRIMARY KEY, -- index number
-name VARCHAR(20), -- short name
-url VARCHAR(200), -- full URL
+kversion INTEGER, -- index number
+name VARCHAR(80), -- short name
+url VARCHAR(300), -- full URL
hash VARCHAR(35)
);
diff --git a/tko/db.py b/tko/db.py
index 270f077..cd547e8 100644
--- a/tko/db.py
+++ b/tko/db.py
@@ -1,4 +1,4 @@
-import sqlite, re
+import sqlite, re, os
class db:
def __init__(self):
@@ -40,26 +40,48 @@
def insert_job(self, tag, job):
- # is kernel version in tree?
self.insert('jobs', {'tag':tag, 'machine':'UNKNOWN'})
job.index = self.find_job(tag)
for test in job.tests:
self.insert_test(job, test)
+
def insert_test(self, job, test):
- # WE ARE MISSING KVERSION HERE!!!!
+ kver = self.insert_kernel_version(test.kernel)
data = {'job_idx':job.index, 'test':test.testname,
'subdir':test.dir,
'status':test.status, 'reason':test.reason}
self.insert('tests', data)
- def lookup_kernel_version(self, base, patches):
- return self.select('kversion', 'kversions', {'base':base})[0]
+ def lookup_kversion(self, kernel):
+ rows = self.select('kversion', 'kversions',
+ {'kversion_hash':kernel.kversion_hash})
+ if rows:
+ return rows[0][0]
+ else:
+ return None
- def insert_kernel_version(self, base, patches):
- self.insert('kversions', {'base': base})
+ def insert_kernel_version(self, kernel):
+ kver = self.lookup_kversion(kernel)
+ if kver:
+ return kver
+ self.insert('kversions', {'base':kernel.base,
+ 'kversion_hash':kernel.kversion_hash})
+ kver = self.lookup_kversion(kernel)
+ for patch in kernel.patches:
+ self.insert_patch(kver, patch)
+ return kver
+
+
+ def insert_patch(self, kver, patch):
+ print patch.reference
+ name = os.path.basename(patch.reference)[:80]
+ self.insert('patches', {'kversion': kver,
+ 'name':name,
+ 'url':patch.reference,
+ 'hash':patch.hash})
def find_job(self, tag):
diff --git a/tko/parse.py b/tko/parse.py
index 30e8acb..d3a5894 100755
--- a/tko/parse.py
+++ b/tko/parse.py
@@ -26,52 +26,29 @@
self.dir = dir
self.type = type
self.control = os.path.join(dir, "control")
+ self.status = os.path.join(dir, "status")
self.machine = ''
self.variables = {}
self.tests = []
self.kernel = None
- print 'FOOFACE ' + self.control
- if not os.path.exists(self.control):
- return
- # HACK. we don't have proper build tags in the status file yet
- # so we hardcode build/ and do it at the start of the job
- print 'POOFACE'
- self.kernel = kernel(os.path.join(dir, 'build'))
self.grope_status()
- def derive_build(self, raw_build):
- # First to expand variables in the build line ...
- self.build = ''
- for element in re.split(r'(\$\w+)', raw_build):
- if element.startswith('$'):
- element = self.variables[element.lstrip('$')]
- self.build += element
-
-
- def derive_patches(self):
- self.patches_short = []
- self.patches_long = []
- for segment in re.split(r'(-p \S+)', self.build):
- if segment.startswith('-p'):
- self.patches_long.append(segment.split(' ')[1])
- self.patches_short = [shorten_patch(p) for p in self.patches_long]
-
-
def grope_status(self):
- status_file = os.path.join(self.dir, "status")
- for line in open(status_file, 'r').readlines():
+ # HACK. we don't have proper build tags in the status file yet
+ # so we hardcode build/ and do it at the start of the job
+ build = os.path.join(self.dir, 'build')
+ if os.path.exists(build):
+ self.kernel = kernel(build)
+
+ if not os.path.exists(self.status):
+ return
+
+ for line in open(self.status, 'r').readlines():
(status, testname, reason) = line.rstrip().split(' ', 2)
- self.tests.append(test(testname, status, reason))
-
-
- def set_status(self, status):
- if status not in status_num:
- return
- self.status = status
- self.status_num = status_num[status]
+ self.tests.append(test(testname, status, reason, self.kernel))
class kernel:
@@ -80,19 +57,23 @@
self.patches = []
log = os.path.join(builddir, 'debug/build_log')
+ if not os.path.exists(log):
+ return
patch_hashes = []
for line in open(log, 'r'):
+ print line
(type, rest) = line.split(': ', 1)
words = rest.split()
if type == 'BUILD VERSION':
self.base = words[0]
if type == 'PATCH':
- self.patches.append(words[0:])
+ print words
+ self.patches.append(patch(*words[0:]))
# patch_hashes.append(words[2])
- self.kversion_hash = self.get_kversion_hash(self.base, patch_hashes)
+ self.kversion_hash = self.get_kver_hash(self.base, patch_hashes)
- def get_kversion_hash(self, base, patch_hashes):
+ def get_kver_hash(self, base, patch_hashes):
"""\
Calculate a hash representing the unique combination of
the kernel base version plus
@@ -101,14 +82,26 @@
return md5.new(key_string).hexdigest()
+class patch:
+ def __init__(self, spec, reference=None, hash=None):
+ # NEITHER OF THE ABOVE SHOULD HAVE DEFAULTS!!!! HACK HACK
+ if not reference:
+ reference = spec
+ print 'PATCH::%s %s %s' % (spec, reference, hash)
+ self.spec = spec
+ self.reference = reference
+ self.hash = hash
+
+
class test:
- def __init__(self, dir, status, reason):
+ def __init__(self, dir, status, reason, kernel):
self.dir = dir
self.status = status
self.reason = reason
self.keyval = os.path.join(dir, 'results/keyval')
self.iterations = []
self.testname = re.sub(r'\..*', '', self.dir)
+ self.kernel = kernel
if not os.path.exists(self.keyval):
self.keyval = None