Merge lp:~salgado/linaro-image-tools/populate-root into lp:linaro-image-tools/11.11

Proposed by Guilherme Salgado
Status: Merged
Merged at revision: 204
Proposed branch: lp:~salgado/linaro-image-tools/populate-root
Merge into: lp:linaro-image-tools/11.11
Diff against target: 379 lines (+256/-48)
4 files modified
hwpack/testing.py (+2/-2)
linaro-media-create (+5/-42)
media_create/rootfs.py (+126/-0)
media_create/tests/test_media_create.py (+123/-4)
To merge this branch: bzr merge lp:~salgado/linaro-image-tools/populate-root
Reviewer Review Type Date Requested Status
James Westby (community) Approve
Review via email: mp+43938@code.launchpad.net

Description of the change

Port populate_rootfs to python.

To post a comment you must log in.
196. By Guilherme Salgado

Fix arg parsing in rootfs.py

197. By Guilherme Salgado

merge trunk

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

I may not be around when this is reviewed so if you, dear reviewer, is happy with it, just go ahead and land. Otherwise I'll address any issues you find when I'm back.

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

27 -unset INITRD_ADDR CREATE_SWAP SWAP_SIZE DEPLOY_STEPS
28 +unset INITRD_ADDR DEPLOY_STEPS
29
30 declare -a HWPACK_FILES
31
32 @@ -30,6 +30,8 @@
33 BOOT_LABEL=boot
34 RFS_LABEL=rootfs
35 IS_LIVE=0
36 +CREATE_SWAP=0
37 +SWAP_SIZE=0

Do we want to unset the variables if we are using them?

Otherwise this looks fine.

Thanks,

James

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

