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
=== modified file 'README'
--- README 2010-12-03 13:22:03 +0000
+++ README 2010-12-14 13:31:33 +0000
@@ -11,6 +11,8 @@
11 - python-debian >= 0.1.16ubuntu111 - python-debian >= 0.1.16ubuntu1
12 - python-argparse12 - python-argparse
13 - dpkg-dev13 - dpkg-dev
14 - python-parted
15 - python-dbus
1416
15And run the following command:17And run the following command:
1618
1719
=== modified file 'linaro-media-create'
--- linaro-media-create 2010-12-12 20:48:29 +0000
+++ linaro-media-create 2010-12-14 13:31:33 +0000
@@ -34,7 +34,7 @@
34MMC_PART_OFFSET=034MMC_PART_OFFSET=0
35BOOTFS_STEP="create_boot_cmd populate_boot"35BOOTFS_STEP="create_boot_cmd populate_boot"
36ROOTFS_STEP="populate_rootfs"36ROOTFS_STEP="populate_rootfs"
37PARTITION_STEP="create_partitions"37SHOULD_CREATE_PARTITIONS="yes"
38mmc_option="0:1"38mmc_option="0:1"
39boot_args_options="rootwait ro"39boot_args_options="rootwait ro"
40HWPACK_FORCE_YES="no"40HWPACK_FORCE_YES="no"
@@ -272,13 +272,13 @@
272 BOOTFS_STEP=""272 BOOTFS_STEP=""
273 ;;273 ;;
274 --no-part)274 --no-part)
275 PARTITION_STEP=""275 SHOULD_CREATE_PARTITIONS="no"
276 ;;276 ;;
277 esac277 esac
278 shift278 shift
279done279done
280280
281DEPLOY_STEPS="$PARTITION_STEP prepare_partitions $BOOTFS_STEP $ROOTFS_STEP"281DEPLOY_STEPS="setup_partitions $BOOTFS_STEP $ROOTFS_STEP"
282282
283python -m media_create.ensure_command mkimage uboot-mkimage283python -m media_create.ensure_command mkimage uboot-mkimage
284python -m media_create.ensure_command uuidgen uuid-runtime284python -m media_create.ensure_command uuidgen uuid-runtime
@@ -309,33 +309,6 @@
309309
310RFS_UUID=`uuidgen -r`310RFS_UUID=`uuidgen -r`
311311
312get_device_by_id() {
313 if [ ! "${IMAGE_FILE}" ]; then
314 for device in /dev/disk/by-id/*; do
315 if [ `realpath $device` = $DEVICE ]; then
316 case "$device" in
317 *-part[0-9]*)
318 echo "device $DEVICE must not be a partition part ($device)" >&2
319 exit 1
320 ;;
321 esac
322 for part_id in `ls "$device-part"*`; do
323 part=`realpath $part_id`
324 part_no=`echo $part_id | sed -e 's/.*-part//g'`
325 # echo "part $part_no found: $part_id" 1>&2
326 # if there is a non-fs-data part (e.g. mx51evk), MMC_PART_OFFSET=1
327 if test "$part_no" = $((1 + $MMC_PART_OFFSET)); then
328 BOOTFS=$part
329 elif test "$part_no" = $((2 + $MMC_PART_OFFSET)) ; then
330 ROOTFS=$part
331 fi
332 done
333 break
334 fi
335 done
336 fi
337}
338
339install_hwpack() {312install_hwpack() {
340 chroot=${DIR}/binary313 chroot=${DIR}/binary
341 # Make sure we unmount /proc in the chroot or else it can't be moved to the314 # Make sure we unmount /proc in the chroot or else it can't be moved to the
@@ -445,33 +418,22 @@
445 loop_devices="$loop_devices $1"418 loop_devices="$loop_devices $1"
446}419}
447420
448create_partitions() {421setup_partitions() {
449 python -m media_create.create_partitions "${DEVIMAGE}" \422 boot_and_root_devices=$(python -m media_create.partitions \
450 "${DEVICE-$IMAGE_FILE}" "$FAT_SIZE" "$HEADS" "$SECTORS" "$CYLINDER_ARG"423 "$DEVIMAGE" "${DEVICE-$IMAGE_FILE}" "$FAT_SIZE" "$IMAGE_SIZE" \
424 "$SHOULD_CREATE_PARTITIONS")
425 eval $boot_and_root_devices
451426
452 if [ "${IMAGE_FILE}" ]; then427 if [ "${IMAGE_FILE}" ]; then
453 sizes_and_offsets=$(python -m media_create.partition_size "${IMAGE_FILE}")
454 eval $sizes_and_offsets
455 BOOTFS=$(sudo losetup -f --show "$IMAGE_FILE" --offset $VFATOFFSET \
456 --sizelimit $VFATSIZE)
457 register_loopback "$BOOTFS"428 register_loopback "$BOOTFS"
458 ROOTFS=$(sudo losetup -f --show "$IMAGE_FILE" --offset $ROOTOFFSET \
459 --sizelimit $ROOTSIZE)
460 register_loopback "$ROOTFS"429 register_loopback "$ROOTFS"
461 fi430 fi
431
432 prepare_partitions
462}433}
463434
464
465prepare_partitions() {435prepare_partitions() {
466436
467 # sync and sleep a bit as the partitions may have been created in the
468 # previous step.
469 echo -n "waiting for partitioning to settle ..."
470 sync
471 sleep 3
472 echo "done."
473 get_device_by_id
474
475 if [ "$BOOTFS_STEP" ]; then437 if [ "$BOOTFS_STEP" ]; then
476 echo ""438 echo ""
477 echo "Formating Boot Partition"439 echo "Formating Boot Partition"
@@ -565,6 +527,7 @@
565 echo "Creating /etc/flash-kernel.conf"527 echo "Creating /etc/flash-kernel.conf"
566 echo ""528 echo ""
567529
530 TARGET_BOOT_DEV=/dev/mmcblk0p$(( 1 + $MMC_PART_OFFSET ))
568 echo "UBOOT_PART=${TARGET_BOOT_DEV}" | sudo tee "${ROOT_DISK}/etc/flash-kernel.conf" >/dev/null531 echo "UBOOT_PART=${TARGET_BOOT_DEV}" | sudo tee "${ROOT_DISK}/etc/flash-kernel.conf" >/dev/null
569532
570 sync533 sync
@@ -572,39 +535,6 @@
572 sudo umount "${ROOT_DISK}" || true535 sudo umount "${ROOT_DISK}" || true
573}536}
574537
575calculatesize() {
576 IMAGE_SIZE=${IMAGE_SIZE/G/M*1024}
577 IMAGE_SIZE=${IMAGE_SIZE/M/K*1024}
578 IMAGE_SIZE=${IMAGE_SIZE/K/*1024}
579 IMAGE_SIZE=$(($IMAGE_SIZE))
580}
581
582setup_sizes() {
583 HEADS=255
584 SECTORS=63
585 SECTORSIZE=512
586 if [ "${IMAGE_FILE}" ]; then
587 calculatesize
588 CYLINDERSIZE=$(($HEADS*$SECTORS*$SECTORSIZE))
589 CYLINDERS=$(($IMAGE_SIZE/$CYLINDERSIZE))
590 CYLINDER_ARG="-C $CYLINDERS"
591 IMAGE_SIZE=$(($CYLINDERS*$CYLINDERSIZE))
592 # Round the size of the raw disk image up to a multiple of 256K
593 # so it is an exact number of SD card erase blocks in length.
594 # Otherwise Linux under qemu cannot access the last part of the
595 # card and is likely to complain that the last partition on the
596 # disk has been truncated.
597 IMAGE_SIZE=$(((($IMAGE_SIZE-1)/(1024*256)+1)*(1024*256)))
598 else
599 CYLINDER_ARG=""
600 fi
601}
602
603# XXX: Apparently there's no need to run qemu-img as root.
604setup_image() {
605 sudo qemu-img create -f raw "$IMAGE_FILE" $IMAGE_SIZE
606}
607
608remove_image_file() {538remove_image_file() {
609 if [ -f "$IMAGE_FILE" ]; then539 if [ -f "$IMAGE_FILE" ]; then
610 rm -v $IMAGE_FILE540 rm -v $IMAGE_FILE
@@ -659,11 +589,6 @@
659trap cleanup_handler EXIT589trap cleanup_handler EXIT
660trap signal_handler INT TERM590trap signal_handler INT TERM
661591
662setup_sizes
663if [ "${IMAGE_FILE}" ]; then
664 setup_image
665fi
666
667serial_opts=""592serial_opts=""
668if [ "$consoles" ]; then593if [ "$consoles" ]; then
669 for c in ${consoles}; do594 for c in ${consoles}; do
@@ -767,9 +692,6 @@
767 fi692 fi
768fi693fi
769694
770TARGET_BOOT_DEV=/dev/mmcblk0p$(( 1 + $MMC_PART_OFFSET ))
771TARGET_ROOT_DEV=/dev/mmcblk0p$(( 2 + $MMC_PART_OFFSET ))
772
773if [ ! "${DEVICE}" -a ! "${IMAGE_FILE}" ]; then695if [ ! "${DEVICE}" -a ! "${IMAGE_FILE}" ]; then
774 usage696 usage
775fi697fi
776698
=== modified file 'media_create/cmd_runner.py'
--- media_create/cmd_runner.py 2010-12-09 19:17:14 +0000
+++ media_create/cmd_runner.py 2010-12-14 13:31:33 +0000
@@ -1,13 +1,19 @@
1import subprocess1import subprocess
22
33
4def run(args, as_root=False, stdout=None):4def run(args, as_root=False, stdin=None, stdout=None, stderr=None):
5 """Run the given command as a sub process.5 """Run the given command as a sub process.
66
7 Return a Popen instance.
8
9 Callsites must wait() or communicate() with the returned Popen instance.
10
7 :param command: A list or tuple containing the command to run and the11 :param command: A list or tuple containing the command to run and the
8 arguments that should be passed to it.12 arguments that should be passed to it.
9 :param as_root: Should the given command be run as root (with sudo)?13 :param as_root: Should the given command be run as root (with sudo)?
10 :param stdout: Same as subprocess.Popen().14 :param stdin: Same as in subprocess.Popen().
15 :param stdout: Same as in subprocess.Popen().
16 :param stderr: Same as in subprocess.Popen().
11 """17 """
12 assert isinstance(args, (list, tuple)), (18 assert isinstance(args, (list, tuple)), (
13 "The command to run must be a list or tuple, found: %s" % type(args))19 "The command to run must be a list or tuple, found: %s" % type(args))
@@ -16,9 +22,7 @@
16 if as_root:22 if as_root:
17 args = args[:]23 args = args[:]
18 args.insert(0, 'sudo')24 args.insert(0, 'sudo')
19 proc = Popen(args, stdout=stdout)25 return Popen(args, stdin=stdin, stdout=stdout, stderr=stderr)
20 proc.wait()
21 return proc.returncode
2226
2327
24class Popen(subprocess.Popen):28class Popen(subprocess.Popen):
2529
=== 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-14 13:31:33 +0000
@@ -1,11 +1,12 @@
1import subprocess1import subprocess
2import sys2import time
33
4from media_create import cmd_runner4from media_create import cmd_runner
55
66
7def run_sfdisk_commands(commands, heads, sectors, cylinders_arg, device,7# TODO: Merge this file with setup_partitions.py.
8 as_root=True):8def run_sfdisk_commands(commands, heads, sectors, cylinders, device,
9 as_root=True, stderr=None):
9 """Run the given commands under sfdisk.10 """Run the given commands under sfdisk.
1011
11 :param commands: A string of sfdisk commands; each on a separate line.12 :param commands: A string of sfdisk commands; each on a separate line.
@@ -15,41 +16,33 @@
15 '-D',16 '-D',
16 '-H', str(heads),17 '-H', str(heads),
17 '-S', str(sectors)]18 '-S', str(sectors)]
18 if cylinders_arg:19 if cylinders is not None:
19 args.append(cylinders_arg)20 args.extend(['-C', str(cylinders)])
20 args.append(device)21 args.append(device)
21 # XXX: There's some stuff duplicated here from cmd_runner.run() but I22 proc = cmd_runner.run(
22 # don't see an easy way to consolidate them as a single function, so I'll23 args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr,
23 # leave it for later.24 as_root=as_root)
24 if as_root:
25 args = args[:]
26 args.insert(0, 'sudo')
27 proc = cmd_runner.Popen(
28 args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
29 stderr=subprocess.PIPE)
30 return proc.communicate("%s\n" % commands)25 return proc.communicate("%s\n" % commands)
3126
3227
33def create_partitions(board, device, fat_size, heads, sectors, cylinders_arg):28def create_partitions(board, media, fat_size, heads, sectors, cylinders=None):
34 """Partition the given device according to the board requirements.29 """Partition the given media according to the board requirements.
3530
36 :param board: A string with the board type (e.g. beagle, panda, etc)31 :param board: A string with the board type (e.g. beagle, panda, etc)
37 :param device: A string containing the path to the device to partition.32 :param media: A setup_partitions.Media object to partition.
38 :param fat_size: The type of FATs used in the boot partition (16 or 32).33 :param fat_size: The type of FATs used in the boot partition (16 or 32).
39 :param heads: Number of heads to use in the disk geometry of34 :param heads: Number of heads to use in the disk geometry of
40 partitions.35 partitions.
41 :param sectors: Number of sectors to use in the disk geometry of36 :param sectors: Number of sectors to use in the disk geometry of
42 partitions.37 partitions.
43 :param cylinders_arg: A string of the form "-C NN" containing the number38 :param cylinders: The number of cylinders to pass to sfdisk's -C argument.
44 of cylinders to use in the disk geometry of partitions.39 If None the -C argument is not passed.
45 """40 """
46 stdout = []41 if media.is_block_device:
47 stderr = []
48 is_block_device = device.startswith('/dev/')
49 if is_block_device:
50 # Overwrite any existing partition tables with a fresh one.42 # Overwrite any existing partition tables with a fresh one.
51 cmd_runner.run(43 proc = cmd_runner.run(
52 ['parted', '-s', device, 'mklabel', 'msdos'], as_root=True)44 ['parted', '-s', media.path, 'mklabel', 'msdos'], as_root=True)
45 proc.wait()
5346
54 if fat_size == 32:47 if fat_size == 32:
55 partition_type = '0x0C'48 partition_type = '0x0C'
@@ -60,28 +53,16 @@
60 # Create a one cylinder partition for fixed-offset bootloader data at53 # Create a one cylinder partition for fixed-offset bootloader data at
61 # the beginning of the image (size is one cylinder, so 8224768 bytes54 # the beginning of the image (size is one cylinder, so 8224768 bytes
62 # with the first sector for MBR).55 # with the first sector for MBR).
63 out, err = run_sfdisk_commands(56 run_sfdisk_commands(',1,0xDA', heads, sectors, cylinders, media.path)
64 ',1,0xDA', heads, sectors, cylinders_arg, device)
65 stdout.append(out)
66 stderr.append(err)
6757
68 # Create a VFAT or FAT16 partition of 9 cylinders (74027520 bytes, ~7058 # Create a VFAT or FAT16 partition of 9 cylinders (74027520 bytes, ~70
69 # MiB), followed by a Linux-type partition containing the rest of the free59 # MiB), followed by a Linux-type partition containing the rest of the free
70 # space.60 # space.
71 sfdisk_cmd = ',9,%s,*\n,,,-' % partition_type61 sfdisk_cmd = ',9,%s,*\n,,,-' % partition_type
72 out, err = run_sfdisk_commands(62 run_sfdisk_commands(sfdisk_cmd, heads, sectors, cylinders, media.path)
73 sfdisk_cmd, heads, sectors, cylinders_arg, device)63
74 stdout.append(out)64 # Sync and sleep to wait for the partition to settle.
75 stderr.append(err)65 cmd_runner.run(['sync']).wait()
76 return "\n".join(stdout), "\n".join(stderr)66 # Sleeping just 1 second seems to be enough here, but if we start getting
7767 # errors because the disk is not partitioned then we should revisit this.
7868 time.sleep(1)
79if __name__ == "__main__":
80 board, device, fat_size, heads, sectors, cylinders_arg = sys.argv[1:]
81 fat_size = int(fat_size)
82 heads = int(heads)
83 sectors = int(sectors)
84 stdout, stderr = create_partitions(
85 board, device, fat_size, heads, sectors, cylinders_arg)
86 print stdout
87 print stderr
8869
=== removed file 'media_create/partition_size.py'
--- media_create/partition_size.py 2010-12-08 19:54:08 +0000
+++ media_create/partition_size.py 1970-01-01 00:00:00 +0000
@@ -1,42 +0,0 @@
1import sys
2
3from parted import (
4 Device,
5 Disk,
6 )
7
8
9def calculate_partition_size_and_offset(device):
10 """Return the size and offset of the boot and root partitions.
11
12 Both the size and offset are in sectors.
13
14 :param device: A string containing the path to the device.
15 :return: A 4-tuple containing the offset and size of the boot partition
16 followed by the offset and size of the root partition.
17 """
18 disk = Disk(Device(device))
19 vfat_partition = None
20 for partition in disk.partitions:
21 if 'boot' in partition.getFlagsAsString():
22 geometry = partition.geometry
23 vfat_offset = geometry.start * 512
24 vfat_size = geometry.length * 512
25 vfat_partition = partition
26
27 assert vfat_partition is not None, (
28 "Couldn't find boot partition on %s" % device)
29 linux_partition = vfat_partition.nextPartition()
30 geometry = linux_partition.geometry
31 linux_offset = geometry.start * 512
32 linux_size = geometry.length * 512
33 return vfat_size, vfat_offset, linux_size, linux_offset
34
35
36if __name__ == "__main__":
37 vsize, voffset, rsize, roffset = calculate_partition_size_and_offset(
38 sys.argv[1])
39 # These values need to be assigned to shell variables in our script, so we
40 # make its job easier by printing something it can pass on to eval.
41 print "VFATSIZE=%s VFATOFFSET=%s ROOTSIZE=%s ROOTOFFSET=%s" % (
42 vsize, voffset, rsize, roffset)
430
=== added file 'media_create/partitions.py'
--- media_create/partitions.py 1970-01-01 00:00:00 +0000
+++ media_create/partitions.py 2010-12-14 13:31:33 +0000
@@ -0,0 +1,193 @@
1import subprocess
2import sys
3
4import dbus
5from parted import (
6 Device,
7 Disk,
8 )
9
10from media_create import cmd_runner
11from media_create.create_partitions import create_partitions
12
13
14HEADS = 255
15SECTORS = 63
16SECTOR_SIZE = 512 # bytes
17CYLINDER_SIZE = HEADS * SECTORS * SECTOR_SIZE
18
19
20# I wonder if it'd make sense to convert this into a small shim which calls
21# the appropriate function for the given type of device? I think it's still
22# small enough that there's not much benefit in doing that, but if it grows we
23# might want to do it.
24def setup_partitions(board, media, fat_size, image_size,
25 should_create_partitions):
26 """Make sure the given device is partitioned to boot the given board.
27
28 :param board: The board's name, as a string.
29 :param media: The Media we should partition.
30 :param fat_size: The FAT size (16 or 32) for the boot partition.
31 :param image_size: The size of the image file, in case we're setting up a
32 QEMU image.
33 :param should_create_partitions: A string with values "yes" or "no",
34 specifying whether or not we should erase existing partitions and
35 create new ones.
36 """
37 cylinders = None
38 if not media.is_block_device:
39 image_size_in_bytes = convert_size_to_bytes(image_size)
40 cylinders = image_size_in_bytes / CYLINDER_SIZE
41 image_size_in_bytes = cylinders * CYLINDER_SIZE
42 proc = cmd_runner.run(
43 ['qemu-img', 'create', '-f', 'raw', media.path, image_size],
44 stdout=open('/dev/null', 'w'))
45 proc.wait()
46
47 if should_create_partitions == "yes":
48 create_partitions(board, media, fat_size, HEADS, SECTORS, cylinders)
49
50 if media.is_block_device:
51 return get_boot_and_root_partitions_for_media(media)
52 else:
53 return get_boot_and_root_loopback_devices(media.path)
54
55
56def get_boot_and_root_loopback_devices(image_file):
57 """Return the boot and root loopback devices for the given image file.
58
59 Register the loopback devices as well.
60 """
61 vfat_size, vfat_offset, linux_size, linux_offset = (
62 calculate_partition_size_and_offset(image_file))
63 proc = cmd_runner.run(
64 ['losetup', '-f', '--show', image_file, '--offset',
65 str(vfat_offset), '--sizelimit', str(vfat_size)],
66 stdout=subprocess.PIPE, as_root=True)
67 boot_device, _ = proc.communicate()
68 proc = cmd_runner.run(
69 ['losetup', '-f', '--show', image_file, '--offset',
70 str(linux_offset), '--sizelimit', str(linux_size)],
71 stdout=subprocess.PIPE, as_root=True)
72 root_device, _ = proc.communicate()
73
74 return boot_device, root_device
75
76
77def calculate_partition_size_and_offset(image_file):
78 """Return the size and offset of the boot and root partitions.
79
80 Both the size and offset are in sectors.
81
82 :param image_file: A string containing the path to the image_file.
83 :return: A 4-tuple containing the offset and size of the boot partition
84 followed by the offset and size of the root partition.
85 """
86 # Here we can use parted.Device to read the partitions because we're
87 # reading from a regular file rather than a block device. If it was a
88 # block device we'd need root rights.
89 disk = Disk(Device(image_file))
90 vfat_partition = None
91 for partition in disk.partitions:
92 if 'boot' in partition.getFlagsAsString():
93 geometry = partition.geometry
94 vfat_offset = geometry.start * 512
95 vfat_size = geometry.length * 512
96 vfat_partition = partition
97
98 assert vfat_partition is not None, (
99 "Couldn't find boot partition on %s" % image_file)
100 linux_partition = vfat_partition.nextPartition()
101 geometry = linux_partition.geometry
102 linux_offset = geometry.start * 512
103 linux_size = geometry.length * 512
104 return vfat_size, vfat_offset, linux_size, linux_offset
105
106
107def get_boot_and_root_partitions_for_media(media):
108 """Return the device files for the boot and root partitions of media.
109
110 If the given media has 2 partitions, the first is boot and the second is
111 root. If there are 3 partitions, the second is boot and third is root.
112
113 If there are any other number of partitions, ValueError is raised.
114
115 This function must only be used for block devices.
116 """
117 assert media.is_block_device, (
118 "This function must only be used for block devices")
119
120 partition_count = _get_partition_count(media)
121
122 if partition_count == 2:
123 partition_offset = 0
124 elif partition_count == 3:
125 partition_offset = 1
126 else:
127 raise ValueError(
128 "Unexpected number of partitions on %s: %d" % (
129 media.path, partition_count))
130
131 boot_partition = "%s%d" % (media.path, 1 + partition_offset)
132 root_partition = "%s%d" % (media.path, 2 + partition_offset)
133 return boot_partition, root_partition
134
135
136def _get_partition_count(media):
137 """Return the number of partitions on the given media."""
138 # We could do the same easily using python-parted but it requires root
139 # rights to read block devices, so we use UDisks here.
140 bus = dbus.SystemBus()
141 udisks = dbus.Interface(
142 bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks"),
143 'org.freedesktop.UDisks')
144 device_path = udisks.get_dbus_method('FindDeviceByDeviceFile')(media.path)
145 device = bus.get_object("org.freedesktop.UDisks", device_path)
146 return device.Get(
147 device_path, 'partition-table-count',
148 dbus_interface='org.freedesktop.DBus.Properties')
149
150
151def convert_size_to_bytes(size):
152 """Convert a size string in Kbytes, Mbytes or Gbytes to bytes."""
153 unit = size[-1].upper()
154 real_size = int(size[:-1])
155 if unit == 'K':
156 real_size = real_size * 1024
157 elif unit == 'M':
158 real_size = real_size * 1024 * 1024
159 elif unit == 'G':
160 real_size = real_size * 1024 * 1024 * 1024
161 else:
162 raise ValueError("Unknown size format: %s. Use K[bytes], M[bytes] "
163 "or G[bytes]" % size)
164
165 # Round the size of the raw disk image up to a multiple of 256K so it is
166 # an exact number of SD card erase blocks in length. Otherwise Linux
167 # under qemu cannot access the last part of the card and is likely to
168 # complain that the last partition on the disk has been truncated. This
169 # doesn't appear to work in all cases, though, as can be seen on
170 # https://bugs.launchpad.net/linux-linaro/+bug/673335.
171 if real_size % (1024 * 256):
172 cylinders = real_size / CYLINDER_SIZE
173 real_size = cylinders * CYLINDER_SIZE
174 real_size = ((((real_size - 1) / (1024 * 256)) + 1) * (1024 * 256))
175
176 return real_size
177
178
179class Media(object):
180 """A representation of the media where Linaro will be installed."""
181
182 def __init__(self, path):
183 self.path = path
184 self.is_block_device = path.startswith('/dev/')
185
186
187if __name__ == "__main__":
188 board, device, fat_size, image_size, should_create_partitions = (
189 sys.argv[1:])
190 fat_size = int(fat_size)
191 boot, root = setup_partitions(
192 board, Media(device), fat_size, image_size, should_create_partitions)
193 print "BOOTFS=%s ROOTFS=%s" % (boot, root)
0194
=== modified file 'media_create/populate_boot.py'
--- media_create/populate_boot.py 2010-12-08 13:09:27 +0000
+++ media_create/populate_boot.py 2010-12-14 13:31:33 +0000
@@ -16,7 +16,9 @@
16 '-n', name,16 '-n', name,
17 '-d', img_data,17 '-d', img_data,
18 img]18 img]
19 return cmd_runner.run(cmd, as_root=as_root, stdout=stdout)19 proc = cmd_runner.run(cmd, as_root=as_root, stdout=stdout)
20 proc.wait()
21 return proc.returncode
2022
2123
22def _get_file_matching(regex):24def _get_file_matching(regex):
@@ -58,24 +60,26 @@
5860
5961
60def install_mx51evk_boot_loader(imx_file, boot_device_or_file):62def install_mx51evk_boot_loader(imx_file, boot_device_or_file):
61 cmd_runner.run([63 proc = cmd_runner.run([
62 "dd",64 "dd",
63 "if=%s" % imx_file,65 "if=%s" % imx_file,
64 "of=%s" % boot_device_or_file,66 "of=%s" % boot_device_or_file,
65 "bs=1024",67 "bs=1024",
66 "seek=1",68 "seek=1",
67 "conv=notrunc"], as_root=True)69 "conv=notrunc"], as_root=True)
70 proc.wait()
6871
6972
70def install_omap_boot_loader(mlo_file, boot_disk):73def install_omap_boot_loader(mlo_file, boot_disk):
71 cmd_runner.run(["cp", "-v", mlo_file, boot_disk], as_root=True)74 cmd_runner.run(["cp", "-v", mlo_file, boot_disk], as_root=True).wait()
72 # XXX: Is this really needed?75 # XXX: Is this really needed?
73 cmd_runner.run(["sync"])76 cmd_runner.run(["sync"]).wait()
7477
7578
76def make_boot_ini(boot_script, boot_disk):79def make_boot_ini(boot_script, boot_disk):
77 cmd_runner.run(80 proc = cmd_runner.run(
78 ["cp", "-v", boot_script, "%s/boot.ini" % boot_disk], as_root=True)81 ["cp", "-v", boot_script, "%s/boot.ini" % boot_disk], as_root=True)
82 proc.wait()
7983
8084
81def populate_boot(board, sub_arch, load_addr, uboot_parts_dir, boot_disk,85def populate_boot(board, sub_arch, load_addr, uboot_parts_dir, boot_disk,
8286
=== modified file 'media_create/tests/fixtures.py'
--- media_create/tests/fixtures.py 2010-12-09 19:58:24 +0000
+++ media_create/tests/fixtures.py 2010-12-14 13:31:33 +0000
@@ -65,12 +65,23 @@
6565
66class MockCmdRunnerPopen(object):66class MockCmdRunnerPopen(object):
67 """A mock for cmd_runner.Popen() which stores the args given to it."""67 """A mock for cmd_runner.Popen() which stores the args given to it."""
68 args = None68 calls = None
69 def __call__(self, args, **kwargs):69 def __call__(self, cmd, *args, **kwargs):
70 self.args = args70 if self.calls is None:
71 self.calls = []
72 if isinstance(cmd, basestring):
73 all_args = [cmd]
74 else:
75 all_args = cmd
76 all_args.extend(args)
77 self.calls.append(all_args)
71 self.returncode = 078 self.returncode = 0
72 return self79 return self
7380
81 def communicate(self, input=None):
82 self.wait()
83 return '', ''
84
74 def wait(self):85 def wait(self):
75 return self.returncode86 return self.returncode
7687
7788
=== modified file 'media_create/tests/test_media_create.py'
--- media_create/tests/test_media_create.py 2010-12-09 20:00:37 +0000
+++ media_create/tests/test_media_create.py 2010-12-14 13:31:33 +0000
@@ -4,6 +4,7 @@
4import string4import string
5import subprocess5import subprocess
6import sys6import sys
7import time
78
8from testtools import TestCase9from testtools import TestCase
910
@@ -12,12 +13,20 @@
12from media_create import cmd_runner13from media_create import cmd_runner
13from media_create import ensure_command14from media_create import ensure_command
14from media_create import populate_boot15from media_create import populate_boot
16from media_create import partitions
15from media_create.boot_cmd import create_boot_cmd17from media_create.boot_cmd import create_boot_cmd
16from media_create.create_partitions import (18from media_create.create_partitions import (
17 create_partitions,19 create_partitions,
18 run_sfdisk_commands,20 run_sfdisk_commands,
19 )21 )
20from media_create.partition_size import calculate_partition_size_and_offset22from media_create.partitions import (
23 calculate_partition_size_and_offset,
24 convert_size_to_bytes,
25 get_boot_and_root_loopback_devices,
26 get_boot_and_root_partitions_for_media,
27 Media,
28 setup_partitions,
29 )
21from media_create.populate_boot import (30from media_create.populate_boot import (
22 make_boot_script,31 make_boot_script,
23 make_uImage,32 make_uImage,
@@ -133,24 +142,28 @@
133 def test_run(self):142 def test_run(self):
134 fixture = MockCmdRunnerPopenFixture()143 fixture = MockCmdRunnerPopenFixture()
135 self.useFixture(fixture)144 self.useFixture(fixture)
136 return_code = cmd_runner.run(['foo', 'bar', 'baz'])145 proc = cmd_runner.run(['foo', 'bar', 'baz'])
137 self.assertEqual(0, return_code)146 self.assertEqual(0, proc.returncode)
138 self.assertEqual(['foo', 'bar', 'baz'], fixture.mock.args)147 self.assertEqual([['foo', 'bar', 'baz']], fixture.mock.calls)
139148
140 def test_run_as_root(self):149 def test_run_as_root(self):
141 fixture = MockCmdRunnerPopenFixture()150 fixture = MockCmdRunnerPopenFixture()
142 self.useFixture(fixture)151 self.useFixture(fixture)
143 cmd_runner.run(['foo', 'bar'], as_root=True)152 cmd_runner.run(['foo', 'bar'], as_root=True)
144 self.assertEqual(['sudo', 'foo', 'bar'], fixture.mock.args)153 self.assertEqual([['sudo', 'foo', 'bar']], fixture.mock.calls)
145154
146 def test_run_succeeds_on_zero_return_code(self):155 def test_run_succeeds_on_zero_return_code(self):
147 return_code = cmd_runner.run(['true'])156 proc = cmd_runner.run(['true'])
148 self.assertEqual(0, return_code)157 # Need to wait() here as we're using the real Popen.
158 proc.wait()
159 self.assertEqual(0, proc.returncode)
149160
150 def test_run_raises_exception_on_non_zero_return_code(self):161 def test_run_raises_exception_on_non_zero_return_code(self):
162 def run_and_wait():
163 proc = cmd_runner.run(['false'])
164 proc.wait()
151 self.assertRaises(165 self.assertRaises(
152 cmd_runner.SubcommandNonZeroReturnValue,166 cmd_runner.SubcommandNonZeroReturnValue, run_and_wait)
153 cmd_runner.run, ['false'])
154167
155 def test_run_must_be_given_list_as_args(self):168 def test_run_must_be_given_list_as_args(self):
156 self.assertRaises(AssertionError, cmd_runner.run, 'true')169 self.assertRaises(AssertionError, cmd_runner.run, 'true')
@@ -181,7 +194,7 @@
181 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'kernel',194 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'kernel',
182 '-C', 'none', '-a', 'load_addr', '-e', 'load_addr', '-n', 'Linux',195 '-C', 'none', '-a', 'load_addr', '-e', 'load_addr', '-n', 'Linux',
183 '-d', 'parts_dir/vmlinuz-*-sub_arch', 'boot_disk/uImage']196 '-d', 'parts_dir/vmlinuz-*-sub_arch', 'boot_disk/uImage']
184 self.assertEqual(expected, fixture.mock.args)197 self.assertEqual([expected], fixture.mock.calls)
185198
186 def test_make_uInitrd(self):199 def test_make_uInitrd(self):
187 self._mock_get_file_matching()200 self._mock_get_file_matching()
@@ -191,7 +204,7 @@
191 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'ramdisk',204 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'ramdisk',
192 '-C', 'none', '-a', '0', '-e', '0', '-n', 'initramfs',205 '-C', 'none', '-a', '0', '-e', '0', '-n', 'initramfs',
193 '-d', 'parts_dir/initrd.img-*-sub_arch', 'boot_disk/uInitrd']206 '-d', 'parts_dir/initrd.img-*-sub_arch', 'boot_disk/uInitrd']
194 self.assertEqual(expected, fixture.mock.args)207 self.assertEqual([expected], fixture.mock.calls)
195208
196 def test_make_boot_script(self):209 def test_make_boot_script(self):
197 self._mock_get_file_matching()210 self._mock_get_file_matching()
@@ -201,7 +214,7 @@
201 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'script',214 'sudo', 'mkimage', '-A', 'arm', '-O', 'linux', '-T', 'script',
202 '-C', 'none', '-a', '0', '-e', '0', '-n', 'boot script',215 '-C', 'none', '-a', '0', '-e', '0', '-n', 'boot script',
203 '-d', 'tmp_dir/boot.cmd', 'boot_script']216 '-d', 'tmp_dir/boot.cmd', 'boot_script']
204 self.assertEqual(expected, fixture.mock.args)217 self.assertEqual([expected], fixture.mock.calls)
205218
206 def test_get_file_matching(self):219 def test_get_file_matching(self):
207 prefix = ''.join(220 prefix = ''.join(
@@ -245,32 +258,46 @@
245258
246class TestCreatePartitions(TestCaseWithFixtures):259class TestCreatePartitions(TestCaseWithFixtures):
247260
261 media = Media('/dev/xdz')
262
263 def setUp(self):
264 super(TestCreatePartitions, self).setUp()
265 # Stub time.sleep() as create_partitions() use that.
266 self.orig_sleep = time.sleep
267 time.sleep = lambda s: None
268
269 def tearDown(self):
270 super(TestCreatePartitions, self).tearDown()
271 time.sleep = self.orig_sleep
272
248 def test_create_partitions_for_mx51evk(self):273 def test_create_partitions_for_mx51evk(self):
249 # For this board we create a one cylinder partition at the beginning.274 # For this board we create a one cylinder partition at the beginning.
250 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())275 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
251 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())276 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())
252277
253 create_partitions('mx51evk', '/dev/sdz', 32, 255, 63, '')278 create_partitions('mx51evk', self.media, 32, 255, 63, '')
254279
255 self.assertEqual(280 self.assertEqual(
256 ['sudo', 'parted', '-s', '/dev/sdz', 'mklabel', 'msdos'],281 [['sudo', 'parted', '-s', self.media.path, 'mklabel', 'msdos'],
257 popen_fixture.mock.args)282 ['sync']],
283 popen_fixture.mock.calls)
258 self.assertEqual(284 self.assertEqual(
259 [(',1,0xDA', 255, 63, '', '/dev/sdz'),285 [(',1,0xDA', 255, 63, '', self.media.path),
260 (',9,0x0C,*\n,,,-', 255, 63, '', '/dev/sdz')],286 (',9,0x0C,*\n,,,-', 255, 63, '', self.media.path)],
261 sfdisk_fixture.mock.calls)287 sfdisk_fixture.mock.calls)
262288
263 def test_create_partitions_for_beagle(self):289 def test_create_partitions_for_beagle(self):
264 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())290 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
265 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())291 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())
266292
267 create_partitions('beagle', '/dev/sdz', 32, 255, 63, '')293 create_partitions('beagle', self.media, 32, 255, 63, '')
268294
269 self.assertEqual(295 self.assertEqual(
270 ['sudo', 'parted', '-s', '/dev/sdz', 'mklabel', 'msdos'],296 [['sudo', 'parted', '-s', self.media.path, 'mklabel', 'msdos'],
271 popen_fixture.mock.args)297 ['sync']],
298 popen_fixture.mock.calls)
272 self.assertEqual(299 self.assertEqual(
273 [(',9,0x0C,*\n,,,-', 255, 63, '', '/dev/sdz')],300 [(',9,0x0C,*\n,,,-', 255, 63, '', self.media.path)],
274 sfdisk_fixture.mock.calls)301 sfdisk_fixture.mock.calls)
275302
276 def test_create_partitions_with_img_file(self):303 def test_create_partitions_with_img_file(self):
@@ -278,11 +305,12 @@
278 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())305 sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture())
279306
280 tempfile = self.createTempFileAsFixture()307 tempfile = self.createTempFileAsFixture()
281 create_partitions('beagle', tempfile, 32, 255, 63, '')308 create_partitions('beagle', Media(tempfile), 32, 255, 63, '')
282309
283 # popen() was not called as there's no existing partition table for310 # Unlike the test for partitioning of a regular block device, in this
284 # us to overwrite on the image file.311 # case parted was not called as there's no existing partition table
285 self.assertEqual(None, popen_fixture.mock.args)312 # for us to overwrite on the image file.
313 self.assertEqual([['sync']], popen_fixture.mock.calls)
286314
287 self.assertEqual(315 self.assertEqual(
288 [(',9,0x0C,*\n,,,-', 255, 63, '', tempfile)],316 [(',9,0x0C,*\n,,,-', 255, 63, '', tempfile)],
@@ -290,10 +318,13 @@
290318
291 def test_run_sfdisk_commands(self):319 def test_run_sfdisk_commands(self):
292 tempfile = self.createTempFileAsFixture()320 tempfile = self.createTempFileAsFixture()
293 cmd_runner.run(['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],321 proc = cmd_runner.run(
294 stdout=subprocess.PIPE)322 ['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],
323 stdout=subprocess.PIPE)
324 proc.communicate()
295 stdout, stderr = run_sfdisk_commands(325 stdout, stderr = run_sfdisk_commands(
296 ',1,0xDA', 5, 63, '', tempfile, as_root=False)326 ',1,0xDA', 5, 63, '', tempfile, as_root=False,
327 stderr=subprocess.PIPE)
297 self.assertIn('Successfully wrote the new partition table', stdout)328 self.assertIn('Successfully wrote the new partition table', stdout)
298329
299 def test_run_sfdisk_commands_raises_on_non_zero_returncode(self):330 def test_run_sfdisk_commands_raises_on_non_zero_returncode(self):
@@ -301,21 +332,132 @@
301 self.assertRaises(332 self.assertRaises(
302 cmd_runner.SubcommandNonZeroReturnValue,333 cmd_runner.SubcommandNonZeroReturnValue,
303 run_sfdisk_commands,334 run_sfdisk_commands,
304 ',1,0xDA', 5, 63, '', tempfile, as_root=False)335 ',1,0xDA', 5, 63, '', tempfile, as_root=False,
305336 stderr=subprocess.PIPE)
306337
307class TestCalculatePartitionSizeAndOffset(TestCaseWithFixtures):338
308339class TestPartitionSetup(TestCaseWithFixtures):
309 def test_foo(self):340
310 tempfile = self.createTempFileAsFixture()341 def setUp(self):
311 cmd_runner.run(['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],342 super(TestPartitionSetup, self).setUp()
312 stdout=subprocess.PIPE)343 # Stub time.sleep() as create_partitions() use that.
313 stdout, stderr = run_sfdisk_commands(344 self.orig_sleep = time.sleep
314 ',1,0x0C,*\n,,,-', 5, 63, '', tempfile, as_root=False)345 time.sleep = lambda s: None
315 self.assertIn('Successfully wrote the new partition table', stdout)346
316347 def tearDown(self):
348 super(TestPartitionSetup, self).tearDown()
349 time.sleep = self.orig_sleep
350
351 def test_convert_size_in_kbytes_to_bytes(self):
352 self.assertEqual(512 * 1024, convert_size_to_bytes('512K'))
353
354 def test_convert_size_in_mbytes_to_bytes(self):
355 self.assertEqual(100 * 1024**2, convert_size_to_bytes('100M'))
356
357 def test_convert_size_in_gbytes_to_bytes(self):
358 self.assertEqual(12 * 1024**3, convert_size_to_bytes('12G'))
359
360 def test_convert_size_in_kbytes_to_bytes_rounds_to_256k_multiple(self):
361 # See comment in convert_size_to_bytes as to why we need to do this.
362 self.assertEqual(
363 3891 * (1024 * 256), convert_size_to_bytes('1000537K'))
364
365 def test_calculate_partition_size_and_offset(self):
366 tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
317 vfat_size, vfat_offset, linux_size, linux_offset = (367 vfat_size, vfat_offset, linux_size, linux_offset = (
318 calculate_partition_size_and_offset(tempfile))368 calculate_partition_size_and_offset(tempfile))
319 self.assertEqual(369 self.assertEqual(
320 [129024L, 32256L, 10321920L, 161280L],370 [129024L, 32256L, 10321920L, 161280L],
321 [vfat_size, vfat_offset, linux_size, linux_offset])371 [vfat_size, vfat_offset, linux_size, linux_offset])
372
373 def test_get_boot_and_root_partitions_for_media_with_2_partitions(self):
374 self.useFixture(MockSomethingFixture(
375 partitions, '_get_partition_count', lambda media: 2))
376 tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
377 media = Media(tempfile)
378 # Pretend the image file is a block device, or else
379 # get_boot_and_root_partitions_for_media will choke.
380 media.is_block_device = True
381 self.assertEqual(
382 ("%s%d" % (tempfile, 1), "%s%d" % (tempfile, 2)),
383 get_boot_and_root_partitions_for_media(media))
384
385 def test_get_boot_and_root_partitions_for_media_with_3_partitions(self):
386 self.useFixture(MockSomethingFixture(
387 partitions, '_get_partition_count', lambda media: 3))
388 tempfile = self._create_qemu_img_with_partitions(
389 ',1,0xDA\n,1,0x0C,*\n,,,-')
390 media = Media(tempfile)
391 # Pretend the image file is a block device, or else
392 # get_boot_and_root_partitions_for_media will choke.
393 media.is_block_device = True
394 self.assertEqual(
395 ("%s%d" % (tempfile, 2), "%s%d" % (tempfile, 3)),
396 get_boot_and_root_partitions_for_media(media))
397
398 def _create_qemu_img_with_partitions(self, sfdisk_commands):
399 tempfile = self.createTempFileAsFixture()
400 proc = cmd_runner.run(
401 ['qemu-img', 'create', '-f', 'raw', tempfile, '10M'],
402 stdout=subprocess.PIPE)
403 proc.communicate()
404 stdout, stderr = run_sfdisk_commands(
405 sfdisk_commands, 5, 63, '', tempfile, as_root=False,
406 # Throw away stderr as sfdisk complains a lot when operating on a
407 # qemu image.
408 stderr=subprocess.PIPE)
409 self.assertIn('Successfully wrote the new partition table', stdout)
410 return tempfile
411
412 def test_get_boot_and_root_loopback_devices(self):
413 tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
414 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
415 # We can't test the return value of get_boot_and_root_loopback_devices
416 # because it'd require running losetup as root, so we just make sure
417 # it calls losetup correctly.
418 get_boot_and_root_loopback_devices(tempfile)
419 self.assertEqual(
420 [['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
421 '32256', '--sizelimit', '129024'],
422 ['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
423 '161280', '--sizelimit', '10321920']],
424 popen_fixture.mock.calls)
425
426 def test_setup_partitions_for_image_file(self):
427 # In practice we could pass an empty image file to setup_partitions,
428 # but here we mock Popen() and thanks to that the image is not setup
429 # (via qemu-img) inside setup_partitions. That's why we pass an
430 # already setup image file.
431 tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
432 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
433 setup_partitions('beagle', Media(tempfile), 32, '2G', 'yes')
434 self.assertEqual(
435 # This is the call that would create the image file.
436 [['qemu-img', 'create', '-f', 'raw', tempfile, '2G'],
437 # This call would partition the image file.
438 ['sudo', 'sfdisk', '-D', '-H', '255', '-S', '63', '-C', '261',
439 tempfile],
440 # Make sure changes are written to disk.
441 ['sync'],
442 # Register boot/root loopback devices so that we can just copy
443 # their contents over and have it written to the image file.
444 ['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
445 '32256', '--sizelimit', '129024'],
446 ['sudo', 'losetup', '-f', '--show', tempfile, '--offset',
447 '161280', '--sizelimit', '10321920']],
448 popen_fixture.mock.calls)
449
450 def test_setup_partitions_for_block_device(self):
451 self.useFixture(MockSomethingFixture(
452 partitions, '_get_partition_count', lambda media: 2))
453 tempfile = self._create_qemu_img_with_partitions(',1,0x0C,*\n,,,-')
454 media = Media(tempfile)
455 # Pretend our tempfile is a block device.
456 media.is_block_device = True
457 popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
458 setup_partitions('beagle', media, 32, '2G', 'yes')
459 self.assertEqual(
460 [['sudo', 'parted', '-s', tempfile, 'mklabel', 'msdos'],
461 ['sudo', 'sfdisk', '-D', '-H', '255', '-S', '63', tempfile],
462 ['sync']],
463 popen_fixture.mock.calls)
322464
=== added file 'tests/integration.txt'
--- tests/integration.txt 1970-01-01 00:00:00 +0000
+++ tests/integration.txt 2010-12-14 13:31:33 +0000
@@ -0,0 +1,25 @@
1A few integration tests we can run while the migration to python is not
2complete. They probably require root or access to specific block devices so
3they are not meant to be automated.
4
5 # This should print nothing to stdout but will create a binary/ dir under
6 # the current directory.
7 >>> python -m media_create.unpack_binary_tarball <path-to-binary-tarball>
8
9 # This will remove the binary/ dir created above
10 >>> python -m media_create.remove_binary_dir
11
12 # Partition (for real!) /dev/sdb for a beagle board and return the devices
13 # for the boot and root partitions.
14 >>> python -m media_create.partitions beagle /dev/sdb 32 2G yes
15 Checking that no-one is using this disk right now
16 ...
17 BOOTFS=/dev/sdb1 ROOTFS=/dev/sdb2
18
19 # Partition /tmp/beagle.img for a beagle board and return the loopback
20 # devices for the boot and root partitions.
21 >>> python -m media_create.partitions beagle /tmp/beagle.img 32 2G yes
22 Warning: /tmp/beagle.img is not a block device
23 ...
24 BOOTFS=/dev/loop0
25 ROOTFS=/dev/loop1

Subscribers

People subscribed via source and target branches