2 # creator.py : ImageCreator and LoopImageCreator base classes
4 # Copyright 2007, Red Hat Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Library General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 from mic.utils.errors import CreatorError
31 from mic.utils.misc import get_filesystem_avail, is_statically_linked,setup_qemu_emulator, create_release
32 from mic.utils.fs_related import find_binary_path, makedirs, BindChrootMount
33 from mic.utils import rpmmisc, runner
34 from mic import kickstart
37 class BaseImageCreator(object):
38 """Installs a system to a chroot directory.
40 ImageCreator is the simplest creator class available; it will install and
41 configure a system image according to the supplied kickstart file.
45 import mic.imgcreate as imgcreate
46 ks = imgcreate.read_kickstart("foo.ks")
47 imgcreate.ImageCreator(ks, "foo").create()
51 def __init__(self, createopts = None, pkgmgr = None):
52 """Initialize an ImageCreator instance.
54 ks -- a pykickstart.KickstartParser instance; this instance will be
55 used to drive the install by e.g. providing the list of packages
56 to be installed, the system configuration and %post scripts
58 name -- a name for the image; used for e.g. image filenames or
65 # A pykickstart.KickstartParser instance."""
66 self.ks = createopts['ks']
67 self.repometadata = createopts['repomd']
69 # A name for the image."""
70 self.name = createopts['name']
72 # The directory in which all temporary files will be created."""
73 self.tmpdir = createopts['tmpdir']
75 self.cachedir = createopts['cachedir']
77 self.destdir = createopts['outdir']
78 # target arch for non-x86 image
79 self.target_arch = createopts['arch']
80 self._local_pkgs_path = createopts['local_pkgs_path']
84 self.repometadata = None
86 self.tmpdir = "/var/tmp/mic"
87 self.cachedir = "/var/tmp/mic/cache"
89 self.target_arch = None
90 self._local_pkgs_path = None
92 self.__builddir = None
93 self.__bindmounts = []
95 self._dep_checks = ["ls", "bash", "cp", "echo", "modprobe", "passwd"]
98 self.distro_name = "MeeGo"
100 # Output image file names"""
103 # A flag to generate checksum"""
104 self._genchecksum = False
106 self._alt_initrd_name = None
108 # the disk image after creation, e.g., bz2.
109 # This value is set with compression_method function. """
110 self.__img_compression_method = None
112 self._recording_pkgs = None
113 self._include_src = None
115 # available size in root fs, init to 0
116 self._root_fs_avail = 0
118 # Name of the disk image file that is created. """
119 self._img_name = None
121 self.image_format = None
123 # Save qemu emulator file name in order to clean up it finally """
124 self.qemu_emulator = None
126 # No ks provided when called by convertor, so skip the dependency check """
128 # If we have btrfs partition we need to check that we have toosl for those """
129 for part in self.ks.handler.partition.partitions:
130 if part.fstype and part.fstype == "btrfs":
131 self._dep_checks.append("mkfs.btrfs")
134 # make sure the specified tmpdir and cachedir exist
135 if not os.path.exists(self.tmpdir):
136 makedirs(self.tmpdir)
137 if not os.path.exists(self.cachedir):
138 makedirs(self.cachedir)
140 def set_target_arch(self, arch):
141 if arch not in rpmmisc.arches:
144 self.target_arch = arch
145 if self.target_arch.startswith("arm"):
146 for dep in self._dep_checks:
147 if dep == "extlinux":
148 self._dep_checks.remove(dep)
150 if not os.path.exists("/usr/bin/qemu-arm") or not is_statically_linked("/usr/bin/qemu-arm"):
151 self._dep_checks.append("qemu-arm-static")
153 if os.path.exists("/proc/sys/vm/vdso_enabled"):
154 vdso_fh = open("/proc/sys/vm/vdso_enabled","r")
155 vdso_value = vdso_fh.read().strip()
157 if (int)(vdso_value) == 1:
158 msger.warning("vdso is enabled on your host, which might cause problems with arm emulations.\n"
159 "\tYou can disable vdso with following command before starting image build:\n"
160 "\techo 0 | sudo tee /proc/sys/vm/vdso_enabled")
171 def __get_instroot(self):
172 if self.__builddir is None:
173 raise CreatorError("_instroot is not valid before calling mount()")
174 return self.__builddir + "/install_root"
175 _instroot = property(__get_instroot)
176 """The location of the install root directory.
178 This is the directory into which the system is installed. Subclasses may
179 mount a filesystem image here or copy files to/from here.
181 Note, this directory does not exist before ImageCreator.mount() is called.
183 Note also, this is a read-only attribute.
187 def __get_outdir(self):
188 if self.__builddir is None:
189 raise CreatorError("_outdir is not valid before calling mount()")
190 return self.__builddir + "/out"
191 _outdir = property(__get_outdir)
192 """The staging location for the final image.
194 This is where subclasses should stage any files that are part of the final
195 image. ImageCreator.package() will copy any files found here into the
196 requested destination directory.
198 Note, this directory does not exist before ImageCreator.mount() is called.
200 Note also, this is a read-only attribute.
205 # Hooks for subclasses
207 def _mount_instroot(self, base_on = None):
208 """Mount or prepare the install root directory.
210 This is the hook where subclasses may prepare the install root by e.g.
211 mounting creating and loopback mounting a filesystem image to
214 There is no default implementation.
216 base_on -- this is the value passed to mount() and can be interpreted
217 as the subclass wishes; it might e.g. be the location of
218 a previously created ISO containing a system image.
223 def _unmount_instroot(self):
224 """Undo anything performed in _mount_instroot().
226 This is the hook where subclasses must undo anything which was done
227 in _mount_instroot(). For example, if a filesystem image was mounted
228 onto _instroot, it should be unmounted here.
230 There is no default implementation.
235 def _create_bootconfig(self):
236 """Configure the image so that it's bootable.
238 This is the hook where subclasses may prepare the image for booting by
239 e.g. creating an initramfs and bootloader configuration.
241 This hook is called while the install root is still mounted, after the
242 packages have been installed and the kickstart configuration has been
243 applied, but before the %post scripts have been executed.
245 There is no default implementation.
250 def _stage_final_image(self):
251 """Stage the final system image in _outdir.
253 This is the hook where subclasses should place the image in _outdir
254 so that package() can copy it to the requested destination directory.
256 By default, this moves the install root into _outdir.
259 shutil.move(self._instroot, self._outdir + "/" + self.name)
261 def get_installed_packages(self):
262 return self._pkgs_content.keys()
264 def _save_recording_pkgs(self, destdir):
265 """Save the list or content of installed packages to file.
267 if self._recording_pkgs not in ('content', 'name'):
270 pkgs = self._pkgs_content.keys()
271 pkgs.sort() # inplace op
273 # save package name list anyhow
274 if not os.path.exists(destdir):
277 namefile = os.path.join(destdir, self.name + '-pkgs.txt')
278 f = open(namefile, "w")
279 content = '\n'.join(pkgs)
282 self.outimage.append(namefile);
284 # if 'content', save more details
285 if self._recording_pkgs == 'content':
286 contfile = os.path.join(destdir, self.name + '-pkgs-content.txt')
287 f = open(contfile, "w")
292 pkgcont = self._pkgs_content[pkg]
294 if pkgcont.has_key('dir'):
295 items = map(lambda x:x+'/', pkgcont['dir'])
296 if pkgcont.has_key('file'):
297 items.extend(pkgcont['file'])
301 content += '\n '.join(items)
307 self.outimage.append(contfile)
309 def _get_required_packages(self):
310 """Return a list of required packages.
312 This is the hook where subclasses may specify a set of packages which
313 it requires to be installed.
315 This returns an empty list by default.
317 Note, subclasses should usually chain up to the base class
318 implementation of this hook.
323 def _get_excluded_packages(self):
324 """Return a list of excluded packages.
326 This is the hook where subclasses may specify a set of packages which
327 it requires _not_ to be installed.
329 This returns an empty list by default.
331 Note, subclasses should usually chain up to the base class
332 implementation of this hook.
335 excluded_packages = []
336 for rpm_path in self._get_local_packages():
337 rpm_name = os.path.basename(rpm_path)
338 package_name = rpmmisc.splitFilename(rpm_name)[0]
339 excluded_packages += [package_name]
340 return excluded_packages
342 def _get_local_packages(self):
343 """Return a list of rpm path to be local installed.
345 This is the hook where subclasses may specify a set of rpms which
346 it requires to be installed locally.
348 This returns an empty list by default.
350 Note, subclasses should usually chain up to the base class
351 implementation of this hook.
354 if self._local_pkgs_path:
355 if os.path.isdir(self._local_pkgs_path):
357 os.path.join(self._local_pkgs_path, '*.rpm'))
358 elif os.path.splitext(self._local_pkgs_path)[-1] == '.rpm':
359 return [self._local_pkgs_path]
363 def _get_fstab(self):
364 """Return the desired contents of /etc/fstab.
366 This is the hook where subclasses may specify the contents of
367 /etc/fstab by returning a string containing the desired contents.
369 A sensible default implementation is provided.
372 s = "/dev/root / %s %s 0 0\n" % (self._fstype, "defaults,noatime" if not self._fsopts else self._fsopts)
373 s += self._get_fstab_special()
376 def _get_fstab_special(self):
377 s = "devpts /dev/pts devpts gid=5,mode=620 0 0\n"
378 s += "tmpfs /dev/shm tmpfs defaults 0 0\n"
379 s += "proc /proc proc defaults 0 0\n"
380 s += "sysfs /sys sysfs defaults 0 0\n"
383 def _get_post_scripts_env(self, in_chroot):
384 """Return an environment dict for %post scripts.
386 This is the hook where subclasses may specify some environment
387 variables for %post scripts by return a dict containing the desired
390 By default, this returns an empty dict.
392 in_chroot -- whether this %post script is to be executed chroot()ed
398 def __get_imgname(self):
400 _name = property(__get_imgname)
401 """The name of the image file.
405 def _get_kernel_versions(self):
406 """Return a dict detailing the available kernel types/versions.
408 This is the hook where subclasses may override what kernel types and
409 versions should be available for e.g. creating the booloader
412 A dict should be returned mapping the available kernel types to a list
413 of the available versions for those kernels.
415 The default implementation uses rpm to iterate over everything
416 providing 'kernel', finds /boot/vmlinuz-* and returns the version
417 obtained from the vmlinuz filename. (This can differ from the kernel
418 RPM's n-v-r in the case of e.g. xen)
421 def get_kernel_versions(instroot):
424 files = glob.glob(instroot + "/boot/vmlinuz-*")
426 version = os.path.basename(file)[8:]
429 versions.add(version)
430 ret["kernel"] = list(versions)
433 def get_version(header):
435 for f in header['filenames']:
436 if f.startswith('/boot/vmlinuz-'):
441 return get_kernel_versions(self._instroot)
443 ts = rpm.TransactionSet(self._instroot)
446 for header in ts.dbMatch('provides', 'kernel'):
447 version = get_version(header)
451 name = header['name']
453 ret[name] = [version]
454 elif not version in ret[name]:
455 ret[name].append(version)
460 # Helpers for subclasses
462 def _do_bindmounts(self):
463 """Mount various system directories onto _instroot.
465 This method is called by mount(), but may also be used by subclasses
466 in order to re-mount the bindmounts after modifying the underlying
470 for b in self.__bindmounts:
473 def _undo_bindmounts(self):
474 """Unmount the bind-mounted system directories from _instroot.
476 This method is usually only called by unmount(), but may also be used
477 by subclasses in order to gain access to the filesystem obscured by
478 the bindmounts - e.g. in order to create device nodes on the image
482 self.__bindmounts.reverse()
483 for b in self.__bindmounts:
487 """Chroot into the install root.
489 This method may be used by subclasses when executing programs inside
490 the install root e.g.
492 subprocess.call(["/bin/ls"], preexec_fn = self.chroot)
495 os.chroot(self._instroot)
498 def _mkdtemp(self, prefix = "tmp-"):
499 """Create a temporary directory.
501 This method may be used by subclasses to create a temporary directory
502 for use in building the final image - e.g. a subclass might create
503 a temporary directory in order to bundle a set of files into a package.
505 The subclass may delete this directory if it wishes, but it will be
506 automatically deleted by cleanup().
508 The absolute path to the temporary directory is returned.
510 Note, this method should only be called after mount() has been called.
512 prefix -- a prefix which should be used when creating the directory;
516 self.__ensure_builddir()
517 return tempfile.mkdtemp(dir = self.__builddir, prefix = prefix)
519 def _mkstemp(self, prefix = "tmp-"):
520 """Create a temporary file.
522 This method may be used by subclasses to create a temporary file
523 for use in building the final image - e.g. a subclass might need
524 a temporary location to unpack a compressed file.
526 The subclass may delete this file if it wishes, but it will be
527 automatically deleted by cleanup().
529 A tuple containing a file descriptor (returned from os.open() and the
530 absolute path to the temporary directory is returned.
532 Note, this method should only be called after mount() has been called.
534 prefix -- a prefix which should be used when creating the file;
538 self.__ensure_builddir()
539 return tempfile.mkstemp(dir = self.__builddir, prefix = prefix)
541 def _mktemp(self, prefix = "tmp-"):
542 """Create a temporary file.
544 This method simply calls _mkstemp() and closes the returned file
547 The absolute path to the temporary file is returned.
549 Note, this method should only be called after mount() has been called.
551 prefix -- a prefix which should be used when creating the file;
556 (f, path) = self._mkstemp(prefix)
561 # Actual implementation
563 def __ensure_builddir(self):
564 if not self.__builddir is None:
568 self.__builddir = tempfile.mkdtemp(dir = self.tmpdir,
569 prefix = "imgcreate-")
570 except OSError, (err, msg):
571 raise CreatorError("Failed create build directory in %s: %s" %
574 def get_cachedir(self, cachedir = None):
578 self.__ensure_builddir()
580 self.cachedir = cachedir
582 self.cachedir = self.__builddir + "/yum-cache"
583 makedirs(self.cachedir)
586 def __sanity_check(self):
587 """Ensure that the config we've been given is sane."""
588 if not (kickstart.get_packages(self.ks) or
589 kickstart.get_groups(self.ks)):
590 raise CreatorError("No packages or groups specified")
592 kickstart.convert_method_to_repo(self.ks)
594 if not kickstart.get_repos(self.ks):
595 raise CreatorError("No repositories specified")
597 def __write_fstab(self):
598 fstab = open(self._instroot + "/etc/fstab", "w")
599 fstab.write(self._get_fstab())
602 def __create_minimal_dev(self):
603 """Create a minimal /dev so that we don't corrupt the host /dev"""
604 origumask = os.umask(0000)
605 devices = (('null', 1, 3, 0666),
606 ('urandom',1, 9, 0666),
607 ('random', 1, 8, 0666),
608 ('full', 1, 7, 0666),
609 ('ptmx', 5, 2, 0666),
611 ('zero', 1, 5, 0666))
612 links = (("/proc/self/fd", "/dev/fd"),
613 ("/proc/self/fd/0", "/dev/stdin"),
614 ("/proc/self/fd/1", "/dev/stdout"),
615 ("/proc/self/fd/2", "/dev/stderr"))
617 for (node, major, minor, perm) in devices:
618 if not os.path.exists(self._instroot + "/dev/" + node):
619 os.mknod(self._instroot + "/dev/" + node, perm | stat.S_IFCHR, os.makedev(major,minor))
620 for (src, dest) in links:
621 if not os.path.exists(self._instroot + dest):
622 os.symlink(src, self._instroot + dest)
626 def mount(self, base_on = None, cachedir = None):
627 """Setup the target filesystem in preparation for an install.
629 This function sets up the filesystem which the ImageCreator will
630 install into and configure. The ImageCreator class merely creates an
631 install root directory, bind mounts some system directories (e.g. /dev)
632 and writes out /etc/fstab. Other subclasses may also e.g. create a
633 sparse file, format it and loopback mount it to the install root.
635 base_on -- a previous install on which to base this install; defaults
636 to None, causing a new image to be created
638 cachedir -- a directory in which to store the Yum cache; defaults to
639 None, causing a new cache to be created; by setting this
640 to another directory, the same cache can be reused across
644 self.__ensure_builddir()
646 makedirs(self._instroot)
647 makedirs(self._outdir)
649 self._mount_instroot(base_on)
651 for d in ("/dev/pts", "/etc", "/boot", "/var/log", "/var/cache/yum", "/sys", "/proc", "/usr/bin"):
652 makedirs(self._instroot + d)
654 if self.target_arch and self.target_arch.startswith("arm"):
655 self.qemu_emulator = setup_qemu_emulator(self._instroot, self.target_arch)
657 self.get_cachedir(cachedir)
659 # bind mount system directories into _instroot
660 for (f, dest) in [("/sys", None), ("/proc", None), ("/proc/sys/fs/binfmt_misc", None),
662 (self.get_cachedir(), "/var/cache/yum")]:
663 self.__bindmounts.append(BindChrootMount(f, self._instroot, dest))
666 self._do_bindmounts()
668 self.__create_minimal_dev()
670 if os.path.exists(self._instroot + "/etc/mtab"):
671 os.unlink(self._instroot + "/etc/mtab")
672 os.symlink("../proc/mounts", self._instroot + "/etc/mtab")
676 # get size of available space in 'instroot' fs
677 self._root_fs_avail = get_filesystem_avail(self._instroot)
680 """Unmounts the target filesystem.
682 The ImageCreator class detaches the system from the install root, but
683 other subclasses may also detach the loopback mounted filesystem image
684 from the install root.
688 os.unlink(self._instroot + "/etc/mtab")
689 if self.qemu_emulator:
690 os.unlink(self._instroot + self.qemu_emulator)
691 """ Clean up yum garbage """
692 instroot_pdir = os.path.dirname(self._instroot + self._instroot)
693 if os.path.exists(instroot_pdir):
694 shutil.rmtree(instroot_pdir, ignore_errors = True)
699 self._undo_bindmounts()
701 self._unmount_instroot()
704 """Unmounts the target filesystem and deletes temporary files.
706 This method calls unmount() and then deletes any temporary files and
707 directories that were created on the host system while building the
710 Note, make sure to call this method once finished with the creator
711 instance in order to ensure no stale files are left on the host e.g.:
713 creator = ImageCreator(ks, name)
720 if not self.__builddir:
725 shutil.rmtree(self.__builddir, ignore_errors = True)
726 self.__builddir = None
728 def __is_excluded_pkg(self, pkg):
729 if pkg in self._excluded_pkgs:
730 self._excluded_pkgs.remove(pkg)
733 for xpkg in self._excluded_pkgs:
734 if xpkg.endswith('*'):
735 if pkg.startswith(xpkg[:-1]):
737 elif xpkg.startswith('*'):
738 if pkg.endswith(xpkg[1:]):
743 def __select_packages(self, pkg_manager):
745 for pkg in self._required_pkgs:
746 e = pkg_manager.selectPackage(pkg)
748 if kickstart.ignore_missing(self.ks):
749 skipped_pkgs.append(pkg)
750 elif self.__is_excluded_pkg(pkg):
751 skipped_pkgs.append(pkg)
753 raise CreatorError("Failed to find package '%s' : %s" %
756 for pkg in skipped_pkgs:
757 msger.warning("Skipping missing package '%s'" % (pkg,))
759 def __select_groups(self, pkg_manager):
761 for group in self._required_groups:
762 e = pkg_manager.selectGroup(group.name, group.include)
764 if kickstart.ignore_missing(self.ks):
765 skipped_groups.append(group)
767 raise CreatorError("Failed to find group '%s' : %s" %
770 for group in skipped_groups:
771 msger.warning("Skipping missing group '%s'" % (group.name,))
773 def __deselect_packages(self, pkg_manager):
774 for pkg in self._excluded_pkgs:
775 pkg_manager.deselectPackage(pkg)
777 def __localinst_packages(self, pkg_manager):
778 for rpm_path in self._get_local_packages():
779 pkg_manager.installLocal(rpm_path)
781 def install(self, repo_urls = {}):
782 """Install packages into the install root.
784 This function installs the packages listed in the supplied kickstart
785 into the install root. By default, the packages are installed from the
786 repository URLs specified in the kickstart.
788 repo_urls -- a dict which maps a repository name to a repository URL;
789 if supplied, this causes any repository URLs specified in
790 the kickstart to be overridden.
795 # initialize pkg list to install
797 self.__sanity_check()
799 self._required_pkgs = \
800 kickstart.get_packages(self.ks, self._get_required_packages())
801 self._excluded_pkgs = \
802 kickstart.get_excluded(self.ks, self._get_excluded_packages())
803 self._required_groups = kickstart.get_groups(self.ks)
805 self._required_pkgs = None
806 self._excluded_pkgs = None
807 self._required_groups = None
809 yum_conf = self._mktemp(prefix = "yum.conf-")
812 if self._include_src:
813 keep_record = 'include_src'
814 if self._recording_pkgs in ('name', 'content'):
815 keep_record = self._recording_pkgs
817 pkg_manager = self.get_pkg_manager(keep_record)
818 pkg_manager.setup(yum_conf, self._instroot)
820 for repo in kickstart.get_repos(self.ks, repo_urls):
821 (name, baseurl, mirrorlist, inc, exc, proxy, proxy_username, proxy_password, debuginfo, source, gpgkey, disable) = repo
823 yr = pkg_manager.addRepository(name, baseurl, mirrorlist, proxy, proxy_username, proxy_password, inc, exc)
825 if kickstart.exclude_docs(self.ks):
826 rpm.addMacro("_excludedocs", "1")
827 rpm.addMacro("__file_context_path", "%{nil}")
828 if kickstart.inst_langs(self.ks) != None:
829 rpm.addMacro("_install_langs", kickstart.inst_langs(self.ks))
833 self.__select_packages(pkg_manager)
834 self.__select_groups(pkg_manager)
835 self.__deselect_packages(pkg_manager)
836 self.__localinst_packages(pkg_manager)
838 BOOT_SAFEGUARD = 256L * 1024 * 1024 # 256M
839 checksize = self._root_fs_avail
841 checksize -= BOOT_SAFEGUARD
843 pkg_manager._add_prob_flags(rpm.RPMPROB_FILTER_IGNOREARCH)
844 pkg_manager.runInstall(checksize)
845 except CreatorError, e:
849 self._pkgs_content = pkg_manager.getAllContent()
851 pkg_manager.closeRpmDB()
855 # do some clean up to avoid lvm info leakage. this sucks.
856 for subdir in ("cache", "backup", "archive"):
857 lvmdir = self._instroot + "/etc/lvm/" + subdir
859 for f in os.listdir(lvmdir):
860 os.unlink(lvmdir + "/" + f)
864 def __run_post_scripts(self):
865 msger.info("Running scripts ...")
866 for s in kickstart.get_post_scripts(self.ks):
867 (fd, path) = tempfile.mkstemp(prefix = "ks-script-",
868 dir = self._instroot + "/tmp")
870 s.script = s.script.replace("\r", "")
871 os.write(fd, s.script)
875 env = self._get_post_scripts_env(s.inChroot)
878 env["INSTALL_ROOT"] = self._instroot
879 env["IMG_NAME"] = self._name
883 preexec = self._chroot
884 script = "/tmp/" + os.path.basename(path)
888 subprocess.call([s.interp, script],
889 preexec_fn = preexec, env = env, stdout = sys.stdout, stderr = sys.stderr)
890 except OSError, (err, msg):
891 raise CreatorError("Failed to execute %%post script "
892 "with '%s' : %s" % (s.interp, msg))
896 def __save_repo_keys(self, repodata):
900 gpgkeydir = "/etc/pki/rpm-gpg"
901 makedirs(self._instroot + gpgkeydir)
902 for repo in repodata:
904 repokey = gpgkeydir + "/RPM-GPG-KEY-%s" % repo["name"]
905 shutil.copy(repo["repokey"], self._instroot + repokey)
907 def configure(self, repodata = None):
908 """Configure the system image according to the kickstart.
910 This method applies the (e.g. keyboard or network) configuration
911 specified in the kickstart and executes the kickstart %post scripts.
913 If neccessary, it also prepares the image to be bootable by e.g.
914 creating an initrd and bootloader configuration.
917 ksh = self.ks.handler
919 msger.info('Applying configurations ...')
921 kickstart.LanguageConfig(self._instroot).apply(ksh.lang)
922 kickstart.KeyboardConfig(self._instroot).apply(ksh.keyboard)
923 kickstart.TimezoneConfig(self._instroot).apply(ksh.timezone)
924 #kickstart.AuthConfig(self._instroot).apply(ksh.authconfig)
925 kickstart.FirewallConfig(self._instroot).apply(ksh.firewall)
926 kickstart.RootPasswordConfig(self._instroot).apply(ksh.rootpw)
927 kickstart.UserConfig(self._instroot).apply(ksh.user)
928 kickstart.ServicesConfig(self._instroot).apply(ksh.services)
929 kickstart.XConfig(self._instroot).apply(ksh.xconfig)
930 kickstart.NetworkConfig(self._instroot).apply(ksh.network)
931 kickstart.RPMMacroConfig(self._instroot).apply(self.ks)
932 kickstart.DesktopConfig(self._instroot).apply(ksh.desktop)
933 self.__save_repo_keys(repodata)
934 kickstart.MoblinRepoConfig(self._instroot).apply(ksh.repo, repodata)
936 msger.warning("Failed to apply configuration to image")
939 self._create_bootconfig()
940 self.__run_post_scripts()
942 def launch_shell(self, launch):
943 """Launch a shell in the install root.
945 This method is launches a bash shell chroot()ed in the install root;
946 this can be useful for debugging.
950 msger.info("Launching shell. Exit to continue.")
951 subprocess.call(["/bin/bash"], preexec_fn = self._chroot)
953 def do_genchecksum(self, image_name):
954 if not self._genchecksum:
957 """ Generate md5sum if /usr/bin/md5sum is available """
958 if os.path.exists("/usr/bin/md5sum"):
959 p = subprocess.Popen(["/usr/bin/md5sum", "-b", image_name],
960 stdout=subprocess.PIPE)
961 (md5sum, errorstr) = p.communicate()
962 if p.returncode != 0:
963 msger.warning("Can't generate md5sum for image %s" % image_name)
965 pattern = re.compile("\*.*$")
966 md5sum = pattern.sub("*" + os.path.basename(image_name), md5sum)
967 fd = open(image_name + ".md5sum", "w")
970 self.outimage.append(image_name+".md5sum")
972 def package(self, destdir = "."):
973 """Prepares the created image for final delivery.
975 In its simplest form, this method merely copies the install root to the
976 supplied destination directory; other subclasses may choose to package
977 the image by e.g. creating a bootable ISO containing the image and
978 bootloader configuration.
980 destdir -- the directory into which the final image should be moved;
981 this defaults to the current directory.
984 self._stage_final_image()
986 if not os.path.exists(destdir):
988 if self.__img_compression_method:
989 if not self._img_name:
990 raise CreatorError("Image name not set.")
992 img_location = os.path.join(self._outdir,self._img_name)
993 if self.__img_compression_method == "bz2":
994 bzip2 = find_binary_path('bzip2')
995 msger.info("Compressing %s with bzip2. Please wait..." % img_location)
996 rc = runner.show([bzip2, "-f", img_location])
998 raise CreatorError("Failed to compress image %s with %s." % (img_location, self.__img_compression_method))
999 for bootimg in glob.glob(os.path.dirname(img_location) + "/*-boot.bin"):
1000 msger.info("Compressing %s with bzip2. Please wait..." % bootimg)
1001 rc = runner.show([bzip2, "-f", bootimg])
1003 raise CreatorError("Failed to compress image %s with %s." % (bootimg, self.__img_compression_method))
1005 if self._recording_pkgs:
1006 self._save_recording_pkgs(destdir)
1008 """ For image formats with two or multiple image files, it will be better to put them under a directory """
1009 if self.image_format in ("raw", "vmdk", "vdi", "nand", "mrstnand"):
1010 destdir = os.path.join(destdir, "%s-%s" % (self.name, self.image_format))
1011 msger.debug("creating destination dir: %s" % destdir)
1014 # Ensure all data is flushed to _outdir
1015 runner.quiet('sync')
1017 for f in os.listdir(self._outdir):
1018 shutil.move(os.path.join(self._outdir, f),
1019 os.path.join(destdir, f))
1020 self.outimage.append(os.path.join(destdir, f))
1021 self.do_genchecksum(os.path.join(destdir, f))
1023 def print_outimage_info(self):
1024 msg = "Your new image can be found here:\n"
1025 self.outimage.sort()
1026 for file in self.outimage:
1027 msg += ' %s\n' % os.path.abspath(file)
1031 def check_depend_tools(self):
1032 for tool in self._dep_checks:
1033 find_binary_path(tool)
1035 def package_output(self, image_format, destdir = ".", package="none"):
1036 if not package or package == "none":
1039 destdir = os.path.abspath(os.path.expanduser(destdir))
1040 (pkg, comp) = os.path.splitext(package)
1042 comp=comp.lstrip(".")
1046 dst = "%s/%s-%s.tar.%s" % (destdir, self.name, image_format, comp)
1048 dst = "%s/%s-%s.tar" % (destdir, self.name, image_format)
1049 msger.info("creating %s" % dst)
1050 tar = tarfile.open(dst, "w:" + comp)
1052 for file in self.outimage:
1053 msger.info("adding %s to %s" % (file, dst))
1054 tar.add(file, arcname=os.path.join("%s-%s" % (self.name, image_format), os.path.basename(file)))
1055 if os.path.isdir(file):
1056 shutil.rmtree(file, ignore_errors = True)
1063 '''All the file in outimage has been packaged into tar.* file'''
1064 self.outimage = [dst]
1066 def release_output(self, config, destdir, name, release):
1067 self.outimage = create_release(config, destdir, name, self.outimage, release)
1069 def save_kernel(self, destdir):
1070 if not os.path.exists(destdir):
1072 for kernel in glob.glob("%s/boot/vmlinuz-*" % self._instroot):
1073 kernelfilename = "%s/%s-%s" % (destdir, self.name, os.path.basename(kernel))
1074 shutil.copy(kernel, kernelfilename)
1075 self.outimage.append(kernelfilename)
1077 def compress_disk_image(self, compression_method):
1079 With this you can set the method that is used to compress the disk
1080 image after it is created.
1083 if compression_method not in ('bz2'):
1084 raise CreatorError("Given disk image compression method ('%s') is not valid." % (compression_method))
1086 self.__img_compression_method = compression_method
1088 def get_pkg_manager(self, recording_pkgs=None):
1089 return self.pkgmgr(creator = self, recording_pkgs = recording_pkgs)