summaryrefslogtreecommitdiffstats
path: root/scripts/lib/mic
diff options
context:
space:
mode:
authorTom Zanussi <tom.zanussi@linux.intel.com>2013-09-19 04:32:19 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-10-01 22:56:03 +0100
commit75c143a7aef46ecea07cf33edd2b1a0192e10149 (patch)
treefe0eb61a0dfda72e52b01385129f6282814581b6 /scripts/lib/mic
parent9fc88f96d40b17c90bac53b90045a87b2d2cff84 (diff)
downloadpoky-75c143a7aef46ecea07cf33edd2b1a0192e10149.tar.gz
wic: Add OpenEmbedded-specific implementation
Reuses the mic/livecd infrastructure but heavily subclasses and modifies it to adapt to the special needs of building images from existing OpenEmbedded build artifacts. In addition to the OE-specific mic objects and modifications to the underlying infrastructure, this adds a mechanism to allow OE kickstart files to be 'canned' and made available to users via the 'wic list images' command. Two initial OE kickstart files have been added as canned .wks files: directdisk, which implements the same thing as the images created by directdisk.bbclass, and mkefidisk, which can essentially be used as a replacement for mkefidisk.sh. Of course, since creation of these images are now driven by .wks files rather than being hard-coded into class files or scripts, they can be easily modified to generate different variations on those images. They also don't require root priveleges, since they don't use mount to create the images. They don't however write to media like mkefidisk.sh does, but rather create images that can be written onto media. (From OE-Core rev: f87acc5e59d3c2c39ff171b5557977dab4c8f4a6) Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com> Signed-off-by: Saul Wold <sgw@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/lib/mic')
-rw-r--r--scripts/lib/mic/conf.py54
-rw-r--r--scripts/lib/mic/creator.py3
-rw-r--r--scripts/lib/mic/imager/baseimager.py82
-rw-r--r--scripts/lib/mic/imager/direct.py472
-rw-r--r--scripts/lib/mic/kickstart/__init__.py6
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/__init__.py7
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/micpartition.py57
-rw-r--r--scripts/lib/mic/kickstart/custom_commands/partition.py389
-rw-r--r--scripts/lib/mic/plugin.py6
-rw-r--r--scripts/lib/mic/plugins/imager/direct_plugin.py92
-rw-r--r--scripts/lib/mic/utils/fs_related.py30
-rw-r--r--scripts/lib/mic/utils/misc.py4
-rw-r--r--scripts/lib/mic/utils/oe/__init__.py22
-rw-r--r--scripts/lib/mic/utils/oe/misc.py108
-rw-r--r--scripts/lib/mic/utils/partitionedfs.py94
15 files changed, 1201 insertions, 225 deletions
diff --git a/scripts/lib/mic/conf.py b/scripts/lib/mic/conf.py
index e37334cc7a..58fad51f89 100644
--- a/scripts/lib/mic/conf.py
+++ b/scripts/lib/mic/conf.py
@@ -23,29 +23,24 @@ from mic import kickstart
23from mic.utils import misc, runner, proxy, errors 23from mic.utils import misc, runner, proxy, errors
24 24
25 25
26DEFAULT_GSITECONF = '/etc/mic/mic.conf'
27
28
29def get_siteconf(): 26def get_siteconf():
30 mic_path = os.path.dirname(__file__) 27 mic_path = os.path.dirname(__file__)
28 eos = mic_path.find('scripts') + len('scripts')
29 scripts_path = mic_path[:eos]
31 30
32 m = re.match(r"(?P<prefix>.*)\/lib(64)?\/.*", mic_path) 31 return scripts_path + "/lib/image/config/wic.conf"
33 if m and m.group('prefix') != "/usr":
34 return os.path.join(m.group('prefix'), "etc/mic/mic.conf")
35
36 return DEFAULT_GSITECONF
37 32
38class ConfigMgr(object): 33class ConfigMgr(object):
39 prefer_backends = ["zypp", "yum"] 34 prefer_backends = ["zypp", "yum"]
40 35
41 DEFAULTS = {'common': { 36 DEFAULTS = {'common': {
42 "distro_name": "Default Distribution", 37 "distro_name": "Default Distribution",
43 "plugin_dir": "/usr/lib/mic/plugins", # TODO use prefix also? 38 "plugin_dir": "/usr/lib/wic/plugins", # TODO use prefix also?
44 }, 39 },
45 'create': { 40 'create': {
46 "tmpdir": '/var/tmp/mic', 41 "tmpdir": '/var/tmp/wic',
47 "cachedir": '/var/tmp/mic/cache', 42 "cachedir": '/var/tmp/wic/cache',
48 "outdir": './mic-output', 43 "outdir": './wic-output',
49 44
50 "arch": None, # None means auto-detect 45 "arch": None, # None means auto-detect
51 "pkgmgr": "auto", 46 "pkgmgr": "auto",
@@ -75,7 +70,7 @@ class ConfigMgr(object):
75 "shell": False, 70 "shell": False,
76 }, 71 },
77 'bootstrap': { 72 'bootstrap': {
78 "rootdir": '/var/tmp/mic-bootstrap', 73 "rootdir": '/var/tmp/wic-bootstrap',
79 "packages": [], 74 "packages": [],
80 }, 75 },
81 } 76 }
@@ -191,39 +186,6 @@ class ConfigMgr(object):
191 self.create['name_prefix'], 186 self.create['name_prefix'],
192 self.create['name_suffix']) 187 self.create['name_suffix'])
193 188
194 msger.info("Retrieving repo metadata:")
195 ksrepos = misc.get_repostrs_from_ks(ks)
196 if not ksrepos:
197 raise errors.KsError('no valid repos found in ks file')
198
199 for repo in ksrepos:
200 if 'baseurl' in repo and repo['baseurl'].startswith("file:"):
201 repourl = repo['baseurl'].replace('file:', '')
202 repourl = "/%s" % repourl.lstrip('/')
203 self.create['localrepos'].append(repourl)
204
205 self.create['repomd'] = misc.get_metadata_from_repos(
206 ksrepos,
207 self.create['cachedir'])
208 msger.raw(" DONE")
209
210 target_archlist, archlist = misc.get_arch(self.create['repomd'])
211 if self.create['arch']:
212 if self.create['arch'] not in archlist:
213 raise errors.ConfigError("Invalid arch %s for repository. "
214 "Valid arches: %s" \
215 % (self.create['arch'], ', '.join(archlist)))
216 else:
217 if len(target_archlist) == 1:
218 self.create['arch'] = str(target_archlist[0])
219 msger.info("\nUse detected arch %s." % target_archlist[0])
220 else:
221 raise errors.ConfigError("Please specify a valid arch, "
222 "the choice can be: %s" \
223 % ', '.join(archlist))
224
225 kickstart.resolve_groups(self.create, self.create['repomd'])
226
227 # check selinux, it will block arm and btrfs image creation 189 # check selinux, it will block arm and btrfs image creation
228 misc.selinux_check(self.create['arch'], 190 misc.selinux_check(self.create['arch'],
229 [p.fstype for p in ks.handler.partition.partitions]) 191 [p.fstype for p in ks.handler.partition.partitions])
diff --git a/scripts/lib/mic/creator.py b/scripts/lib/mic/creator.py
index af5fb82a1e..f3d0de19fc 100644
--- a/scripts/lib/mic/creator.py
+++ b/scripts/lib/mic/creator.py
@@ -293,9 +293,6 @@ class Creator(cmdln.Cmdln):
293 if len(argv) == 1: 293 if len(argv) == 1:
294 return ['help', argv[0]] 294 return ['help', argv[0]]
295 295
296 if os.geteuid() != 0:
297 raise msger.error("Root permission is required, abort")
298
299 return argv 296 return argv
300 297
301 def do_auto(self, subcmd, opts, *args): 298 def do_auto(self, subcmd, opts, *args):
diff --git a/scripts/lib/mic/imager/baseimager.py b/scripts/lib/mic/imager/baseimager.py
index 6efc6c1294..4d6be29a0e 100644
--- a/scripts/lib/mic/imager/baseimager.py
+++ b/scripts/lib/mic/imager/baseimager.py
@@ -1,4 +1,3 @@
1
2#!/usr/bin/python -tt 1#!/usr/bin/python -tt
3# 2#
4# Copyright (c) 2007 Red Hat Inc. 3# Copyright (c) 2007 Red Hat Inc.
@@ -69,9 +68,10 @@ class BaseImageCreator(object):
69 68
70 self.ks = None 69 self.ks = None
71 self.name = "target" 70 self.name = "target"
72 self.tmpdir = "/var/tmp/mic" 71 self.tmpdir = "/var/tmp/wic"
73 self.cachedir = "/var/tmp/mic/cache" 72 self.cachedir = "/var/tmp/wic/cache"
74 self.workdir = "/var/tmp/mic/build" 73 self.workdir = "/var/tmp/wic/build"
74
75 self.destdir = "." 75 self.destdir = "."
76 self.installerfw_prefix = "INSTALLERFW_" 76 self.installerfw_prefix = "INSTALLERFW_"
77 self.target_arch = "noarch" 77 self.target_arch = "noarch"
@@ -463,7 +463,7 @@ class BaseImageCreator(object):
463 env[self.installerfw_prefix + "DISTRO_NAME"] = self.distro_name 463 env[self.installerfw_prefix + "DISTRO_NAME"] = self.distro_name
464 464
465 # Name of the image creation tool 465 # Name of the image creation tool
466 env[self.installerfw_prefix + "INSTALLER_NAME"] = "mic" 466 env[self.installerfw_prefix + "INSTALLER_NAME"] = "wic"
467 467
468 # The real current location of the mounted file-systems 468 # The real current location of the mounted file-systems
469 if in_chroot: 469 if in_chroot:
@@ -668,7 +668,7 @@ class BaseImageCreator(object):
668 if cachedir: 668 if cachedir:
669 self.cachedir = cachedir 669 self.cachedir = cachedir
670 else: 670 else:
671 self.cachedir = self.__builddir + "/mic-cache" 671 self.cachedir = self.__builddir + "/wic-cache"
672 fs.makedirs(self.cachedir) 672 fs.makedirs(self.cachedir)
673 return self.cachedir 673 return self.cachedir
674 674
@@ -751,52 +751,8 @@ class BaseImageCreator(object):
751 self.__setup_tmpdir() 751 self.__setup_tmpdir()
752 self.__ensure_builddir() 752 self.__ensure_builddir()
753 753
754 # prevent popup dialog in Ubuntu(s)
755 misc.hide_loopdev_presentation()
756
757 fs.makedirs(self._instroot)
758 fs.makedirs(self._outdir)
759
760 self._mount_instroot(base_on) 754 self._mount_instroot(base_on)
761 755
762 for d in ("/dev/pts",
763 "/etc",
764 "/boot",
765 "/var/log",
766 "/sys",
767 "/proc",
768 "/usr/bin"):
769 fs.makedirs(self._instroot + d)
770
771 if self.target_arch and self.target_arch.startswith("arm"):
772 self.qemu_emulator = misc.setup_qemu_emulator(self._instroot,
773 self.target_arch)
774
775
776 self.get_cachedir(cachedir)
777
778 # bind mount system directories into _instroot
779 for (f, dest) in [("/sys", None),
780 ("/proc", None),
781 ("/proc/sys/fs/binfmt_misc", None),
782 ("/dev/pts", None)]:
783 self.__bindmounts.append(
784 fs.BindChrootMount(
785 f, self._instroot, dest))
786
787 self._do_bindmounts()
788
789 self.__create_minimal_dev()
790
791 if os.path.exists(self._instroot + "/etc/mtab"):
792 os.unlink(self._instroot + "/etc/mtab")
793 os.symlink("../proc/mounts", self._instroot + "/etc/mtab")
794
795 self.__write_fstab()
796
797 # get size of available space in 'instroot' fs
798 self._root_fs_avail = misc.get_filesystem_avail(self._instroot)
799
800 def unmount(self): 756 def unmount(self):
801 """Unmounts the target filesystem. 757 """Unmounts the target filesystem.
802 758
@@ -805,34 +761,8 @@ class BaseImageCreator(object):
805 from the install root. 761 from the install root.
806 762
807 """ 763 """
808 try:
809 mtab = self._instroot + "/etc/mtab"
810 if not os.path.islink(mtab):
811 os.unlink(self._instroot + "/etc/mtab")
812
813 if self.qemu_emulator:
814 os.unlink(self._instroot + self.qemu_emulator)
815 except OSError:
816 pass
817
818 self._undo_bindmounts()
819
820 """ Clean up yum garbage """
821 try:
822 instroot_pdir = os.path.dirname(self._instroot + self._instroot)
823 if os.path.exists(instroot_pdir):
824 shutil.rmtree(instroot_pdir, ignore_errors = True)
825 yumlibdir = self._instroot + "/var/lib/yum"
826 if os.path.exists(yumlibdir):
827 shutil.rmtree(yumlibdir, ignore_errors = True)
828 except OSError:
829 pass
830
831 self._unmount_instroot() 764 self._unmount_instroot()
832 765
833 # reset settings of popup dialog in Ubuntu(s)
834 misc.unhide_loopdev_presentation()
835
836 766
837 def cleanup(self): 767 def cleanup(self):
838 """Unmounts the target filesystem and deletes temporary files. 768 """Unmounts the target filesystem and deletes temporary files.
diff --git a/scripts/lib/mic/imager/direct.py b/scripts/lib/mic/imager/direct.py
new file mode 100644
index 0000000000..d24bc684fe
--- /dev/null
+++ b/scripts/lib/mic/imager/direct.py
@@ -0,0 +1,472 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (c) 2013, Intel Corporation.
5# All rights reserved.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# DESCRIPTION
21# This implements the 'direct' image creator class for 'wic', based
22# loosely on the raw image creator from 'mic'
23#
24# AUTHORS
25# Tom Zanussi <tom.zanussi (at] linux.intel.com>
26#
27
28import os
29import stat
30import shutil
31
32from mic import kickstart, msger
33from mic.utils import fs_related, runner, misc
34from mic.utils.partitionedfs import PartitionedMount
35from mic.utils.errors import CreatorError, MountError
36from mic.imager.baseimager import BaseImageCreator
37from mic.utils.oe.misc import *
38
39class DirectImageCreator(BaseImageCreator):
40 """
41 Installs a system into a file containing a partitioned disk image.
42
43 DirectImageCreator is an advanced ImageCreator subclass; an image
44 file is formatted with a partition table, each partition created
45 from a rootfs or other OpenEmbedded build artifact and dd'ed into
46 the virtual disk. The disk image can subsequently be dd'ed onto
47 media and used on actual hardware.
48 """
49
50 def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir,
51 kernel_dir, native_sysroot, hdddir, staging_data_dir,
52 creatoropts=None, pkgmgr=None, compress_image=None,
53 generate_bmap=None, fstab_entry="uuid"):
54 """
55 Initialize a DirectImageCreator instance.
56
57 This method takes the same arguments as ImageCreator.__init__()
58 """
59 BaseImageCreator.__init__(self, creatoropts, pkgmgr)
60
61 self.__instimage = None
62 self.__imgdir = None
63 self.__disks = {}
64 self.__disk_format = "direct"
65 self._disk_names = []
66 self._ptable_format = self.ks.handler.bootloader.ptable
67 self.use_uuid = fstab_entry == "uuid"
68 self.compress_image = compress_image
69 self.bmap_needed = generate_bmap
70
71 self.oe_builddir = oe_builddir
72 if image_output_dir:
73 self.tmpdir = image_output_dir
74 self.cachedir = "%s/cache" % image_output_dir
75 self.rootfs_dir = rootfs_dir
76 self.bootimg_dir = bootimg_dir
77 self.kernel_dir = kernel_dir
78 self.native_sysroot = native_sysroot
79 self.hdddir = hdddir
80 self.staging_data_dir = staging_data_dir
81 self.boot_type = ""
82
83 def __write_fstab(self):
84 """overriden to generate fstab (temporarily) in rootfs. This
85 is called from mount_instroot, make sure it doesn't get called
86 from BaseImage.mount()"""
87
88 image_rootfs = self.rootfs_dir
89
90 parts = self._get_parts()
91
92 fstab = image_rootfs + "/etc/fstab"
93
94 self._save_fstab(fstab)
95 fstab_lines = self._get_fstab(fstab, parts)
96 self._update_fstab(fstab_lines, parts)
97 self._write_fstab(fstab, fstab_lines)
98
99 return fstab
100
101 def _update_fstab(self, fstab_lines, parts):
102 """Assume partition order same as in wks"""
103 for num, p in enumerate(parts, 1):
104 if p.mountpoint == "/" or p.mountpoint == "/boot":
105 continue
106 if self._ptable_format == 'msdos' and num > 3:
107 device_name = "/dev/" + p.disk + str(num + 1)
108 else:
109 device_name = "/dev/" + p.disk + str(num)
110 fstab_entry = device_name + "\t" + p.mountpoint + "\t" + p.fstype + "\tdefaults\t0\t0\n"
111 fstab_lines.append(fstab_entry)
112
113 def _write_fstab(self, fstab, fstab_lines):
114 fstab = open(fstab, "w")
115 for line in fstab_lines:
116 fstab.write(line)
117 fstab.close()
118
119 def _save_fstab(self, fstab):
120 """Save the current fstab in rootfs"""
121 shutil.copyfile(fstab, fstab + ".orig")
122
123 def _restore_fstab(self, fstab):
124 """Restore the saved fstab in rootfs"""
125 shutil.move(fstab + ".orig", fstab)
126
127 def _get_fstab(self, fstab, parts):
128 """Return the desired contents of /etc/fstab."""
129 f = open(fstab, "r")
130 fstab_contents = f.readlines()
131 f.close()
132
133 return fstab_contents
134
135 def _get_parts(self):
136 if not self.ks:
137 raise CreatorError("Failed to get partition info, "
138 "please check your kickstart setting.")
139
140 # Set a default partition if no partition is given out
141 if not self.ks.handler.partition.partitions:
142 partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
143 args = partstr.split()
144 pd = self.ks.handler.partition.parse(args[1:])
145 if pd not in self.ks.handler.partition.partitions:
146 self.ks.handler.partition.partitions.append(pd)
147
148 # partitions list from kickstart file
149 return kickstart.get_partitions(self.ks)
150
151 def get_disk_names(self):
152 """ Returns a list of physical target disk names (e.g., 'sdb') which
153 will be created. """
154
155 if self._disk_names:
156 return self._disk_names
157
158 #get partition info from ks handler
159 parts = self._get_parts()
160
161 for i in range(len(parts)):
162 if parts[i].disk:
163 disk_name = parts[i].disk
164 else:
165 raise CreatorError("Failed to create disks, no --ondisk "
166 "specified in partition line of ks file")
167
168 if parts[i].mountpoint and not parts[i].fstype:
169 raise CreatorError("Failed to create disks, no --fstype "
170 "specified for partition with mountpoint "
171 "'%s' in the ks file")
172
173 self._disk_names.append(disk_name)
174
175 return self._disk_names
176
177 def _full_name(self, name, extention):
178 """ Construct full file name for a file we generate. """
179 return "%s-%s.%s" % (self.name, name, extention)
180
181 def _full_path(self, path, name, extention):
182 """ Construct full file path to a file we generate. """
183 return os.path.join(path, self._full_name(name, extention))
184
185 def get_boot_type(self):
186 """ Determine the boot type from fstype and mountpoint. """
187 parts = self._get_parts()
188
189 boot_type = ""
190
191 for p in parts:
192 if p.mountpoint == "/boot":
193 if p.fstype == "msdos":
194 boot_type = "pcbios"
195 else:
196 boot_type = p.fstype
197 return boot_type
198
199 #
200 # Actual implemention
201 #
202 def _mount_instroot(self, base_on = None):
203 """
204 For 'wic', we already have our build artifacts and don't want
205 to loop mount anything to install into, we just create
206 filesystems from the artifacts directly and combine them into
207 a partitioned image.
208
209 We still want to reuse as much of the basic mic machinery
210 though; despite the fact that we don't actually do loop or any
211 other kind of mounting we still want to do many of the same
212 things to prepare images, so we basically just adapt to the
213 basic framework and reinterpret what 'mounting' means in our
214 context.
215
216 _instroot would normally be something like
217 /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for
218 installing packages, etc. We don't currently need to do that,
219 so we simplify life by just using /var/tmp/wic/build as our
220 workdir.
221 """
222 parts = self._get_parts()
223
224 self.__instimage = PartitionedMount(self._instroot)
225
226 fstab = self.__write_fstab()
227
228 self.boot_type = self.get_boot_type()
229
230 if not self.bootimg_dir:
231 if self.boot_type == "pcbios":
232 self.bootimg_dir = self.staging_data_dir
233 elif self.boot_type == "efi":
234 self.bootimg_dir = self.hdddir
235
236 if self.boot_type == "pcbios":
237 self._create_syslinux_config()
238 elif self.boot_type == "efi":
239 self._create_grubefi_config()
240 else:
241 raise CreatorError("Failed to detect boot type (no /boot partition?), "
242 "please check your kickstart setting.")
243
244 for p in parts:
245 if p.fstype == "efi":
246 p.fstype = "msdos"
247 # need to create the filesystems in order to get their
248 # sizes before we can add them and do the layout.
249 # PartitionedMount.mount() actually calls __format_disks()
250 # to create the disk images and carve out the partitions,
251 # then self.install() calls PartitionedMount.install()
252 # which calls __install_partitition() for each partition
253 # to dd the fs into the partitions. It would be nice to
254 # be able to use e.g. ExtDiskMount etc to create the
255 # filesystems, since that's where existing e.g. mkfs code
256 # is, but those are only created after __format_disks()
257 # which needs the partition sizes so needs them created
258 # before its called. Well, the existing setup is geared
259 # to installing packages into mounted filesystems - maybe
260 # when/if we need to actually do package selection we
261 # should modify things to use those objects, but for now
262 # we can avoid that.
263 p.prepare(self.workdir, self.oe_builddir, self.boot_type,
264 self.rootfs_dir, self.bootimg_dir, self.kernel_dir,
265 self.native_sysroot)
266
267 self.__instimage.add_partition(int(p.size),
268 p.disk,
269 p.mountpoint,
270 p.source_file,
271 p.fstype,
272 p.label,
273 fsopts = p.fsopts,
274 boot = p.active,
275 align = p.align,
276 part_type = p.part_type)
277 self._restore_fstab(fstab)
278 self.__instimage.layout_partitions(self._ptable_format)
279
280 self.__imgdir = self.workdir
281 for disk_name, disk in self.__instimage.disks.items():
282 full_path = self._full_path(self.__imgdir, disk_name, "direct")
283 msger.debug("Adding disk %s as %s with size %s bytes" \
284 % (disk_name, full_path, disk['min_size']))
285 disk_obj = fs_related.DiskImage(full_path, disk['min_size'])
286 self.__disks[disk_name] = disk_obj
287 self.__instimage.add_disk(disk_name, disk_obj)
288
289 self.__instimage.mount()
290
291 def install(self, repo_urls=None):
292 """
293 Install fs images into partitions
294 """
295 for disk_name, disk in self.__instimage.disks.items():
296 full_path = self._full_path(self.__imgdir, disk_name, "direct")
297 msger.debug("Installing disk %s as %s with size %s bytes" \
298 % (disk_name, full_path, disk['min_size']))
299 self.__instimage.install(full_path)
300
301 def configure(self, repodata = None):
302 """
303 Configure the system image according to kickstart.
304
305 For now, it just prepares the image to be bootable by e.g.
306 creating and installing a bootloader configuration.
307 """
308 if self.boot_type == "pcbios":
309 self._install_syslinux()
310
311 def print_outimage_info(self):
312 """
313 Print the image(s) and artifacts used, for the user.
314 """
315 msg = "The new image(s) can be found here:\n"
316
317 for disk_name, disk in self.__instimage.disks.items():
318 full_path = self._full_path(self.__imgdir, disk_name, "direct")
319 msg += ' %s\n\n' % full_path
320
321 msg += 'The following build artifacts were used to create the image(s):\n'
322 msg += ' ROOTFS_DIR: %s\n' % self.rootfs_dir
323 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
324 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
325 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
326
327 msger.info(msg)
328
329 def _get_boot_config(self):
330 """
331 Return the rootdev/root_part_uuid (if specified by
332 --part-type)
333
334 Assume partition order same as in wks
335 """
336 rootdev = None
337 root_part_uuid = None
338 parts = self._get_parts()
339 for num, p in enumerate(parts, 1):
340 if p.mountpoint == "/":
341 if self._ptable_format == 'msdos' and num > 3:
342 rootdev = "/dev/%s%-d" % (p.disk, num + 1)
343 else:
344 rootdev = "/dev/%s%-d" % (p.disk, num)
345 root_part_uuid = p.part_type
346
347 return (rootdev, root_part_uuid)
348
349 def _create_syslinux_config(self):
350 hdddir = "%s/hdd/boot" % self.workdir
351 rm_cmd = "rm -rf " + self.workdir
352 exec_cmd(rm_cmd)
353
354 install_cmd = "install -d %s" % hdddir
355 tmp = exec_cmd(install_cmd)
356
357 splash = os.path.join(self.workdir, "/hdd/boot/splash.jpg")
358 if os.path.exists(splash):
359 splashline = "menu background splash.jpg"
360 else:
361 splashline = ""
362
363 (rootdev, root_part_uuid) = self._get_boot_config()
364 options = self.ks.handler.bootloader.appendLine
365
366 syslinux_conf = ""
367 syslinux_conf += "PROMPT 0\n"
368 timeout = kickstart.get_timeout(self.ks)
369 if not timeout:
370 timeout = 0
371 syslinux_conf += "TIMEOUT " + str(timeout) + "\n"
372 syslinux_conf += "\n"
373 syslinux_conf += "ALLOWOPTIONS 1\n"
374 syslinux_conf += "SERIAL 0 115200\n"
375 syslinux_conf += "\n"
376 if splashline:
377 syslinux_conf += "%s\n" % splashline
378 syslinux_conf += "DEFAULT boot\n"
379 syslinux_conf += "LABEL boot\n"
380
381 kernel = "/vmlinuz"
382 syslinux_conf += "KERNEL " + kernel + "\n"
383
384 if self._ptable_format == 'msdos':
385 rootstr = rootdev
386 else:
387 if not root_part_uuid:
388 raise MountError("Cannot find the root GPT partition UUID")
389 rootstr = "PARTUUID=%s" % root_part_uuid
390
391 syslinux_conf += "APPEND label=boot root=%s %s\n" % (rootstr, options)
392
393 msger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg" \
394 % self.workdir)
395 cfg = open("%s/hdd/boot/syslinux.cfg" % self.workdir, "w")
396 cfg.write(syslinux_conf)
397 cfg.close()
398
399 def _create_grubefi_config(self):
400 hdddir = "%s/hdd/boot" % self.workdir
401 rm_cmd = "rm -rf %s" % self.workdir
402 exec_cmd(rm_cmd)
403
404 install_cmd = "install -d %s/EFI/BOOT" % hdddir
405 tmp = exec_cmd(install_cmd)
406
407 splash = os.path.join(self.workdir, "/EFI/boot/splash.jpg")
408 if os.path.exists(splash):
409 splashline = "menu background splash.jpg"
410 else:
411 splashline = ""
412
413 (rootdev, root_part_uuid) = self._get_boot_config()
414 options = self.ks.handler.bootloader.appendLine
415
416 grubefi_conf = ""
417 grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n"
418 grubefi_conf += "default=boot\n"
419 timeout = kickstart.get_timeout(self.ks)
420 if not timeout:
421 timeout = 0
422 grubefi_conf += "timeout=%s\n" % timeout
423 grubefi_conf += "menuentry 'boot'{\n"
424
425 kernel = "/vmlinuz"
426
427 if self._ptable_format == 'msdos':
428 rootstr = rootdev
429 else:
430 if not root_part_uuid:
431 raise MountError("Cannot find the root GPT partition UUID")
432 rootstr = "PARTUUID=%s" % root_part_uuid
433
434 grubefi_conf += "linux %s root=%s rootwait %s\n" \
435 % (kernel, rootstr, options)
436 grubefi_conf += "}\n"
437 if splashline:
438 syslinux_conf += "%s\n" % splashline
439
440 msger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg" \
441 % self.workdir)
442 cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % self.workdir, "w")
443 cfg.write(grubefi_conf)
444 cfg.close()
445
446 def _install_syslinux(self):
447 mbrfile = "%s/syslinux/" % self.bootimg_dir
448 if self._ptable_format == 'gpt':
449 mbrfile += "gptmbr.bin"
450 else:
451 mbrfile += "mbr.bin"
452
453 if not os.path.exists(mbrfile):
454 msger.error("Couldn't find %s. If using the -e option, do you have the right MACHINE set in local.conf? If not, is the bootimg_dir path correct?" % mbrfile)
455
456 for disk_name, disk in self.__instimage.disks.items():
457 full_path = self._full_path(self.__imgdir, disk_name, "direct")
458 msger.debug("Installing MBR on disk %s as %s with size %s bytes" \
459 % (disk_name, full_path, disk['min_size']))
460
461 rc = runner.show(['dd', 'if=%s' % mbrfile,
462 'of=%s' % full_path, 'conv=notrunc'])
463 if rc != 0:
464 raise MountError("Unable to set MBR to %s" % full_path)
465
466 def _unmount_instroot(self):
467 if not self.__instimage is None:
468 try:
469 self.__instimage.cleanup()
470 except MountError, err:
471 msger.warning("%s" % err)
472
diff --git a/scripts/lib/mic/kickstart/__init__.py b/scripts/lib/mic/kickstart/__init__.py
index f9a53343d1..7e645caa11 100644
--- a/scripts/lib/mic/kickstart/__init__.py
+++ b/scripts/lib/mic/kickstart/__init__.py
@@ -99,11 +99,11 @@ def read_kickstart(path):
99 commandMap[using_version]["desktop"] = desktop.Mic_Desktop 99 commandMap[using_version]["desktop"] = desktop.Mic_Desktop
100 commandMap[using_version]["repo"] = micrepo.Mic_Repo 100 commandMap[using_version]["repo"] = micrepo.Mic_Repo
101 commandMap[using_version]["bootloader"] = micboot.Mic_Bootloader 101 commandMap[using_version]["bootloader"] = micboot.Mic_Bootloader
102 commandMap[using_version]["part"] = partition.Mic_Partition 102 commandMap[using_version]["part"] = partition.Wic_Partition
103 commandMap[using_version]["partition"] = partition.Mic_Partition 103 commandMap[using_version]["partition"] = partition.Wic_Partition
104 commandMap[using_version]["installerfw"] = installerfw.Mic_installerfw 104 commandMap[using_version]["installerfw"] = installerfw.Mic_installerfw
105 dataMap[using_version]["RepoData"] = micrepo.Mic_RepoData 105 dataMap[using_version]["RepoData"] = micrepo.Mic_RepoData
106 dataMap[using_version]["PartData"] = partition.Mic_PartData 106 dataMap[using_version]["PartData"] = partition.Wic_PartData
107 superclass = ksversion.returnClassForVersion(version=using_version) 107 superclass = ksversion.returnClassForVersion(version=using_version)
108 108
109 class KSHandlers(superclass): 109 class KSHandlers(superclass):
diff --git a/scripts/lib/mic/kickstart/custom_commands/__init__.py b/scripts/lib/mic/kickstart/custom_commands/__init__.py
index 5f4c440369..6aed0ff6fa 100644
--- a/scripts/lib/mic/kickstart/custom_commands/__init__.py
+++ b/scripts/lib/mic/kickstart/custom_commands/__init__.py
@@ -1,12 +1,17 @@
1from desktop import Mic_Desktop 1from desktop import Mic_Desktop
2from micrepo import Mic_Repo, Mic_RepoData 2from micrepo import Mic_Repo, Mic_RepoData
3from partition import Mic_Partition 3from micpartition import Mic_Partition
4from micpartition import Mic_PartData
4from installerfw import Mic_installerfw 5from installerfw import Mic_installerfw
6from partition import Wic_Partition
5 7
6__all__ = ( 8__all__ = (
7 "Mic_Desktop", 9 "Mic_Desktop",
8 "Mic_Repo", 10 "Mic_Repo",
9 "Mic_RepoData", 11 "Mic_RepoData",
10 "Mic_Partition", 12 "Mic_Partition",
13 "Mic_PartData",
11 "Mic_installerfw", 14 "Mic_installerfw",
15 "Wic_Partition",
16 "Wic_PartData",
12) 17)
diff --git a/scripts/lib/mic/kickstart/custom_commands/micpartition.py b/scripts/lib/mic/kickstart/custom_commands/micpartition.py
new file mode 100644
index 0000000000..59a87fb486
--- /dev/null
+++ b/scripts/lib/mic/kickstart/custom_commands/micpartition.py
@@ -0,0 +1,57 @@
1#!/usr/bin/python -tt
2#
3# Marko Saukko <marko.saukko@cybercom.com>
4#
5# Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
6#
7# This copyrighted material is made available to anyone wishing to use, modify,
8# copy, or redistribute it subject to the terms and conditions of the GNU
9# General Public License v.2. This program is distributed in the hope that it
10# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
11# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12# See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along with
15# this program; if not, write to the Free Software Foundation, Inc., 51
16# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18from pykickstart.commands.partition import *
19
20class Mic_PartData(FC4_PartData):
21 removedKeywords = FC4_PartData.removedKeywords
22 removedAttrs = FC4_PartData.removedAttrs
23
24 def __init__(self, *args, **kwargs):
25 FC4_PartData.__init__(self, *args, **kwargs)
26 self.deleteRemovedAttrs()
27 self.align = kwargs.get("align", None)
28 self.extopts = kwargs.get("extopts", None)
29 self.part_type = kwargs.get("part_type", None)
30
31 def _getArgsAsStr(self):
32 retval = FC4_PartData._getArgsAsStr(self)
33
34 if self.align:
35 retval += " --align"
36 if self.extopts:
37 retval += " --extoptions=%s" % self.extopts
38 if self.part_type:
39 retval += " --part-type=%s" % self.part_type
40
41 return retval
42
43class Mic_Partition(FC4_Partition):
44 removedKeywords = FC4_Partition.removedKeywords
45 removedAttrs = FC4_Partition.removedAttrs
46
47 def _getParser(self):
48 op = FC4_Partition._getParser(self)
49 # The alignment value is given in kBytes. e.g., value 8 means that
50 # the partition is aligned to start from 8096 byte boundary.
51 op.add_option("--align", type="int", action="store", dest="align",
52 default=None)
53 op.add_option("--extoptions", type="string", action="store", dest="extopts",
54 default=None)
55 op.add_option("--part-type", type="string", action="store", dest="part_type",
56 default=None)
57 return op
diff --git a/scripts/lib/mic/kickstart/custom_commands/partition.py b/scripts/lib/mic/kickstart/custom_commands/partition.py
index 59a87fb486..302cace234 100644
--- a/scripts/lib/mic/kickstart/custom_commands/partition.py
+++ b/scripts/lib/mic/kickstart/custom_commands/partition.py
@@ -1,57 +1,370 @@
1#!/usr/bin/python -tt 1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
2# 3#
3# Marko Saukko <marko.saukko@cybercom.com> 4# Copyright (c) 2013, Intel Corporation.
5# All rights reserved.
4# 6#
5# Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). 7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
6# 10#
7# This copyrighted material is made available to anyone wishing to use, modify, 11# This program is distributed in the hope that it will be useful,
8# copy, or redistribute it subject to the terms and conditions of the GNU 12# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# General Public License v.2. This program is distributed in the hope that it 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the 14# GNU General Public License for more details.
11# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15#
12# See the GNU General Public License for more details. 16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# DESCRIPTION
21# This module provides the OpenEmbedded partition object definitions.
22#
23# AUTHORS
24# Tom Zanussi <tom.zanussi (at] linux.intel.com>
13# 25#
14# You should have received a copy of the GNU General Public License along with 26
15# this program; if not, write to the Free Software Foundation, Inc., 51 27import shutil
16# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 28
18from pykickstart.commands.partition import * 29from pykickstart.commands.partition import *
30from mic.utils.oe.misc import *
31
32from mic.kickstart.custom_commands import *
19 33
20class Mic_PartData(FC4_PartData): 34BOOTDD_EXTRA_SPACE = 16384
21 removedKeywords = FC4_PartData.removedKeywords 35
22 removedAttrs = FC4_PartData.removedAttrs 36class Wic_PartData(Mic_PartData):
37 removedKeywords = Mic_PartData.removedKeywords
38 removedAttrs = Mic_PartData.removedAttrs
23 39
24 def __init__(self, *args, **kwargs): 40 def __init__(self, *args, **kwargs):
25 FC4_PartData.__init__(self, *args, **kwargs) 41 Mic_PartData.__init__(self, *args, **kwargs)
26 self.deleteRemovedAttrs() 42 self.deleteRemovedAttrs()
27 self.align = kwargs.get("align", None) 43 self.source = kwargs.get("source", None)
28 self.extopts = kwargs.get("extopts", None) 44 self.source_file = ""
29 self.part_type = kwargs.get("part_type", None) 45 self.size = 0
30 46
31 def _getArgsAsStr(self): 47 def _getArgsAsStr(self):
32 retval = FC4_PartData._getArgsAsStr(self) 48 retval = Mic_PartData._getArgsAsStr(self)
33 49
34 if self.align: 50 if self.source:
35 retval += " --align" 51 retval += " --source=%s" % self.source
36 if self.extopts:
37 retval += " --extoptions=%s" % self.extopts
38 if self.part_type:
39 retval += " --part-type=%s" % self.part_type
40 52
41 return retval 53 return retval
42 54
43class Mic_Partition(FC4_Partition): 55 def prepare(self, cr_workdir, oe_builddir, boot_type, rootfs_dir,
44 removedKeywords = FC4_Partition.removedKeywords 56 bootimg_dir, kernel_dir, native_sysroot):
45 removedAttrs = FC4_Partition.removedAttrs 57 """
58 Prepare content for individual partitions, depending on
59 partition command parameters.
60 """
61 if not self.source:
62 if self.fstype and self.fstype == "swap":
63 self.prepare_swap_partition(cr_workdir, oe_builddir,
64 native_sysroot)
65 elif self.fstype:
66 self.prepare_empty_partition(cr_workdir, oe_builddir,
67 native_sysroot)
68 return
69
70 if self.source == "bootimg" and boot_type == "pcbios":
71 self.prepare_bootimg_pcbios(cr_workdir, oe_builddir, bootimg_dir,
72 kernel_dir, native_sysroot)
73 elif self.source == "bootimg" and boot_type == "efi":
74 self.prepare_bootimg_efi(cr_workdir, oe_builddir, bootimg_dir,
75 kernel_dir, native_sysroot)
76 elif self.source.startswith("rootfs"):
77 self.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir,
78 native_sysroot)
79
80 def prepare_bootimg_pcbios(self, cr_workdir, oe_builddir, bootimg_dir,
81 kernel_dir, native_sysroot):
82 """
83 Prepare content for a legacy bios boot partition.
84 """
85 staging_kernel_dir = kernel_dir
86 staging_data_dir = bootimg_dir
87
88 hdddir = "%s/hdd/boot" % cr_workdir
89
90 install_cmd = "install -m 0644 %s/bzImage %s/vmlinuz" \
91 % (staging_kernel_dir, hdddir)
92 tmp = exec_cmd(install_cmd)
93
94 install_cmd = "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" \
95 % (staging_data_dir, hdddir)
96 tmp = exec_cmd(install_cmd)
97
98 du_cmd = "du -bks %s" % hdddir
99 rc, out = exec_cmd(du_cmd)
100 blocks = int(out.split()[0])
101
102 blocks += BOOTDD_EXTRA_SPACE
103
104 # Ensure total sectors is an integral number of sectors per
105 # track or mcopy will complain. Sectors are 512 bytes, and we
106 # generate images with 32 sectors per track. This calculation is
107 # done in blocks, thus the mod by 16 instead of 32.
108 blocks += (16 - (blocks % 16))
109
110 # dosfs image, created by mkdosfs
111 bootimg = "%s/boot.img" % cr_workdir
112
113 dosfs_cmd = "mkdosfs -n boot -S 512 -C %s %d" % (bootimg, blocks)
114 exec_native_cmd(dosfs_cmd, native_sysroot)
115
116 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
117 exec_native_cmd(mcopy_cmd, native_sysroot)
118
119 syslinux_cmd = "syslinux %s" % bootimg
120 exec_native_cmd(syslinux_cmd, native_sysroot)
121
122 chmod_cmd = "chmod 644 %s" % bootimg
123 exec_cmd(chmod_cmd)
124
125 du_cmd = "du -Lbms %s" % bootimg
126 rc, out = exec_cmd(du_cmd)
127 bootimg_size = out.split()[0]
128
129 self.size = bootimg_size
130 self.source_file = bootimg
131
132 def prepare_bootimg_efi(self, cr_workdir, oe_builddir, bootimg_dir,
133 kernel_dir, native_sysroot):
134 """
135 Prepare content for an EFI (grub) boot partition.
136 """
137 staging_kernel_dir = kernel_dir
138 staging_data_dir = bootimg_dir
139
140 hdddir = "%s/hdd/boot" % cr_workdir
141
142 install_cmd = "install -m 0644 %s/bzImage %s/vmlinuz" % \
143 (staging_kernel_dir, hdddir)
144 tmp = exec_cmd(install_cmd)
145
146 shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir,
147 "%s/grub.cfg" % cr_workdir)
148
149 cp_cmd = "cp %s/EFI/BOOT/* %s/EFI/BOOT" % (staging_data_dir, hdddir)
150 exec_cmd(cp_cmd, True)
151
152 shutil.move("%s/grub.cfg" % cr_workdir,
153 "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir)
154
155 du_cmd = "du -bks %s" % hdddir
156 rc, out = exec_cmd(du_cmd)
157 blocks = int(out.split()[0])
158
159 blocks += BOOTDD_EXTRA_SPACE
160
161 # Ensure total sectors is an integral number of sectors per
162 # track or mcopy will complain. Sectors are 512 bytes, and we
163 # generate images with 32 sectors per track. This calculation is
164 # done in blocks, thus the mod by 16 instead of 32.
165 blocks += (16 - (blocks % 16))
166
167 # dosfs image, created by mkdosfs
168 bootimg = "%s/boot.img" % cr_workdir
169
170 dosfs_cmd = "mkdosfs -n efi -C %s %d" % (bootimg, blocks)
171 exec_native_cmd(dosfs_cmd, native_sysroot)
172
173 mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
174 exec_native_cmd(mcopy_cmd, native_sysroot)
175
176 chmod_cmd = "chmod 644 %s" % bootimg
177 exec_cmd(chmod_cmd)
178
179 du_cmd = "du -Lbms %s" % bootimg
180 rc, out = exec_cmd(du_cmd)
181 bootimg_size = out.split()[0]
182
183 self.size = bootimg_size
184 self.source_file = bootimg
185
186 def prepare_rootfs_from_fs_image(self, cr_workdir, oe_builddir,
187 rootfs_dir):
188 """
189 Handle an already-created partition e.g. xxx.ext3
190 """
191 rootfs = oe_builddir
192 du_cmd = "du -Lbms %s" % rootfs
193 rc, out = exec_cmd(du_cmd)
194 rootfs_size = out.split()[0]
195
196 self.size = rootfs_size
197 self.source_file = rootfs
198
199 def prepare_rootfs(self, cr_workdir, oe_builddir, rootfs_dir,
200 native_sysroot):
201 """
202 Prepare content for a rootfs partition i.e. create a partition
203 and fill it from a /rootfs dir.
204
205 Currently handles ext2/3/4 and btrfs.
206 """
207 if self.fstype.startswith("ext"):
208 return self.prepare_rootfs_ext(cr_workdir, oe_builddir,
209 rootfs_dir, native_sysroot)
210 elif self.fstype.startswith("btrfs"):
211 return self.prepare_rootfs_btrfs(cr_workdir, oe_builddir,
212 rootfs_dir, native_sysroot)
213
214 def prepare_rootfs_ext(self, cr_workdir, oe_builddir, rootfs_dir,
215 native_sysroot):
216 """
217 Prepare content for an ext2/3/4 rootfs partition.
218 """
219 populate_script = "%s/usr/bin/populate-extfs.sh" % native_sysroot
220 image_extra_space = 10240
221
222 image_rootfs = rootfs_dir
223 rootfs = "%s/rootfs.%s" % (cr_workdir, self.fstype)
224
225 du_cmd = "du -ks %s" % image_rootfs
226 rc, out = exec_cmd(du_cmd)
227 actual_rootfs_size = out.split()[0]
228
229 rootfs_size = int(actual_rootfs_size) + image_extra_space
230
231 dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \
232 (rootfs, rootfs_size)
233 rc, out = exec_cmd(dd_cmd)
234
235 extra_imagecmd = "-i 8192"
236
237 mkfs_cmd = "mkfs.%s -F %s %s" % (self.fstype, extra_imagecmd, rootfs)
238 rc, out = exec_native_cmd(mkfs_cmd, native_sysroot)
239
240 populate_cmd = populate_script + " " + image_rootfs + " " + rootfs
241 rc, out = exec_native_cmd(populate_cmd, native_sysroot)
242
243 # get the rootfs size in the right units for kickstart (Mb)
244 du_cmd = "du -Lbms %s" % rootfs
245 rc, out = exec_cmd(du_cmd)
246 rootfs_size = out.split()[0]
247
248 self.size = rootfs_size
249 self.source_file = rootfs
250
251 return 0
252
253 def prepare_rootfs_btrfs(self, cr_workdir, oe_builddir, rootfs_dir,
254 native_sysroot):
255 """
256 Prepare content for a btrfs rootfs partition.
257
258 Currently handles ext2/3/4 and btrfs.
259 """
260 image_extra_space = 10240
261
262 image_rootfs = rootfs_dir
263 rootfs = "%s/rootfs.%s" % (cr_workdir, self.fstype)
264
265 du_cmd = "du -ks %s" % image_rootfs
266 rc, out = exec_cmd(du_cmd)
267 actual_rootfs_size = out.split()[0]
268
269 rootfs_size = int(actual_rootfs_size) + image_extra_space
270
271 dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \
272 (rootfs, rootfs_size)
273 rc, out = exec_cmd(dd_cmd)
274
275 mkfs_cmd = "mkfs.%s -b %d -r %s %s" % \
276 (self.fstype, rootfs_size * 1024, image_rootfs, rootfs)
277 rc, out = exec_native_cmd(mkfs_cmd, native_sysroot)
278
279 # get the rootfs size in the right units for kickstart (Mb)
280 du_cmd = "du -Lbms %s" % rootfs
281 rc, out = exec_cmd(du_cmd)
282 rootfs_size = out.split()[0]
283
284 self.size = rootfs_size
285 self.source_file = rootfs
286
287 def prepare_empty_partition(self, cr_workdir, oe_builddir, native_sysroot):
288 """
289 Prepare an empty partition.
290 """
291 if self.fstype.startswith("ext"):
292 return self.prepare_empty_partition_ext(cr_workdir, oe_builddir,
293 native_sysroot)
294 elif self.fstype.startswith("btrfs"):
295 return self.prepare_empty_partition_btrfs(cr_workdir, oe_builddir,
296 native_sysroot)
297
298 def prepare_empty_partition_ext(self, cr_workdir, oe_builddir,
299 native_sysroot):
300 """
301 Prepare an empty ext2/3/4 partition.
302 """
303 fs = "%s/fs.%s" % (cr_workdir, self.fstype)
304
305 dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \
306 (fs, self.size)
307 rc, out = exec_cmd(dd_cmd)
308
309 extra_imagecmd = "-i 8192"
310
311 mkfs_cmd = "mkfs.%s -F %s %s" % (self.fstype, extra_imagecmd, fs)
312 rc, out = exec_native_cmd(mkfs_cmd, native_sysroot)
313
314 self.source_file = fs
315
316 return 0
317
318 def prepare_empty_partition_btrfs(self, cr_workdir, oe_builddir,
319 native_sysroot):
320 """
321 Prepare an empty btrfs partition.
322 """
323 fs = "%s/fs.%s" % (cr_workdir, self.fstype)
324
325 dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \
326 (fs, self.size)
327 rc, out = exec_cmd(dd_cmd)
328
329 mkfs_cmd = "mkfs.%s -b %d %s" % (self.fstype, self.size * 1024, rootfs)
330 rc, out = exec_native_cmd(mkfs_cmd, native_sysroot)
331
332 mkfs_cmd = "mkfs.%s -F %s %s" % (self.fstype, extra_imagecmd, fs)
333 rc, out = exec_native_cmd(mkfs_cmd, native_sysroot)
334
335 self.source_file = fs
336
337 return 0
338
339 def prepare_swap_partition(self, cr_workdir, oe_builddir, native_sysroot):
340 """
341 Prepare a swap partition.
342 """
343 fs = "%s/fs.%s" % (cr_workdir, self.fstype)
344
345 dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \
346 (fs, self.size)
347 rc, out = exec_cmd(dd_cmd)
348
349 import uuid
350 label_str = ""
351 if self.label:
352 label_str = "-L %s" % self.label
353 mkswap_cmd = "mkswap %s -U %s %s" % (label_str, str(uuid.uuid1()), fs)
354 rc, out = exec_native_cmd(mkswap_cmd, native_sysroot)
355
356 self.source_file = fs
357
358 return 0
359
360class Wic_Partition(Mic_Partition):
361 removedKeywords = Mic_Partition.removedKeywords
362 removedAttrs = Mic_Partition.removedAttrs
46 363
47 def _getParser(self): 364 def _getParser(self):
48 op = FC4_Partition._getParser(self) 365 op = Mic_Partition._getParser(self)
49 # The alignment value is given in kBytes. e.g., value 8 means that 366 # use specified source file to fill the partition
50 # the partition is aligned to start from 8096 byte boundary. 367 # and calculate partition size
51 op.add_option("--align", type="int", action="store", dest="align", 368 op.add_option("--source", type="string", action="store",
52 default=None) 369 dest="source", default=None)
53 op.add_option("--extoptions", type="string", action="store", dest="extopts",
54 default=None)
55 op.add_option("--part-type", type="string", action="store", dest="part_type",
56 default=None)
57 return op 370 return op
diff --git a/scripts/lib/mic/plugin.py b/scripts/lib/mic/plugin.py
index 18c93ad259..7c296e9765 100644
--- a/scripts/lib/mic/plugin.py
+++ b/scripts/lib/mic/plugin.py
@@ -40,7 +40,11 @@ class PluginMgr(object):
40 return cls._instance 40 return cls._instance
41 41
42 def __init__(self): 42 def __init__(self):
43 self.plugin_dir = configmgr.common['plugin_dir'] 43 mic_path = os.path.dirname(__file__)
44 eos = mic_path.find('scripts') + len('scripts')
45 scripts_path = mic_path[:eos]
46
47 self.plugin_dir = scripts_path + "/lib/mic/plugins"
44 48
45 def append_dirs(self, dirs): 49 def append_dirs(self, dirs):
46 for path in dirs: 50 for path in dirs:
diff --git a/scripts/lib/mic/plugins/imager/direct_plugin.py b/scripts/lib/mic/plugins/imager/direct_plugin.py
new file mode 100644
index 0000000000..53381e5e01
--- /dev/null
+++ b/scripts/lib/mic/plugins/imager/direct_plugin.py
@@ -0,0 +1,92 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (c) 2013, Intel Corporation.
5# All rights reserved.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# DESCRIPTION
21# This implements the 'direct' imager plugin class for 'wic', based
22# loosely on the raw imager plugin from 'mic'
23#
24# AUTHORS
25# Tom Zanussi <tom.zanussi (at] linux.intel.com>
26#
27
28import os
29import shutil
30import re
31import tempfile
32
33from mic import chroot, msger, rt_util
34from mic.utils import misc, fs_related, errors, runner, cmdln
35from mic.conf import configmgr
36from mic.plugin import pluginmgr
37from mic.utils.partitionedfs import PartitionedMount
38
39import mic.imager.direct as direct
40from mic.pluginbase import ImagerPlugin
41
42class DirectPlugin(ImagerPlugin):
43 name = 'direct'
44
45 @classmethod
46 def do_create(self, subcmd, opts, *args):
47 """
48 Create direct image, called from creator as 'direct' cmd
49 """
50 if len(args) != 9:
51 raise errors.Usage("Extra arguments given")
52
53 staging_data_dir = args[0]
54 hdddir = args[1]
55 native_sysroot = args[2]
56 kernel_dir = args[3]
57 bootimg_dir = args[4]
58 rootfs_dir = args[5]
59
60 creatoropts = configmgr.create
61 ksconf = args[6]
62
63 image_output_dir = args[7]
64 oe_builddir = args[8]
65
66 configmgr._ksconf = ksconf
67
68 creator = direct.DirectImageCreator(oe_builddir,
69 image_output_dir,
70 rootfs_dir,
71 bootimg_dir,
72 kernel_dir,
73 native_sysroot,
74 hdddir,
75 staging_data_dir,
76 creatoropts,
77 None,
78 None,
79 None)
80
81 try:
82 creator.mount(None, creatoropts["cachedir"])
83 creator.install()
84 creator.configure(creatoropts["repomd"])
85 creator.print_outimage_info()
86
87 except errors.CreatorError:
88 raise
89 finally:
90 creator.cleanup()
91
92 return 0
diff --git a/scripts/lib/mic/utils/fs_related.py b/scripts/lib/mic/utils/fs_related.py
index b9b9a97175..61617353eb 100644
--- a/scripts/lib/mic/utils/fs_related.py
+++ b/scripts/lib/mic/utils/fs_related.py
@@ -29,7 +29,7 @@ import uuid
29from mic import msger 29from mic import msger
30from mic.utils import runner 30from mic.utils import runner
31from mic.utils.errors import * 31from mic.utils.errors import *
32 32from mic.utils.oe.misc import *
33 33
34def find_binary_inchroot(binary, chroot): 34def find_binary_inchroot(binary, chroot):
35 paths = ["/usr/sbin", 35 paths = ["/usr/sbin",
@@ -280,6 +280,34 @@ class RawDisk(Disk):
280 def exists(self): 280 def exists(self):
281 return True 281 return True
282 282
283
284class DiskImage(Disk):
285 """
286 A Disk backed by a file.
287 """
288 def __init__(self, image_file, size):
289 Disk.__init__(self, size)
290 self.image_file = image_file
291
292 def exists(self):
293 return os.path.exists(self.image_file)
294
295 def create(self):
296 if self.device is not None:
297 return
298
299 blocks = self.size / 1024
300 if self.size - blocks * 1024:
301 blocks += 1
302
303 # create disk image
304 dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=1" % \
305 (self.image_file, blocks)
306 rc, out = exec_cmd(dd_cmd)
307
308 self.device = self.image_file
309
310
283class LoopbackDisk(Disk): 311class LoopbackDisk(Disk):
284 """A Disk backed by a file via the loop module.""" 312 """A Disk backed by a file via the loop module."""
285 def __init__(self, lofile, size): 313 def __init__(self, lofile, size):
diff --git a/scripts/lib/mic/utils/misc.py b/scripts/lib/mic/utils/misc.py
index 63024346a9..67ddef2e44 100644
--- a/scripts/lib/mic/utils/misc.py
+++ b/scripts/lib/mic/utils/misc.py
@@ -512,8 +512,8 @@ def uncompress_squashfs(squashfsimg, outdir):
512 if (rc != 0): 512 if (rc != 0):
513 raise SquashfsError("Failed to uncompress %s." % squashfsimg) 513 raise SquashfsError("Failed to uncompress %s." % squashfsimg)
514 514
515def mkdtemp(dir = "/var/tmp", prefix = "mic-tmp-"): 515def mkdtemp(dir = "/var/tmp", prefix = "wic-tmp-"):
516 """ FIXME: use the dir in mic.conf instead """ 516 """ FIXME: use the dir in wic.conf instead """
517 517
518 makedirs(dir) 518 makedirs(dir)
519 return tempfile.mkdtemp(dir = dir, prefix = prefix) 519 return tempfile.mkdtemp(dir = dir, prefix = prefix)
diff --git a/scripts/lib/mic/utils/oe/__init__.py b/scripts/lib/mic/utils/oe/__init__.py
new file mode 100644
index 0000000000..d10e802116
--- /dev/null
+++ b/scripts/lib/mic/utils/oe/__init__.py
@@ -0,0 +1,22 @@
1#
2# OpenEmbedded mic utils library
3#
4# Copyright (c) 2013, Intel Corporation.
5# All rights reserved.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# AUTHORS
21# Tom Zanussi <tom.zanussi (at] linux.intel.com>
22#
diff --git a/scripts/lib/mic/utils/oe/misc.py b/scripts/lib/mic/utils/oe/misc.py
new file mode 100644
index 0000000000..9edaa230e4
--- /dev/null
+++ b/scripts/lib/mic/utils/oe/misc.py
@@ -0,0 +1,108 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (c) 2013, Intel Corporation.
5# All rights reserved.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# DESCRIPTION
21# This module provides a place to collect various mic-related utils
22# for the OpenEmbedded Image Tools.
23#
24# AUTHORS
25# Tom Zanussi <tom.zanussi (at] linux.intel.com>
26#
27
28from mic import msger
29from mic.utils import runner
30
31def exec_cmd(cmd_and_args, as_shell = False, catch = 3):
32 """
33 Execute command, catching stderr, stdout
34
35 Need to execute as_shell if the command uses wildcards
36 """
37 msger.debug("exec_cmd: %s" % cmd_and_args)
38 args = cmd_and_args.split()
39 msger.debug(args)
40
41 if (as_shell):
42 rc, out = runner.runtool(cmd_and_args, catch)
43 else:
44 rc, out = runner.runtool(args, catch)
45 out = out.strip()
46 msger.debug("exec_cmd: output for %s (rc = %d): %s" % \
47 (cmd_and_args, rc, out))
48
49 if rc != 0:
50 # We don't throw exception when return code is not 0, because
51 # parted always fails to reload part table with loop devices. This
52 # prevents us from distinguishing real errors based on return
53 # code.
54 msger.debug("WARNING: %s returned '%s' instead of 0" % (args[0], rc))
55
56 return (rc, out)
57
58
59def exec_cmd_quiet(cmd_and_args, as_shell = False):
60 """
61 Execute command, catching nothing in the output
62
63 Need to execute as_shell if the command uses wildcards
64 """
65 return exec_cmd(cmd_and_args, as_shell, 0)
66
67
68def exec_native_cmd(cmd_and_args, native_sysroot, catch = 3):
69 """
70 Execute native command, catching stderr, stdout
71
72 Need to execute as_shell if the command uses wildcards
73
74 Always need to execute native commands as_shell
75 """
76 native_paths = \
77 "export PATH=%s/sbin:PATH=%s/usr/sbin:PATH=%s/usr/bin:$PATH" % \
78 (native_sysroot, native_sysroot, native_sysroot)
79 native_cmd_and_args = "%s;%s" % (native_paths, cmd_and_args)
80 msger.debug("exec_native_cmd: %s" % cmd_and_args)
81
82 args = cmd_and_args.split()
83 msger.debug(args)
84
85 return exec_cmd(native_cmd_and_args, True, catch)
86
87
88def exec_native_cmd_quiet(cmd_and_args, native_sysroot):
89 """
90 Execute native command, catching nothing in the output
91
92 Need to execute as_shell if the command uses wildcards
93
94 Always need to execute native commands as_shell
95 """
96 return exec_native_cmd(cmd_and_args, native_sysroot, 0)
97
98
99# kickstart doesn't support variable substution in commands, so this
100# is our current simplistic scheme for supporting that
101
102wks_vars = dict()
103
104def get_wks_var(key):
105 return wks_vars[key]
106
107def add_wks_var(key, val):
108 wks_vars[key] = val
diff --git a/scripts/lib/mic/utils/partitionedfs.py b/scripts/lib/mic/utils/partitionedfs.py
index 04758440e1..e8cded26e0 100644
--- a/scripts/lib/mic/utils/partitionedfs.py
+++ b/scripts/lib/mic/utils/partitionedfs.py
@@ -25,6 +25,7 @@ from mic.utils import runner
25from mic.utils.errors import MountError 25from mic.utils.errors import MountError
26from mic.utils.fs_related import * 26from mic.utils.fs_related import *
27from mic.utils.gpt_parser import GptParser 27from mic.utils.gpt_parser import GptParser
28from mic.utils.oe.misc import *
28 29
29# Overhead of the MBR partitioning scheme (just one sector) 30# Overhead of the MBR partitioning scheme (just one sector)
30MBR_OVERHEAD = 1 31MBR_OVERHEAD = 1
@@ -93,7 +94,7 @@ class PartitionedMount(Mount):
93 self.partitions.append(part) 94 self.partitions.append(part)
94 self.__add_disk(part['disk_name']) 95 self.__add_disk(part['disk_name'])
95 96
96 def add_partition(self, size, disk_name, mountpoint, fstype = None, 97 def add_partition(self, size, disk_name, mountpoint, source_file = None, fstype = None,
97 label=None, fsopts = None, boot = False, align = None, 98 label=None, fsopts = None, boot = False, align = None,
98 part_type = None): 99 part_type = None):
99 """ Add the next partition. Prtitions have to be added in the 100 """ Add the next partition. Prtitions have to be added in the
@@ -141,6 +142,7 @@ class PartitionedMount(Mount):
141 part = { 'ks_pnum' : ks_pnum, # Partition number in the KS file 142 part = { 'ks_pnum' : ks_pnum, # Partition number in the KS file
142 'size': size, # In sectors 143 'size': size, # In sectors
143 'mountpoint': mountpoint, # Mount relative to chroot 144 'mountpoint': mountpoint, # Mount relative to chroot
145 'source_file': source_file, # partition contents
144 'fstype': fstype, # Filesystem type 146 'fstype': fstype, # Filesystem type
145 'fsopts': fsopts, # Filesystem mount options 147 'fsopts': fsopts, # Filesystem mount options
146 'label': label, # Partition label 148 'label': label, # Partition label
@@ -723,67 +725,51 @@ class PartitionedMount(Mount):
723 725
724 self.snapshot_created = True 726 self.snapshot_created = True
725 727
728 def __install_partition(self, num, source_file, start, size):
729 """
730 Install source_file contents into a partition.
731 """
732 if not source_file: # nothing to install
733 return
734
735 # Start is included in the size so need to substract one from the end.
736 end = start + size - 1
737 msger.debug("Installed %s in partition %d, sectors %d-%d, size %d sectors" % (source_file, num, start, end, size))
738
739 dd_cmd = "dd if=%s of=%s bs=%d seek=%d count=%d conv=notrunc" % \
740 (source_file, self.image_file, self.sector_size, start, size)
741 rc, out = exec_cmd(dd_cmd)
742
743
744 def install(self, image_file):
745 msger.debug("Installing partitions")
746
747 self.image_file = image_file
748
749 for p in self.partitions:
750 d = self.disks[p['disk_name']]
751 if d['ptable_format'] == "msdos" and p['num'] == 5:
752 # The last sector of the 3rd partition was reserved for the EBR
753 # of the first _logical_ partition. This is why the extended
754 # partition should start one sector before the first logical
755 # partition.
756 self.__install_partition(p['num'], p['source_file'],
757 p['start'] - 1,
758 d['offset'] - p['start'])
759
760 self.__install_partition(p['num'], p['source_file'],
761 p['start'], p['size'])
762
726 def mount(self): 763 def mount(self):
727 for dev in self.disks.keys(): 764 for dev in self.disks.keys():
728 d = self.disks[dev] 765 d = self.disks[dev]
729 d['disk'].create() 766 d['disk'].create()
730 767
731 self.__format_disks() 768 self.__format_disks()
732 self.__map_partitions()
733 self.__calculate_mountorder()
734 769
735 for mp in self.mountOrder: 770 self.__calculate_mountorder()
736 p = None
737 for p1 in self.partitions:
738 if p1['mountpoint'] == mp:
739 p = p1
740 break
741
742 if not p['label']:
743 if p['mountpoint'] == "/":
744 p['label'] = 'platform'
745 else:
746 p['label'] = mp.split('/')[-1]
747
748 if mp == 'swap':
749 import uuid
750 p['uuid'] = str(uuid.uuid1())
751 runner.show([self.mkswap,
752 '-L', p['label'],
753 '-U', p['uuid'],
754 p['device']])
755 continue
756 771
757 rmmountdir = False 772 return
758 if p['mountpoint'] == "/":
759 rmmountdir = True
760 if p['fstype'] == "vfat" or p['fstype'] == "msdos":
761 myDiskMount = VfatDiskMount
762 elif p['fstype'] in ("ext2", "ext3", "ext4"):
763 myDiskMount = ExtDiskMount
764 elif p['fstype'] == "btrfs":
765 myDiskMount = BtrfsDiskMount
766 else:
767 raise MountError("Fail to support file system " + p['fstype'])
768
769 if p['fstype'] == "btrfs" and not p['fsopts']:
770 p['fsopts'] = "subvolid=0"
771
772 pdisk = myDiskMount(RawDisk(p['size'] * self.sector_size, p['device']),
773 self.mountdir + p['mountpoint'],
774 p['fstype'],
775 4096,
776 p['label'],
777 rmmountdir,
778 self.skipformat,
779 fsopts = p['fsopts'])
780 pdisk.mount(pdisk.fsopts)
781 if p['fstype'] == "btrfs" and p['mountpoint'] == "/":
782 if not self.skipformat:
783 self.__create_subvolumes(p, pdisk)
784 self.__mount_subvolumes(p, pdisk)
785 p['mount'] = pdisk
786 p['uuid'] = pdisk.uuid
787 773
788 def resparse(self, size = None): 774 def resparse(self, size = None):
789 # Can't re-sparse a disk image - too hard 775 # Can't re-sparse a disk image - too hard