Merge lp:~salgado/linaro-image-tools/partition-setup into lp:linaro-image-tools/11.11

Proposed by Guilherme Salgado
Status: Merged
Merged at revision: 192
Proposed branch: lp:~salgado/linaro-image-tools/partition-setup
Merge into: lp:linaro-image-tools/11.11
Diff against target: 1022 lines (+472/-230)
10 files modified
README (+2/-0)
linaro-media-create (+11/-89)
media_create/cmd_runner.py (+9/-5)
media_create/create_partitions.py (+26/-45)
media_create/partition_size.py (+0/-42)
media_create/partitions.py (+193/-0)
media_create/populate_boot.py (+9/-5)
media_create/tests/fixtures.py (+14/-3)
media_create/tests/test_media_create.py (+183/-41)
tests/integration.txt (+25/-0)
To merge this branch: bzr merge lp:~salgado/linaro-image-tools/partition-setup
Reviewer Review Type Date Requested Status
Martin Ohlsson (community) Approve
Review via email: mp+43520@code.launchpad.net

Description of the change

This is a rather large branch with lots of changes that were needed to port
some shell code that was interrelated and thus needed to be ported at once.

- Change cmd_runner.run() to return a Popen instance so that it's more
  flexible

- Simplify create_partitions now that it's only called from python code

- Create a new partitions.py module which provides a single function for doing
  everything related with partitioning. That's where most of the new code is,
  but everything should be self-explanatory (or have comments) there.

- Merge partition_size.py into the above file

- Extend MockCmdRunnerPopen so that it stores the arguments passed in to each
  of its invocations; useful when testing functions that run multiple
  subprocesses.

- Create a new tests/integration.txt file which just lists some integration
  tests that are useful but must not be automated.

To post a comment you must log in.
Revision history for this message
Martin Ohlsson (martin-ohlson) wrote :

> === modified file 'media_create/create_partitions.py'
> --- media_create/create_partitions.py 2010-12-09 19:58:24 +0000
> +++ media_create/create_partitions.py 2010-12-13 14:51:10 +0000
>
> cmd_runner.run(
> - ['parted', '-s', device, 'mklabel', 'msdos'], as_root=True)
> + ['parted', '-s', media.path, 'mklabel', 'msdos'], as_root=True)
> + # It sems to be necessary to sleep a bit here to avoid a race
> + # condition with the sfdisk commands executed below. Try removing it
> + # and running the integration tests to see how it fails.
> + time.sleep(0.5)

Isn't it possible to wait() for the parted command to finish?

/Martin

Revision history for this message
Peter Maydell (pmaydell) wrote :

146 - # Round the size of the raw disk image up to a multiple of 256K
147 - # so it is an exact number of SD card erase blocks in length.
148 - # Otherwise Linux under qemu cannot access the last part of the
149 - # card and is likely to complain that the last partition on the
150 - # disk has been truncated.
151 - IMAGE_SIZE=$(((($IMAGE_SIZE-1)/(1024*256)+1)*(1024*256)))

So this bug fix has gone from the shell script but I can't find the bit in the Python which implements it. Am I missing something?

Revision history for this message
Guilherme Salgado (salgado) wrote :

On Mon, 2010-12-13 at 16:25 +0000, Martin Ohlsson wrote:
> > === modified file 'media_create/create_partitions.py'
> > --- media_create/create_partitions.py 2010-12-09 19:58:24 +0000
> > +++ media_create/create_partitions.py 2010-12-13 14:51:10 +0000
> >
> > cmd_runner.run(
> > - ['parted', '-s', device, 'mklabel', 'msdos'], as_root=True)
> > + ['parted', '-s', media.path, 'mklabel', 'msdos'], as_root=True)
> > + # It sems to be necessary to sleep a bit here to avoid a race
> > + # condition with the sfdisk commands executed below. Try removing it
> > + # and running the integration tests to see how it fails.
> > + time.sleep(0.5)
>
> Isn't it possible to wait() for the parted command to finish?

Very good catch! Friday I updated a bunch of callsites that were
expecting cmd_runner.run() to wait() for them but forgot this one and
today it didn't occur to me that the race could be a consequence of the
now-missing wait() call. I've updated this and a few other callsites
that I missed to update when I changed cmd_runner.run(). Thanks!

193. By Guilherme Salgado

Update a few cmd_runner.run() callsites that were not wait()ing

Revision history for this message
Guilherme Salgado (salgado) wrote :

On Mon, 2010-12-13 at 16:39 +0000, Peter Maydell wrote:
> 146 - # Round the size of the raw disk image up to a multiple of 256K
> 147 - # so it is an exact number of SD card erase blocks in length.
> 148 - # Otherwise Linux under qemu cannot access the last part of the
> 149 - # card and is likely to complain that the last partition on the
> 150 - # disk has been truncated.
> 151 - IMAGE_SIZE=$(((($IMAGE_SIZE-1)/(1024*256)+1)*(1024*256)))
>
> So this bug fix has gone from the shell script but I can't find the bit in the Python which implements it. Am I missing something

Looks like it was lost in translation indeed. However, qemu was able to
boot an image I generated using the code on this branch. Would the lack
of this fix render the image unusable or should I expect to see other
symptoms? Is there any chance the fix is no longer needed or is needed
only for certain versions of qemu?

Revision history for this message
Peter Maydell (pmaydell) wrote :

The symptom would be that on bootup fsck complains about the filesystem being larger than the partition. It may well happen only for certain image sizes -- if you happened to get l-m-c to use an image size that's already a multiple of 256K then you wouldn't see any effect.

(We've also had problems reported even with the bugfix, eg https://bugs.launchpad.net/linux-linaro/+bug/673335 -- so something's still not quite right.)

However, I think it would be better to deal with possible improvements to that bug fix elsewhere, not as part of this conversion-to-python process.

Revision history for this message
James Westby (james-w) wrote :

Hi,

I haven't read this in the detail that I would like, but I couldn't see any issues.

Could I suggest that your integration tests tell you what the expected result should
be?

Thanks,

James

Revision history for this message
Martin Ohlsson (martin-ohlson) wrote :

Hi Guilherme,

I read the code a couple of times without noticing anything strange besides my previous comment.

/Martin

review: Approve
Revision history for this message
Guilherme Salgado (salgado) wrote :

