This patch adds bootloader functionality to autoserv:
From: Benjamin Poirier <poirier@google.com>
Signed-off-by: Martin J. Bligh <mbligh@google.com>
* removed Lilo and Grub classes
* Bootloader uses boottool, boottool support brought in from autotest, get_title(), get_info() improved, add_kernel() has 'default' parameter to add an entry and make it the default one in one simple step
* Kernel has new methods to ease installation, get_version(), get_initrd_name() and get_image_name()
* DEBKernel changed to implement those method and call the host's bootloader during install()
Installing and booting .deb kernel now works.
Example usage:
rh= hosts.SSHHost("192.168.0.1")
print rh.run("uname -a").stdout
kernel= deb_kernel.DEBKernel()
kernel.get("/home/poirier/linux-2.6.22_2.6.22_amd64.deb")
kernel.install(rh)
rh.reboot()
rh.wait_up()
print rh.run("uname -a").stdout
Credit goes to Ryan for bringing in boottool support.
git-svn-id: http://test.kernel.org/svn/autotest/trunk@574 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/server/deb_kernel.py b/server/deb_kernel.py
index a8c1471..3560068 100644
--- a/server/deb_kernel.py
+++ b/server/deb_kernel.py
@@ -35,19 +35,83 @@
super(DEBKernel, self).__init__()
- def install(self, host):
- # this directory will get cleaned up for us automatically
+ def install(self, host, **kwargs):
+ """Install a kernel on the remote host.
+
+ This will also invoke the guest's bootloader to set this
+ kernel as the default kernel.
+
+ Args:
+ host: the host on which to install the kernel
+ [kwargs]: remaining keyword arguments will be passed
+ to Bootloader.add_kernel()
+
+ Raises:
+ AutoservError: no package has yet been obtained. Call
+ DEBKernel.get() with a .deb package.
+ """
+ if self.source_material is None:
+ raise AutoservError("A kernel must first be \
+ specified via get()")
+
remote_tmpdir = host.get_tmp_dir()
basename = os.path.basename(self.source_material)
remote_filename = os.path.join(remote_tmpdir, basename)
host.send_file(self.source_material, remote_filename)
- try:
- result = host.run('dpkg -i %s'
- % remote_filename)
- if result.exit_status:
- raise AutoservError('dpkg failed \
- installing %s:\n\n%s'% (remote_filename,
- result.stderr))
- except NameError, e:
- raise AutoservError('A kernel must first be \
- specified via get_from_file or get_from_url')
+ host.run('dpkg -i "%s"' % (utils.sh_escape(remote_filename),))
+ host.run('mkinitramfs -o "%s" "%s"' % (
+ utils.sh_escape(self.get_initrd_name()),
+ utils.sh_escape(self.get_version()),))
+
+ host.bootloader.add_kernel(self.get_image_name(),
+ initrd=self.get_initrd_name(), **kwargs)
+
+ def get_version(self):
+ """Get the version of the kernel to be installed.
+
+ Returns:
+ The version string, as would be returned
+ by 'make kernelrelease'.
+
+ Raises:
+ AutoservError: no package has yet been obtained. Call
+ DEBKernel.get() with a .deb package.
+ """
+ if self.source_material is None:
+ raise AutoservError("A kernel must first be \
+ specified via get()")
+
+ retval= utils.run('dpkg-deb -f "%s" version' %
+ utils.sh_escape(self.source_material),)
+ return retval.stdout.strip()
+
+ def get_image_name(self):
+ """Get the name of the kernel image to be installed.
+
+ Returns:
+ The full path to the kernel image file as it will be
+ installed on the host.
+
+ Raises:
+ AutoservError: no package has yet been obtained. Call
+ DEBKernel.get() with a .deb package.
+ """
+ return "/boot/vmlinuz-%s" % (self.get_version(),)
+
+ def get_initrd_name(self):
+ """Get the name of the initrd file to be installed.
+
+ Returns:
+ The full path to the initrd file as it will be
+ installed on the host. If the package includes no
+ initrd file, None is returned
+
+ Raises:
+ AutoservError: no package has yet been obtained. Call
+ DEBKernel.get() with a .deb package.
+ """
+ if self.source_material is None:
+ raise AutoservError("A kernel must first be \
+ specified via get()")
+
+ return "/boot/initrd.img-%s" % (self.get_version(),)
diff --git a/server/hosts/__init__.py b/server/hosts/__init__.py
index 1b40526..41adf94 100644
--- a/server/hosts/__init__.py
+++ b/server/hosts/__init__.py
@@ -26,8 +26,6 @@
# bootloader classes
from bootloader import Bootloader
-from lilo import Lilo
-from grub import Grub
# command result class
from base_classes import CmdResult
diff --git a/server/hosts/base_classes.py b/server/hosts/base_classes.py
index dac153a..4564fc1 100644
--- a/server/hosts/base_classes.py
+++ b/server/hosts/base_classes.py
@@ -19,7 +19,7 @@
import time
import textwrap
-
+import bootloader
class Host(object):
"""This class represents a machine on which you can run programs.
@@ -36,6 +36,7 @@
def __init__(self):
super(Host, self).__init__()
+ self.bootloader= bootloader.Bootloader(self)
def run(self, command):
pass
diff --git a/server/hosts/bootloader.py b/server/hosts/bootloader.py
index eeaa5fb..a7f717e 100644
--- a/server/hosts/bootloader.py
+++ b/server/hosts/bootloader.py
@@ -11,23 +11,144 @@
poirier@google.com (Benjamin Poirier),
stutsman@google.com (Ryan Stutsman)"""
+import os.path
+import sys
+import weakref
+
+import errors
+import utils
+
+
+BOOTTOOL_SRC = '../client/tools/boottool' # Get it from autotest client
+
class Bootloader(object):
"""This class represents a bootloader.
It can be used to add a kernel to the list of kernels that can be
booted by a bootloader. It can also make sure that this kernel will
- be the one chosen at next reboot.
+ be the one chosen at next reboot."""
- Implementation details:
- This is an abstract class, leaf subclasses must implement the methods
- listed here. You must not instantiate this class but should
- instantiate one of those leaf subclasses."""
+ def __init__(self, host, xen_mode=False):
+ super(Bootloader, self).__init__()
+ self.__host = weakref.ref(host)
+ self.__boottool_path = None
+ self.xen_mode = xen_mode
- host = None
+ def get_type(self):
+ return self.__run_boottool('--bootloader-probe').stdout.strip()
- def add_entry(self, name, image, initrd, root, options, default=True):
- pass
+ def get_architecture(self):
+ return self.__run_boottool('--arch-probe').stdout.strip()
- def remove_entry(self, name):
- pass
+ def get_titles(self):
+ return self.__run_boottool('--info all | grep title | '
+ 'cut -d " " -f2-').stdout.strip().split('\n')
+
+ def get_default(self):
+ return self.__run_boottool('--default').stdout.strip()
+
+ def get_info(self, index):
+ retval= self.__run_boottool(
+ '--info=%s' % index).stdout.strip().split("\n")
+
+ result= {}
+ for line in retval:
+ (key, val,)= line.split(":")
+ result[key.strip()]= val.strip()
+
+ return result
+
+ def set_default(self, index):
+ self.__run_boottool('--set-default=%s' % index)
+
+ # 'kernel' can be a position number or a title
+ def add_args(self, kernel, args):
+ parameters = '--update-kernel=%s --args="%s"' % (kernel, args)
+
+ #add parameter if this is a Xen entry
+ if self.xen_mode:
+ parameters += ' --xen'
+
+ self.__run_boottool(parameters)
+
+ def add_xen_hypervisor_args(self, kernel, args):
+ self.__run_boottool('--xen --update-xenhyper=%s --xha="%s"' \
+ % (kernel, args))
+
+ def remove_args(self, kernel, args):
+ params = '--update-kernel=%s --remove-args=%s' % (kernel, args)
+
+ #add parameter if this is a Xen entry
+ if self.xen_mode:
+ params += ' --xen'
+
+ self.__run_boottool(params)
+
+ def remove_xen_hypervisor_args(self, kernel, args):
+ self.__run_boottool('--xen --update-xenhyper=%s '
+ '--remove-args="%s"') % (kernel, args)
+
+ def add_kernel(self, path, title='autoserv', root=None, args=None,
+ initrd=None, xen_hypervisor=None, default=True):
+ """
+ If an entry with the same title is already present, it will be
+ replaced.
+ """
+ if title in self.get_titles():
+ self.__run_boottool('--remove-kernel "%s"' % (
+ utils.sh_escape(title),))
+
+ parameters = '--add-kernel "%s" --title "%s"' % (
+ utils.sh_escape(path), utils.sh_escape(title),)
+
+ if root:
+ parameters += ' --root "%s"' % (utils.sh_escape(root),)
+
+ if args:
+ parameters += ' --args "%s"' % (utils.sh_escape(args),)
+
+ # add an initrd now or forever hold your peace
+ if initrd:
+ parameters += ' --initrd "%s"' % (
+ utils.sh_escape(initrd),)
+
+ if default:
+ parameters += ' --make-default'
+
+ # add parameter if this is a Xen entry
+ if self.xen_mode:
+ parameters += ' --xen'
+ if xen_hypervisor:
+ parameters += ' --xenhyper "%s"' % (
+ utils.sh_escape(xen_hypervisor),)
+
+ self.__run_boottool(parameters)
+
+ def remove_kernel(self, kernel):
+ self.__run_boottool('--remove-kernel=%s' % kernel)
+
+ def boot_once(self, title):
+ self.__run_boottool('--boot-once --title=%s' % title)
+
+ def __install_boottool(self):
+ if self.__host() is None:
+ raise errors.AutoservError("Host does not exist anymore")
+ tmpdir = self.__host().get_tmp_dir()
+ self.__host().send_file(os.path.abspath(os.path.join(
+ os.path.dirname(sys.argv[0]), BOOTTOOL_SRC)), tmpdir)
+ self.__boottool_path= os.path.join(tmpdir,
+ os.path.basename(BOOTTOOL_SRC))
+
+ def __get_boottool_path(self):
+ if not self.__boottool_path:
+ self.__install_boottool()
+ return self.__boottool_path
+
+ def __set_boottool_path(self, path):
+ self.__boottool_path = path
+
+ boottool_path = property(__get_boottool_path, __set_boottool_path)
+
+ def __run_boottool(self, cmd):
+ return self.__host().run(self.boottool_path + ' ' + cmd)
diff --git a/server/kernel.py b/server/kernel.py
index db6762a..998eae9 100644
--- a/server/kernel.py
+++ b/server/kernel.py
@@ -28,4 +28,11 @@
must not instantiate this class but should instantiate one of those
leaf subclasses."""
- pass
+ def get_version():
+ pass
+
+ def get_image_name():
+ pass
+
+ def get_initrd_name():
+ pass