summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/imager/direct.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic/imager/direct.py')
-rw-r--r--scripts/lib/wic/imager/direct.py363
1 files changed, 363 insertions, 0 deletions
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