On Mon, 2010-12-13 at 17:37 +0000, Peter Maydell wrote:
> The symptom would be that on bootup fsck complains about the
> filesystem being larger than the partition. It may well happen only
> for certain image sizes -- if you happened to get l-m-c to use an
> image size that's already a multiple of 256K then you wouldn't see any
> effect.
>
> (We've also had problems reported even with the bugfix, eg
> https://bugs.launchpad.net/linux-linaro/+bug/673335 -- so something's
> still not quite right.)
>
> However, I think it would be better to deal with possible improvements
> to that bug fix elsewhere, not as part of this conversion-to-python
> process.

Agreed, but if I knew how to reproduce I could add that as a comment in
the code to save time when somebody gets to work on those specific bits.

The bug-fix in now present in the python version.

194. By Guilherme Salgado

A few tweaks a bug fixes suggested by reviewers

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2010-12-03 13:22:03 +0000
3+++ README 2010-12-14 13:31:33 +0000
4@@ -11,6 +11,8 @@
5 - python-debian >= 0.1.16ubuntu1
6 - python-argparse
7 - dpkg-dev
8+ - python-parted
9+ - python-dbus
10
11 And run the following command:
12
13
14=== modified file 'linaro-media-create'
15--- linaro-media-create 2010-12-12 20:48:29 +0000
16+++ linaro-media-create 2010-12-14 13:31:33 +0000
17@@ -34,7 +34,7 @@
18 MMC_PART_OFFSET=0
19 BOOTFS_STEP="create_boot_cmd populate_boot"
20 ROOTFS_STEP="populate_rootfs"
21-PARTITION_STEP="create_partitions"
22+SHOULD_CREATE_PARTITIONS="yes"
23 mmc_option="0:1"
24 boot_args_options="rootwait ro"
25 HWPACK_FORCE_YES="no"
26@@ -272,13 +272,13 @@
27 BOOTFS_STEP=""
28 ;;
29 --no-part)
30- PARTITION_STEP=""
31+ SHOULD_CREATE_PARTITIONS="no"
32 ;;
33 esac
34 shift
35 done
36
37-DEPLOY_STEPS="$PARTITION_STEP prepare_partitions $BOOTFS_STEP $ROOTFS_STEP"
38+DEPLOY_STEPS="setup_partitions $BOOTFS_STEP $ROOTFS_STEP"
39
40 python -m media_create.ensure_command mkimage uboot-mkimage
41 python -m media_create.ensure_command uuidgen uuid-runtime
42@@ -309,33 +309,6 @@
43
44 RFS_UUID=`uuidgen -r`
45
46-get_device_by_id() {
47- if [ ! "${IMAGE_FILE}" ]; then
48- for device in /dev/disk/by-id/*; do
49- if [ `realpath $device` = $DEVICE ]; then
50- case "$device" in
51- *-part[0-9]*)
52- echo "device $DEVICE must not be a partition part ($device)" >&2
53- exit 1
54- ;;
55- esac
56- for part_id in `ls "$device-part"*`; do
57- part=`realpath $part_id`
58- part_no=`echo $part_id | sed -e 's/.*-part//g'`
59- # echo "part $part_no found: $part_id" 1>&2
60- # if there is a non-fs-data part (e.g. mx51evk), MMC_PART_OFFSET=1
61- if test "$part_no" = $((1 + $MMC_PART_OFFSET)); then
62- BOOTFS=$part
63- elif test "$part_no" = $((2 + $MMC_PART_OFFSET)) ; then
64- ROOTFS=$part
65- fi
66- done
67- break
68- fi
69- done
70- fi
71-}
72-
73 install_hwpack() {
74 chroot=${DIR}/binary
75 # Make sure we unmount /proc in the chroot or else it can't be moved to the
76@@ -445,33 +418,22 @@
77 loop_devices="$loop_devices $1"
78 }
79
80-create_partitions() {
81- python -m media_create.create_partitions "${DEVIMAGE}" \
82- "${DEVICE-$IMAGE_FILE}" "$FAT_SIZE" "$HEADS" "$SECTORS" "$CYLINDER_ARG"
83+setup_partitions() {
84+ boot_and_root_devices=$(python -m media_create.partitions \
85+ "$DEVIMAGE" "${DEVICE-$IMAGE_FILE}" "$FAT_SIZE" "$IMAGE_SIZE" \
86+ "$SHOULD_CREATE_PARTITIONS")
87+ eval $boot_and_root_devices
88
89 if [ "${IMAGE_FILE}" ]; then
90- sizes_and_offsets=$(python -m media_create.partition_size "${IMAGE_FILE}")
91- eval $sizes_and_offsets
92- BOOTFS=$(sudo losetup -f --show "$IMAGE_FILE" --offset $VFATOFFSET \
93- --sizelimit $VFATSIZE)
94 register_loopback "$BOOTFS"
95- ROOTFS=$(sudo losetup -f --show "$IMAGE_FILE" --offset $ROOTOFFSET \
96- --sizelimit $ROOTSIZE)
97 register_loopback "$ROOTFS"
98 fi
99+
100+ prepare_partitions
101 }
102
103-
104 prepare_partitions() {
105
106- # sync and sleep a bit as the partitions may have been created in the
107- # previous step.
108- echo -n "waiting for partitioning to settle ..."
109- sync
110- sleep 3
111- echo "done."
112- get_device_by_id
113-
114 if [ "$BOOTFS_STEP" ]; then
115 echo ""
116 echo "Formating Boot Partition"
117@@ -565,6 +527,7 @@
118 echo "Creating /etc/flash-kernel.conf"
119 echo ""
120
121+ TARGET_BOOT_DEV=/dev/mmcblk0p$(( 1 + $MMC_PART_OFFSET ))
122 echo "UBOOT_PART=${TARGET_BOOT_DEV}" | sudo tee "${ROOT_DISK}/etc/flash-kernel.conf" >/dev/null
123
124 sync
125@@ -572,39 +535,6 @@
126 sudo umount "${ROOT_DISK}" || true
127 }
128
129-calculatesize() {
130- IMAGE_SIZE=${IMAGE_SIZE/G/M*1024}
131- IMAGE_SIZE=${IMAGE_SIZE/M/K*1024}
132- IMAGE_SIZE=${IMAGE_SIZE/K/*1024}
133- IMAGE_SIZE=$(($IMAGE_SIZE))
134-}
135-
136-setup_sizes() {
137- HEADS=255
138- SECTORS=63
139- SECTORSIZE=512
140- if [ "${IMAGE_FILE}" ]; then
141- calculatesize
142- CYLINDERSIZE=$(($HEADS*$SECTORS*$SECTORSIZE))
143- CYLINDERS=$(($IMAGE_SIZE/$CYLINDERSIZE))
144- CYLINDER_ARG="-C $CYLINDERS"
145- IMAGE_SIZE=$(($CYLINDERS*$CYLINDERSIZE))
146- # Round the size of the raw disk image up to a multiple of 256K
147- # so it is an exact number of SD card erase blocks in length.
148- # Otherwise Linux under qemu cannot access the last part of the
149- # card and is likely to complain that the last partition on the
150- # disk has been truncated.
151- IMAGE_SIZE=$(((($IMAGE_SIZE-1)/(1024*256)+1)*(1024*256)))
152- else
153- CYLINDER_ARG=""
154- fi
155-}
156-
157-# XXX: Apparently there's no need to run qemu-img as root.
158-setup_image() {
159- sudo qemu-img create -f raw "$IMAGE_FILE" $IMAGE_SIZE
160-}
161-
162 remove_image_file() {
163 if [ -f "$IMAGE_FILE" ]; then
164 rm -v $IMAGE_FILE
165@@ -659,11 +589,6 @@
166 trap cleanup_handler EXIT
167 trap signal_handler INT TERM
168
169-setup_sizes
170-if [ "${IMAGE_FILE}" ]; then
171- setup_image
172-fi
173-
174 serial_opts=""
175 if [ "$consoles" ]; then
176 for c in ${consoles}; do
177@@ -767,9 +692,6 @@
178 fi
179 fi
180
181-TARGET_BOOT_DEV=/dev/mmcblk0p$(( 1 + $MMC_PART_OFFSET ))
182-TARGET_ROOT_DEV=/dev/mmcblk0p$(( 2 + $MMC_PART_OFFSET ))
183-
184 if [ ! "${DEVICE}" -a ! "${IMAGE_FILE}" ]; then
185 usage
186 fi
187
188=== modified file 'media_create/cmd_runner.py'
189--- media_create/cmd_runner.py 2010-12-09 19:17:14 +0000
190+++ media_create/cmd_runner.py 2010-12-14 13:31:33 +0000
191@@ -1,13 +1,19 @@
192 import subprocess
193
194
195-def run(args, as_root=False, stdout=None):
196+def run(args, as_root=False, stdin=None, stdout=None, stderr=None):
197 """Run the given command as a sub process.
198
199+ Return a Popen instance.
200+
201+ Callsites must wait() or communicate() with the returned Popen instance.
202+
203 :param command: A list or tuple containing the command to run and the
204 arguments that should be passed to it.
205 :param as_root: Should the given command be run as root (with sudo)?
206- :param stdout: Same as subprocess.Popen().
207+ :param stdin: Same as in subprocess.Popen().
208+ :param stdout: Same as in subprocess.Popen().
209+ :param stderr: Same as in subprocess.Popen().
210 """
211 assert isinstance(args, (list, tuple)), (
212 "The command to run must be a list or tuple, found: %s" % type(args))
213@@ -16,9 +22,7 @@
214 if as_root:
215 args = args[:]
216 args.insert(0, 'sudo')
217- proc = Popen(args, stdout=stdout)
218- proc.wait()
219- return proc.returncode
220+ return Popen(args, stdin=stdin, stdout=stdout, stderr=stderr)
221
222
223 class Popen(subprocess.Popen):
224
225=== modified file 'media_create/create_partitions.py'
226--- media_create/create_partitions.py 2010-12-09 19:58:24 +0000
227+++ media_create/create_partitions.py 2010-12-14 13:31:33 +0000
228@@ -1,11 +1,12 @@
229 import subprocess
230-import sys
231+import time
232
233 from media_create import cmd_runner
234
235
236-def run_sfdisk_commands(commands, heads, sectors, cylinders_arg, device,
237- as_root=True):
238+# TODO: Merge this file with setup_partitions.py.
239+def run_sfdisk_commands(commands, heads, sectors, cylinders, device,
240+ as_root=True, stderr=None):
241 """Run the given commands under sfdisk.
242
243 :param commands: A string of sfdisk commands; each on a separate line.
244@@ -15,41 +16,33 @@
245 '-D',
246 '-H', str(heads),
247 '-S', str(sectors)]
248- if cylinders_arg:
249- args.append(cylinders_arg)
250+ if cylinders is not None:
251+ args.extend(['-C', str(cylinders)])
252 args.append(device)
253- # XXX: There's some stuff duplicated here from cmd_runner.run() but I
254- # don't see an easy way to consolidate them as a single function, so I'll
255- # leave it for later.
256- if as_root:
257- args = args[:]
258- args.insert(0, 'sudo')
259- proc = cmd_runner.Popen(
260- args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
261- stderr=subprocess.PIPE)
262+ proc = cmd_runner.run(
263+ args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr,
264+ as_root=as_root)
265 return proc.communicate("%s\n" % commands)
266
267
268-def create_partitions(board, device, fat_size, heads, sectors, cylinders_arg):
269- """Partition the given device according to the board requirements.
270+def create_partitions(board, media, fat_size, heads, sectors, cylinders=None):
271+ """Partition the given media according to the board requirements.
272
273 :param board: A string with the board type (e.g. beagle, panda, etc)
274- :param device: A string containing the path to the device to partition.
275+ :param media: A setup_partitions.Media object to partition.
276 :param fat_size: The type of FATs used in the boot partition (16 or 32).
277 :param heads: Number of heads to use in the disk geometry of
278 partitions.
279 :param sectors: Number of sectors to use in the disk geometry of
280 partitions.
281- :param cylinders_arg: A string of the form "-C NN" containing the number
282- of cylinders to use in the disk geometry of partitions.
283+ :param cylinders: The number of cylinders to pass to sfdisk's -C argument.
284+ If None the -C argument is not passed.
285 """
286- stdout = []
287- stderr = []
288- is_block_device = device.startswith('/dev/')
289- if is_block_device:
290+ if media.is_block_device:
291 # Overwrite any existing partition tables with a fresh one.
292- cmd_runner.run(
293- ['parted', '-s', device, 'mklabel', 'msdos'], as_root=True)
294+ proc = cmd_runner.run(
295+ ['parted', '-s', media.path, 'mklabel', 'msdos'], as_root=True)
296+ proc.wait()
297
298 if fat_size == 32:
299 partition_type = '0x0C'
300@@ -60,28 +53,16 @@
301 # Create a one cylinder partition for fixed-offset bootloader data at
302 # the beginning of the image (size is one cylinder, so 8224768 bytes
303 # with the first sector for MBR).
304- out, err = run_sfdisk_commands(
305- ',1,0xDA', heads, sectors, cylinders_arg, device)
306- stdout.append(out)
307- stderr.append(err)
308+ run_sfdisk_commands(',1,0xDA', heads, sectors, cylinders, media.path)
309
310 # Create a VFAT or FAT16 partition of 9 cylinders (74027520 bytes, ~70
311 # MiB), followed by a Linux-type partition containing the rest of the free
312 # space.
313 sfdisk_cmd = ',9,%s,*\n,,,-' % partition_type
314- out, err = run_sfdisk_commands(
315- sfdisk_cmd, heads, sectors, cylinders_arg, device)
316- stdout.append(out)
317- stderr.append(err)
318- return "\n".join(stdout), "\n".join(stderr)
319-
320-
321-if __name__ == "__main__":
322- board, device, fat_size, heads, sectors, cylinders_arg = sys.argv[1:]
323- fat_size = int(fat_size)
324- heads = int(heads)
325- sectors = int(sectors)
326- stdout, stderr = create_partitions(
327- board, device, fat_size, heads, sectors, cylinders_arg)
328- print stdout
329- print stderr
330+ run_sfdisk_commands(sfdisk_cmd, heads, sectors, cylinders, media.path)
331+
332+ # Sync and sleep to wait for the partition to settle.
333+ cmd_runner.run(['sync']).wait()
334+ # Sleeping just 1 second seems to be enough here, but if we start getting
335+ # errors because the disk is not partitioned then we should revisit this.
336+ time.sleep(1)
337
338=== removed file 'media_create/partition_size.py'
339--- media_create/partition_size.py 2010-12-08 19:54:08 +0000
340+++ media_create/partition_size.py 1970-01-01 00:00:00 +0000
341@@ -1,42 +0,0 @@
342-import sys
343-
344-from parted import (
345- Device,
346- Disk,
347- )
348-
349-
350-def calculate_partition_size_and_offset(device):
351- """Return the size and offset of the boot and root partitions.
352-
353- Both the size and offset are in sectors.
354-
355- :param device: A string containing the path to the device.
356- :return: A 4-tuple containing the offset and size of the boot partition
357- followed by the offset and size of the root partition.
358- """
359- disk = Disk(Device(device))
360- vfat_partition = None
361- for partition in disk.partitions:
362- if 'boot' in partition.getFlagsAsString():
363- geometry = partition.geometry
364- vfat_offset = geometry.start * 512
365- vfat_size = geometry.length * 512
366- vfat_partition = partition
367-
368- assert vfat_partition is not None, (
369- "Couldn't find boot partition on %s" % device)
370- linux_partition = vfat_partition.nextPartition()
371- geometry = linux_partition.geometry
372- linux_offset = geometry.start * 512
373- linux_size = geometry.length * 512
374- return vfat_size, vfat_offset, linux_size, linux_offset
375-
376-
377-if __name__ == "__main__":
378- vsize, voffset, rsize, roffset = calculate_partition_size_and_offset(
379- sys.argv[1])
380- # These values need to be assigned to shell variables in our script, so we
381- # make its job easier by printing something it can pass on to eval.
382- print "VFATSIZE=%s VFATOFFSET=%s ROOTSIZE=%s ROOTOFFSET=%s" % (
383- vsize, voffset, rsize, roffset)
384
385=== added file 'media_create/partitions.py'
386--- media_create/partitions.py 1970-01-01 00:00:00 +0000
387+++ media_create/partitions.py 2010-12-14 13:31:33 +0000
388@@ -0,0 +1,193 @@
389+import subprocess
390+import sys
391+
392+import dbus
393+from parted import (
394+ Device,
395+ Disk,
396+ )
397+
398+from media_create import cmd_runner
399+from media_create.create_partitions import create_partitions
400+
401+
402+HEADS = 255
403+SECTORS = 63
404+SECTOR_SIZE = 512 # bytes
405+CYLINDER_SIZE = HEADS * SECTORS * SECTOR_SIZE
406+
407+
408+# I wonder if it'd make sense to convert this into a small shim which calls
409+# the appropriate function for the given type of device? I think it's still
410+# small enough that there's not much benefit in doing that, but if it grows we
411+# might want to do it.
412+def setup_partitions(board, media, fat_size, image_size,
413+ should_create_partitions):
414+ """Make sure the given device is partitioned to boot the given board.
415+
416+ :param board: The board's name, as a string.
417+ :param media: The Media we should partition.
418+ :param fat_size: The FAT size (16 or 32) for the boot partition.
419+ :param image_size: The size of the image file, in case we're setting up a
420+ QEMU image.
421+ :param should_create_partitions: A string with values "yes" or "no",
422+ specifying whether or not we should erase existing partitions and
423+ create new ones.
424+ """
425+ cylinders = None
426+ if not media.is_block_device:
427+ image_size_in_bytes = convert_size_to_bytes(image_size)
428+ cylinders = image_size_in_bytes / CYLINDER_SIZE
429+ image_size_in_bytes = cylinders * CYLINDER_SIZE
430+ proc = cmd_runner.run(
431+ ['qemu-img', 'create', '-f', 'raw', media.path, image_size],
432+ stdout=open('/dev/null', 'w'))
433+ proc.wait()
434+
435+ if should_create_partitions == "yes":
436+ create_partitions(board, media, fat_size, HEADS, SECTORS, cylinders)
437+
438+ if media.is_block_device:
439+ return get_boot_and_root_partitions_for_media(media)
440+ else:
441+ return get_boot_and_root_loopback_devices(media.path)
442+
443+
444+def get_boot_and_root_loopback_devices(image_file):
445+ """Return the boot and root loopback devices for the given image file.
446+
447+ Register the loopback devices as well.
448+ """
449+ vfat_size, vfat_offset, linux_size, linux_offset = (
450+ calculate_partition_size_and_offset(image_file))
451+ proc = cmd_runner.run(
452+ ['losetup', '-f', '--show', image_file, '--offset',
453+ str(vfat_offset), '--sizelimit', str(vfat_size)],
454+ stdout=subprocess.PIPE, as_root=True)
455+ boot_device, _ = proc.communicate()
456+ proc = cmd_runner.run(
457+ ['losetup', '-f', '--show', image_file, '--offset',
458+ str(linux_offset), '--sizelimit', str(linux_size)],
459+ stdout=subprocess.PIPE, as_root=True)
460+ root_device, _ = proc.communicate()
461+
462+ return boot_device, root_device
463+
464+
465+def calculate_partition_size_and_offset(image_file):
466+ """Return the size and offset of the boot and root partitions.
467+
468+ Both the size and offset are in sectors.
469+
470+ :param image_file: A string containing the path to the image_file.
471+ :return: A 4-tuple containing the offset and size of the boot partition
472+ followed by the offset and size of the root partition.
473+ """
474+ # Here we can use parted.Device to read the partitions because we're
475+ # reading from a regular file rather than a block device. If it was a
476+ # block device we'd need root rights.
477+ disk = Disk(Device(image_file))
478+ vfat_partition = None
479+ for partition in disk.partitions:
480+ if 'boot' in partition.getFlagsAsString():
481+ geometry = partition.geometry
482+ vfat_offset = geometry.start * 512
483+ vfat_size = geometry.length * 512
484+ vfat_partition = partition
485+
486+ assert vfat_partition is not None, (
487+ "Couldn't find boot partition on %s" % image_file)
488+ linux_partition = vfat_partition.nextPartition()
489+ geometry = linux_partition.geometry
490+ linux_offset = geometry.start * 512
491+ linux_size = geometry.length * 512
492+ return vfat_size, vfat_offset, linux_size, linux_offset
493+
494+
495+def get_boot_and_root_partitions_for_media(media):
496+ """Return the device files for the boot and root partitions of media.
497+
498+ If the given media has 2 partitions, the first is boot and the second is
499+ root. If there are 3 partitions, the second is boot and third is root.
500+
501+ If there are any other number of partitions, ValueError is raised.
502+
503+ This function must only be used for block devices.
504+ """
505+ assert media.is_block_device, (
506+ "This function must only be used for block devices")
507+
508+ partition_count = _get_partition_count(media)
509+
510+ if partition_count == 2:
511+ partition_offset = 0
512+ elif partition_count == 3:
513+ partition_offset = 1
514+ else:
515+ raise ValueError(
516+ "Unexpected number of partitions on %s: %d" % (
517+ media.path, partition_count))
518+
519+ boot_partition = "%s%d" % (media.path, 1 + partition_offset)
520+ root_partition = "%s%d" % (media.path, 2 + partition_offset)
521+ return boot_partition, root_partition
522+
523+
524+def _get_partition_count(media):
525+ """Return the number of partitions on the given media."""
526+ # We could do the same easily using python-parted but it requires root
527+ # rights to read block devices, so we use UDisks here.
528+ bus = dbus.SystemBus()
529+ udisks = dbus.Interface(
530+ bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks"),
531+ 'org.freedesktop.UDisks')
532+ device_path = udisks.get_dbus_method('FindDeviceByDeviceFile')(media.path)
533+ device = bus.get_object("org.freedesktop.UDisks", device_path)
534+ return device.Get(
535+ device_path, 'partition-table-count',
536+ dbus_interface='org.freedesktop.DBus.Properties')
537+
538+
539+def convert_size_to_bytes(size):
540+ """Convert a size string in Kbytes, Mbytes or Gbytes to bytes."""
541+ unit = size[-1].upper()
542+ real_size = int(size[:-1])
543+ if unit == 'K':
544+ real_size = real_size * 1024
545+ elif unit == 'M':
546+ real_size = real_size * 1024 * 1024
547+ elif unit == 'G':
548+ real_size = real_size * 1024 * 1024 * 1024
549+ else:
550+ raise ValueError("Unknown size format: %s. Use K[bytes], M[bytes] "
551+ "or G[bytes]" % size)
552+
553+ # Round the size of the raw disk image up to a multiple of 256K so it is
554+ # an exact number of SD card erase blocks in length. Otherwise Linux
555+ # under qemu cannot access the last part of the card and is likely to
556+ # complain that the last partition on the disk has been truncated. This
557+ # doesn't appear to work in all cases, though, as can be seen on
558+ # https://bugs.launchpad.net/linux-linaro/+bug/673335.
559+ if real_size % (1024 * 256):
560+ cylinders = real_size / CYLINDER_SIZE
561+ real_size = cylinders * CYLINDER_SIZE
562+ real_size = ((((real_size - 1) / (1024 * 256)) + 1) * (1024 * 256))
563+
564+ return real_size
565+
566+
567+class Media(object):
568+ """A representation of the media where Linaro will be installed."""
569+
570+ def __init__(self, path):
571+ self.path = path
572+ self.is_block_device = path.startswith('/dev/')
573+
574+
575+if __name__ == "__main__":
576+ board, device, fat_size, image_size, should_create_partitions = (
577+ sys.argv[1:])
578+ fat_size = int(fat_size)
579+ boot, root = setup_partitions(
580+ board, Media(device), fat_size, image_size, should_create_partitions)
581+ print "BOOTFS=%s ROOTFS=%s" % (boot, root)
582
583=== modified file 'media_create/populate_boot.py'
584--- media_create/populate_boot.py 2010-12-08 13:09:27 +0000
585+++ media_create/populate_boot.py 2010-12-14 13:31:33 +0000
586@@ -16,7 +16,9 @@
587 '-n', name,
588 '-d', img_data,
589 img]
590- return cmd_runner.run(cmd, as_root=as_root, stdout=stdout)
591+ proc = cmd_runner.run(cmd, as_root=as_root, stdout=stdout)
592+ proc.wait()
593+ return proc.returncode
594
595
596 def _get_file_matching(regex):
597@@ -58,24 +60,26 @@
598
599
600 def install_mx51evk_boot_loader(imx_file, boot_device_or_file):
601- cmd_runner.run([
602+ proc = cmd_runner.run([
603 "dd",
604 "if=%s" % imx_file,
605 "of=%s" % boot_device_or_file,
606 "bs=1024",
607 "seek=1",
608 "conv=notrunc"], as_root=True)
609+ proc.wait()
610
611
612 def install_omap_boot_loader(mlo_file, boot_disk):
613- cmd_runner.run(["cp", "-v", mlo_file, boot_disk], as_root=True)
614+ cmd_runner.run(["cp", "-v", mlo_file, boot_disk], as_root=True).wait()
615 # XXX: Is this really needed?
616- cmd_runner.run(["sync"])
617+ cmd_runner.run(["sync"]).wait()
618
619
620 def make_boot_ini(boot_script, boot_disk):
621- cmd_runner.run(
622+ proc = cmd_runner.run(
623 ["cp", "-v", boot_script, "%s/boot.ini" % boot_disk], as_root=True)
624+ proc.wait()
625
626
627 def populate_boot(board, sub_arch, load_addr, uboot_parts_dir, boot_disk,
628
629=== modified file 'media_create/tests/fixtures.py'
630--- media_create/tests/fixtures.py 2010-12-09 19:58:24 +0000
631+++ media_create/tests/fixtures.py 2010-12-14 13:31:33 +0000
632@@ -65,12 +65,23 @@
633
634 class MockCmdRunnerPopen(object):
635 """A mock for cmd_runner.Popen() which stores the args given to it."""
636- args = None
637- def __call__(self, args, **kwargs):
638- self.args = args
639+ calls = None
640+ def __call__(self, cmd, *args, **kwargs):
641+ if self.calls is None:
642+ self.calls = []
643+ if isinstance(cmd, basestring):
644+ all_args = [cmd]
645+ else:
646+ all_args = cmd
647+ all_args.extend(args)
648+ self.calls.append(all_args)
649 self.returncode = 0
650 return self
651
652+ def communicate(self, input=None):
653+ self.wait()
654+ return '', ''
655+
656 def wait(self):
657 return self.returncode
658
659
660=== modified file 'media_create/tests/test_media_create.py'
661--- media_create/tests/test_media_create.py 2010-12-09 20:00:37 +0000
662+++ media_create/tests/test_media_create.py 2010-12-14 13:31:33 +0000
663@@ -4,6 +4,7 @@
664 import string
665 import subprocess
666 import sys
667+import time
668
669 from testtools import TestCase
670
671@@ -12,12 +13,20 @@
672 from media_create import cmd_runner
673 from media_create import ensure_command
674 from media_create import populate_boot
675+from media_create import partitions
676 from media_create.boot_cmd import create_boot_cmd
677 from media_create.create_partitions import (
678 create_partitions,
679 run_sfdisk_commands,
680 )
681-from media_create.partition_size import calculate_partition_size_and_offset
682+from media_create.partitions import (
683+ calculate_partition_size_and_offset,
684+ convert_size_to_bytes,
685+ get_boot_and_root_loopback_devices,
686+ get_boot_and_root_partitions_for_media,
687+ Media,
688+ setup_partitions,
689+ )
690 from media_create.populate_boot import (
691 make_boot_script,
692 make_uImage,
693@@ -133,24 +142,28 @@
694 def test_run(self):
695 fixture = MockCmdRunnerPopenFixture()
696 self.useFixture(fixture)
697- return_code = cmd_runner.run(['foo', 'bar', 'baz'])
698- self.assertEqual(0, return_code)
699- self.assertEqual(['foo', 'bar', 'baz'], fixture.mock.args)
700+ proc = cmd_runner.run(['foo', 'bar', 'baz'])
701+ self.assertEqual(0, proc.returncode)
702+ self.assertEqual([['foo', 'bar', 'baz']], fixture.mock.calls)
703
704 def test_run_as_root(self):
705 fixture = MockCmdRunnerPopenFixture()
706 self.useFixture(fixture)
707 cmd_runner.run(['foo', 'bar'], as_root=True)
708- self.assertEqual(['sudo', 'foo', 'bar'], fixture.mock.args)
709+ self.assertEqual([['sudo', 'foo', 'bar']], fixture.mock.calls)
710
711 def test_run_succeeds_on_zero_return_code(self):
712- return_code = cmd_runner.run(['true'])
713- self.assertEqual(0, return_code)
714+ proc = cmd_runner.run(['true'])
715+ # Need to wait() here as we're using the real Popen.
716+ proc.wait()
717+ self.assertEqual(0, proc.returncode)
718
719 def test_run_raises_exception_on_non_zero_return_code(self):
720+ def run_and_wait():
721+ proc = cmd_runner.run(['false'])
722+ proc.wait()
723 self.assertRaises(
724- cmd_runner.SubcommandNonZeroReturnValue,
725- cmd_runner.run, ['false'])
726+ cmd_runner.SubcommandNonZeroReturnValue, run_and_wait)
727
728 def test_run_must_be_given_list_as_args(self):
729 self.assertRaises(AssertionError, cmd_runner.run, 'true')
730@@ -181,7 +194,7 @@
731 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'kernel',
732 '-C', 'none', '-a', 'load_addr', '-e', 'load_addr', '-n', 'Linux',
733 '-d', 'parts_dir/vmlinuz-*-sub_arch', 'boot_disk/uImage']
734- self.assertEqual(expected, fixture.mock.args)
735+ self.assertEqual([expected], fixture.mock.calls)
736
737 def test_make_uInitrd(self):
738 self._mock_get_file_matching()
739@@ -191,7 +204,7 @@
740 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'ramdisk',
741 '-C', 'none', '-a', '0', '-e', '0', '-n', 'initramfs',
742 '-d', 'parts_dir/initrd.img-*-sub_arch', 'boot_disk/uInitrd']
743- self.assertEqual(expected, fixture.mock.args)
744+ self.assertEqual([expected], fixture.mock.calls)
745
746 def test_make_boot_script(self):
747 self._mock_get_file_matching()
748@@ -201,7 +214,7 @@
749 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'script',
750 '-C', 'none', '-a', '0', '-e', '0', '-n', 'boot script',
751 '-d', 'tmp_dir/boot.cmd', 'boot_script']
752- self.assertEqual(expected, fixture.mock.args)
753+ self.assertEqual([expected], fixture.mock.calls)
754
755 def test_get_file_matching(self):
756 prefix = ''.join(
757@@ -245,32 +258,46 @@
758
759 class TestCreatePartitions(TestCaseWithFixtures):
760
761+ media = Media('/dev/xdz')
762+
763+ def setUp(self):
764+ super(TestCreatePartitions, self).setUp()
765+ # Stub time.sleep() as create_partitions() use that.
766+ self.orig_sleep = time.sleep
767+ time.sleep = lambda s: None
768+
769+ def tearDown(self):
770+ super(TestCreatePartitions, self).tearDown()
771+ time.sleep = self.orig_sleep
772+
773 def test_create_partitions_for_mx51evk(self):
774 # For this board we create a one cylinder partition at the beginning.
775 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
776 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())
777
778- create_partitions('mx51evk', '/dev/sdz', 32, 255, 63, '')
779+ create_partitions('mx51evk', self.media, 32, 255, 63, '')
780
781 self.assertEqual(
782- ['sudo', 'parted', '-s', '/dev/sdz', 'mklabel', 'msdos'],
783- popen_fixture.mock.args)
784+ [['sudo', 'parted', '-s', self.media.path, 'mklabel', 'msdos'],
785+ ['sync']],
786+ popen_fixture.mock.calls)
787 self.assertEqual(
788- [(',1,0xDA', 255, 63, '', '/dev/sdz'),
789- (',9,0x0C,*\n,,,-', 255, 63, '', '/dev/sdz')],
790+ [(',1,0xDA', 255, 63, '', self.media.path),
791+ (',9,0x0C,*\n,,,-', 255, 63, '', self.media.path)],
792 sfdisk_fixture.mock.calls)
793
794 def test_create_partitions_for_beagle(self):
795 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
796 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())
797
798- create_partitions('beagle', '/dev/sdz', 32, 255, 63, '')
799+ create_partitions('beagle', self.media, 32, 255, 63, '')
800
801 self.assertEqual(
802- ['sudo', 'parted', '-s', '/dev/sdz', 'mklabel', 'msdos'],
803- popen_fixture.mock.args)
804+ [['sudo', 'parted', '-s', self.media.path, 'mklabel', 'msdos'],
805+ ['sync']],
806+ popen_fixture.mock.calls)
807 self.assertEqual(
808- [(',9,0x0C,*\n,,,-', 255, 63, '', '/dev/sdz')],
809+ [(',9,0x0C,*\n,,,-', 255, 63, '', self.media.path)],
810 sfdisk_fixture.mock.calls)
811
812 def test_create_partitions_with_img_file(self):
813@@ -278,11 +305,12 @@
814 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())
815
816 tempfile = self.createTempFileAsFixture()
817- create_partitions('beagle', tempfile, 32, 255, 63, '')
818+ create_partitions('beagle', Media(tempfile), 32, 255, 63, '')
819
820- # popen() was not called as there's no existing partition table for
821- # us to overwrite on the image file.
822- self.assertEqual(None, popen_fixture.mock.args)
823+ # Unlike the test for partitioning of a regular block device, in this
824+ # case parted was not called as there's no existing partition table
825+ # for us to overwrite on the image file.
826+ self.assertEqual([['sync']], popen_fixture.mock.calls)
827
828 self.assertEqual(
829 [(',9,0x0C,*\n,,,-', 255, 63, '', tempfile)],
830@@ -290,10 +318,13 @@
831
832 def test_run_sfdisk_commands(self):
833 tempfile = self.createTempFileAsFixture()
834- cmd_runner.run(['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],
835- stdout=subprocess.PIPE)
836+ proc = cmd_runner.run(
837+ ['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],
838+ stdout=subprocess.PIPE)
839+ proc.communicate()
840 stdout, stderr = run_sfdisk_commands(
841- ',1,0xDA', 5, 63, '', tempfile, as_root=False)
842+ ',1,0xDA', 5, 63, '', tempfile, as_root=False,
843+ stderr=subprocess.PIPE)
844 self.assertIn('Successfully wrote the new partition table', stdout)
845
846 def test_run_sfdisk_commands_raises_on_non_zero_returncode(self):
847@@ -301,21 +332,132 @@
848 self.assertRaises(
849 cmd_runner.SubcommandNonZeroReturnValue,
850 run_sfdisk_commands,
851- ',1,0xDA', 5, 63, '', tempfile, as_root=False)
852-
853-
854-class TestCalculatePartitionSizeAndOffset(TestCaseWithFixtures):
855-
856- def test_foo(self):
857- tempfile = self.createTempFileAsFixture()
858- cmd_runner.run(['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],
859- stdout=subprocess.PIPE)
860- stdout, stderr = run_sfdisk_commands(
861- ',1,0x0C,*\n,,,-', 5, 63, '', tempfile, as_root=False)
862- self.assertIn('Successfully wrote the new partition table', stdout)
863-
864+ ',1,0xDA', 5, 63, '', tempfile, as_root=False,
865+ stderr=subprocess.PIPE)
866+
867+
868+class TestPartitionSetup(TestCaseWithFixtures):
869+
870+ def setUp(self):
871+ super(TestPartitionSetup, self).setUp()
872+ # Stub time.sleep() as create_partitions() use that.
873+ self.orig_sleep = time.sleep
874+ time.sleep = lambda s: None
875+
876+ def tearDown(self):
877+ super(TestPartitionSetup, self).tearDown()
878+ time.sleep = self.orig_sleep
879+
880+ def test_convert_size_in_kbytes_to_bytes(self):
881+ self.assertEqual(512 * 1024, convert_size_to_bytes('512K'))
882+
883+ def test_convert_size_in_mbytes_to_bytes(self):
884+ self.assertEqual(100 * 1024**2, convert_size_to_bytes('100M'))
885+
886+ def test_convert_size_in_gbytes_to_bytes(self):
887+ self.assertEqual(12 * 1024**3, convert_size_to_bytes('12G'))
888+
889+ def test_convert_size_in_kbytes_to_bytes_rounds_to_256k_multiple(self):
890+ # See comment in convert_size_to_bytes as to why we need to do this.
891+ self.assertEqual(
892+ 3891 * (1024 * 256), convert_size_to_bytes('1000537K'))
893+
894+ def test_calculate_partition_size_and_offset(self):
895+ tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
896 vfat_size, vfat_offset, linux_size, linux_offset = (
897 calculate_partition_size_and_offset(tempfile))
898 self.assertEqual(
899 [129024L, 32256L, 10321920L, 161280L],
900 [vfat_size, vfat_offset, linux_size, linux_offset])
901+
902+ def test_get_boot_and_root_partitions_for_media_with_2_partitions(self):
903+ self.useFixture(MockSomethingFixture(
904+ partitions, '_get_partition_count', lambda media: 2))
905+ tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
906+ media = Media(tempfile)
907+ # Pretend the image file is a block device, or else
908+ # get_boot_and_root_partitions_for_media will choke.
909+ media.is_block_device = True
910+ self.assertEqual(
911+ ("%s%d" % (tempfile, 1), "%s%d" % (tempfile, 2)),
912+ get_boot_and_root_partitions_for_media(media))
913+
914+ def test_get_boot_and_root_partitions_for_media_with_3_partitions(self):
915+ self.useFixture(MockSomethingFixture(
916+ partitions, '_get_partition_count', lambda media: 3))
917+ tempfile = self._create_qemu_img_with_partitions(
918+ ',1,0xDA\n,1,0x0C,*\n,,,-')
919+ media = Media(tempfile)
920+ # Pretend the image file is a block device, or else
921+ # get_boot_and_root_partitions_for_media will choke.
922+ media.is_block_device = True
923+ self.assertEqual(
924+ ("%s%d" % (tempfile, 2), "%s%d" % (tempfile, 3)),
925+ get_boot_and_root_partitions_for_media(media))
926+
927+ def _create_qemu_img_with_partitions(self, sfdisk_commands):
928+ tempfile = self.createTempFileAsFixture()
929+ proc = cmd_runner.run(
930+ ['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],
931+ stdout=subprocess.PIPE)
932+ proc.communicate()
933+ stdout, stderr = run_sfdisk_commands(
934+ sfdisk_commands, 5, 63, '', tempfile, as_root=False,
935+ # Throw away stderr as sfdisk complains a lot when operating on a
936+ # qemu image.
937+ stderr=subprocess.PIPE)
938+ self.assertIn('Successfully wrote the new partition table', stdout)
939+ return tempfile
940+
941+ def test_get_boot_and_root_loopback_devices(self):
942+ tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
943+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
944+ # We can't test the return value of get_boot_and_root_loopback_devices
945+ # because it'd require running losetup as root, so we just make sure
946+ # it calls losetup correctly.
947+ get_boot_and_root_loopback_devices(tempfile)
948+ self.assertEqual(
949+ [['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
950+ '32256', '--sizelimit', '129024'],
951+ ['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
952+ '161280', '--sizelimit', '10321920']],
953+ popen_fixture.mock.calls)
954+
955+ def test_setup_partitions_for_image_file(self):
956+ # In practice we could pass an empty image file to setup_partitions,
957+ # but here we mock Popen() and thanks to that the image is not setup
958+ # (via qemu-img) inside setup_partitions. That's why we pass an
959+ # already setup image file.
960+ tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
961+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
962+ setup_partitions('beagle', Media(tempfile), 32, '2G', 'yes')
963+ self.assertEqual(
964+ # This is the call that would create the image file.
965+ [['qemu-img', 'create', '-f', 'raw', tempfile, '2G'],
966+ # This call would partition the image file.
967+ ['sudo', 'sfdisk', '-D', '-H', '255', '-S', '63', '-C', '261',
968+ tempfile],
969+ # Make sure changes are written to disk.
970+ ['sync'],
971+ # Register boot/root loopback devices so that we can just copy
972+ # their contents over and have it written to the image file.
973+ ['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
974+ '32256', '--sizelimit', '129024'],
975+ ['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
976+ '161280', '--sizelimit', '10321920']],
977+ popen_fixture.mock.calls)
978+
979+ def test_setup_partitions_for_block_device(self):
980+ self.useFixture(MockSomethingFixture(
981+ partitions, '_get_partition_count', lambda media: 2))
982+ tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
983+ media = Media(tempfile)
984+ # Pretend our tempfile is a block device.
985+ media.is_block_device = True
986+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
987+ setup_partitions('beagle', media, 32, '2G', 'yes')
988+ self.assertEqual(
989+ [['sudo', 'parted', '-s', tempfile, 'mklabel', 'msdos'],
990+ ['sudo', 'sfdisk', '-D', '-H', '255', '-S', '63', tempfile],
991+ ['sync']],
992+ popen_fixture.mock.calls)
993
994=== added file 'tests/integration.txt'
995--- tests/integration.txt 1970-01-01 00:00:00 +0000
996+++ tests/integration.txt 2010-12-14 13:31:33 +0000
997@@ -0,0 +1,25 @@
998+A few integration tests we can run while the migration to python is not
999+complete. They probably require root or access to specific block devices so
1000+they are not meant to be automated.
1001+
1002+ # This should print nothing to stdout but will create a binary/ dir under
1003+ # the current directory.
1004+ >>> python -m media_create.unpack_binary_tarball <path-to-binary-tarball>
1005+
1006+ # This will remove the binary/ dir created above
1007+ >>> python -m media_create.remove_binary_dir
1008+
1009+ # Partition (for real!) /dev/sdb for a beagle board and return the devices
1010+ # for the boot and root partitions.
1011+ >>> python -m media_create.partitions beagle /dev/sdb 32 2G yes
1012+ Checking that no-one is using this disk right now
1013+ ...
1014+ BOOTFS=/dev/sdb1 ROOTFS=/dev/sdb2
1015+
1016+ # Partition /tmp/beagle.img for a beagle board and return the loopback
1017+ # devices for the boot and root partitions.
1018+ >>> python -m media_create.partitions beagle /tmp/beagle.img 32 2G yes
1019+ Warning: /tmp/beagle.img is not a block device
1020+ ...
1021+ BOOTFS=/dev/loop0
1022+ ROOTFS=/dev/loop1

Subscribers

People subscribed via source and target branches