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