• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import os, shutil, copy, pickle, re, glob, time, logging
2from autotest_lib.client.bin import kernel_config, os_dep, kernelexpand, test
3from autotest_lib.client.bin import utils
4from autotest_lib.client.common_lib import log, error, packages
5
6
7def tee_output_logdir_mark(fn):
8    def tee_logdir_mark_wrapper(self, *args, **dargs):
9        mark = self.__class__.__name__ + "." + fn.__name__
10        logging.info("--- START %s ---", mark)
11        self.job.logging.tee_redirect_debug_dir(self.log_dir)
12        try:
13            result = fn(self, *args, **dargs)
14        finally:
15            self.job.logging.restore()
16            logging.info("--- END %s ---", mark)
17
18        return result
19
20    tee_logdir_mark_wrapper.__name__ = fn.__name__
21    return tee_logdir_mark_wrapper
22
23
24def _add_kernel_to_bootloader(bootloader, base_args, tag, args, image, initrd):
25    """
26    Add a kernel with the specified tag to the boot config using the given
27    bootloader object. Also process the base_args and args kernel arguments
28    by removing all root= options and give the last root= option value to
29    the bootloader as a root device.
30
31    @param bootloader: bootloader object
32    @param base_args: base cmdline kernel arguments
33    @param tag: kernel tag
34    @param args: kernel cmdline arguments that are merged with base_args; a
35            root= option in "args" will override any from base_args
36    @param image: kernel image file
37    @param initrd: initrd file
38    """
39    # remove existing entry if present
40    bootloader.remove_kernel(tag)
41
42    if base_args:
43        args = ' '.join((base_args, args))
44
45    root_prefix = 'root='
46    # stores the last root= value
47    root = None
48    # a list with all arguments that don't start with root= so we give them
49    # later to bootloader.add_kernel()
50    arglist = []
51
52    for arg in args.split():
53        if arg.startswith(root_prefix):
54            # set the current root value with the one from the argument
55            # thus after processing all the arguments we keep the last
56            # root value (so root= options from args overrides any from
57            # base_args)
58            root = arg[len(root_prefix):]
59        else:
60            arglist.append(arg)
61
62    # Add the kernel entry. it will keep all arguments from the default entry.
63    # args='_dummy_' is used to workaround a boottool limitation of not being
64    # able to add arguments to a kernel that does not already have any of its
65    # own by way of its own append= section below the image= line in lilo.conf.
66    bootloader.add_kernel(image, tag, initrd=initrd, root=root, args='_dummy_')
67    # Now, for each argument in arglist, try to add it to the kernel that was
68    # just added. In each step, if the arg already existed on the args string,
69    # that particular arg will be skipped
70    for a in arglist:
71        bootloader.add_args(kernel=tag, args=a)
72    bootloader.remove_args(kernel=tag, args='_dummy_')
73
74
75class BootableKernel(object):
76
77    def __init__(self, job):
78        self.job = job
79        self.installed_as = None  # kernel choice in bootloader menu
80        self.image = None
81        self.initrd = ''
82
83
84    def _boot_kernel(self, args, ident_check, expected_ident, subdir, notes):
85        """
86        Boot a kernel, with post-boot kernel id check
87
88        @param args:  kernel cmdline arguments
89        @param ident_check: check kernel id after boot
90        @param expected_ident:
91        @param subdir: job-step qualifier in status log
92        @param notes:  additional comment in status log
93        """
94        # If we can check the kernel identity do so.
95        if ident_check:
96            when = int(time.time())
97            args += " IDENT=%d" % when
98            self.job.next_step_prepend(["job.end_reboot_and_verify", when,
99                                        expected_ident, subdir, notes])
100        else:
101            self.job.next_step_prepend(["job.end_reboot", subdir,
102                                        expected_ident, notes])
103
104        self.add_to_bootloader(args)
105
106        # defer fsck for next reboot, to avoid reboots back to default kernel
107        utils.system('touch /fastboot')  # this file is removed automatically
108
109        # Boot it.
110        self.job.start_reboot()
111        self.job.reboot(tag=self.installed_as)
112
113
114    def add_to_bootloader(self, args=''):
115        # Point bootloader to the selected tag.
116        _add_kernel_to_bootloader(self.job.bootloader,
117                                  self.job.config_get('boot.default_args'),
118                                  self.installed_as, args, self.image,
119                                  self.initrd)
120
121
122class kernel(BootableKernel):
123    """ Class for compiling kernels.
124
125    Data for the object includes the src files
126    used to create the kernel, patches applied, config (base + changes),
127    the build directory itself, and logged output
128
129    Properties:
130            job
131                    Backpointer to the job object we're part of
132            autodir
133                    Path to the top level autotest dir (/usr/local/autotest)
134            src_dir
135                    <tmp_dir>/src/
136            build_dir
137                    <tmp_dir>/linux/
138            config_dir
139                    <results_dir>/config/
140            log_dir
141                    <results_dir>/debug/
142            results_dir
143                    <results_dir>/results/
144    """
145
146    autodir = ''
147
148    def __init__(self, job, base_tree, subdir, tmp_dir, build_dir, leave=False):
149        """Initialize the kernel build environment
150
151        job
152                which job this build is part of
153        base_tree
154                base kernel tree. Can be one of the following:
155                        1. A local tarball
156                        2. A URL to a tarball
157                        3. A local directory (will symlink it)
158                        4. A shorthand expandable (eg '2.6.11-git3')
159        subdir
160                subdir in the results directory (eg "build")
161                (holds config/, debug/, results/)
162        tmp_dir
163
164        leave
165                Boolean, whether to leave existing tmpdir or not
166        """
167        super(kernel, self).__init__(job)
168        self.autodir = job.autodir
169
170        self.src_dir    = os.path.join(tmp_dir, 'src')
171        self.build_dir  = os.path.join(tmp_dir, build_dir)
172                # created by get_kernel_tree
173        self.config_dir = os.path.join(subdir, 'config')
174        self.log_dir    = os.path.join(subdir, 'debug')
175        self.results_dir = os.path.join(subdir, 'results')
176        self.subdir     = os.path.basename(subdir)
177
178        if not leave:
179            if os.path.isdir(self.src_dir):
180                utils.system('rm -rf ' + self.src_dir)
181            if os.path.isdir(self.build_dir):
182                utils.system('rm -rf ' + self.build_dir)
183
184        if not os.path.exists(self.src_dir):
185            os.mkdir(self.src_dir)
186        for path in [self.config_dir, self.log_dir, self.results_dir]:
187            if os.path.exists(path):
188                utils.system('rm -rf ' + path)
189            os.mkdir(path)
190
191        logpath = os.path.join(self.log_dir, 'build_log')
192        self.logfile = open(logpath, 'w+')
193        self.applied_patches = []
194
195        self.target_arch = None
196        self.build_target = 'bzImage'
197        self.build_image = None
198
199        arch = utils.get_current_kernel_arch()
200        if arch == 's390' or arch == 's390x':
201            self.build_target = 'image'
202        elif arch == 'ia64':
203            self.build_target = 'all'
204            self.build_image = 'vmlinux.gz'
205
206        if not leave:
207            self.logfile.write('BASE: %s\n' % base_tree)
208
209            # Where we have direct version hint record that
210            # for later configuration selection.
211            shorthand = re.compile(r'^\d+\.\d+\.\d+')
212            if shorthand.match(base_tree):
213                self.base_tree_version = base_tree
214            else:
215                self.base_tree_version = None
216
217            # Actually extract the tree.  Make sure we know it occured
218            self.extract(base_tree)
219
220
221    def kernelexpand(self, kernel):
222        # If we have something like a path, just use it as it is
223        if '/' in kernel:
224            return [kernel]
225
226        # Find the configured mirror list.
227        mirrors = self.job.config_get('mirror.mirrors')
228        if not mirrors:
229            # LEGACY: convert the kernel.org mirror
230            mirror = self.job.config_get('mirror.ftp_kernel_org')
231            if mirror:
232                korg = 'http://www.kernel.org/pub/linux/kernel'
233                mirrors = [
234                  [ korg + '/v2.6', mirror + '/v2.6' ],
235                  [ korg + '/people/akpm/patches/2.6', mirror + '/akpm' ],
236                  [ korg + '/people/mbligh', mirror + '/mbligh' ],
237                ]
238
239        patches = kernelexpand.expand_classic(kernel, mirrors)
240        print patches
241
242        return patches
243
244
245    @log.record
246    @tee_output_logdir_mark
247    def extract(self, base_tree):
248        if os.path.exists(base_tree):
249            self.get_kernel_tree(base_tree)
250        else:
251            base_components = self.kernelexpand(base_tree)
252            print 'kernelexpand: '
253            print base_components
254            self.get_kernel_tree(base_components.pop(0))
255            if base_components:      # apply remaining patches
256                self.patch(*base_components)
257
258
259    @log.record
260    @tee_output_logdir_mark
261    def patch(self, *patches):
262        """Apply a list of patches (in order)"""
263        if not patches:
264            return
265        print 'Applying patches: ', patches
266        self.apply_patches(self.get_patches(patches))
267
268
269    @log.record
270    @tee_output_logdir_mark
271    def config(self, config_file = '', config_list = None, defconfig = False, make = None):
272        self.set_cross_cc()
273        config = kernel_config.kernel_config(self.job, self.build_dir,
274                 self.config_dir, config_file, config_list,
275                 defconfig, self.base_tree_version, make)
276
277
278    def get_patches(self, patches):
279        """fetch the patches to the local src_dir"""
280        local_patches = []
281        for patch in patches:
282            dest = os.path.join(self.src_dir, os.path.basename(patch))
283            # FIXME: this isn't unique. Append something to it
284            # like wget does if it's not there?
285            print "get_file %s %s %s %s" % (patch, dest, self.src_dir,
286                                            os.path.basename(patch))
287            utils.get_file(patch, dest)
288            # probably safer to use the command, not python library
289            md5sum = utils.system_output('md5sum ' + dest).split()[0]
290            local_patches.append((patch, dest, md5sum))
291        return local_patches
292
293
294    def apply_patches(self, local_patches):
295        """apply the list of patches, in order"""
296        builddir = self.build_dir
297        os.chdir(builddir)
298
299        if not local_patches:
300            return None
301        for (spec, local, md5sum) in local_patches:
302            if local.endswith('.bz2') or local.endswith('.gz'):
303                ref = spec
304            else:
305                ref = utils.force_copy(local, self.results_dir)
306                ref = self.job.relative_path(ref)
307            patch_id = "%s %s %s" % (spec, ref, md5sum)
308            log = "PATCH: " + patch_id + "\n"
309            print log
310            utils.cat_file_to_cmd(local, 'patch -p1 > /dev/null')
311            self.logfile.write(log)
312            self.applied_patches.append(patch_id)
313
314
315    def get_kernel_tree(self, base_tree):
316        """Extract/link base_tree to self.build_dir"""
317
318        # if base_tree is a dir, assume uncompressed kernel
319        if os.path.isdir(base_tree):
320            print 'Symlinking existing kernel source'
321            if os.path.islink(self.build_dir):
322                os.remove(self.build_dir)
323            os.symlink(base_tree, self.build_dir)
324
325        # otherwise, extract tarball
326        else:
327            os.chdir(os.path.dirname(self.src_dir))
328            # Figure out local destination for tarball
329            tarball = os.path.join(self.src_dir, os.path.basename(base_tree.split(';')[0]))
330            utils.get_file(base_tree, tarball)
331            print 'Extracting kernel tarball:', tarball, '...'
332            utils.extract_tarball_to_dir(tarball, self.build_dir)
333
334
335    def extraversion(self, tag, append=True):
336        os.chdir(self.build_dir)
337        extraversion_sub = r's/^CONFIG_LOCALVERSION=\s*"\(.*\)"/CONFIG_LOCALVERSION='
338        cfg = self.build_dir + '/.config'
339        if append:
340            p = extraversion_sub + '"\\1-%s"/' % tag
341        else:
342            p = extraversion_sub + '"-%s"/' % tag
343        utils.system('mv %s %s.old' % (cfg, cfg))
344        utils.system("sed '%s' < %s.old > %s" % (p, cfg, cfg))
345        self.config(make='oldconfig')
346
347
348    @log.record
349    @tee_output_logdir_mark
350    def build(self, make_opts = '', logfile = '', extraversion='autotest'):
351        """build the kernel
352
353        make_opts
354                additional options to make, if any
355        """
356        os_dep.commands('gcc', 'make')
357        if logfile == '':
358            logfile = os.path.join(self.log_dir, 'kernel_build')
359        os.chdir(self.build_dir)
360        if extraversion:
361            self.extraversion(extraversion)
362        self.set_cross_cc()
363        # setup_config_file(config_file, config_overrides)
364
365        # Not needed on 2.6, but hard to tell -- handle failure
366        utils.system('make dep', ignore_status=True)
367        threads = 2 * utils.count_cpus()
368        build_string = 'make -j %d %s %s' % (threads, make_opts,
369                                     self.build_target)
370                                # eg make bzImage, or make zImage
371        print build_string
372        utils.system(build_string)
373        if kernel_config.modules_needed('.config'):
374            utils.system('make -j %d modules' % (threads))
375
376        kernel_version = self.get_kernel_build_ver()
377        kernel_version = re.sub('-autotest', '', kernel_version)
378        self.logfile.write('BUILD VERSION: %s\n' % kernel_version)
379
380        utils.force_copy(self.build_dir+'/System.map',
381                                  self.results_dir)
382
383
384    def build_timed(self, threads, timefile = '/dev/null', make_opts = '',
385                                                    output = '/dev/null'):
386        """time the bulding of the kernel"""
387        os.chdir(self.build_dir)
388        self.set_cross_cc()
389
390        self.clean()
391        build_string = "/usr/bin/time -o %s make %s -j %s vmlinux" \
392                                        % (timefile, make_opts, threads)
393        build_string += ' > %s 2>&1' % output
394        print build_string
395        utils.system(build_string)
396
397        if (not os.path.isfile('vmlinux')):
398            errmsg = "no vmlinux found, kernel build failed"
399            raise error.TestError(errmsg)
400
401
402    @log.record
403    @tee_output_logdir_mark
404    def clean(self):
405        """make clean in the kernel tree"""
406        os.chdir(self.build_dir)
407        print "make clean"
408        utils.system('make clean > /dev/null 2> /dev/null')
409
410
411    @log.record
412    @tee_output_logdir_mark
413    def mkinitrd(self, version, image, system_map, initrd):
414        """Build kernel initrd image.
415        Try to use distro specific way to build initrd image.
416        Parameters:
417                version
418                        new kernel version
419                image
420                        new kernel image file
421                system_map
422                        System.map file
423                initrd
424                        initrd image file to build
425        """
426        vendor = utils.get_os_vendor()
427
428        if os.path.isfile(initrd):
429            print "Existing %s file, will remove it." % initrd
430            os.remove(initrd)
431
432        args = self.job.config_get('kernel.mkinitrd_extra_args')
433
434        # don't leak 'None' into mkinitrd command
435        if not args:
436            args = ''
437
438        # It is important to match the version with a real directory inside
439        # /lib/modules
440        real_version_list = glob.glob('/lib/modules/%s*' % version)
441        rl = len(real_version_list)
442        if rl == 0:
443            logging.error("No directory %s found under /lib/modules. Initramfs"
444                          "creation will most likely fail and your new kernel"
445                          "will fail to build", version)
446        else:
447            if rl > 1:
448                logging.warning("Found more than one possible match for "
449                                "kernel version %s under /lib/modules", version)
450            version = os.path.basename(real_version_list[0])
451
452        if vendor in ['Red Hat', 'Fedora Core']:
453            try:
454                cmd = os_dep.command('dracut')
455                full_cmd = '%s -f %s %s' % (cmd, initrd, version)
456            except ValueError:
457                cmd = os_dep.command('mkinitrd')
458                full_cmd = '%s %s %s %s' % (cmd, args, initrd, version)
459            utils.system(full_cmd)
460        elif vendor in ['SUSE']:
461            utils.system('mkinitrd %s -k %s -i %s -M %s' %
462                         (args, image, initrd, system_map))
463        elif vendor in ['Debian', 'Ubuntu']:
464            if os.path.isfile('/usr/sbin/mkinitrd'):
465                cmd = '/usr/sbin/mkinitrd'
466            elif os.path.isfile('/usr/sbin/mkinitramfs'):
467                cmd = '/usr/sbin/mkinitramfs'
468            else:
469                raise error.TestError('No Debian initrd builder')
470            utils.system('%s %s -o %s %s' % (cmd, args, initrd, version))
471        else:
472            raise error.TestError('Unsupported vendor %s' % vendor)
473
474
475    def set_build_image(self, image):
476        self.build_image = image
477
478
479    @log.record
480    @tee_output_logdir_mark
481    def install(self, tag='autotest', prefix = '/'):
482        """make install in the kernel tree"""
483
484        # Record that we have installed the kernel, and
485        # the tag under which we installed it.
486        self.installed_as = tag
487
488        os.chdir(self.build_dir)
489
490        if not os.path.isdir(prefix):
491            os.mkdir(prefix)
492        self.boot_dir = os.path.join(prefix, 'boot')
493        if not os.path.isdir(self.boot_dir):
494            os.mkdir(self.boot_dir)
495
496        if not self.build_image:
497            images = glob.glob('arch/*/boot/' + self.build_target)
498            if len(images):
499                self.build_image = images[0]
500            else:
501                self.build_image = self.build_target
502
503        # remember installed files
504        self.vmlinux = self.boot_dir + '/vmlinux-' + tag
505        if (self.build_image != 'vmlinux'):
506            self.image = self.boot_dir + '/vmlinuz-' + tag
507        else:
508            self.image = self.vmlinux
509        self.system_map = self.boot_dir + '/System.map-' + tag
510        self.config_file = self.boot_dir + '/config-' + tag
511        self.initrd = ''
512
513        # copy to boot dir
514        utils.force_copy('vmlinux', self.vmlinux)
515        if (self.build_image != 'vmlinux'):
516            utils.force_copy(self.build_image, self.image)
517        utils.force_copy('System.map', self.system_map)
518        utils.force_copy('.config', self.config_file)
519
520        if not kernel_config.modules_needed('.config'):
521            return
522
523        utils.system('make modules_install INSTALL_MOD_PATH=%s' % prefix)
524        if prefix == '/':
525            self.initrd = self.boot_dir + '/initrd-' + tag
526            self.mkinitrd(self.get_kernel_build_ver(), self.image,
527                          self.system_map, self.initrd)
528
529
530    def get_kernel_build_arch(self, arch=None):
531        """
532        Work out the current kernel architecture (as a kernel arch)
533        """
534        if not arch:
535            arch = utils.get_current_kernel_arch()
536        if re.match('i.86', arch):
537            return 'i386'
538        elif re.match('sun4u', arch):
539            return 'sparc64'
540        elif re.match('arm.*', arch):
541            return 'arm'
542        elif re.match('sa110', arch):
543            return 'arm'
544        elif re.match('s390x', arch):
545            return 's390'
546        elif re.match('parisc64', arch):
547            return 'parisc'
548        elif re.match('ppc.*', arch):
549            return 'powerpc'
550        elif re.match('mips.*', arch):
551            return 'mips'
552        else:
553            return arch
554
555
556    def get_kernel_build_release(self):
557        releasem = re.compile(r'.*UTS_RELEASE\s+"([^"]+)".*');
558        versionm = re.compile(r'.*UTS_VERSION\s+"([^"]+)".*');
559
560        release = None
561        version = None
562
563        for f in [self.build_dir + "/include/linux/version.h",
564                  self.build_dir + "/include/linux/utsrelease.h",
565                  self.build_dir + "/include/linux/compile.h",
566                  self.build_dir + "/include/generated/utsrelease.h",
567                  self.build_dir + "/include/generated/compile.h"]:
568            if os.path.exists(f):
569                fd = open(f, 'r')
570                for line in fd.readlines():
571                    m = releasem.match(line)
572                    if m:
573                        release = m.groups()[0]
574                    m = versionm.match(line)
575                    if m:
576                        version = m.groups()[0]
577                fd.close()
578
579        return (release, version)
580
581
582    def get_kernel_build_ident(self):
583        (release, version) = self.get_kernel_build_release()
584
585        if not release or not version:
586            raise error.JobError('kernel has no identity')
587
588        return release + '::' + version
589
590
591    def boot(self, args='', ident=True):
592        """ install and boot this kernel, do not care how
593            just make it happen.
594        """
595
596        # If the kernel has not yet been installed,
597        #   install it now as default tag.
598        if not self.installed_as:
599            self.install()
600
601        expected_ident = self.get_kernel_build_ident()
602        self._boot_kernel(args, ident, expected_ident,
603                          self.subdir, self.applied_patches)
604
605
606    def get_kernel_build_ver(self):
607        """Check Makefile and .config to return kernel version"""
608        version = patchlevel = sublevel = extraversion = localversion = ''
609
610        for line in open(self.build_dir + '/Makefile', 'r').readlines():
611            if line.startswith('VERSION'):
612                version = line[line.index('=') + 1:].strip()
613            if line.startswith('PATCHLEVEL'):
614                patchlevel = line[line.index('=') + 1:].strip()
615            if line.startswith('SUBLEVEL'):
616                sublevel = line[line.index('=') + 1:].strip()
617            if line.startswith('EXTRAVERSION'):
618                extraversion = line[line.index('=') + 1:].strip()
619
620        for line in open(self.build_dir + '/.config', 'r').readlines():
621            if line.startswith('CONFIG_LOCALVERSION='):
622                localversion = line.rstrip().split('"')[1]
623
624        return "%s.%s.%s%s%s" %(version, patchlevel, sublevel, extraversion, localversion)
625
626
627    def set_build_target(self, build_target):
628        if build_target:
629            self.build_target = build_target
630            print 'BUILD TARGET: %s' % self.build_target
631
632
633    def set_cross_cc(self, target_arch=None, cross_compile=None,
634                     build_target='bzImage'):
635        """Set up to cross-compile.
636                This is broken. We need to work out what the default
637                compile produces, and if not, THEN set the cross
638                compiler.
639        """
640
641        if self.target_arch:
642            return
643
644        # if someone has set build_target, don't clobber in set_cross_cc
645        # run set_build_target before calling set_cross_cc
646        if not self.build_target:
647            self.set_build_target(build_target)
648
649        # If no 'target_arch' given assume native compilation
650        if target_arch is None:
651            target_arch = utils.get_current_kernel_arch()
652            if target_arch == 'ppc64':
653                if self.build_target == 'bzImage':
654                    self.build_target = 'vmlinux'
655
656        if not cross_compile:
657            cross_compile = self.job.config_get('kernel.cross_cc')
658
659        if cross_compile:
660            os.environ['CROSS_COMPILE'] = cross_compile
661        else:
662            if os.environ.has_key('CROSS_COMPILE'):
663                del os.environ['CROSS_COMPILE']
664
665        return                 # HACK. Crap out for now.
666
667        # At this point I know what arch I *want* to build for
668        # but have no way of working out what arch the default
669        # compiler DOES build for.
670
671        def install_package(package):
672            raise NotImplementedError("I don't exist yet!")
673
674        if target_arch == 'ppc64':
675            install_package('ppc64-cross')
676            cross_compile = os.path.join(self.autodir, 'sources/ppc64-cross/bin')
677
678        elif target_arch == 'x86_64':
679            install_package('x86_64-cross')
680            cross_compile = os.path.join(self.autodir, 'sources/x86_64-cross/bin')
681
682        os.environ['ARCH'] = self.target_arch = target_arch
683
684        self.cross_compile = cross_compile
685        if self.cross_compile:
686            os.environ['CROSS_COMPILE'] = self.cross_compile
687
688
689    def pickle_dump(self, filename):
690        """dump a pickle of ourself out to the specified filename
691
692        we can't pickle the backreference to job (it contains fd's),
693        nor would we want to. Same for logfile (fd's).
694        """
695        temp = copy.copy(self)
696        temp.job = None
697        temp.logfile = None
698        pickle.dump(temp, open(filename, 'w'))
699
700
701class rpm_kernel(BootableKernel):
702    """
703    Class for installing a binary rpm kernel package
704    """
705
706    def __init__(self, job, rpm_package, subdir):
707        super(rpm_kernel, self).__init__(job)
708        self.rpm_package = rpm_package
709        self.log_dir = os.path.join(subdir, 'debug')
710        self.subdir = os.path.basename(subdir)
711        if os.path.exists(self.log_dir):
712            utils.system('rm -rf ' + self.log_dir)
713        os.mkdir(self.log_dir)
714
715
716    def build(self, *args, **dargs):
717        """
718        Dummy function, binary kernel so nothing to build.
719        """
720        pass
721
722
723    @log.record
724    @tee_output_logdir_mark
725    def install(self, tag='autotest', install_vmlinux=True):
726        self.installed_as = tag
727
728        self.image = None
729        self.initrd = ''
730        for rpm_pack in self.rpm_package:
731            rpm_name = utils.system_output('rpm -qp ' + rpm_pack)
732
733            # install
734            utils.system('rpm -i --force ' + rpm_pack)
735
736            # get file list
737            files = utils.system_output('rpm -ql ' + rpm_name).splitlines()
738
739            # search for vmlinuz
740            for file in files:
741                if file.startswith('/boot/vmlinuz'):
742                    self.full_version = file[len('/boot/vmlinuz-'):]
743                    self.image = file
744                    self.rpm_flavour = rpm_name.split('-')[1]
745
746                    # get version and release number
747                    self.version, self.release = utils.system_output(
748                            'rpm --queryformat="%{VERSION}\\n%{RELEASE}\\n" -q '
749                            + rpm_name).splitlines()[0:2]
750
751                    # prefer /boot/kernel-version before /boot/kernel
752                    if self.full_version:
753                        break
754
755            # search for initrd
756            for file in files:
757                if file.startswith('/boot/init'):
758                    self.initrd = file
759                    # prefer /boot/initrd-version before /boot/initrd
760                    if len(file) > len('/boot/initrd'):
761                        break
762
763        if self.image == None:
764            errmsg = "specified rpm file(s) don't contain /boot/vmlinuz"
765            raise error.TestError(errmsg)
766
767        # install vmlinux
768        if install_vmlinux:
769            for rpm_pack in self.rpm_package:
770                vmlinux = utils.system_output(
771                        'rpm -q -l -p %s | grep /boot/vmlinux' % rpm_pack)
772            utils.system('cd /; rpm2cpio %s | cpio -imuv .%s 2>&1'
773                         % (rpm_pack, vmlinux))
774            if not os.path.exists(vmlinux):
775                raise error.TestError('%s does not exist after installing %s'
776                                      % (vmlinux, rpm_pack))
777
778
779    def boot(self, args='', ident=True):
780        """ install and boot this kernel
781        """
782
783        # If the kernel has not yet been installed,
784        #   install it now as default tag.
785        if not self.installed_as:
786            self.install()
787
788        expected_ident = self.full_version
789        if not expected_ident:
790            expected_ident = '-'.join([self.version,
791                                       self.rpm_flavour,
792                                       self.release])
793
794        self._boot_kernel(args, ident, expected_ident,
795                          None, 'rpm')
796
797
798class rpm_kernel_suse(rpm_kernel):
799    """ Class for installing openSUSE/SLE rpm kernel package
800    """
801
802    def install(self):
803        # do not set the new kernel as the default one
804        os.environ['PBL_AUTOTEST'] = '1'
805
806        rpm_kernel.install(self, 'dummy')
807        self.installed_as = self.job.bootloader.get_title_for_kernel(self.image)
808        if not self.installed_as:
809            errmsg = "cannot find installed kernel in bootloader configuration"
810            raise error.TestError(errmsg)
811
812
813    def add_to_bootloader(self, tag='dummy', args=''):
814        """ Set parameters of this kernel in bootloader
815        """
816
817        # pull the base argument set from the job config
818        baseargs = self.job.config_get('boot.default_args')
819        if baseargs:
820            args = baseargs + ' ' + args
821
822        self.job.bootloader.add_args(tag, args)
823
824
825def rpm_kernel_vendor(job, rpm_package, subdir):
826    vendor = utils.get_os_vendor()
827    if vendor == "SUSE":
828        return rpm_kernel_suse(job, rpm_package, subdir)
829    else:
830        return rpm_kernel(job, rpm_package, subdir)
831
832
833# just make the preprocessor a nop
834def _preprocess_path_dummy(path):
835    return path.strip()
836
837
838# pull in some optional site-specific path pre-processing
839preprocess_path = utils.import_site_function(__file__,
840    "autotest_lib.client.bin.site_kernel", "preprocess_path",
841    _preprocess_path_dummy)
842
843
844def auto_kernel(job, path, subdir, tmp_dir, build_dir, leave=False):
845    """
846    Create a kernel object, dynamically selecting the appropriate class to use
847    based on the path provided.
848    """
849    kernel_paths = [preprocess_path(path)]
850    if kernel_paths[0].endswith('.list'):
851    # Fetch the list of packages to install
852        kernel_list = os.path.join(tmp_dir, 'kernel.list')
853        utils.get_file(kernel_paths[0], kernel_list)
854        kernel_paths = [p.strip() for p in open(kernel_list).readlines()]
855
856    if kernel_paths[0].endswith('.rpm'):
857        rpm_paths = []
858        for kernel_path in kernel_paths:
859            if os.path.exists(kernel_path):
860                rpm_paths.append(kernel_path)
861            else:
862                # Fetch the rpm into the job's packages directory and pass it to
863                # rpm_kernel
864                rpm_name = os.path.basename(kernel_path)
865
866                # If the preprocessed path (kernel_path) is only a name then
867                # search for the kernel in all the repositories, else fetch the
868                # kernel from that specific path.
869                job.pkgmgr.fetch_pkg(rpm_name, os.path.join(job.pkgdir, rpm_name),
870                                     repo_url=os.path.dirname(kernel_path))
871
872                rpm_paths.append(os.path.join(job.pkgdir, rpm_name))
873        return rpm_kernel_vendor(job, rpm_paths, subdir)
874    else:
875        if len(kernel_paths) > 1:
876            raise error.TestError("don't know what to do with more than one non-rpm kernel file")
877        return kernel(job,kernel_paths[0], subdir, tmp_dir, build_dir, leave)
878