Merge lp:~salgado/linaro-image-tools/bug-815885 into lp:linaro-image-tools/11.11

Proposed by Guilherme Salgado
Status: Merged
Approved by: James Westby
Approved revision: 386
Merged at revision: 388
Proposed branch: lp:~salgado/linaro-image-tools/bug-815885
Merge into: lp:linaro-image-tools/11.11
Diff against target: 337 lines (+136/-74)
6 files modified
linaro_image_tools/media_create/android_boards.py (+2/-0)
linaro_image_tools/media_create/boards.py (+17/-22)
linaro_image_tools/media_create/chroot_utils.py (+2/-0)
linaro_image_tools/media_create/partitions.py (+35/-0)
linaro_image_tools/media_create/rootfs.py (+31/-45)
linaro_image_tools/media_create/tests/test_media_create.py (+49/-7)
To merge this branch: bzr merge lp:~salgado/linaro-image-tools/bug-815885
Reviewer Review Type Date Requested Status
James Westby (community) Approve
Review via email: mp+69133@code.launchpad.net

Description of the change

This branch adds a context manager that mounts/umounts a given partition.

This provides a simple API that will mount a partition and ensure it is
umounted once we're done.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) wrote :

Hi,

Thanks for this.

I don't like that it will mask errors that happen while
the content manager is in use with errors from unmounting, but
I think that's better than the bugs that the current
implementation has.

Thanks,

James

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

Does this fix the l-a-m-c error that Zach reported too?

Also, does this have any effect on https://code.launchpad.net/~berolinux/linaro-image-tools/fix-linaro-android-media-create/+merge/68980 ?

Thanks,

James

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

> I don't like that it will mask errors that happen while
> the content manager is in use with errors from unmounting, but
> I think that's better than the bugs that the current
> implementation has.

Yeah, that's not ideal indeed. And maybe a good reason for not using cmd_runner.run()'s default behavior there. What do you think?

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

> Does this fix the l-a-m-c error that Zach reported too?
>
> Also, does this have any effect on https://code.launchpad.net/~berolinux
> /linaro-image-tools/fix-linaro-android-media-create/+merge/68980 ?

It fixes the bug and makes the MP above unnecessary; I've just commented there.

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

On Mon, 25 Jul 2011 18:36:13 -0000, Guilherme Salgado <email address hidden> wrote:
> > I don't like that it will mask errors that happen while
> > the content manager is in use with errors from unmounting, but
> > I think that's better than the bugs that the current
> > implementation has.
>
> Yeah, that's not ideal indeed. And maybe a good reason for not using cmd_runner.run()'s default behavior there. What do you think?

Yeah, maybe logging a warning if the command fails instead of throwing
an exception up the stack?

Thanks,

James

386. By Guilherme Salgado