IIUC, we were unsetting the CREATE_SWAP and SWAP_SIZE because further down the code would then be able to just check for their existence ([ "$CREATE_SWAP" ]) instead of checking for a specific value. Given that these things are now passed on to a python function, I want them to always exist, so I don't unset them anymore.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hwpack/testing.py'
2--- hwpack/testing.py 2010-12-17 00:58:17 +0000
3+++ hwpack/testing.py 2010-12-17 11:20:41 +0000
4@@ -172,12 +172,12 @@
5 fixture.setUp()
6 return fixture
7
8- def createTempFileAsFixture(self, prefix='tmp'):
9+ def createTempFileAsFixture(self, prefix='tmp', dir=None):
10 """Create a temp file and make sure it is removed on tearDown.
11
12 :return: The filename of the file created.
13 """
14- _, filename = tempfile.mkstemp(prefix=prefix)
15+ _, filename = tempfile.mkstemp(prefix=prefix, dir=dir)
16 self.addCleanup(os.unlink, filename)
17 return filename
18
19
20=== modified file 'linaro-media-create'
21--- linaro-media-create 2010-12-16 20:27:05 +0000
22+++ linaro-media-create 2010-12-17 11:20:41 +0000
23@@ -20,7 +20,7 @@
24 set -e
25
26 unset DEVICE BOOTFS ROOTFS IMAGE_FILE HWPACK_FILES UBOOT_FLAVOR KERNEL_ADDR
27-unset INITRD_ADDR CREATE_SWAP SWAP_SIZE DEPLOY_STEPS
28+unset INITRD_ADDR DEPLOY_STEPS
29
30 declare -a HWPACK_FILES
31
32@@ -30,6 +30,8 @@
33 BOOT_LABEL=boot
34 RFS_LABEL=rootfs
35 IS_LIVE=0
36+CREATE_SWAP=0
37+SWAP_SIZE=0
38 IS_LOWMEM=0
39 FAT_SIZE=32
40 IMAGE_SIZE=2G
41@@ -495,47 +497,8 @@
42 }
43
44 populate_rootfs() {
45- echo ""
46- echo "Populating rootfs Partition"
47- echo "Be patient, this may take a few minutes"
48- echo ""
49- mkdir -p "${ROOT_DISK}"
50- sudo mount ${ROOTFS} "${ROOT_DISK}"
51-
52- sudo mv ${DIR}/binary/* "$ROOT_DISK"
53-
54- # add fstab entry for rootfs and boot
55- echo "UUID=${RFS_UUID} / ${RFS} errors=remount-ro 0 1 " | sudo tee -a "${ROOT_DISK}/etc/fstab"
56-
57- if [ "$CREATE_SWAP" ] ; then
58-
59- echo ""
60- echo "Creating SWAP File"
61- echo ""
62-
63- SPACE_LEFT=$(LC_ALL=C df "${ROOT_DISK}" | grep ${ROOTFS} | awk '{print $4}')
64-
65- let SIZE=$SWAP_SIZE*1024
66-
67- if [ $SPACE_LEFT -ge $SIZE ] ; then
68- sudo dd if=/dev/zero "of=${ROOT_DISK}/SWAP.swap" bs=1M count=$SWAP_SIZE
69- sudo mkswap "${ROOT_DISK}/SWAP.swap"
70- echo "/SWAP.swap none swap sw 0 0" | sudo tee -a "${ROOT_DISK}/etc/fstab"
71- else
72- echo "SWAP file bigger then whats left on partition"
73- fi
74- fi
75-
76- echo ""
77- echo "Creating /etc/flash-kernel.conf"
78- echo ""
79-
80- TARGET_BOOT_DEV=/dev/mmcblk0p$(( 1 + $MMC_PART_OFFSET ))
81- echo "UBOOT_PART=${TARGET_BOOT_DEV}" | sudo tee "${ROOT_DISK}/etc/flash-kernel.conf" >/dev/null
82-
83- sync
84- sync
85- sudo umount "${ROOT_DISK}" || true
86+ python -m media_create.rootfs "binary/" "$ROOT_DISK" "$ROOTFS" "$RFS" \
87+ "$RFS_UUID" "$CREATE_SWAP" "$SWAP_SIZE" "$MMC_PART_OFFSET"
88 }
89
90 remove_image_file() {
91
92=== added file 'media_create/rootfs.py'
93--- media_create/rootfs.py 1970-01-01 00:00:00 +0000
94+++ media_create/rootfs.py 2010-12-17 11:20:41 +0000
95@@ -0,0 +1,126 @@
96+import glob
97+import os
98+import sys
99+import tempfile
100+
101+from media_create import cmd_runner
102+
103+
104+def populate_rootfs(content_dir, root_disk, partition, rootfs_type,
105+ rootfs_uuid, should_create_swap, swap_size,
106+ partition_offset):
107+ """Populate the rootfs and make the necessary tweaks to make it usable.
108+
109+ This consists of:
110+ 1. Create a directory on the path specified by root_disk
111+ 2. Mount the given partition onto the created directory.
112+ 3. Move the contents of content_dir to that directory.
113+ 4. If should_create_swap, then create it with the given size.
114+ 5. Add fstab entries for the / filesystem and swap (if created).
115+ 6. Create a /etc/flash-kernel.conf containing the target's boot device.
116+ 7. Unmount the partition we mounted on step 2.
117+ """
118+ print "\nPopulating rootfs partition"
119+ print "Be patient, this may take a few minutes\n"
120+ # Create a directory to mount the rootfs partition.
121+ os.makedirs(root_disk)
122+
123+ cmd_runner.run(['mount', partition, root_disk], as_root=True).wait()
124+
125+ move_contents(content_dir, root_disk)
126+
127+ fstab_additions = ["UUID=%s / %s errors=remount-ro 0 1 " % (
128+ rootfs_uuid, rootfs_type)]
129+ if should_create_swap:
130+ print "\nCreating SWAP File\n"
131+ if has_space_left_for_swap(root_disk, swap_size):
132+ proc = cmd_runner.run([
133+ 'dd',
134+ 'if=/dev/zero',
135+ 'of=%s/SWAP.swap' % root_disk,
136+ 'bs=1M',
137+ 'count=%s' % swap_size], as_root=True)
138+ proc.wait()
139+ proc = cmd_runner.run(
140+ ['mkswap', '%s/SWAP.swap' % root_disk], as_root=True)
141+ proc.wait()
142+ fstab_additions.append("/SWAP.swap none swap sw 0 0")
143+ else:
144+ print ("Swap file is bigger than space left on partition; "
145+ "continuing without swap.")
146+
147+ append_to_fstab(root_disk, fstab_additions)
148+
149+ print "\nCreating /etc/flash-kernel.conf\n"
150+ create_flash_kernel_config(root_disk, 1 + partition_offset)
151+
152+ cmd_runner.run(['sync']).wait()
153+ # The old code used to ignore failures here, but I don't think that's
154+ # desirable so I'm using cmd_runner.run()'s standard behaviour, which will
155+ # fail on a non-zero return value.
156+ cmd_runner.run(['umount', root_disk], as_root=True).wait()
157+
158+
159+def create_flash_kernel_config(root_disk, boot_partition_number):
160+ """Create a flash-kernel.conf file under root_disk/etc.
161+
162+ Uses the given partition number to figure out the boot partition.
163+ """
164+ target_boot_dev = '/dev/mmcblk0p%s' % boot_partition_number
165+ flash_kernel = os.path.join(root_disk, 'etc', 'flash-kernel.conf')
166+ write_data_to_protected_file(
167+ flash_kernel, "UBOOT_PART=%s" % target_boot_dev)
168+
169+
170+def move_contents(from_, root_disk):
171+ """Move everything under from_ to the given root disk.
172+
173+ Uses sudo for moving.
174+ """
175+ assert os.path.isdir(from_), "%s is not a directory" % from_
176+ files = glob.glob(os.path.join(from_, '*'))
177+ mv_cmd = ['mv']
178+ mv_cmd.extend(files)
179+ mv_cmd.append(root_disk)
180+ cmd_runner.run(mv_cmd, as_root=True).wait()
181+
182+
183+def has_space_left_for_swap(root_disk, swap_size_in_mega_bytes):
184+ """Is there enough space for a swap file in the given root disk?"""
185+ statvfs = os.statvfs(root_disk)
186+ free_space = statvfs.f_bavail * statvfs.f_bsize
187+ swap_size_in_bytes = int(swap_size_in_mega_bytes) * 1024**2
188+ if free_space >= swap_size_in_bytes:
189+ return True
190+ return False
191+
192+
193+def append_to_fstab(root_disk, fstab_additions):
194+ fstab = os.path.join(root_disk, 'etc', 'fstab')
195+ data = open(fstab).read() + '\n' + '\n'.join(fstab_additions)
196+ write_data_to_protected_file(fstab, data)
197+
198+
199+def write_data_to_protected_file(path, data):
200+ """Write data to the file on the given path.
201+
202+ This is meant to be used when the given file is only writable by root, and
203+ we overcome that by writing the data to a tempfile and then moving the
204+ tempfile on top of the given one using sudo.
205+ """
206+ _, tmpfile = tempfile.mkstemp()
207+ with open(tmpfile, 'w') as fd:
208+ fd.write(data)
209+ cmd_runner.run(['mv', '-f', tmpfile, path], as_root=True).wait()
210+
211+
212+if __name__ == '__main__':
213+ (contents_dir, root_disk, partition, rootfs_type, rootfs_uuid, create_swap,
214+ swap_size, partition_offset) = sys.argv[1:]
215+ # The create_swap argument comes as a string containing either 1 or 0, so
216+ # we convert it to int and then to boolean.
217+ should_create_swap = bool(int(create_swap))
218+ partition_offset = int(partition_offset)
219+ populate_rootfs(
220+ contents_dir, root_disk, partition, rootfs_type, rootfs_uuid,
221+ should_create_swap, swap_size, partition_offset)
222
223=== modified file 'media_create/tests/test_media_create.py'
224--- media_create/tests/test_media_create.py 2010-12-14 14:14:06 +0000
225+++ media_create/tests/test_media_create.py 2010-12-17 11:20:41 +0000
226@@ -14,6 +14,7 @@
227 from media_create import ensure_command
228 from media_create import populate_boot
229 from media_create import partitions
230+from media_create import rootfs
231 from media_create.boot_cmd import create_boot_cmd
232 from media_create.partitions import (
233 calculate_partition_size_and_offset,
234@@ -33,6 +34,13 @@
235 _run_mkimage,
236 )
237 from media_create.remove_binary_dir import remove_binary_dir
238+from media_create.rootfs import (
239+ create_flash_kernel_config,
240+ has_space_left_for_swap,
241+ move_contents,
242+ populate_rootfs,
243+ write_data_to_protected_file,
244+ )
245 from media_create.unpack_binary_tarball import unpack_binary_tarball
246
247 from media_create.tests.fixtures import (
248@@ -244,12 +252,11 @@
249
250 img = self.createTempFileAsFixture()
251 # Use that fake boot script to create a boot loader using mkimage.
252- # Send stdout to file as mkimage will print to stdout and we don't
253- # want that.
254+ # Send stdout to /dev/null as mkimage will print to stdout and we
255+ # don't want that.
256 retval = _run_mkimage(
257 'script', '0', '0', 'boot script', filename, img,
258- stdout=open(self.createTempFileAsFixture(), 'w'),
259- as_root=False)
260+ stdout=open('/dev/null', 'w'), as_root=False)
261
262 self.assertEqual(0, retval)
263
264@@ -459,3 +466,115 @@
265 ['sudo', 'sfdisk', '-D', '-H', '255', '-S', '63', tempfile],
266 ['sync']],
267 popen_fixture.mock.calls)
268+
269+
270+class TestPopulateRootFS(TestCaseWithFixtures):
271+
272+ lines_added_to_fstab = None
273+ create_flash_kernel_config_called = False
274+
275+ def test_populate_rootfs(self):
276+ def fake_append_to_fstab(disk, additions):
277+ self.lines_added_to_fstab = additions
278+
279+ def fake_create_flash_kernel_config(disk, partition_offset):
280+ self.create_flash_kernel_config_called = True
281+
282+ # Mock stdout, cmd_runner.Popen(), append_to_fstab and
283+ # create_flash_kernel_config.
284+ self.useFixture(MockSomethingFixture(
285+ sys, 'stdout', open('/dev/null', 'w')))
286+ self.useFixture(MockSomethingFixture(
287+ rootfs, 'append_to_fstab', fake_append_to_fstab))
288+ self.useFixture(MockSomethingFixture(
289+ rootfs, 'create_flash_kernel_config',
290+ fake_create_flash_kernel_config))
291+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
292+ # Store a dummy rootdisk and contents_dir in a tempdir.
293+ tempdir = self.useFixture(CreateTempDirFixture()).tempdir
294+ root_disk = os.path.join(tempdir, 'rootdisk')
295+ contents_dir = os.path.join(tempdir, 'contents')
296+ contents_bin = os.path.join(contents_dir, 'bin')
297+ contents_etc = os.path.join(contents_dir, 'etc')
298+ os.makedirs(contents_bin)
299+ os.makedirs(contents_etc)
300+
301+ populate_rootfs(
302+ contents_dir, root_disk, partition='/dev/rootfs',
303+ rootfs_type='ext3', rootfs_uuid='uuid', should_create_swap=True,
304+ swap_size=100, partition_offset=0)
305+
306+ self.assertEqual(
307+ ['UUID=uuid / ext3 errors=remount-ro 0 1 ',
308+ '/SWAP.swap none swap sw 0 0'],
309+ self.lines_added_to_fstab)
310+ self.assertEqual(True, self.create_flash_kernel_config_called)
311+ swap_file = os.path.join(root_disk, 'SWAP.swap')
312+ expected = [
313+ ['sudo', 'mount', '/dev/rootfs', root_disk],
314+ ['sudo', 'mv', contents_bin, contents_etc, root_disk],
315+ ['sudo', 'dd', 'if=/dev/zero', 'of=%s' % swap_file, 'bs=1M',
316+ 'count=100'],
317+ ['sudo', 'mkswap', swap_file],
318+ ['sync'],
319+ ['sudo', 'umount', root_disk]]
320+ self.assertEqual(expected, popen_fixture.mock.calls)
321+
322+ def test_create_flash_kernel_config(self):
323+ fixture = self.useFixture(MockCmdRunnerPopenFixture())
324+ tempdir = self.useFixture(CreateTempDirFixture()).tempdir
325+
326+ create_flash_kernel_config(tempdir, boot_partition_number=1)
327+
328+ calls = fixture.mock.calls
329+ self.assertEqual(1, len(calls), calls)
330+ call = calls[0]
331+ # The call writes to a tmpfile and then moves it to the,
332+ # /etc/flash-kernel.conf, so the tmpfile is the next to last in the
333+ # list of arguments stored.
334+ tmpfile = call[-2]
335+ self.assertEqual(
336+ ['sudo', 'mv', '-f', tmpfile,
337+ '%s/etc/flash-kernel.conf' % tempdir],
338+ call)
339+ self.assertEqual('UBOOT_PART=/dev/mmcblk0p1', open(tmpfile).read())
340+
341+ def test_move_contents(self):
342+ tempdir = self.useFixture(CreateTempDirFixture()).tempdir
343+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
344+ file1 = self.createTempFileAsFixture(dir=tempdir)
345+
346+ move_contents(tempdir, '/tmp/')
347+
348+ self.assertEqual([['sudo', 'mv', file1, '/tmp/']],
349+ popen_fixture.mock.calls)
350+
351+ def test_has_space_left_for_swap(self):
352+ statvfs = os.statvfs('/')
353+ space_left = statvfs.f_bavail * statvfs.f_bsize
354+ swap_size_in_megs = space_left / (1024**2)
355+ self.assertTrue(
356+ has_space_left_for_swap('/', swap_size_in_megs))
357+
358+ def test_has_no_space_left_for_swap(self):
359+ statvfs = os.statvfs('/')
360+ space_left = statvfs.f_bavail * statvfs.f_bsize
361+ swap_size_in_megs = (space_left / (1024**2)) + 1
362+ self.assertFalse(
363+ has_space_left_for_swap('/', swap_size_in_megs))
364+
365+ def test_write_data_to_protected_file(self):
366+ fixture = self.useFixture(MockCmdRunnerPopenFixture())
367+ data = 'foo'
368+ path = '/etc/nonexistant'
369+
370+ write_data_to_protected_file(path, data)
371+
372+ calls = fixture.mock.calls
373+ self.assertEqual(1, len(calls), calls)
374+ call = calls[0]
375+ # The call moves tmpfile to the given path, so tmpfile is the next to
376+ # last in the list of arguments stored.
377+ tmpfile = call[-2]
378+ self.assertEqual(['sudo', 'mv', '-f', tmpfile, path], call)
379+ self.assertEqual(data, open(tmpfile).read())

Subscribers

People subscribed via source and target branches