Merge lp:~salgado/linaro-image-tools/port-install_hwpacks into lp:linaro-image-tools/11.11
- port-install_hwpacks
- Merge into trunk
Proposed by
Guilherme Salgado
Status: | Merged |
---|---|
Merged at revision: | 230 |
Proposed branch: | lp:~salgado/linaro-image-tools/port-install_hwpacks |
Merge into: | lp:linaro-image-tools/11.11 |
Diff against target: |
387 lines (+248/-71) 4 files modified
linaro-media-create (+2/-65) media_create/cmd_runner.py (+5/-6) media_create/hwpack.py (+120/-0) media_create/tests/test_media_create.py (+121/-0) |
To merge this branch: | bzr merge lp:~salgado/linaro-image-tools/port-install_hwpacks |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby (community) | Approve | ||
Review via email: mp+45264@code.launchpad.net |
Commit message
Description of the change
Port install_hwpacks to python
To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote : | # |
Hi James,
Thanks for the review!
The reason why I went with atexit is that most of the functions here are
just setting things up in the chroot so that install_hwpack[s] can run,
and we want these things to be undone only after install_hwpack[s] has
finished. To achieve that using try/finally blocks I'd need to have
most of this code in a single function, which is not nice.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'linaro-media-create' |
2 | --- linaro-media-create 2011-01-05 15:52:33 +0000 |
3 | +++ linaro-media-create 2011-01-05 16:55:11 +0000 |
4 | @@ -269,66 +269,8 @@ |
5 | RFS_UUID=`uuidgen -r` |
6 | |
7 | install_hwpacks () { |
8 | - chroot=${DIR}/binary |
9 | - # Make sure we unmount /proc in the chroot or else it can't be moved to the |
10 | - # rootfs. |
11 | - |
12 | - LINARO_HWPACK_INSTALL=$(which linaro-hwpack-install) |
13 | - |
14 | - sudo mv -f "${chroot}/etc/resolv.conf" ${TMP_DIR}/resolv.conf.orig |
15 | - sudo cp /etc/resolv.conf "${chroot}/etc/resolv.conf" |
16 | - |
17 | - sudo mv -f "${chroot}/etc/hosts" ${TMP_DIR}/hosts.orig |
18 | - sudo cp /etc/hosts "${chroot}/etc/hosts" |
19 | - |
20 | - local arch_is_arm=no |
21 | - case `uname -m` in |
22 | - arm*) |
23 | - arch_is_arm=yes |
24 | - ;; |
25 | - *) |
26 | - python -m media_create.ensure_command qemu-arm-static qemu-arm-static |
27 | - python -m media_create.ensure_command qemu-img qemu-kvm |
28 | - sudo cp /usr/bin/qemu-arm-static "${chroot}/usr/bin" |
29 | - ;; |
30 | - esac |
31 | - sudo cp "$LINARO_HWPACK_INSTALL" "${chroot}/usr/bin" |
32 | - |
33 | - # Actually install the hwpack. |
34 | - sudo mount proc "${chroot}/proc" -t proc |
35 | - cleanup_chroot() { |
36 | - sudo umount -v "$chroot/proc" |
37 | - } |
38 | - |
39 | - for HWPACK_FILE in "${HWPACK_FILES[@]}"; do |
40 | - |
41 | - sudo cp "$HWPACK_FILE" "$chroot" |
42 | - echo "" |
43 | - echo "---------------------------------------------------" |
44 | - echo "Installing (apt-get) $HWPACK_FILE in target rootfs." |
45 | - echo "" |
46 | - if [ "$HWPACK_FORCE_YES" = "yes" ]; then |
47 | - sudo LC_ALL=C chroot "$chroot" linaro-hwpack-install --force-yes /"$(basename "$HWPACK_FILE")" |
48 | - else |
49 | - sudo LC_ALL=C chroot "$chroot" linaro-hwpack-install /"$(basename "$HWPACK_FILE")" |
50 | - fi |
51 | - echo "---------------------------------------------------" |
52 | - echo "" |
53 | - sudo rm -f "${chroot}/$(basename "$HWPACK_FILE")" |
54 | - |
55 | - done |
56 | - |
57 | - # Revert some changes we did to the rootfs as we don't want them in the |
58 | - # image. |
59 | - sudo umount "${chroot}/proc" |
60 | - cleanup_chroot() { :; } |
61 | - |
62 | - sudo mv -f ${TMP_DIR}/resolv.conf.orig "${chroot}/etc/resolv.conf" |
63 | - sudo mv -f ${TMP_DIR}/hosts.orig "${chroot}/etc/hosts" |
64 | - if [ "arch_is_arm" = no ]; then |
65 | - sudo rm -f "${chroot}/usr/bin/qemu-arm-static" |
66 | - fi |
67 | - sudo rm -f "${chroot}/usr/bin/linaro-hwpack-install" |
68 | + python -m media_create.hwpack "${DIR}/binary" "$HWPACK_FORCE_YES" \ |
69 | + "${HWPACK_FILES[@]}" |
70 | } |
71 | |
72 | unpack_binary_tarball() { |
73 | @@ -444,10 +386,6 @@ |
74 | exit 2 |
75 | } |
76 | |
77 | -# Initially, no action is needed to clean up mounts in the chroot. |
78 | -# install_hwpack will temporarily define this function when needed. |
79 | -cleanup_chroot() { :; } |
80 | - |
81 | cleanup_handler() { |
82 | status=$? |
83 | |
84 | @@ -463,7 +401,6 @@ |
85 | echo "Performing cleanup..." |
86 | echo |
87 | |
88 | - cleanup_chroot |
89 | cleanup_sd |
90 | cleanup_mountpoints |
91 | cleanup_tempfiles |
92 | |
93 | === modified file 'media_create/cmd_runner.py' |
94 | --- media_create/cmd_runner.py 2010-12-17 07:03:34 +0000 |
95 | +++ media_create/cmd_runner.py 2011-01-05 16:55:11 +0000 |
96 | @@ -26,10 +26,6 @@ |
97 | return Popen(args, stdin=stdin, stdout=stdout, stderr=stderr) |
98 | |
99 | |
100 | -def get_extended_env(ext): |
101 | - return os.environ.update(ext) |
102 | - |
103 | - |
104 | class Popen(subprocess.Popen): |
105 | """A version of Popen which raises an error on non-zero returncode. |
106 | |
107 | @@ -37,9 +33,12 @@ |
108 | SubcommandNonZeroReturnValue if it's non-zero. |
109 | """ |
110 | |
111 | - def __init__(self, args, **kwargs): |
112 | + def __init__(self, args, env=None, **kwargs): |
113 | self._my_args = args |
114 | - super(Popen, self).__init__(args, **kwargs) |
115 | + if env is None: |
116 | + env = os.environ.copy() |
117 | + env['LC_ALL'] = 'C' |
118 | + super(Popen, self).__init__(args, env=env, **kwargs) |
119 | |
120 | def wait(self): |
121 | returncode = super(Popen, self).wait() |
122 | |
123 | === added file 'media_create/hwpack.py' |
124 | --- media_create/hwpack.py 1970-01-01 00:00:00 +0000 |
125 | +++ media_create/hwpack.py 2011-01-05 16:55:11 +0000 |
126 | @@ -0,0 +1,120 @@ |
127 | +import atexit |
128 | +import os |
129 | +import platform |
130 | +import shutil |
131 | +import sys |
132 | +import tempfile |
133 | + |
134 | +from media_create import cmd_runner |
135 | +from media_create.ensure_command import ensure_command |
136 | + |
137 | + |
138 | +def install_hwpacks(chroot_dir, tmp_dir, hwpack_force_yes, *hwpack_files): |
139 | + """Install the given hwpacks onto the given chroot.""" |
140 | + |
141 | + chroot_etc = os.path.join(chroot_dir, 'etc') |
142 | + temporarily_overwrite_file_on_dir('/etc/resolv.conf', chroot_etc, tmp_dir) |
143 | + temporarily_overwrite_file_on_dir('/etc/hosts', chroot_etc, tmp_dir) |
144 | + |
145 | + if not platform.machine().startswith('arm'): |
146 | + ensure_command('qemu-arm-static', 'qemu-arm-static') |
147 | + ensure_command('qemu-img', 'qemu-kvm') |
148 | + copy_file('/usr/bin/qemu-arm-static', |
149 | + os.path.join(chroot_dir, 'usr', 'bin')) |
150 | + |
151 | + # FIXME: This is an ugly hack to make sure we use the l-h-i script from |
152 | + # the current development tree when possible. |
153 | + here = os.path.dirname(__file__) |
154 | + linaro_hwpack_install_path = os.path.join( |
155 | + here, '..', 'linaro-hwpack-install') |
156 | + if not os.path.exists(linaro_hwpack_install_path): |
157 | + linaro_hwpack_install_path = '/usr/bin/linaro-hwpack-install' |
158 | + copy_file(linaro_hwpack_install_path, |
159 | + os.path.join(chroot_dir, 'usr', 'bin')) |
160 | + |
161 | + mount_chroot_proc(chroot_dir) |
162 | + |
163 | + for hwpack_file in hwpack_files: |
164 | + install_hwpack(chroot_dir, hwpack_file, hwpack_force_yes) |
165 | + |
166 | + |
167 | +def install_hwpack(chroot_dir, hwpack_file, hwpack_force_yes): |
168 | + """Install an hwpack on the given chroot. |
169 | + |
170 | + Copy the hwpack file to the chroot and run linaro-hwpack-install passing |
171 | + that hwpack file to it. If hwpack_force_yes is True, also pass |
172 | + --force-yes to linaro-hwpack-install. |
173 | + """ |
174 | + hwpack_basename = os.path.basename(hwpack_file) |
175 | + copy_file(hwpack_file, chroot_dir) |
176 | + print "-" * 60 |
177 | + print "Installing (apt-get) %s in target rootfs." % hwpack_basename |
178 | + args = ['chroot', chroot_dir, 'linaro-hwpack-install'] |
179 | + if hwpack_force_yes: |
180 | + args.append('--force-yes') |
181 | + args.append('/%s' % hwpack_basename) |
182 | + cmd_runner.run(args, as_root=True).wait() |
183 | + print "-" * 60 |
184 | + |
185 | + |
186 | +def mount_chroot_proc(chroot_dir): |
187 | + """Mount a /proc filesystem on the given chroot. |
188 | + |
189 | + Also register an atexit function to unmount that /proc filesystem. |
190 | + """ |
191 | + chroot_proc = os.path.join(chroot_dir, 'proc') |
192 | + proc = cmd_runner.run( |
193 | + ['mount', 'proc', chroot_proc, '-t', 'proc'], as_root=True) |
194 | + proc.wait() |
195 | + def umount_chroot_proc(): |
196 | + cmd_runner.run(['umount', '-v', chroot_proc], as_root=True).wait() |
197 | + atexit.register(umount_chroot_proc) |
198 | + |
199 | + |
200 | +def copy_file(filepath, directory): |
201 | + """Copy the given file to the given directory. |
202 | + |
203 | + The copying of the file is done in a subprocess and run using sudo. |
204 | + |
205 | + We also register an atexit function to remove the file from the given |
206 | + directory. |
207 | + """ |
208 | + cmd_runner.run(['cp', filepath, directory], as_root=True).wait() |
209 | + |
210 | + def undo(): |
211 | + new_path = os.path.join(directory, os.path.basename(filepath)) |
212 | + cmd_runner.run(['rm', '-f', new_path], as_root=True).wait() |
213 | + atexit.register(undo) |
214 | + |
215 | + |
216 | +def temporarily_overwrite_file_on_dir(filepath, directory, tmp_dir): |
217 | + """Temporarily replace a file on the given directory. |
218 | + |
219 | + We'll move the existing file on the given directory to a temp dir, then |
220 | + copy over the given file to that directory and register an atexit function |
221 | + to move the orig file back to the given directory. |
222 | + """ |
223 | + basename = os.path.basename(filepath) |
224 | + path_to_orig = os.path.join(tmp_dir, basename) |
225 | + # Move the existing file from the given directory to the temp dir. |
226 | + cmd_runner.run( |
227 | + ['mv', '-f', os.path.join(directory, basename), path_to_orig], |
228 | + as_root=True).wait() |
229 | + # Now copy the given file onto the given directory. |
230 | + cmd_runner.run(['cp', filepath, directory], as_root=True).wait() |
231 | + |
232 | + def undo(): |
233 | + cmd_runner.run( |
234 | + ['mv', '-f', path_to_orig, directory], as_root=True).wait() |
235 | + atexit.register(undo) |
236 | + |
237 | + |
238 | +if __name__ == '__main__': |
239 | + tmp_dir = tempfile.mkdtemp() |
240 | + atexit.register(shutil.rmtree, tmp_dir) |
241 | + chroot_dir, hwpack_force_yes = sys.argv[1:3] |
242 | + hwpack_force_yes = False |
243 | + if hwpack_force_yes == "yes": |
244 | + hwpack_force_yes = True |
245 | + hwpacks = sys.argv[3:] |
246 | + install_hwpacks(chroot_dir, tmp_dir, hwpack_force_yes, *hwpacks) |
247 | |
248 | === modified file 'media_create/tests/test_media_create.py' |
249 | --- media_create/tests/test_media_create.py 2010-12-20 10:00:07 +0000 |
250 | +++ media_create/tests/test_media_create.py 2011-01-05 16:55:11 +0000 |
251 | @@ -1,4 +1,5 @@ |
252 | from contextlib import contextmanager |
253 | +import atexit |
254 | import os |
255 | import random |
256 | import string |
257 | @@ -17,6 +18,13 @@ |
258 | from media_create import partitions |
259 | from media_create import rootfs |
260 | from media_create.boot_cmd import create_boot_cmd |
261 | +from media_create.hwpack import ( |
262 | + copy_file, |
263 | + install_hwpack, |
264 | + install_hwpacks, |
265 | + mount_chroot_proc, |
266 | + temporarily_overwrite_file_on_dir, |
267 | + ) |
268 | from media_create.partitions import ( |
269 | calculate_partition_size_and_offset, |
270 | convert_size_to_bytes, |
271 | @@ -639,3 +647,116 @@ |
272 | def test_check_device_not_found(self): |
273 | self._mock_does_device_exist_false() |
274 | self.assertFalse(check_device.check_device(None)) |
275 | + |
276 | + |
277 | +class AtExitRegister(object): |
278 | + funcs = None |
279 | + def __call__(self, func, *args, **kwargs): |
280 | + if self.funcs is None: |
281 | + self.funcs = [] |
282 | + self.funcs.append((func, args, kwargs)) |
283 | + |
284 | + |
285 | +class TestInstallHWPack(TestCaseWithFixtures): |
286 | + |
287 | + def test_temporarily_overwrite_file_on_dir(self): |
288 | + fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
289 | + temporarily_overwrite_file_on_dir('/path/to/file', '/dir', '/tmp/dir') |
290 | + self.assertEquals( |
291 | + [['sudo', 'mv', '-f', '/dir/file', '/tmp/dir/file'], |
292 | + ['sudo', 'cp', '/path/to/file', '/dir']], |
293 | + fixture.mock.calls) |
294 | + |
295 | + fixture.mock.calls = [] |
296 | + self._run_registered_atexit_functions() |
297 | + self.assertEquals( |
298 | + [['sudo', 'mv', '-f', '/tmp/dir/file', '/dir']], |
299 | + fixture.mock.calls) |
300 | + |
301 | + def test_copy_file(self): |
302 | + fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
303 | + copy_file('/path/to/file', '/dir') |
304 | + self.assertEquals( |
305 | + [['sudo', 'cp', '/path/to/file', '/dir']], |
306 | + fixture.mock.calls) |
307 | + |
308 | + fixture.mock.calls = [] |
309 | + self._run_registered_atexit_functions() |
310 | + self.assertEquals( |
311 | + [['sudo', 'rm', '-f', '/dir/file']], fixture.mock.calls) |
312 | + |
313 | + def test_mount_chroot_proc(self): |
314 | + fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
315 | + mount_chroot_proc('chroot') |
316 | + self.assertEquals( |
317 | + [['sudo', 'mount', 'proc', 'chroot/proc', '-t', 'proc']], |
318 | + fixture.mock.calls) |
319 | + |
320 | + fixture.mock.calls = [] |
321 | + self._run_registered_atexit_functions() |
322 | + self.assertEquals( |
323 | + [['sudo', 'umount', '-v', 'chroot/proc']], fixture.mock.calls) |
324 | + |
325 | + def test_install_hwpack(self): |
326 | + self.useFixture(MockSomethingFixture( |
327 | + sys, 'stdout', open('/dev/null', 'w'))) |
328 | + fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
329 | + force_yes = False |
330 | + install_hwpack('chroot', 'hwpack.tgz', force_yes) |
331 | + self.assertEquals( |
332 | + [['sudo', 'cp', 'hwpack.tgz', 'chroot'], |
333 | + ['sudo', 'chroot', 'chroot', 'linaro-hwpack-install', |
334 | + '/hwpack.tgz']], |
335 | + fixture.mock.calls) |
336 | + |
337 | + fixture.mock.calls = [] |
338 | + self._run_registered_atexit_functions() |
339 | + self.assertEquals( |
340 | + [['sudo', 'rm', '-f', 'chroot/hwpack.tgz']], fixture.mock.calls) |
341 | + |
342 | + def test_install_hwpacks(self): |
343 | + self.useFixture(MockSomethingFixture( |
344 | + sys, 'stdout', open('/dev/null', 'w'))) |
345 | + fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
346 | + force_yes = True |
347 | + install_hwpacks( |
348 | + 'chroot', '/tmp/dir', force_yes, 'hwpack1.tgz', 'hwpack2.tgz') |
349 | + self.assertEquals( |
350 | + [['sudo', 'mv', '-f', 'chroot/etc/resolv.conf', |
351 | + '/tmp/dir/resolv.conf'], |
352 | + ['sudo', 'cp', '/etc/resolv.conf', 'chroot/etc'], |
353 | + ['sudo', 'mv', '-f', 'chroot/etc/hosts', '/tmp/dir/hosts'], |
354 | + ['sudo', 'cp', '/etc/hosts', 'chroot/etc'], |
355 | + ['sudo', 'cp', '/usr/bin/qemu-arm-static', 'chroot/usr/bin'], |
356 | + ['sudo', 'cp', 'media_create/../linaro-hwpack-install', |
357 | + 'chroot/usr/bin'], |
358 | + ['sudo', 'mount', 'proc', 'chroot/proc', '-t', 'proc'], |
359 | + ['sudo', 'cp', 'hwpack1.tgz', 'chroot'], |
360 | + ['sudo', 'chroot', 'chroot', 'linaro-hwpack-install', |
361 | + '--force-yes', '/hwpack1.tgz'], |
362 | + ['sudo', 'cp', 'hwpack2.tgz', 'chroot'], |
363 | + ['sudo', 'chroot', 'chroot', 'linaro-hwpack-install', |
364 | + '--force-yes', '/hwpack2.tgz']], |
365 | + fixture.mock.calls) |
366 | + |
367 | + fixture.mock.calls = [] |
368 | + self._run_registered_atexit_functions() |
369 | + self.assertEquals( |
370 | + [['sudo', 'mv', '-f', '/tmp/dir/resolv.conf', 'chroot/etc'], |
371 | + ['sudo', 'mv', '-f', '/tmp/dir/hosts', 'chroot/etc'], |
372 | + ['sudo', 'rm', '-f', 'chroot/usr/bin/qemu-arm-static'], |
373 | + ['sudo', 'rm', '-f', 'chroot/usr/bin/linaro-hwpack-install'], |
374 | + ['sudo', 'umount', '-v', 'chroot/proc'], |
375 | + ['sudo', 'rm', '-f', 'chroot/hwpack1.tgz'], |
376 | + ['sudo', 'rm', '-f', 'chroot/hwpack2.tgz']], |
377 | + fixture.mock.calls) |
378 | + |
379 | + def _run_registered_atexit_functions(self): |
380 | + for func, args, kwargs in self.atexit_fixture.mock.funcs: |
381 | + func(*args, **kwargs) |
382 | + |
383 | + def setUp(self): |
384 | + super(TestInstallHWPack, self).setUp() |
385 | + self.atexit_fixture = self.useFixture(MockSomethingFixture( |
386 | + atexit, 'register', AtExitRegister())) |
387 | + |
Looks good, thanks.
I'm interested in why you chose to use atexit rather than finally
blocks etc?
Thanks,
James