Merge lp:~salgado/linaro-image-tools/populate-root into lp:linaro-image-tools/11.11
- populate-root
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby (community) | Approve | ||
Review via email: mp+43938@code.launchpad.net |
Commit message
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 : | # |
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()) |
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.