Change the partition_mounted() context manager to only log a warning when umount() fails with a SubcommandNonZeroReturnValue

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'linaro_image_tools/media_create/android_boards.py'
2--- linaro_image_tools/media_create/android_boards.py 2011-07-18 18:25:30 +0000
3+++ linaro_image_tools/media_create/android_boards.py 2011-07-26 12:11:39 +0000
4@@ -78,6 +78,8 @@
5 @classmethod
6 def populate_boot_script(cls, boot_partition, boot_disk, consoles):
7 cmd_runner.run(['mkdir', '-p', boot_disk]).wait()
8+ # TODO: Use partition_mounted() here to make sure the partition is
9+ # always umounted after we're done.
10 cmd_runner.run(['mount', boot_partition, boot_disk],
11 as_root=True).wait()
12
13
14=== modified file 'linaro_image_tools/media_create/boards.py'
15--- linaro_image_tools/media_create/boards.py 2011-07-19 22:19:43 +0000
16+++ linaro_image_tools/media_create/boards.py 2011-07-26 12:11:39 +0000
17@@ -37,7 +37,8 @@
18
19 from linaro_image_tools import cmd_runner
20
21-from linaro_image_tools.media_create.partitions import SECTOR_SIZE
22+from linaro_image_tools.media_create.partitions import (
23+ partition_mounted, SECTOR_SIZE)
24
25
26 KERNEL_GLOB = 'vmlinuz-*-%(kernel_flavor)s'
27@@ -465,28 +466,22 @@
28 uboot_parts_dir = os.path.join(chroot_dir, parts_dir)
29
30 cmd_runner.run(['mkdir', '-p', boot_disk]).wait()
31- cmd_runner.run(['mount', boot_partition, boot_disk],
32- as_root=True).wait()
33-
34- if cls.uboot_in_boot_part:
35- assert cls.uboot_flavor is not None, (
36- "uboot_in_boot_part is set but not uboot_flavor")
37- with cls.hardwarepack_handler:
38- uboot_bin = cls.get_file('u_boot', default=os.path.join(
39+ with partition_mounted(boot_partition, boot_disk):
40+ if cls.uboot_in_boot_part:
41+ assert cls.uboot_flavor is not None, (
42+ "uboot_in_boot_part is set but not uboot_flavor")
43+ with cls.hardwarepack_handler:
44+ default = os.path.join(
45 chroot_dir, 'usr', 'lib', 'u-boot', cls.uboot_flavor,
46- 'u-boot.bin'))
47- cmd_runner.run(
48- ['cp', '-v', uboot_bin, boot_disk], as_root=True).wait()
49-
50- cls.make_boot_files(
51- uboot_parts_dir, is_live, is_lowmem, consoles, chroot_dir,
52- rootfs_uuid, boot_disk, boot_device_or_file)
53-
54- cmd_runner.run(['sync']).wait()
55- try:
56- cmd_runner.run(['umount', boot_disk], as_root=True).wait()
57- except cmd_runner.SubcommandNonZeroReturnValue:
58- pass
59+ 'u-boot.bin')
60+ uboot_bin = cls.get_file('u_boot', default=default)
61+ proc = cmd_runner.run(
62+ ['cp', '-v', uboot_bin, boot_disk], as_root=True)
63+ proc.wait()
64+
65+ cls.make_boot_files(
66+ uboot_parts_dir, is_live, is_lowmem, consoles, chroot_dir,
67+ rootfs_uuid, boot_disk, boot_device_or_file)
68
69 @classmethod
70 def _get_kflavor_files(cls, path):
71
72=== modified file 'linaro_image_tools/media_create/chroot_utils.py'
73--- linaro_image_tools/media_create/chroot_utils.py 2011-04-29 11:47:02 +0000
74+++ linaro_image_tools/media_create/chroot_utils.py 2011-07-26 12:11:39 +0000
75@@ -93,6 +93,8 @@
76 prepare_chroot(chroot_dir, tmp_dir)
77
78 try:
79+ # TODO: Use the partition_mounted() contextmanager here and get rid of
80+ # mount_chroot_proc() altogether.
81 mount_chroot_proc(chroot_dir)
82 print "-" * 60
83 print "Installing (apt-get) %s in target rootfs." % " ".join(packages)
84
85=== modified file 'linaro_image_tools/media_create/partitions.py'
86--- linaro_image_tools/media_create/partitions.py 2011-06-29 09:16:39 +0000
87+++ linaro_image_tools/media_create/partitions.py 2011-07-26 12:11:39 +0000
88@@ -18,7 +18,9 @@
89 # along with Linaro Image Tools. If not, see <http://www.gnu.org/licenses/>.
90
91 import atexit
92+from contextlib import contextmanager
93 import glob
94+import logging
95 import re
96 import subprocess
97 import time
98@@ -169,6 +171,39 @@
99 return bootfs, rootfs
100
101
102+def umount(path):
103+ # The old code used to ignore failures here, but I don't think that's
104+ # desirable so I'm using cmd_runner.run()'s standard behaviour, which will
105+ # fail on a non-zero return value.
106+ cmd_runner.run(['umount', path], as_root=True).wait()
107+
108+
109+@contextmanager
110+def partition_mounted(device, path, *args):
111+ """A context manager that mounts the given device and umounts when done.
112+
113+ We use a try/finally to make sure the device is umounted even if there's
114+ an uncaught exception in the with block. Also, before umounting we call
115+ 'sync'.
116+
117+ :param *args: Extra arguments to the mount command.
118+ """
119+ subprocess_args = ['mount', device, path]
120+ subprocess_args.extend(args)
121+ cmd_runner.run(subprocess_args, as_root=True).wait()
122+ try:
123+ yield
124+ finally:
125+ cmd_runner.run(['sync']).wait()
126+ try:
127+ umount(path)
128+ except cmd_runner.SubcommandNonZeroReturnValue, e:
129+ logger = logging.getLogger("linaro_image_tools")
130+ logger.warn("Failed to umount %s, but ignoring it because of a "
131+ "previous error" % path)
132+ logger.warn(e)
133+
134+
135 def get_uuid(partition):
136 """Find UUID of the given partition."""
137 proc = cmd_runner.run(
138
139=== modified file 'linaro_image_tools/media_create/rootfs.py'
140--- linaro_image_tools/media_create/rootfs.py 2011-07-21 21:31:15 +0000
141+++ linaro_image_tools/media_create/rootfs.py 2011-07-26 12:11:39 +0000
142@@ -17,27 +17,19 @@
143 # You should have received a copy of the GNU General Public License
144 # along with Linaro Image Tools. If not, see <http://www.gnu.org/licenses/>.
145
146-import atexit
147 import os
148 import subprocess
149 import tempfile
150
151 from linaro_image_tools import cmd_runner
152
153-
154-def umount(device):
155- # The old code used to ignore failures here, but I don't think that's
156- # desirable so I'm using cmd_runner.run()'s standard behaviour, which will
157- # fail on a non-zero return value.
158- cmd_runner.run(['umount', device], as_root=True).wait()
159+from linaro_image_tools.media_create.partitions import partition_mounted
160
161
162 def populate_partition(content_dir, root_disk, partition):
163 os.makedirs(root_disk)
164- cmd_runner.run(['mount', partition, root_disk], as_root=True).wait()
165- atexit.register(umount, root_disk)
166- move_contents(content_dir, root_disk)
167- cmd_runner.run(['sync']).wait()
168+ with partition_mounted(partition, root_disk):
169+ move_contents(content_dir, root_disk)
170
171
172 def rootfs_mount_options(rootfs_type):
173@@ -68,40 +60,34 @@
174 # Create a directory to mount the rootfs partition.
175 os.makedirs(root_disk)
176
177- cmd_runner.run(['mount', partition, root_disk], as_root=True).wait()
178- # We use an atexit handler to umount the partition to make sure it's
179- # umounted even if something fails when it's being populated.
180- atexit.register(umount, root_disk)
181-
182- move_contents(content_dir, root_disk)
183-
184- mount_options = rootfs_mount_options(rootfs_type)
185- fstab_additions = ["UUID=%s / %s %s 0 1" % (
186- rootfs_uuid, rootfs_type, mount_options)]
187- if should_create_swap:
188- print "\nCreating SWAP File\n"
189- if has_space_left_for_swap(root_disk, swap_size):
190- proc = cmd_runner.run([
191- 'dd',
192- 'if=/dev/zero',
193- 'of=%s/SWAP.swap' % root_disk,
194- 'bs=1M',
195- 'count=%s' % swap_size], as_root=True)
196- proc.wait()
197- proc = cmd_runner.run(
198- ['mkswap', '%s/SWAP.swap' % root_disk], as_root=True)
199- proc.wait()
200- fstab_additions.append("/SWAP.swap none swap sw 0 0")
201- else:
202- print ("Swap file is bigger than space left on partition; "
203- "continuing without swap.")
204-
205- append_to_fstab(root_disk, fstab_additions)
206-
207- print "\nCreating /etc/flash-kernel.conf\n"
208- create_flash_kernel_config(root_disk, 1 + partition_offset)
209-
210- cmd_runner.run(['sync']).wait()
211+ with partition_mounted(partition, root_disk):
212+ move_contents(content_dir, root_disk)
213+
214+ mount_options = rootfs_mount_options(rootfs_type)
215+ fstab_additions = ["UUID=%s / %s %s 0 1" % (
216+ rootfs_uuid, rootfs_type, mount_options)]
217+ if should_create_swap:
218+ print "\nCreating SWAP File\n"
219+ if has_space_left_for_swap(root_disk, swap_size):
220+ proc = cmd_runner.run([
221+ 'dd',
222+ 'if=/dev/zero',
223+ 'of=%s/SWAP.swap' % root_disk,
224+ 'bs=1M',
225+ 'count=%s' % swap_size], as_root=True)
226+ proc.wait()
227+ proc = cmd_runner.run(
228+ ['mkswap', '%s/SWAP.swap' % root_disk], as_root=True)
229+ proc.wait()
230+ fstab_additions.append("/SWAP.swap none swap sw 0 0")
231+ else:
232+ print ("Swap file is bigger than space left on partition; "
233+ "continuing without swap.")
234+
235+ append_to_fstab(root_disk, fstab_additions)
236+
237+ print "\nCreating /etc/flash-kernel.conf\n"
238+ create_flash_kernel_config(root_disk, 1 + partition_offset)
239
240
241 def create_flash_kernel_config(root_disk, boot_partition_number):
242
243=== modified file 'linaro_image_tools/media_create/tests/test_media_create.py'
244--- linaro_image_tools/media_create/tests/test_media_create.py 2011-07-21 21:24:13 +0000
245+++ linaro_image_tools/media_create/tests/test_media_create.py 2011-07-26 12:11:39 +0000
246@@ -89,6 +89,7 @@
247 get_android_loopback_devices,
248 get_boot_and_root_partitions_for_media,
249 Media,
250+ partition_mounted,
251 run_sfdisk_commands,
252 setup_partitions,
253 get_uuid,
254@@ -101,7 +102,6 @@
255 move_contents,
256 populate_rootfs,
257 rootfs_mount_options,
258- umount,
259 write_data_to_protected_file,
260 )
261 from linaro_image_tools.media_create.tests.fixtures import (
262@@ -2016,6 +2016,52 @@
263 popen_fixture.mock.commands_executed)
264
265
266+class TestException(Exception):
267+ """Just a test exception."""
268+
269+
270+class TestMountedPartitionContextManager(TestCaseWithFixtures):
271+
272+ def test_basic(self):
273+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
274+ def test_func():
275+ with partition_mounted('foo', 'bar', '-t', 'proc'):
276+ pass
277+ test_func()
278+ expected = ['%s mount foo bar -t proc' % sudo_args,
279+ 'sync',
280+ '%s umount bar' % sudo_args]
281+ self.assertEqual(expected, popen_fixture.mock.commands_executed)
282+
283+ def test_exception_raised_inside_with_block(self):
284+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
285+ def test_func():
286+ with partition_mounted('foo', 'bar'):
287+ raise TestException('something')
288+ try:
289+ test_func()
290+ except TestException:
291+ pass
292+ expected = ['%s mount foo bar' % sudo_args,
293+ 'sync',
294+ '%s umount bar' % sudo_args]
295+ self.assertEqual(expected, popen_fixture.mock.commands_executed)
296+
297+ def test_umount_failure(self):
298+ # We ignore a SubcommandNonZeroReturnValue from umount because
299+ # otherwise it could shadow an exception raised inside the 'with'
300+ # block.
301+ popen_fixture = self.useFixture(MockCmdRunnerPopenFixture())
302+ def failing_umount(path):
303+ raise cmd_runner.SubcommandNonZeroReturnValue('umount', 1)
304+ self.useFixture(MockSomethingFixture(
305+ partitions, 'umount', failing_umount))
306+ def test_func():
307+ with partition_mounted('foo', 'bar'):
308+ pass
309+ test_func()
310+
311+
312 class TestPopulateBoot(TestCaseWithFixtures):
313
314 expected_args = (
315@@ -2105,8 +2151,6 @@
316 def fake_create_flash_kernel_config(disk, partition_offset):
317 self.create_flash_kernel_config_called = True
318
319- atexit_fixture = self.useFixture(MockSomethingFixture(
320- atexit, 'register', AtExitRegister()))
321 # Mock stdout, cmd_runner.Popen(), append_to_fstab and
322 # create_flash_kernel_config.
323 self.useFixture(MockSomethingFixture(
324@@ -2152,11 +2196,9 @@
325 '%s dd if=/dev/zero of=%s bs=1M count=100' % (
326 sudo_args, swap_file),
327 '%s mkswap %s' % (sudo_args, swap_file),
328- 'sync']
329+ 'sync',
330+ '%s umount %s' % (sudo_args, root_disk)]
331 self.assertEqual(expected, popen_fixture.mock.commands_executed)
332- self.assertEqual(1, len(atexit_fixture.mock.funcs))
333- self.assertEqual(
334- (umount, (root_disk,), {}), atexit_fixture.mock.funcs[0])
335
336 def test_create_flash_kernel_config(self):
337 fixture = self.useFixture(MockCmdRunnerPopenFixture())

Subscribers

People subscribed via source and target branches