summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/imager
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/imager')
-rw-r--r--scripts/lib/wic/imager/__init__.py0
-rw-r--r--scripts/lib/wic/imager/baseimager.py193
-rw-r--r--scripts/lib/wic/imager/direct.py363
3 files changed, 556 insertions, 0 deletions
diff --git a/scripts/lib/wic/imager/__init__.py b/scripts/lib/wic/imager/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/scripts/lib/wic/imager/__init__.py
diff --git a/scripts/lib/wic/imager/baseimager.py b/scripts/lib/wic/imager/baseimager.py
new file mode 100644
index 0000000000..e8305272a2
--- /dev/null
+++ b/scripts/lib/wic/imager/baseimager.py
@@ -0,0 +1,193 @@
1#!/usr/bin/env python -tt
2#
3# Copyright (c) 2007 Red Hat Inc.
4# Copyright (c) 2009, 2010, 2011 Intel, Inc.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; version 2 of the License
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13# for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc., 59
17# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19from __future__ import with_statement
20import os, sys
21import tempfile
22import shutil
23
24from wic import kickstart
25from wic import msger
26from wic.utils.errors import CreatorError
27from wic.utils import misc, runner, fs_related as fs
28
29class BaseImageCreator(object):
30 """Base class for image creation.
31
32 BaseImageCreator is the simplest creator class available; it will
33 create a system image according to the supplied kickstart file.
34
35 e.g.
36
37 import wic.imgcreate as imgcreate
38 ks = imgcreate.read_kickstart("foo.ks")
39 imgcreate.ImageCreator(ks, "foo").create()
40 """
41
42 def __del__(self):
43 self.cleanup()
44
45 def __init__(self, createopts = None):
46 """Initialize an ImageCreator instance.
47
48 ks -- a pykickstart.KickstartParser instance; this instance will be
49 used to drive the install by e.g. providing the list of packages
50 to be installed, the system configuration and %post scripts
51
52 name -- a name for the image; used for e.g. image filenames or
53 filesystem labels
54 """
55
56 self.__builddir = None
57
58 self.ks = None
59 self.name = "target"
60 self.tmpdir = "/var/tmp/wic"
61 self.workdir = "/var/tmp/wic/build"
62
63 # setup tmpfs tmpdir when enabletmpfs is True
64 self.enabletmpfs = False
65
66 if createopts:
67 # Mapping table for variables that have different names.
68 optmap = {"outdir" : "destdir",
69 }
70
71 # update setting from createopts
72 for key in createopts.keys():
73 if key in optmap:
74 option = optmap[key]
75 else:
76 option = key
77 setattr(self, option, createopts[key])
78
79 self.destdir = os.path.abspath(os.path.expanduser(self.destdir))
80
81 self._dep_checks = ["ls", "bash", "cp", "echo"]
82
83 # Output image file names
84 self.outimage = []
85
86 # No ks provided when called by convertor, so skip the dependency check
87 if self.ks:
88 # If we have btrfs partition we need to check necessary tools
89 for part in self.ks.handler.partition.partitions:
90 if part.fstype and part.fstype == "btrfs":
91 self._dep_checks.append("mkfs.btrfs")
92 break
93
94 # make sure the specified tmpdir and cachedir exist
95 if not os.path.exists(self.tmpdir):
96 os.makedirs(self.tmpdir)
97
98
99 #
100 # Hooks for subclasses
101 #
102 def _create(self):
103 """Create partitions for the disk image(s)
104
105 This is the hook where subclasses may create the partitions
106 that will be assembled into disk image(s).
107
108 There is no default implementation.
109 """
110 pass
111
112 def _cleanup(self):
113 """Undo anything performed in _create().
114
115 This is the hook where subclasses must undo anything which was
116 done in _create().
117
118 There is no default implementation.
119
120 """
121 pass
122
123 #
124 # Actual implementation
125 #
126 def __ensure_builddir(self):
127 if not self.__builddir is None:
128 return
129
130 try:
131 self.workdir = os.path.join(self.tmpdir, "build")
132 if not os.path.exists(self.workdir):
133 os.makedirs(self.workdir)
134 self.__builddir = tempfile.mkdtemp(dir = self.workdir,
135 prefix = "imgcreate-")
136 except OSError, (err, msg):
137 raise CreatorError("Failed create build directory in %s: %s" %
138 (self.tmpdir, msg))
139
140 def __setup_tmpdir(self):
141 if not self.enabletmpfs:
142 return
143
144 runner.show('mount -t tmpfs -o size=4G tmpfs %s' % self.workdir)
145
146 def __clean_tmpdir(self):
147 if not self.enabletmpfs:
148 return
149
150 runner.show('umount -l %s' % self.workdir)
151
152 def create(self):
153 """Create partitions for the disk image(s)
154
155 Create the partitions that will be assembled into disk
156 image(s).
157 """
158 self.__setup_tmpdir()
159 self.__ensure_builddir()
160
161 self._create()
162
163 def cleanup(self):
164 """Undo anything performed in create().
165
166 Note, make sure to call this method once finished with the creator
167 instance in order to ensure no stale files are left on the host e.g.:
168
169 creator = ImageCreator(ks, name)
170 try:
171 creator.create()
172 finally:
173 creator.cleanup()
174
175 """
176 if not self.__builddir:
177 return
178
179 self._cleanup()
180
181 shutil.rmtree(self.__builddir, ignore_errors = True)
182 self.__builddir = None
183
184 self.__clean_tmpdir()
185
186
187 def print_outimage_info(self):
188 msg = "The new image can be found here:\n"
189 self.outimage.sort()
190 for file in self.outimage:
191 msg += ' %s\n' % os.path.abspath(file)
192
193 msger.info(msg)
diff --git a/scripts/lib/wic/imager/direct.py b/scripts/lib/wic/imager/direct.py
new file mode 100644
index 0000000000..d0ae504daf
--- /dev/null
+++ b/scripts/lib/wic/imager/direct.py
@@ -0,0 +1,363 @@
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'
22#
23# AUTHORS
24# Tom Zanussi <tom.zanussi (at] linux.intel.com>
25#
26
27import os
28import stat
29import shutil
30
31from wic import kickstart, msger
32from wic.utils import fs_related, runner, misc
33from wic.utils.partitionedfs import Image
34from wic.utils.errors import CreatorError, ImageError
35from wic.imager.baseimager import BaseImageCreator
36from wic.utils.oe.misc import *
37from wic.plugin import pluginmgr
38
39disk_methods = {
40 "do_install_disk":None,
41}
42
43class DirectImageCreator(BaseImageCreator):
44 """
45 Installs a system into a file containing a partitioned disk image.
46
47 DirectImageCreator is an advanced ImageCreator subclass; an image
48 file is formatted with a partition table, each partition created
49 from a rootfs or other OpenEmbedded build artifact and dd'ed into
50 the virtual disk. The disk image can subsequently be dd'ed onto
51 media and used on actual hardware.
52 """
53
54 def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir,
55 kernel_dir, native_sysroot, creatoropts=None):
56 """
57 Initialize a DirectImageCreator instance.
58
59 This method takes the same arguments as ImageCreator.__init__()
60 """
61 BaseImageCreator.__init__(self, creatoropts)
62
63 self.__image = None
64 self.__disks = {}
65 self.__disk_format = "direct"
66 self._disk_names = []
67 self._ptable_format = self.ks.handler.bootloader.ptable
68
69 self.oe_builddir = oe_builddir
70 if image_output_dir:
71 self.tmpdir = image_output_dir
72 self.rootfs_dir = rootfs_dir
73 self.bootimg_dir = bootimg_dir
74 self.kernel_dir = kernel_dir
75 self.native_sysroot = native_sysroot
76
77 def __write_fstab(self, image_rootfs):
78 """overriden to generate fstab (temporarily) in rootfs. This is called
79 from _create, make sure it doesn't get called from
80 BaseImage.create()
81 """
82 if image_rootfs is None:
83 return None
84
85 fstab = image_rootfs + "/etc/fstab"
86 if not os.path.isfile(fstab):
87 return None
88
89 parts = self._get_parts()
90
91 self._save_fstab(fstab)
92 fstab_lines = self._get_fstab(fstab, parts)
93 self._update_fstab(fstab_lines, parts)
94 self._write_fstab(fstab, fstab_lines)
95
96 return fstab
97
98 def _update_fstab(self, fstab_lines, parts):
99 """Assume partition order same as in wks"""
100 for num, p in enumerate(parts, 1):
101 if not p.mountpoint or p.mountpoint == "/" or p.mountpoint == "/boot":
102 continue
103 if self._ptable_format == 'msdos' and num > 3:
104 device_name = "/dev/" + p.disk + str(num + 1)
105 else:
106 device_name = "/dev/" + p.disk + str(num)
107
108 opts = "defaults"
109 if p.fsopts:
110 opts = p.fsopts
111
112 fstab_entry = device_name + "\t" + \
113 p.mountpoint + "\t" + \
114 p.fstype + "\t" + \
115 opts + "\t0\t0\n"
116 fstab_lines.append(fstab_entry)
117
118 def _write_fstab(self, fstab, fstab_lines):
119 fstab = open(fstab, "w")
120 for line in fstab_lines:
121 fstab.write(line)
122 fstab.close()
123
124 def _save_fstab(self, fstab):
125 """Save the current fstab in rootfs"""
126 shutil.copyfile(fstab, fstab + ".orig")
127
128 def _restore_fstab(self, fstab):
129 """Restore the saved fstab in rootfs"""
130 if fstab is None:
131 return
132 shutil.move(fstab + ".orig", fstab)
133
134 def _get_fstab(self, fstab, parts):
135 """Return the desired contents of /etc/fstab."""
136 f = open(fstab, "r")
137 fstab_contents = f.readlines()
138 f.close()
139
140 return fstab_contents
141
142 def set_bootimg_dir(self, bootimg_dir):
143 """
144 Accessor for bootimg_dir, the actual location used for the source
145 of the bootimg. Should be set by source plugins (only if they
146 change the default bootimg source) so the correct info gets
147 displayed for print_outimage_info().
148 """
149 self.bootimg_dir = bootimg_dir
150
151 def _get_parts(self):
152 if not self.ks:
153 raise CreatorError("Failed to get partition info, "
154 "please check your kickstart setting.")
155
156 # Set a default partition if no partition is given out
157 if not self.ks.handler.partition.partitions:
158 partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
159 args = partstr.split()
160 pd = self.ks.handler.partition.parse(args[1:])
161 if pd not in self.ks.handler.partition.partitions:
162 self.ks.handler.partition.partitions.append(pd)
163
164 # partitions list from kickstart file
165 return kickstart.get_partitions(self.ks)
166
167 def get_disk_names(self):
168 """ Returns a list of physical target disk names (e.g., 'sdb') which
169 will be created. """
170
171 if self._disk_names:
172 return self._disk_names
173
174 #get partition info from ks handler
175 parts = self._get_parts()
176
177 for i in range(len(parts)):
178 if parts[i].disk:
179 disk_name = parts[i].disk
180 else:
181 raise CreatorError("Failed to create disks, no --ondisk "
182 "specified in partition line of ks file")
183
184 if parts[i].mountpoint and not parts[i].fstype:
185 raise CreatorError("Failed to create disks, no --fstype "
186 "specified for partition with mountpoint "
187 "'%s' in the ks file")
188
189 self._disk_names.append(disk_name)
190
191 return self._disk_names
192
193 def _full_name(self, name, extention):
194 """ Construct full file name for a file we generate. """
195 return "%s-%s.%s" % (self.name, name, extention)
196
197 def _full_path(self, path, name, extention):
198 """ Construct full file path to a file we generate. """
199 return os.path.join(path, self._full_name(name, extention))
200
201 def get_default_source_plugin(self):
202 """
203 The default source plugin i.e. the plugin that's consulted for
204 overall image generation tasks outside of any particular
205 partition. For convenience, we just hang it off the
206 bootloader handler since it's the one non-partition object in
207 any setup. By default the default plugin is set to the same
208 plugin as the /boot partition; since we hang it off the
209 bootloader object, the default can be explicitly set using the
210 --source bootloader param.
211 """
212 return self.ks.handler.bootloader.source
213
214 #
215 # Actual implemention
216 #
217 def _create(self):
218 """
219 For 'wic', we already have our build artifacts - we just create
220 filesystems from the artifacts directly and combine them into
221 a partitioned image.
222 """
223 parts = self._get_parts()
224
225 self.__image = Image()
226
227 for p in parts:
228 # as a convenience, set source to the boot partition source
229 # instead of forcing it to be set via bootloader --source
230 if not self.ks.handler.bootloader.source and p.mountpoint == "/boot":
231 self.ks.handler.bootloader.source = p.source
232
233 for p in parts:
234 # need to create the filesystems in order to get their
235 # sizes before we can add them and do the layout.
236 # Image.create() actually calls __format_disks() to create
237 # the disk images and carve out the partitions, then
238 # self.assemble() calls Image.assemble() which calls
239 # __write_partitition() for each partition to dd the fs
240 # into the partitions.
241
242 p.install_pkgs(self, self.workdir, self.oe_builddir, self.rootfs_dir,
243 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
244
245 fstab = self.__write_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
246
247 p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
248 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
249
250 self._restore_fstab(fstab)
251
252 self.__image.add_partition(int(p.size),
253 p.disk,
254 p.mountpoint,
255 p.source_file,
256 p.fstype,
257 p.label,
258 fsopts = p.fsopts,
259 boot = p.active,
260 align = p.align,
261 part_type = p.part_type)
262
263 self.__image.layout_partitions(self._ptable_format)
264
265 self.__imgdir = self.workdir
266 for disk_name, disk in self.__image.disks.items():
267 full_path = self._full_path(self.__imgdir, disk_name, "direct")
268 msger.debug("Adding disk %s as %s with size %s bytes" \
269 % (disk_name, full_path, disk['min_size']))
270 disk_obj = fs_related.DiskImage(full_path, disk['min_size'])
271 self.__disks[disk_name] = disk_obj
272 self.__image.add_disk(disk_name, disk_obj)
273
274 self.__image.create()
275
276 def assemble(self):
277 """
278 Assemble partitions into disk image(s)
279 """
280 for disk_name, disk in self.__image.disks.items():
281 full_path = self._full_path(self.__imgdir, disk_name, "direct")
282 msger.debug("Assembling disk %s as %s with size %s bytes" \
283 % (disk_name, full_path, disk['min_size']))
284 self.__image.assemble(full_path)
285
286 def finalize(self):
287 """
288 Finalize the disk image.
289
290 For example, prepare the image to be bootable by e.g.
291 creating and installing a bootloader configuration.
292
293 """
294 source_plugin = self.get_default_source_plugin()
295 if source_plugin:
296 self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods)
297 for disk_name, disk in self.__image.disks.items():
298 self._source_methods["do_install_disk"](disk, disk_name, self,
299 self.workdir,
300 self.oe_builddir,
301 self.bootimg_dir,
302 self.kernel_dir,
303 self.native_sysroot)
304
305 def print_outimage_info(self):
306 """
307 Print the image(s) and artifacts used, for the user.
308 """
309 msg = "The new image(s) can be found here:\n"
310
311 parts = self._get_parts()
312
313 for disk_name, disk in self.__image.disks.items():
314 full_path = self._full_path(self.__imgdir, disk_name, "direct")
315 msg += ' %s\n\n' % full_path
316
317 msg += 'The following build artifacts were used to create the image(s):\n'
318 for p in parts:
319 if p.get_rootfs() is None:
320 continue
321 if p.mountpoint == '/':
322 str = ':'
323 else:
324 str = '["%s"]:' % p.label
325 msg += ' ROOTFS_DIR%s%s\n' % (str.ljust(20), p.get_rootfs())
326
327 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
328 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
329 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
330
331 msger.info(msg)
332
333 def _get_boot_config(self):
334 """
335 Return the rootdev/root_part_uuid (if specified by
336 --part-type)
337
338 Assume partition order same as in wks
339 """
340 rootdev = None
341 root_part_uuid = None
342 parts = self._get_parts()
343 for num, p in enumerate(parts, 1):
344 if p.mountpoint == "/":
345 part = ''
346 if p.disk.startswith('mmcblk'):
347 part = 'p'
348
349 if self._ptable_format == 'msdos' and num > 3:
350 rootdev = "/dev/%s%s%-d" % (p.disk, part, num + 1)
351 else:
352 rootdev = "/dev/%s%s%-d" % (p.disk, part, num)
353 root_part_uuid = p.part_type
354
355 return (rootdev, root_part_uuid)
356
357 def _cleanup(self):
358 if not self.__image is None:
359 try:
360 self.__image.cleanup()
361 except ImageError, err:
362 msger.warning("%s" % err)
363