summaryrefslogtreecommitdiffstats
path: root/scripts/lib/mic/imager/direct.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/mic/imager/direct.py')
-rw-r--r--scripts/lib/mic/imager/direct.py384
1 files changed, 384 insertions, 0 deletions
diff --git a/scripts/lib/mic/imager/direct.py b/scripts/lib/mic/imager/direct.py
new file mode 100644
index 0000000000..fef9d0ed32
--- /dev/null
+++ b/scripts/lib/mic/imager/direct.py
@@ -0,0 +1,384 @@
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 *
38from mic.plugin import pluginmgr
39
40disk_methods = {
41 "do_install_disk":None,
42}
43
44class DirectImageCreator(BaseImageCreator):
45 """
46 Installs a system into a file containing a partitioned disk image.
47
48 DirectImageCreator is an advanced ImageCreator subclass; an image
49 file is formatted with a partition table, each partition created
50 from a rootfs or other OpenEmbedded build artifact and dd'ed into
51 the virtual disk. The disk image can subsequently be dd'ed onto
52 media and used on actual hardware.
53 """
54
55 def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir,
56 kernel_dir, native_sysroot, hdddir, staging_data_dir,
57 creatoropts=None, pkgmgr=None, compress_image=None,
58 generate_bmap=None, fstab_entry="uuid"):
59 """
60 Initialize a DirectImageCreator instance.
61
62 This method takes the same arguments as ImageCreator.__init__()
63 """
64 BaseImageCreator.__init__(self, creatoropts, pkgmgr)
65
66 self.__instimage = None
67 self.__imgdir = None
68 self.__disks = {}
69 self.__disk_format = "direct"
70 self._disk_names = []
71 self._ptable_format = self.ks.handler.bootloader.ptable
72 self.use_uuid = fstab_entry == "uuid"
73 self.compress_image = compress_image
74 self.bmap_needed = generate_bmap
75
76 self.oe_builddir = oe_builddir
77 if image_output_dir:
78 self.tmpdir = image_output_dir
79 self.cachedir = "%s/cache" % image_output_dir
80 self.rootfs_dir = rootfs_dir
81 self.bootimg_dir = bootimg_dir
82 self.kernel_dir = kernel_dir
83 self.native_sysroot = native_sysroot
84 self.hdddir = hdddir
85 self.staging_data_dir = staging_data_dir
86
87 def __write_fstab(self, image_rootfs):
88 """overriden to generate fstab (temporarily) in rootfs. This
89 is called from mount_instroot, make sure it doesn't get called
90 from BaseImage.mount()"""
91 if image_rootfs is None:
92 return None
93
94 fstab = image_rootfs + "/etc/fstab"
95 if not os.path.isfile(fstab):
96 return None
97
98 parts = self._get_parts()
99
100 self._save_fstab(fstab)
101 fstab_lines = self._get_fstab(fstab, parts)
102 self._update_fstab(fstab_lines, parts)
103 self._write_fstab(fstab, fstab_lines)
104
105 return fstab
106
107 def _update_fstab(self, fstab_lines, parts):
108 """Assume partition order same as in wks"""
109 for num, p in enumerate(parts, 1):
110 if not p.mountpoint or p.mountpoint == "/" or p.mountpoint == "/boot":
111 continue
112 if self._ptable_format == 'msdos' and num > 3:
113 device_name = "/dev/" + p.disk + str(num + 1)
114 else:
115 device_name = "/dev/" + p.disk + str(num)
116 fstab_entry = device_name + "\t" + p.mountpoint + "\t" + p.fstype + "\tdefaults\t0\t0\n"
117 fstab_lines.append(fstab_entry)
118
119 def _write_fstab(self, fstab, fstab_lines):
120 fstab = open(fstab, "w")
121 for line in fstab_lines:
122 fstab.write(line)
123 fstab.close()
124
125 def _save_fstab(self, fstab):
126 """Save the current fstab in rootfs"""
127 shutil.copyfile(fstab, fstab + ".orig")
128
129 def _restore_fstab(self, fstab):
130 """Restore the saved fstab in rootfs"""
131 if fstab is None:
132 return
133 shutil.move(fstab + ".orig", fstab)
134
135 def _get_fstab(self, fstab, parts):
136 """Return the desired contents of /etc/fstab."""
137 f = open(fstab, "r")
138 fstab_contents = f.readlines()
139 f.close()
140
141 return fstab_contents
142
143 def set_bootimg_dir(self, bootimg_dir):
144 """
145 Accessor for bootimg_dir, the actual location used for the source
146 of the bootimg. Should be set by source plugins (only if they
147 change the default bootimg source) so the correct info gets
148 displayed for print_outimage_info().
149 """
150 self.bootimg_dir = bootimg_dir
151
152 def _get_parts(self):
153 if not self.ks:
154 raise CreatorError("Failed to get partition info, "
155 "please check your kickstart setting.")
156
157 # Set a default partition if no partition is given out
158 if not self.ks.handler.partition.partitions:
159 partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
160 args = partstr.split()
161 pd = self.ks.handler.partition.parse(args[1:])
162 if pd not in self.ks.handler.partition.partitions:
163 self.ks.handler.partition.partitions.append(pd)
164
165 # partitions list from kickstart file
166 return kickstart.get_partitions(self.ks)
167
168 def get_disk_names(self):
169 """ Returns a list of physical target disk names (e.g., 'sdb') which
170 will be created. """
171
172 if self._disk_names:
173 return self._disk_names
174
175 #get partition info from ks handler
176 parts = self._get_parts()
177
178 for i in range(len(parts)):
179 if parts[i].disk:
180 disk_name = parts[i].disk
181 else:
182 raise CreatorError("Failed to create disks, no --ondisk "
183 "specified in partition line of ks file")
184
185 if parts[i].mountpoint and not parts[i].fstype:
186 raise CreatorError("Failed to create disks, no --fstype "
187 "specified for partition with mountpoint "
188 "'%s' in the ks file")
189
190 self._disk_names.append(disk_name)
191
192 return self._disk_names
193
194 def _full_name(self, name, extention):
195 """ Construct full file name for a file we generate. """
196 return "%s-%s.%s" % (self.name, name, extention)
197
198 def _full_path(self, path, name, extention):
199 """ Construct full file path to a file we generate. """
200 return os.path.join(path, self._full_name(name, extention))
201
202 def get_default_source_plugin(self):
203 """
204 The default source plugin i.e. the plugin that's consulted for
205 overall image generation tasks outside of any particular
206 partition. For convenience, we just hang it off the
207 bootloader handler since it's the one non-partition object in
208 any setup. By default the default plugin is set to the same
209 plugin as the /boot partition; since we hang it off the
210 bootloader object, the default can be explicitly set using the
211 --source bootloader param.
212 """
213 return self.ks.handler.bootloader.source
214
215 #
216 # Actual implemention
217 #
218 def _mount_instroot(self, base_on = None):
219 """
220 For 'wic', we already have our build artifacts and don't want
221 to loop mount anything to install into, we just create
222 filesystems from the artifacts directly and combine them into
223 a partitioned image.
224
225 We still want to reuse as much of the basic mic machinery
226 though; despite the fact that we don't actually do loop or any
227 other kind of mounting we still want to do many of the same
228 things to prepare images, so we basically just adapt to the
229 basic framework and reinterpret what 'mounting' means in our
230 context.
231
232 _instroot would normally be something like
233 /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for
234 installing packages, etc. We don't currently need to do that,
235 so we simplify life by just using /var/tmp/wic/build as our
236 workdir.
237 """
238 parts = self._get_parts()
239
240 self.__instimage = PartitionedMount(self._instroot)
241
242 for p in parts:
243 # as a convenience, set source to the boot partition source
244 # instead of forcing it to be set via bootloader --source
245 if not self.ks.handler.bootloader.source and p.mountpoint == "/boot":
246 self.ks.handler.bootloader.source = p.source
247
248 for p in parts:
249 # need to create the filesystems in order to get their
250 # sizes before we can add them and do the layout.
251 # PartitionedMount.mount() actually calls __format_disks()
252 # to create the disk images and carve out the partitions,
253 # then self.install() calls PartitionedMount.install()
254 # which calls __install_partitition() for each partition
255 # to dd the fs into the partitions. It would be nice to
256 # be able to use e.g. ExtDiskMount etc to create the
257 # filesystems, since that's where existing e.g. mkfs code
258 # is, but those are only created after __format_disks()
259 # which needs the partition sizes so needs them created
260 # before its called. Well, the existing setup is geared
261 # to installing packages into mounted filesystems - maybe
262 # when/if we need to actually do package selection we
263 # should modify things to use those objects, but for now
264 # we can avoid that.
265
266 p.install_pkgs(self, self.workdir, self.oe_builddir, self.rootfs_dir,
267 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
268
269 p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
270 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
271
272 fstab = self.__write_fstab(p.get_rootfs())
273 self._restore_fstab(fstab)
274
275 self.__instimage.add_partition(int(p.size),
276 p.disk,
277 p.mountpoint,
278 p.source_file,
279 p.fstype,
280 p.label,
281 fsopts = p.fsopts,
282 boot = p.active,
283 align = p.align,
284 part_type = p.part_type)
285 self.__instimage.layout_partitions(self._ptable_format)
286
287 self.__imgdir = self.workdir
288 for disk_name, disk in self.__instimage.disks.items():
289 full_path = self._full_path(self.__imgdir, disk_name, "direct")
290 msger.debug("Adding disk %s as %s with size %s bytes" \
291 % (disk_name, full_path, disk['min_size']))
292 disk_obj = fs_related.DiskImage(full_path, disk['min_size'])
293 self.__disks[disk_name] = disk_obj
294 self.__instimage.add_disk(disk_name, disk_obj)
295
296 self.__instimage.mount()
297
298 def install(self, repo_urls=None):
299 """
300 Install fs images into partitions
301 """
302 for disk_name, disk in self.__instimage.disks.items():
303 full_path = self._full_path(self.__imgdir, disk_name, "direct")
304 msger.debug("Installing disk %s as %s with size %s bytes" \
305 % (disk_name, full_path, disk['min_size']))
306 self.__instimage.install(full_path)
307
308 def configure(self, repodata = None):
309 """
310 Configure the system image according to kickstart.
311
312 For now, it just prepares the image to be bootable by e.g.
313 creating and installing a bootloader configuration.
314 """
315 source_plugin = self.get_default_source_plugin()
316 if source_plugin:
317 self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods)
318 for disk_name, disk in self.__instimage.disks.items():
319 self._source_methods["do_install_disk"](disk, disk_name, self,
320 self.workdir,
321 self.oe_builddir,
322 self.bootimg_dir,
323 self.kernel_dir,
324 self.native_sysroot)
325
326 def print_outimage_info(self):
327 """
328 Print the image(s) and artifacts used, for the user.
329 """
330 msg = "The new image(s) can be found here:\n"
331
332 parts = self._get_parts()
333
334 for disk_name, disk in self.__instimage.disks.items():
335 full_path = self._full_path(self.__imgdir, disk_name, "direct")
336 msg += ' %s\n\n' % full_path
337
338 msg += 'The following build artifacts were used to create the image(s):\n'
339 for p in parts:
340 if p.get_rootfs() is None:
341 continue
342 if p.mountpoint == '/':
343 str = ':'
344 else:
345 str = '["%s"]:' % p.label
346 msg += ' ROOTFS_DIR%s%s\n' % (str.ljust(20), p.get_rootfs())
347
348 msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
349 msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
350 msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
351
352 msger.info(msg)
353
354 def _get_boot_config(self):
355 """
356 Return the rootdev/root_part_uuid (if specified by
357 --part-type)
358
359 Assume partition order same as in wks
360 """
361 rootdev = None
362 root_part_uuid = None
363 parts = self._get_parts()
364 for num, p in enumerate(parts, 1):
365 if p.mountpoint == "/":
366 part = ''
367 if p.disk.startswith('mmcblk'):
368 part = 'p'
369
370 if self._ptable_format == 'msdos' and num > 3:
371 rootdev = "/dev/%s%s%-d" % (p.disk, part, num + 1)
372 else:
373 rootdev = "/dev/%s%s%-d" % (p.disk, part, num)
374 root_part_uuid = p.part_type
375
376 return (rootdev, root_part_uuid)
377
378 def _unmount_instroot(self):
379 if not self.__instimage is None:
380 try:
381 self.__instimage.cleanup()
382 except MountError, err:
383 msger.warning("%s" % err)
384