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.py362
1 files changed, 362 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..5b12856289
--- /dev/null
+++ b/scripts/lib/wic/imager/direct.py
@@ -0,0 +1,362 @@
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, hdddir, staging_data_dir,
56 creatoropts=None):
57 """
58 Initialize a DirectImageCreator instance.
59
60 This method takes the same arguments as ImageCreator.__init__()
61 """
62 BaseImageCreator.__init__(self, creatoropts)
63
64 self.__image = None
65 self.__disks = {}
66 self.__disk_format = "direct"
67 self._disk_names = []
68 self._ptable_format = self.ks.handler.bootloader.ptable
69
70 self.oe_builddir = oe_builddir
71 if image_output_dir:
72 self.tmpdir = image_output_dir
73 self.rootfs_dir = rootfs_dir
74 self.bootimg_dir = bootimg_dir
75 self.kernel_dir = kernel_dir
76 self.native_sysroot = native_sysroot
77 self.hdddir = hdddir
78 self.staging_data_dir = staging_data_dir
79
80 def __write_fstab(self, image_rootfs):
81 """overriden to generate fstab (temporarily) in rootfs. This is called
82 from _create, make sure it doesn't get called from
83 BaseImage.create()
84 """
85 if image_rootfs is None:
86 return None
87
88 fstab = image_rootfs + "/etc/fstab"
89 if not os.path.isfile(fstab):
90 return None
91
92 parts = self._get_parts()
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 not p.mountpoint or 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
111 opts = "defaults"
112 if p.fsopts:
113 opts = p.fsopts
114
115 fstab_entry = device_name + "\t" + \
116 p.mountpoint + "\t" + \
117 p.fstype + "\t" + \
118 opts + "\t0\t0\n"
119 fstab_lines.append(fstab_entry)
120
121 def _write_fstab(self, fstab, fstab_lines):
122 fstab = open(fstab, "w")
123 for line in fstab_lines:
124 fstab.write(line)
125 fstab.close()
126
127 def _save_fstab(self, fstab):
128 """Save the current fstab in rootfs"""
129 shutil.copyfile(fstab, fstab + ".orig")
130
131 def _restore_fstab(self, fstab):
132 """Restore the saved fstab in rootfs"""
133 if fstab is None:
134 return
135 shutil.move(fstab + ".orig", fstab)
136
137 def _get_fstab(self, fstab, parts):
138 """Return the desired contents of /etc/fstab."""
139 f = open(fstab, "r")
140 fstab_contents = f.readlines()
141 f.close()
142
143 return fstab_contents
144
145 def set_bootimg_dir(self, bootimg_dir):
146 """
147 Accessor for bootimg_dir, the actual location used for the source
148 of the bootimg. Should be set by source plugins (only if they
149 change the default bootimg source) so the correct info gets
150 displayed for print_outimage_info().
151 """
152 self.bootimg_dir = bootimg_dir
153
154 def _get_parts(self):
155 if not self.ks:
156 raise CreatorError("Failed to get partition info, "
157 "please check your kickstart setting.")
158
159 # Set a default partition if no partition is given out
160 if not self.ks.handler.partition.partitions:
161 partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
162 args = partstr.split()
163 pd = self.ks.handler.partition.parse(args[1:])
164 if pd not in self.ks.handler.partition.partitions:
165 self.ks.handler.partition.partitions.append(pd)
166
167 # partitions list from kickstart file
168 return kickstart.get_partitions(self.ks)
169
170 def get_disk_names(self):
171 """ Returns a list of physical target disk names (e.g., 'sdb') which
172 will be created. """
173
174 if self._disk_names:
175 return self._disk_names
176
177 #get partition info from ks handler
178 parts = self._get_parts()
179
180 for i in range(len(parts)):
181 if parts[i].disk:
182 disk_name = parts[i].disk
183 else:
184 raise CreatorError("Failed to create disks, no --ondisk "
185 "specified in partition line of ks file")
186
187 if parts[i].mountpoint and not parts[i].fstype:
188 raise CreatorError("Failed to create disks, no --fstype "
189 "specified for partition with mountpoint "
190 "'%s' in the ks file")
191
192 self._disk_names.append(disk_name)
193
194 return self._disk_names
195
196 def _full_name(self, name, extention):
197 """ Construct full file name for a file we generate. """
198 return "%s-%s.%s" % (self.name, name, extention)
199
200 def _full_path(self, path, name, extention):
201 """ Construct full file path to a file we generate. """
202 return os.path.join(path, self._full_name(name, extention))
203
204 def get_default_source_plugin(self):
205 """
206 The default source plugin i.e. the plugin that's consulted for
207 overall image generation tasks outside of any particular
208 partition. For convenience, we just hang it off the
209 bootloader handler since it's the one non-partition object in
210 any setup. By default the default plugin is set to the same
211 plugin as the /boot partition; since we hang it off the
212 bootloader object, the default can be explicitly set using the
213 --source bootloader param.
214 """
215 return self.ks.handler.bootloader.source
216
217 #
218 # Actual implemention
219 #
220 def _create(self):
221 """
222 For 'wic', we already have our build artifacts - we just create
223 filesystems from the artifacts directly and combine them into
224 a partitioned image.
225 """
226 parts = self._get_parts()
227
228 self.__image = Image()
229
230 for p in parts:
231 # as a convenience, set source to the boot partition source
232 # instead of forcing it to be set via bootloader --source
233 if not self.ks.handler.bootloader.source and p.mountpoint == "/boot":
234 self.ks.handler.bootloader.source = p.source
235
236 for p in parts:
237 # need to create the filesystems in order to get their
238 # sizes before we can add them and do the layout.
239 # Image.create() actually calls __format_disks() to create
240 # the disk images and carve out the partitions, then
241 # self.assemble() calls Image.assemble() which calls
242 # __write_partitition() for each partition to dd the fs
243 # into the partitions.
244 fstab = self.__write_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
245
246 p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
247 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
248
249 self._restore_fstab(fstab)
250
251 self.__image.add_partition(int(p.size),
252 p.disk,
253 p.mountpoint,
254 p.source_file,
255 p.fstype,
256 p.label,
257 fsopts = p.fsopts,
258 boot = p.active,
259 align = p.align,
260 part_type = p.part_type)
261
262 self.__image.layout_partitions(self._ptable_format)
263
264 self.__imgdir = self.workdir
265 for disk_name, disk in self.__image.disks.items():
266 full_path = self._full_path(self.__imgdir, disk_name, "direct")
267 msger.debug("Adding disk %s as %s with size %s bytes" \
268 % (disk_name, full_path, disk['min_size']))
269 disk_obj = fs_related.DiskImage(full_path, disk['min_size'])
270 self.__disks[disk_name] = disk_obj
271 self.__image.add_disk(disk_name, disk_obj)
272
273 self.__image.create()
274
275 def assemble(self):
276 """
277 Assemble partitions into disk image(s)
278 """
279 for disk_name, disk in self.__image.disks.items():
280 full_path = self._full_path(self.__imgdir, disk_name, "direct")
281 msger.debug("Assembling disk %s as %s with size %s bytes" \
282 % (disk_name, full_path, disk['min_size']))
283 self.__image.assemble(full_path)
284
285 def finalize(self):
286 """
287 Finalize the disk image.
288
289 For example, prepare the image to be bootable by e.g.
290 creating and installing a bootloader configuration.
291
292 """
293 source_plugin = self.get_default_source_plugin()
294 if source_plugin:
295 self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods)
296 for disk_name, disk in self.__image.disks.items():
297 self._source_methods["do_install_disk"](disk, disk_name, self,
298 self.workdir,
299 self.oe_builddir,
300 self.bootimg_dir,
301 self.kernel_dir,
302 self.native_sysroot)
303
304 def print_outimage_info(self):
305 """
306 Print the image(s) and artifacts used, for the user.
307 """
308 msg = "The new image(s) can be found here:\n"
309
310 parts = self._get_parts()
311
312 for disk_name, disk in self.__image.disks.items():
313 full_path = self._full_path(self.__imgdir, disk_name, "direct")
314 msg += ' %s\n\n' % full_path
315
316 msg += 'The following build artifacts were used to create the image(s):\n'
317 for p in parts:
318 if p.get_rootfs() is None:
319 continue
320 if p.mountpoint == '/':
321 str = ':'
322 else:
323 str = '["%s"]:' % p.label
324 msg += ' ROOTFS_DIR%s%s\n' % (str.ljust(20), p.get_rootfs())
325
326 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
327 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
328 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
329
330 msger.info(msg)
331
332 def _get_boot_config(self):
333 """
334 Return the rootdev/root_part_uuid (if specified by
335 --part-type)
336
337 Assume partition order same as in wks
338 """
339 rootdev = None
340 root_part_uuid = None
341 parts = self._get_parts()
342 for num, p in enumerate(parts, 1):
343 if p.mountpoint == "/":
344 part = ''
345 if p.disk.startswith('mmcblk'):
346 part = 'p'
347
348 if self._ptable_format == 'msdos' and num > 3:
349 rootdev = "/dev/%s%s%-d" % (p.disk, part, num + 1)
350 else:
351 rootdev = "/dev/%s%s%-d" % (p.disk, part, num)
352 root_part_uuid = p.part_type
353
354 return (rootdev, root_part_uuid)
355
356 def _cleanup(self):
357 if not self.__image is None:
358 try:
359 self.__image.cleanup()
360 except ImageError, err:
361 msger.warning("%s" % err)
362