summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scripts/lib/mic/imager/baseimager.py1030
-rw-r--r--scripts/lib/mic/imager/direct.py4
2 files changed, 9 insertions, 1025 deletions
diff --git a/scripts/lib/mic/imager/baseimager.py b/scripts/lib/mic/imager/baseimager.py
index b7212493b4..55f2deaf2c 100644
--- a/scripts/lib/mic/imager/baseimager.py
+++ b/scripts/lib/mic/imager/baseimager.py
@@ -18,37 +18,31 @@
18 18
19from __future__ import with_statement 19from __future__ import with_statement
20import os, sys 20import os, sys
21import stat
22import tempfile 21import tempfile
23import shutil 22import shutil
24import subprocess
25import re
26import tarfile
27import glob
28 23
29from mic import kickstart 24from mic import kickstart
30from mic import msger 25from mic import msger
31from mic.utils.errors import CreatorError, Abort 26from mic.utils.errors import CreatorError
32from mic.utils import misc, runner, fs_related as fs 27from mic.utils import misc, runner, fs_related as fs
33 28
34class BaseImageCreator(object): 29class BaseImageCreator(object):
35 """Installs a system to a chroot directory. 30 """Base class for image creation.
36 31
37 ImageCreator is the simplest creator class available; it will install and 32 BaseImageCreator is the simplest creator class available; it will
38 configure a system image according to the supplied kickstart file. 33 create a system image according to the supplied kickstart file.
39 34
40 e.g. 35 e.g.
41 36
42 import mic.imgcreate as imgcreate 37 import mic.imgcreate as imgcreate
43 ks = imgcreate.read_kickstart("foo.ks") 38 ks = imgcreate.read_kickstart("foo.ks")
44 imgcreate.ImageCreator(ks, "foo").create() 39 imgcreate.ImageCreator(ks, "foo").create()
45
46 """ 40 """
47 41
48 def __del__(self): 42 def __del__(self):
49 self.cleanup() 43 self.cleanup()
50 44
51 def __init__(self, createopts = None, pkgmgr = None): 45 def __init__(self, createopts = None):
52 """Initialize an ImageCreator instance. 46 """Initialize an ImageCreator instance.
53 47
54 ks -- a pykickstart.KickstartParser instance; this instance will be 48 ks -- a pykickstart.KickstartParser instance; this instance will be
@@ -59,36 +53,19 @@ class BaseImageCreator(object):
59 filesystem labels 53 filesystem labels
60 """ 54 """
61 55
62 self.pkgmgr = pkgmgr
63
64 self.__builddir = None 56 self.__builddir = None
65 self.__bindmounts = []
66 57
67 self.ks = None 58 self.ks = None
68 self.name = "target" 59 self.name = "target"
69 self.tmpdir = "/var/tmp/wic" 60 self.tmpdir = "/var/tmp/wic"
70 self.cachedir = "/var/tmp/wic/cache"
71 self.workdir = "/var/tmp/wic/build" 61 self.workdir = "/var/tmp/wic/build"
72 62
73 self.destdir = "."
74 self.installerfw_prefix = "INSTALLERFW_"
75 self.target_arch = "noarch"
76 self._local_pkgs_path = None
77 self.pack_to = None
78 self.repourl = {}
79
80 # If the kernel is save to the destdir when copy_kernel cmd is called.
81 self._need_copy_kernel = False
82 # setup tmpfs tmpdir when enabletmpfs is True 63 # setup tmpfs tmpdir when enabletmpfs is True
83 self.enabletmpfs = False 64 self.enabletmpfs = False
84 65
85 if createopts: 66 if createopts:
86 # Mapping table for variables that have different names. 67 # Mapping table for variables that have different names.
87 optmap = {"pkgmgr" : "pkgmgr_name", 68 optmap = {"outdir" : "destdir",
88 "outdir" : "destdir",
89 "arch" : "target_arch",
90 "local_pkgs_path" : "_local_pkgs_path",
91 "copy_kernel" : "_need_copy_kernel",
92 } 69 }
93 70
94 # update setting from createopts 71 # update setting from createopts
@@ -101,41 +78,11 @@ class BaseImageCreator(object):
101 78
102 self.destdir = os.path.abspath(os.path.expanduser(self.destdir)) 79 self.destdir = os.path.abspath(os.path.expanduser(self.destdir))
103 80
104 if 'release' in createopts and createopts['release']: 81 self._dep_checks = ["ls", "bash", "cp", "echo"]
105 self.name = createopts['release'] + '_' + self.name
106
107 if self.pack_to:
108 if '@NAME@' in self.pack_to:
109 self.pack_to = self.pack_to.replace('@NAME@', self.name)
110 (tar, ext) = os.path.splitext(self.pack_to)
111 if ext in (".gz", ".bz2") and tar.endswith(".tar"):
112 ext = ".tar" + ext
113 if ext not in misc.pack_formats:
114 self.pack_to += ".tar"
115
116 self._dep_checks = ["ls", "bash", "cp", "echo", "modprobe"]
117 82
118 # Output image file names 83 # Output image file names
119 self.outimage = [] 84 self.outimage = []
120 85
121 # A flag to generate checksum
122 self._genchecksum = False
123
124 self._alt_initrd_name = None
125
126 self._recording_pkgs = []
127
128 # available size in root fs, init to 0
129 self._root_fs_avail = 0
130
131 # Name of the disk image file that is created.
132 self._img_name = None
133
134 self.image_format = None
135
136 # Save qemu emulator file name in order to clean up it finally
137 self.qemu_emulator = None
138
139 # No ks provided when called by convertor, so skip the dependency check 86 # No ks provided when called by convertor, so skip the dependency check
140 if self.ks: 87 if self.ks:
141 # If we have btrfs partition we need to check necessary tools 88 # If we have btrfs partition we need to check necessary tools
@@ -144,31 +91,9 @@ class BaseImageCreator(object):
144 self._dep_checks.append("mkfs.btrfs") 91 self._dep_checks.append("mkfs.btrfs")
145 break 92 break
146 93
147 if self.target_arch and self.target_arch.startswith("arm"):
148 for dep in self._dep_checks:
149 if dep == "extlinux":
150 self._dep_checks.remove(dep)
151
152 if not os.path.exists("/usr/bin/qemu-arm") or \
153 not misc.is_statically_linked("/usr/bin/qemu-arm"):
154 self._dep_checks.append("qemu-arm-static")
155
156 if os.path.exists("/proc/sys/vm/vdso_enabled"):
157 vdso_fh = open("/proc/sys/vm/vdso_enabled","r")
158 vdso_value = vdso_fh.read().strip()
159 vdso_fh.close()
160 if (int)(vdso_value) == 1:
161 msger.warning("vdso is enabled on your host, which might "
162 "cause problems with arm emulations.\n"
163 "\tYou can disable vdso with following command before "
164 "starting image build:\n"
165 "\techo 0 | sudo tee /proc/sys/vm/vdso_enabled")
166
167 # make sure the specified tmpdir and cachedir exist 94 # make sure the specified tmpdir and cachedir exist
168 if not os.path.exists(self.tmpdir): 95 if not os.path.exists(self.tmpdir):
169 os.makedirs(self.tmpdir) 96 os.makedirs(self.tmpdir)
170 if not os.path.exists(self.cachedir):
171 os.makedirs(self.cachedir)
172 97
173 98
174 # 99 #
@@ -190,23 +115,6 @@ class BaseImageCreator(object):
190 115
191 """ 116 """
192 117
193 def __get_outdir(self):
194 if self.__builddir is None:
195 raise CreatorError("_outdir is not valid before calling mount()")
196 return self.__builddir + "/out"
197 _outdir = property(__get_outdir)
198 """The staging location for the final image.
199
200 This is where subclasses should stage any files that are part of the final
201 image. ImageCreator.package() will copy any files found here into the
202 requested destination directory.
203
204 Note, this directory does not exist before ImageCreator.mount() is called.
205
206 Note also, this is a read-only attribute.
207
208 """
209
210 118
211 # 119 #
212 # Hooks for subclasses 120 # Hooks for subclasses
@@ -239,408 +147,6 @@ class BaseImageCreator(object):
239 """ 147 """
240 pass 148 pass
241 149
242 def _create_bootconfig(self):
243 """Configure the image so that it's bootable.
244
245 This is the hook where subclasses may prepare the image for booting by
246 e.g. creating an initramfs and bootloader configuration.
247
248 This hook is called while the install root is still mounted, after the
249 packages have been installed and the kickstart configuration has been
250 applied, but before the %post scripts have been executed.
251
252 There is no default implementation.
253
254 """
255 pass
256
257 def _stage_final_image(self):
258 """Stage the final system image in _outdir.
259
260 This is the hook where subclasses should place the image in _outdir
261 so that package() can copy it to the requested destination directory.
262
263 By default, this moves the install root into _outdir.
264
265 """
266 shutil.move(self._instroot, self._outdir + "/" + self.name)
267
268 def get_installed_packages(self):
269 return self._pkgs_content.keys()
270
271 def _save_recording_pkgs(self, destdir):
272 """Save the list or content of installed packages to file.
273 """
274 pkgs = self._pkgs_content.keys()
275 pkgs.sort() # inplace op
276
277 if not os.path.exists(destdir):
278 os.makedirs(destdir)
279
280 content = None
281 if 'vcs' in self._recording_pkgs:
282 vcslst = ["%s %s" % (k, v) for (k, v) in self._pkgs_vcsinfo.items()]
283 content = '\n'.join(sorted(vcslst))
284 elif 'name' in self._recording_pkgs:
285 content = '\n'.join(pkgs)
286 if content:
287 namefile = os.path.join(destdir, self.name + '.packages')
288 f = open(namefile, "w")
289 f.write(content)
290 f.close()
291 self.outimage.append(namefile);
292
293 # if 'content', save more details
294 if 'content' in self._recording_pkgs:
295 contfile = os.path.join(destdir, self.name + '.files')
296 f = open(contfile, "w")
297
298 for pkg in pkgs:
299 content = pkg + '\n'
300
301 pkgcont = self._pkgs_content[pkg]
302 content += ' '
303 content += '\n '.join(pkgcont)
304 content += '\n'
305
306 content += '\n'
307 f.write(content)
308 f.close()
309 self.outimage.append(contfile)
310
311 if 'license' in self._recording_pkgs:
312 licensefile = os.path.join(destdir, self.name + '.license')
313 f = open(licensefile, "w")
314
315 f.write('Summary:\n')
316 for license in reversed(sorted(self._pkgs_license, key=\
317 lambda license: len(self._pkgs_license[license]))):
318 f.write(" - %s: %s\n" \
319 % (license, len(self._pkgs_license[license])))
320
321 f.write('\nDetails:\n')
322 for license in reversed(sorted(self._pkgs_license, key=\
323 lambda license: len(self._pkgs_license[license]))):
324 f.write(" - %s:\n" % (license))
325 for pkg in sorted(self._pkgs_license[license]):
326 f.write(" - %s\n" % (pkg))
327 f.write('\n')
328
329 f.close()
330 self.outimage.append(licensefile)
331
332 def _get_required_packages(self):
333 """Return a list of required packages.
334
335 This is the hook where subclasses may specify a set of packages which
336 it requires to be installed.
337
338 This returns an empty list by default.
339
340 Note, subclasses should usually chain up to the base class
341 implementation of this hook.
342
343 """
344 return []
345
346 def _get_excluded_packages(self):
347 """Return a list of excluded packages.
348
349 This is the hook where subclasses may specify a set of packages which
350 it requires _not_ to be installed.
351
352 This returns an empty list by default.
353
354 Note, subclasses should usually chain up to the base class
355 implementation of this hook.
356
357 """
358 return []
359
360 def _get_local_packages(self):
361 """Return a list of rpm path to be local installed.
362
363 This is the hook where subclasses may specify a set of rpms which
364 it requires to be installed locally.
365
366 This returns an empty list by default.
367
368 Note, subclasses should usually chain up to the base class
369 implementation of this hook.
370
371 """
372 if self._local_pkgs_path:
373 if os.path.isdir(self._local_pkgs_path):
374 return glob.glob(
375 os.path.join(self._local_pkgs_path, '*.rpm'))
376 elif os.path.splitext(self._local_pkgs_path)[-1] == '.rpm':
377 return [self._local_pkgs_path]
378
379 return []
380
381 def _get_fstab(self):
382 """Return the desired contents of /etc/fstab.
383
384 This is the hook where subclasses may specify the contents of
385 /etc/fstab by returning a string containing the desired contents.
386
387 A sensible default implementation is provided.
388
389 """
390 s = "/dev/root / %s %s 0 0\n" \
391 % (self._fstype,
392 "defaults,noatime" if not self._fsopts else self._fsopts)
393 s += self._get_fstab_special()
394 return s
395
396 def _get_fstab_special(self):
397 s = "devpts /dev/pts devpts gid=5,mode=620 0 0\n"
398 s += "tmpfs /dev/shm tmpfs defaults 0 0\n"
399 s += "proc /proc proc defaults 0 0\n"
400 s += "sysfs /sys sysfs defaults 0 0\n"
401 return s
402
403 def _set_part_env(self, pnum, prop, value):
404 """ This is a helper function which generates an environment variable
405 for a property "prop" with value "value" of a partition number "pnum".
406
407 The naming convention is:
408 * Variables start with INSTALLERFW_PART
409 * Then goes the partition number, the order is the same as
410 specified in the KS file
411 * Then goes the property name
412 """
413
414 if value == None:
415 value = ""
416 else:
417 value = str(value)
418
419 name = self.installerfw_prefix + ("PART%d_" % pnum) + prop
420 return { name : value }
421
422 def _get_post_scripts_env(self, in_chroot):
423 """Return an environment dict for %post scripts.
424
425 This is the hook where subclasses may specify some environment
426 variables for %post scripts by return a dict containing the desired
427 environment.
428
429 in_chroot -- whether this %post script is to be executed chroot()ed
430 into _instroot.
431 """
432
433 env = {}
434 pnum = 0
435
436 for p in kickstart.get_partitions(self.ks):
437 env.update(self._set_part_env(pnum, "SIZE", p.size))
438 env.update(self._set_part_env(pnum, "MOUNTPOINT", p.mountpoint))
439 env.update(self._set_part_env(pnum, "FSTYPE", p.fstype))
440 env.update(self._set_part_env(pnum, "LABEL", p.label))
441 env.update(self._set_part_env(pnum, "FSOPTS", p.fsopts))
442 env.update(self._set_part_env(pnum, "BOOTFLAG", p.active))
443 env.update(self._set_part_env(pnum, "ALIGN", p.align))
444 env.update(self._set_part_env(pnum, "TYPE_ID", p.part_type))
445 env.update(self._set_part_env(pnum, "DEVNODE",
446 "/dev/%s%d" % (p.disk, pnum + 1)))
447 pnum += 1
448
449 # Count of paritions
450 env[self.installerfw_prefix + "PART_COUNT"] = str(pnum)
451
452 # Partition table format
453 ptable_format = self.ks.handler.bootloader.ptable
454 env[self.installerfw_prefix + "PTABLE_FORMAT"] = ptable_format
455
456 # The kerned boot parameters
457 kernel_opts = self.ks.handler.bootloader.appendLine
458 env[self.installerfw_prefix + "KERNEL_OPTS"] = kernel_opts
459
460 # Name of the distribution
461 env[self.installerfw_prefix + "DISTRO_NAME"] = self.distro_name
462
463 # Name of the image creation tool
464 env[self.installerfw_prefix + "INSTALLER_NAME"] = "wic"
465
466 # The real current location of the mounted file-systems
467 if in_chroot:
468 mount_prefix = "/"
469 else:
470 mount_prefix = self._instroot
471 env[self.installerfw_prefix + "MOUNT_PREFIX"] = mount_prefix
472
473 # These are historical variables which lack the common name prefix
474 if not in_chroot:
475 env["INSTALL_ROOT"] = self._instroot
476 env["IMG_NAME"] = self._name
477
478 return env
479
480 def __get_imgname(self):
481 return self.name
482 _name = property(__get_imgname)
483 """The name of the image file.
484
485 """
486
487 def _get_kernel_versions(self):
488 """Return a dict detailing the available kernel types/versions.
489
490 This is the hook where subclasses may override what kernel types and
491 versions should be available for e.g. creating the booloader
492 configuration.
493
494 A dict should be returned mapping the available kernel types to a list
495 of the available versions for those kernels.
496
497 The default implementation uses rpm to iterate over everything
498 providing 'kernel', finds /boot/vmlinuz-* and returns the version
499 obtained from the vmlinuz filename. (This can differ from the kernel
500 RPM's n-v-r in the case of e.g. xen)
501
502 """
503 def get_kernel_versions(instroot):
504 ret = {}
505 versions = set()
506 files = glob.glob(instroot + "/boot/vmlinuz-*")
507 for file in files:
508 version = os.path.basename(file)[8:]
509 if version is None:
510 continue
511 versions.add(version)
512 ret["kernel"] = list(versions)
513 return ret
514
515 def get_version(header):
516 version = None
517 for f in header['filenames']:
518 if f.startswith('/boot/vmlinuz-'):
519 version = f[14:]
520 return version
521
522 if self.ks is None:
523 return get_kernel_versions(self._instroot)
524
525 ts = rpm.TransactionSet(self._instroot)
526
527 ret = {}
528 for header in ts.dbMatch('provides', 'kernel'):
529 version = get_version(header)
530 if version is None:
531 continue
532
533 name = header['name']
534 if not name in ret:
535 ret[name] = [version]
536 elif not version in ret[name]:
537 ret[name].append(version)
538
539 return ret
540
541
542 #
543 # Helpers for subclasses
544 #
545 def _do_bindmounts(self):
546 """Mount various system directories onto _instroot.
547
548 This method is called by mount(), but may also be used by subclasses
549 in order to re-mount the bindmounts after modifying the underlying
550 filesystem.
551
552 """
553 for b in self.__bindmounts:
554 b.mount()
555
556 def _undo_bindmounts(self):
557 """Unmount the bind-mounted system directories from _instroot.
558
559 This method is usually only called by unmount(), but may also be used
560 by subclasses in order to gain access to the filesystem obscured by
561 the bindmounts - e.g. in order to create device nodes on the image
562 filesystem.
563
564 """
565 self.__bindmounts.reverse()
566 for b in self.__bindmounts:
567 b.unmount()
568
569 def _chroot(self):
570 """Chroot into the install root.
571
572 This method may be used by subclasses when executing programs inside
573 the install root e.g.
574
575 subprocess.call(["/bin/ls"], preexec_fn = self.chroot)
576
577 """
578 os.chroot(self._instroot)
579 os.chdir("/")
580
581 def _mkdtemp(self, prefix = "tmp-"):
582 """Create a temporary directory.
583
584 This method may be used by subclasses to create a temporary directory
585 for use in building the final image - e.g. a subclass might create
586 a temporary directory in order to bundle a set of files into a package.
587
588 The subclass may delete this directory if it wishes, but it will be
589 automatically deleted by cleanup().
590
591 The absolute path to the temporary directory is returned.
592
593 Note, this method should only be called after mount() has been called.
594
595 prefix -- a prefix which should be used when creating the directory;
596 defaults to "tmp-".
597
598 """
599 self.__ensure_builddir()
600 return tempfile.mkdtemp(dir = self.__builddir, prefix = prefix)
601
602 def _mkstemp(self, prefix = "tmp-"):
603 """Create a temporary file.
604
605 This method may be used by subclasses to create a temporary file
606 for use in building the final image - e.g. a subclass might need
607 a temporary location to unpack a compressed file.
608
609 The subclass may delete this file if it wishes, but it will be
610 automatically deleted by cleanup().
611
612 A tuple containing a file descriptor (returned from os.open() and the
613 absolute path to the temporary directory is returned.
614
615 Note, this method should only be called after mount() has been called.
616
617 prefix -- a prefix which should be used when creating the file;
618 defaults to "tmp-".
619
620 """
621 self.__ensure_builddir()
622 return tempfile.mkstemp(dir = self.__builddir, prefix = prefix)
623
624 def _mktemp(self, prefix = "tmp-"):
625 """Create a temporary file.
626
627 This method simply calls _mkstemp() and closes the returned file
628 descriptor.
629
630 The absolute path to the temporary file is returned.
631
632 Note, this method should only be called after mount() has been called.
633
634 prefix -- a prefix which should be used when creating the file;
635 defaults to "tmp-".
636
637 """
638
639 (f, path) = self._mkstemp(prefix)
640 os.close(f)
641 return path
642
643
644 # 150 #
645 # Actual implementation 151 # Actual implementation
646 # 152 #
@@ -658,64 +164,6 @@ class BaseImageCreator(object):
658 raise CreatorError("Failed create build directory in %s: %s" % 164 raise CreatorError("Failed create build directory in %s: %s" %
659 (self.tmpdir, msg)) 165 (self.tmpdir, msg))
660 166
661 def get_cachedir(self, cachedir = None):
662 if self.cachedir:
663 return self.cachedir
664
665 self.__ensure_builddir()
666 if cachedir:
667 self.cachedir = cachedir
668 else:
669 self.cachedir = self.__builddir + "/wic-cache"
670 fs.makedirs(self.cachedir)
671 return self.cachedir
672
673 def __sanity_check(self):
674 """Ensure that the config we've been given is sane."""
675 if not (kickstart.get_packages(self.ks) or
676 kickstart.get_groups(self.ks)):
677 raise CreatorError("No packages or groups specified")
678
679 kickstart.convert_method_to_repo(self.ks)
680
681 if not kickstart.get_repos(self.ks):
682 raise CreatorError("No repositories specified")
683
684 def __write_fstab(self):
685 fstab_contents = self._get_fstab()
686 if fstab_contents:
687 fstab = open(self._instroot + "/etc/fstab", "w")
688 fstab.write(fstab_contents)
689 fstab.close()
690
691 def __create_minimal_dev(self):
692 """Create a minimal /dev so that we don't corrupt the host /dev"""
693 origumask = os.umask(0000)
694 devices = (('null', 1, 3, 0666),
695 ('urandom',1, 9, 0666),
696 ('random', 1, 8, 0666),
697 ('full', 1, 7, 0666),
698 ('ptmx', 5, 2, 0666),
699 ('tty', 5, 0, 0666),
700 ('zero', 1, 5, 0666))
701
702 links = (("/proc/self/fd", "/dev/fd"),
703 ("/proc/self/fd/0", "/dev/stdin"),
704 ("/proc/self/fd/1", "/dev/stdout"),
705 ("/proc/self/fd/2", "/dev/stderr"))
706
707 for (node, major, minor, perm) in devices:
708 if not os.path.exists(self._instroot + "/dev/" + node):
709 os.mknod(self._instroot + "/dev/" + node,
710 perm | stat.S_IFCHR,
711 os.makedev(major,minor))
712
713 for (src, dest) in links:
714 if not os.path.exists(self._instroot + dest):
715 os.symlink(src, self._instroot + dest)
716
717 os.umask(origumask)
718
719 def __setup_tmpdir(self): 167 def __setup_tmpdir(self):
720 if not self.enabletmpfs: 168 if not self.enabletmpfs:
721 return 169 return
@@ -789,338 +237,6 @@ class BaseImageCreator(object):
789 237
790 self.__clean_tmpdir() 238 self.__clean_tmpdir()
791 239
792 def __is_excluded_pkg(self, pkg):
793 if pkg in self._excluded_pkgs:
794 self._excluded_pkgs.remove(pkg)
795 return True
796
797 for xpkg in self._excluded_pkgs:
798 if xpkg.endswith('*'):
799 if pkg.startswith(xpkg[:-1]):
800 return True
801 elif xpkg.startswith('*'):
802 if pkg.endswith(xpkg[1:]):
803 return True
804
805 return None
806
807 def __select_packages(self, pkg_manager):
808 skipped_pkgs = []
809 for pkg in self._required_pkgs:
810 e = pkg_manager.selectPackage(pkg)
811 if e:
812 if kickstart.ignore_missing(self.ks):
813 skipped_pkgs.append(pkg)
814 elif self.__is_excluded_pkg(pkg):
815 skipped_pkgs.append(pkg)
816 else:
817 raise CreatorError("Failed to find package '%s' : %s" %
818 (pkg, e))
819
820 for pkg in skipped_pkgs:
821 msger.warning("Skipping missing package '%s'" % (pkg,))
822
823 def __select_groups(self, pkg_manager):
824 skipped_groups = []
825 for group in self._required_groups:
826 e = pkg_manager.selectGroup(group.name, group.include)
827 if e:
828 if kickstart.ignore_missing(self.ks):
829 skipped_groups.append(group)
830 else:
831 raise CreatorError("Failed to find group '%s' : %s" %
832 (group.name, e))
833
834 for group in skipped_groups:
835 msger.warning("Skipping missing group '%s'" % (group.name,))
836
837 def __deselect_packages(self, pkg_manager):
838 for pkg in self._excluded_pkgs:
839 pkg_manager.deselectPackage(pkg)
840
841 def __localinst_packages(self, pkg_manager):
842 for rpm_path in self._get_local_packages():
843 pkg_manager.installLocal(rpm_path)
844
845 def __preinstall_packages(self, pkg_manager):
846 if not self.ks:
847 return
848
849 self._preinstall_pkgs = kickstart.get_pre_packages(self.ks)
850 for pkg in self._preinstall_pkgs:
851 pkg_manager.preInstall(pkg)
852
853 def __attachment_packages(self, pkg_manager):
854 if not self.ks:
855 return
856
857 self._attachment = []
858 for item in kickstart.get_attachment(self.ks):
859 if item.startswith('/'):
860 fpaths = os.path.join(self._instroot, item.lstrip('/'))
861 for fpath in glob.glob(fpaths):
862 self._attachment.append(fpath)
863 continue
864
865 filelist = pkg_manager.getFilelist(item)
866 if filelist:
867 # found rpm in rootfs
868 for pfile in pkg_manager.getFilelist(item):
869 fpath = os.path.join(self._instroot, pfile.lstrip('/'))
870 self._attachment.append(fpath)
871 continue
872
873 # try to retrieve rpm file
874 (url, proxies) = pkg_manager.package_url(item)
875 if not url:
876 msger.warning("Can't get url from repo for %s" % item)
877 continue
878 fpath = os.path.join(self.cachedir, os.path.basename(url))
879 if not os.path.exists(fpath):
880 # download pkgs
881 try:
882 fpath = grabber.myurlgrab(url, fpath, proxies, None)
883 except CreatorError:
884 raise
885
886 tmpdir = self._mkdtemp()
887 misc.extract_rpm(fpath, tmpdir)
888 for (root, dirs, files) in os.walk(tmpdir):
889 for fname in files:
890 fpath = os.path.join(root, fname)
891 self._attachment.append(fpath)
892
893 def install(self, repo_urls=None):
894 """Install packages into the install root.
895
896 This function installs the packages listed in the supplied kickstart
897 into the install root. By default, the packages are installed from the
898 repository URLs specified in the kickstart.
899
900 repo_urls -- a dict which maps a repository name to a repository URL;
901 if supplied, this causes any repository URLs specified in
902 the kickstart to be overridden.
903
904 """
905
906 # initialize pkg list to install
907 if self.ks:
908 self.__sanity_check()
909
910 self._required_pkgs = \
911 kickstart.get_packages(self.ks, self._get_required_packages())
912 self._excluded_pkgs = \
913 kickstart.get_excluded(self.ks, self._get_excluded_packages())
914 self._required_groups = kickstart.get_groups(self.ks)
915 else:
916 self._required_pkgs = None
917 self._excluded_pkgs = None
918 self._required_groups = None
919
920 pkg_manager = self.get_pkg_manager()
921 pkg_manager.setup()
922
923 if hasattr(self, 'install_pkgs') and self.install_pkgs:
924 if 'debuginfo' in self.install_pkgs:
925 pkg_manager.install_debuginfo = True
926
927 for repo in kickstart.get_repos(self.ks, repo_urls):
928 (name, baseurl, mirrorlist, inc, exc,
929 proxy, proxy_username, proxy_password, debuginfo,
930 source, gpgkey, disable, ssl_verify, nocache,
931 cost, priority) = repo
932
933 yr = pkg_manager.addRepository(name, baseurl, mirrorlist, proxy,
934 proxy_username, proxy_password, inc, exc, ssl_verify,
935 nocache, cost, priority)
936
937 if kickstart.exclude_docs(self.ks):
938 rpm.addMacro("_excludedocs", "1")
939 rpm.addMacro("_dbpath", "/var/lib/rpm")
940 rpm.addMacro("__file_context_path", "%{nil}")
941 if kickstart.inst_langs(self.ks) != None:
942 rpm.addMacro("_install_langs", kickstart.inst_langs(self.ks))
943
944 try:
945 self.__preinstall_packages(pkg_manager)
946 self.__select_packages(pkg_manager)
947 self.__select_groups(pkg_manager)
948 self.__deselect_packages(pkg_manager)
949 self.__localinst_packages(pkg_manager)
950
951 BOOT_SAFEGUARD = 256L * 1024 * 1024 # 256M
952 checksize = self._root_fs_avail
953 if checksize:
954 checksize -= BOOT_SAFEGUARD
955 if self.target_arch:
956 pkg_manager._add_prob_flags(rpm.RPMPROB_FILTER_IGNOREARCH)
957 pkg_manager.runInstall(checksize)
958 except CreatorError, e:
959 raise
960 except KeyboardInterrupt:
961 raise
962 else:
963 self._pkgs_content = pkg_manager.getAllContent()
964 self._pkgs_license = pkg_manager.getPkgsLicense()
965 self._pkgs_vcsinfo = pkg_manager.getVcsInfo()
966 self.__attachment_packages(pkg_manager)
967 finally:
968 pkg_manager.close()
969
970 # hook post install
971 self.postinstall()
972
973 # do some clean up to avoid lvm info leakage. this sucks.
974 for subdir in ("cache", "backup", "archive"):
975 lvmdir = self._instroot + "/etc/lvm/" + subdir
976 try:
977 for f in os.listdir(lvmdir):
978 os.unlink(lvmdir + "/" + f)
979 except:
980 pass
981
982 def postinstall(self):
983 self.copy_attachment()
984
985 def __run_post_scripts(self):
986 msger.info("Running scripts ...")
987 if os.path.exists(self._instroot + "/tmp"):
988 shutil.rmtree(self._instroot + "/tmp")
989 os.mkdir (self._instroot + "/tmp", 0755)
990 for s in kickstart.get_post_scripts(self.ks):
991 (fd, path) = tempfile.mkstemp(prefix = "ks-script-",
992 dir = self._instroot + "/tmp")
993
994 s.script = s.script.replace("\r", "")
995 os.write(fd, s.script)
996 os.close(fd)
997 os.chmod(path, 0700)
998
999 env = self._get_post_scripts_env(s.inChroot)
1000
1001 if not s.inChroot:
1002 preexec = None
1003 script = path
1004 else:
1005 preexec = self._chroot
1006 script = "/tmp/" + os.path.basename(path)
1007
1008 try:
1009 try:
1010 subprocess.call([s.interp, script],
1011 preexec_fn = preexec,
1012 env = env,
1013 stdout = sys.stdout,
1014 stderr = sys.stderr)
1015 except OSError, (err, msg):
1016 raise CreatorError("Failed to execute %%post script "
1017 "with '%s' : %s" % (s.interp, msg))
1018 finally:
1019 os.unlink(path)
1020
1021 def __save_repo_keys(self, repodata):
1022 if not repodata:
1023 return None
1024
1025 gpgkeydir = "/etc/pki/rpm-gpg"
1026 fs.makedirs(self._instroot + gpgkeydir)
1027 for repo in repodata:
1028 if repo["repokey"]:
1029 repokey = gpgkeydir + "/RPM-GPG-KEY-%s" % repo["name"]
1030 shutil.copy(repo["repokey"], self._instroot + repokey)
1031
1032 def configure(self, repodata = None):
1033 """Configure the system image according to the kickstart.
1034
1035 This method applies the (e.g. keyboard or network) configuration
1036 specified in the kickstart and executes the kickstart %post scripts.
1037
1038 If necessary, it also prepares the image to be bootable by e.g.
1039 creating an initrd and bootloader configuration.
1040
1041 """
1042 ksh = self.ks.handler
1043
1044 msger.info('Applying configurations ...')
1045 try:
1046 kickstart.LanguageConfig(self._instroot).apply(ksh.lang)
1047 kickstart.KeyboardConfig(self._instroot).apply(ksh.keyboard)
1048 kickstart.TimezoneConfig(self._instroot).apply(ksh.timezone)
1049 #kickstart.AuthConfig(self._instroot).apply(ksh.authconfig)
1050 kickstart.FirewallConfig(self._instroot).apply(ksh.firewall)
1051 kickstart.RootPasswordConfig(self._instroot).apply(ksh.rootpw)
1052 kickstart.UserConfig(self._instroot).apply(ksh.user)
1053 kickstart.ServicesConfig(self._instroot).apply(ksh.services)
1054 kickstart.XConfig(self._instroot).apply(ksh.xconfig)
1055 kickstart.NetworkConfig(self._instroot).apply(ksh.network)
1056 kickstart.RPMMacroConfig(self._instroot).apply(self.ks)
1057 kickstart.DesktopConfig(self._instroot).apply(ksh.desktop)
1058 self.__save_repo_keys(repodata)
1059 kickstart.MoblinRepoConfig(self._instroot).apply(ksh.repo, repodata, self.repourl)
1060 except:
1061 msger.warning("Failed to apply configuration to image")
1062 raise
1063
1064 self._create_bootconfig()
1065 self.__run_post_scripts()
1066
1067 def launch_shell(self, launch):
1068 """Launch a shell in the install root.
1069
1070 This method is launches a bash shell chroot()ed in the install root;
1071 this can be useful for debugging.
1072
1073 """
1074 if launch:
1075 msger.info("Launching shell. Exit to continue.")
1076 subprocess.call(["/bin/bash"], preexec_fn = self._chroot)
1077
1078 def do_genchecksum(self, image_name):
1079 if not self._genchecksum:
1080 return
1081
1082 md5sum = misc.get_md5sum(image_name)
1083 with open(image_name + ".md5sum", "w") as f:
1084 f.write("%s %s" % (md5sum, os.path.basename(image_name)))
1085 self.outimage.append(image_name+".md5sum")
1086
1087 def package(self, destdir = "."):
1088 """Prepares the created image for final delivery.
1089
1090 In its simplest form, this method merely copies the install root to the
1091 supplied destination directory; other subclasses may choose to package
1092 the image by e.g. creating a bootable ISO containing the image and
1093 bootloader configuration.
1094
1095 destdir -- the directory into which the final image should be moved;
1096 this defaults to the current directory.
1097
1098 """
1099 self._stage_final_image()
1100
1101 if not os.path.exists(destdir):
1102 fs.makedirs(destdir)
1103
1104 if self._recording_pkgs:
1105 self._save_recording_pkgs(destdir)
1106
1107 # For image formats with two or multiple image files, it will be
1108 # better to put them under a directory
1109 if self.image_format in ("raw", "vmdk", "vdi", "nand", "mrstnand"):
1110 destdir = os.path.join(destdir, "%s-%s" \
1111 % (self.name, self.image_format))
1112 msger.debug("creating destination dir: %s" % destdir)
1113 fs.makedirs(destdir)
1114
1115 # Ensure all data is flushed to _outdir
1116 runner.quiet('sync')
1117
1118 misc.check_space_pre_cp(self._outdir, destdir)
1119 for f in os.listdir(self._outdir):
1120 shutil.move(os.path.join(self._outdir, f),
1121 os.path.join(destdir, f))
1122 self.outimage.append(os.path.join(destdir, f))
1123 self.do_genchecksum(os.path.join(destdir, f))
1124 240
1125 def print_outimage_info(self): 241 def print_outimage_info(self):
1126 msg = "The new image can be found here:\n" 242 msg = "The new image can be found here:\n"
@@ -1129,135 +245,3 @@ class BaseImageCreator(object):
1129 msg += ' %s\n' % os.path.abspath(file) 245 msg += ' %s\n' % os.path.abspath(file)
1130 246
1131 msger.info(msg) 247 msger.info(msg)
1132
1133 def check_depend_tools(self):
1134 for tool in self._dep_checks:
1135 fs.find_binary_path(tool)
1136
1137 def package_output(self, image_format, destdir = ".", package="none"):
1138 if not package or package == "none":
1139 return
1140
1141 destdir = os.path.abspath(os.path.expanduser(destdir))
1142 (pkg, comp) = os.path.splitext(package)
1143 if comp:
1144 comp=comp.lstrip(".")
1145
1146 if pkg == "tar":
1147 if comp:
1148 dst = "%s/%s-%s.tar.%s" %\
1149 (destdir, self.name, image_format, comp)
1150 else:
1151 dst = "%s/%s-%s.tar" %\
1152 (destdir, self.name, image_format)
1153
1154 msger.info("creating %s" % dst)
1155 tar = tarfile.open(dst, "w:" + comp)
1156
1157 for file in self.outimage:
1158 msger.info("adding %s to %s" % (file, dst))
1159 tar.add(file,
1160 arcname=os.path.join("%s-%s" \
1161 % (self.name, image_format),
1162 os.path.basename(file)))
1163 if os.path.isdir(file):
1164 shutil.rmtree(file, ignore_errors = True)
1165 else:
1166 os.remove(file)
1167
1168 tar.close()
1169
1170 '''All the file in outimage has been packaged into tar.* file'''
1171 self.outimage = [dst]
1172
1173 def release_output(self, config, destdir, release):
1174 """ Create release directory and files
1175 """
1176
1177 def _rpath(fn):
1178 """ release path """
1179 return os.path.join(destdir, fn)
1180
1181 outimages = self.outimage
1182
1183 # new ks
1184 new_kspath = _rpath(self.name+'.ks')
1185 with open(config) as fr:
1186 with open(new_kspath, "w") as wf:
1187 # When building a release we want to make sure the .ks
1188 # file generates the same build even when --release not used.
1189 wf.write(fr.read().replace("@BUILD_ID@", release))
1190 outimages.append(new_kspath)
1191
1192 # save log file, logfile is only available in creator attrs
1193 if hasattr(self, 'logfile') and not self.logfile:
1194 log_path = _rpath(self.name + ".log")
1195 # touch the log file, else outimages will filter it out
1196 with open(log_path, 'w') as wf:
1197 wf.write('')
1198 msger.set_logfile(log_path)
1199 outimages.append(_rpath(self.name + ".log"))
1200
1201 # rename iso and usbimg
1202 for f in os.listdir(destdir):
1203 if f.endswith(".iso"):
1204 newf = f[:-4] + '.img'
1205 elif f.endswith(".usbimg"):
1206 newf = f[:-7] + '.img'
1207 else:
1208 continue
1209 os.rename(_rpath(f), _rpath(newf))
1210 outimages.append(_rpath(newf))
1211
1212 # generate MD5SUMS
1213 with open(_rpath("MD5SUMS"), "w") as wf:
1214 for f in os.listdir(destdir):
1215 if f == "MD5SUMS":
1216 continue
1217
1218 if os.path.isdir(os.path.join(destdir, f)):
1219 continue
1220
1221 md5sum = misc.get_md5sum(_rpath(f))
1222 # There needs to be two spaces between the sum and
1223 # filepath to match the syntax with md5sum.
1224 # This way also md5sum -c MD5SUMS can be used by users
1225 wf.write("%s *%s\n" % (md5sum, f))
1226
1227 outimages.append("%s/MD5SUMS" % destdir)
1228
1229 # Filter out the nonexist file
1230 for fp in outimages[:]:
1231 if not os.path.exists("%s" % fp):
1232 outimages.remove(fp)
1233
1234 def copy_kernel(self):
1235 """ Copy kernel files to the outimage directory.
1236 NOTE: This needs to be called before unmounting the instroot.
1237 """
1238
1239 if not self._need_copy_kernel:
1240 return
1241
1242 if not os.path.exists(self.destdir):
1243 os.makedirs(self.destdir)
1244
1245 for kernel in glob.glob("%s/boot/vmlinuz-*" % self._instroot):
1246 kernelfilename = "%s/%s-%s" % (self.destdir,
1247 self.name,
1248 os.path.basename(kernel))
1249 msger.info('copy kernel file %s as %s' % (os.path.basename(kernel),
1250 kernelfilename))
1251 shutil.copy(kernel, kernelfilename)
1252 self.outimage.append(kernelfilename)
1253
1254 def copy_attachment(self):
1255 """ Subclass implement it to handle attachment files
1256 NOTE: This needs to be called before unmounting the instroot.
1257 """
1258 pass
1259
1260 def get_pkg_manager(self):
1261 return self.pkgmgr(target_arch = self.target_arch,
1262 instroot = self._instroot,
1263 cachedir = self.cachedir)
diff --git a/scripts/lib/mic/imager/direct.py b/scripts/lib/mic/imager/direct.py
index 7e2b63a37c..93f0cd9e6c 100644
--- a/scripts/lib/mic/imager/direct.py
+++ b/scripts/lib/mic/imager/direct.py
@@ -54,14 +54,14 @@ class DirectImageCreator(BaseImageCreator):
54 54
55 def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir, 55 def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir,
56 kernel_dir, native_sysroot, hdddir, staging_data_dir, 56 kernel_dir, native_sysroot, hdddir, staging_data_dir,
57 creatoropts=None, pkgmgr=None, compress_image=None, 57 creatoropts=None, compress_image=None,
58 generate_bmap=None, fstab_entry="uuid"): 58 generate_bmap=None, fstab_entry="uuid"):
59 """ 59 """
60 Initialize a DirectImageCreator instance. 60 Initialize a DirectImageCreator instance.
61 61
62 This method takes the same arguments as ImageCreator.__init__() 62 This method takes the same arguments as ImageCreator.__init__()
63 """ 63 """
64 BaseImageCreator.__init__(self, creatoropts, pkgmgr) 64 BaseImageCreator.__init__(self, creatoropts)
65 65
66 self.__instimage = None 66 self.__instimage = None
67 self.__imgdir = None 67 self.__imgdir = None