Merge lp:~salgado/linaro-image-tools/port-create_partitions into lp:linaro-image-tools/11.11
- port-create_partitions
- Merge into trunk
Proposed by
Guilherme Salgado
Status: | Merged |
---|---|
Merged at revision: | 188 |
Proposed branch: | lp:~salgado/linaro-image-tools/port-create_partitions |
Merge into: | lp:linaro-image-tools/11.11 |
Diff against target: |
377 lines (+199/-56) 5 files modified
hwpack/testing.py (+9/-0) linaro-media-create (+3/-34) media_create/create_partitions.py (+87/-0) media_create/tests/fixtures.py (+28/-9) media_create/tests/test_media_create.py (+72/-13) |
To merge this branch: | bzr merge lp:~salgado/linaro-image-tools/port-create_partitions |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby (community) | Approve | ||
Review via email: mp+43105@code.launchpad.net |
Commit message
Description of the change
Port most of create_partitions to python
To post a comment you must log in.
- 180. By Guilherme Salgado
-
merge trunk
Revision history for this message
Guilherme Salgado (salgado) wrote : | # |
I can now use cmd_runner.Popen() here and avoid the duplication. I'll do so after I land that branch.
The board-specific code is going away soon indeed.
Thanks a lot for the review.
- 181. By Guilherme Salgado
-
merge trunk
- 182. By Guilherme Salgado
-
Use the new cmd_runner.Popen instead of subprocess.Popen
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-08 00:34:09 +0000 |
3 | +++ hwpack/testing.py 2010-12-09 20:01:29 +0000 |
4 | @@ -161,6 +161,15 @@ |
5 | fixture.setUp() |
6 | return fixture |
7 | |
8 | + def createTempFileAsFixture(self, prefix='tmp'): |
9 | + """Create a temp file and make sure it is removed on tearDown. |
10 | + |
11 | + :return: The filename of the file created. |
12 | + """ |
13 | + _, filename = tempfile.mkstemp(prefix=prefix) |
14 | + self.addCleanup(os.unlink, filename) |
15 | + return filename |
16 | + |
17 | |
18 | class ConfigFileFixture(object): |
19 | |
20 | |
21 | === modified file 'linaro-media-create' |
22 | --- linaro-media-create 2010-12-03 18:55:10 +0000 |
23 | +++ linaro-media-create 2010-12-09 20:01:29 +0000 |
24 | @@ -438,40 +438,8 @@ |
25 | } |
26 | |
27 | create_partitions() { |
28 | - if [ "${DEVICE}" ]; then |
29 | - sudo parted -s ${DEVICE} mklabel msdos |
30 | - fi |
31 | - |
32 | - if [ "${IMAGE_FILE}" ]; then |
33 | - partdev=${IMAGE_FILE} |
34 | - else |
35 | - partdev=${DEVICE} |
36 | - fi |
37 | - |
38 | - if [ "$FAT_SIZE" = "32" ]; then |
39 | - PARTITION_TYPE="0x0C" |
40 | - else |
41 | - PARTITION_TYPE="0x0E" |
42 | - fi |
43 | - |
44 | - # Create: |
45 | - # - on mx51evk, a one cylinder partition for fixed-offset bootloader data at |
46 | - # the beginning of the image (size is one cylinder, so 8224768 bytes with |
47 | - # the first sector for MBR), |
48 | - # - a VFAT or FAT16 partition of 9 cylinders (74027520 bytes, ~70 MiB) |
49 | - # and a linux partition of the rest |
50 | - # - a Linux type rootfs for the rest of the space |
51 | - ( |
52 | - if [ "$DEVIMAGE" = mx51evk ]; then |
53 | - cat <<EOF |
54 | -,1,0xDA |
55 | -EOF |
56 | - fi |
57 | - cat <<EOF |
58 | -,9,$PARTITION_TYPE,* |
59 | -,,,- |
60 | -EOF |
61 | - ) | sudo sfdisk -D -H $HEADS -S $SECTORS $CYLINDER_ARG $partdev |
62 | + python -m media_create.create_partitions "${DEVIMAGE}" \ |
63 | + "${DEVICE-$IMAGE_FILE}" "$FAT_SIZE" "$HEADS" "$SECTORS" "$CYLINDER_ARG" |
64 | |
65 | if [ "${IMAGE_FILE}" ]; then |
66 | VFATOFFSET=$(($(LC_ALL=C fdisk -l -u "$IMAGE_FILE" | grep FAT | awk '{print $3}')*512)) |
67 | @@ -628,6 +596,7 @@ |
68 | fi |
69 | } |
70 | |
71 | +# XXX: Apparently there's no need to run qemu-img as root. |
72 | setup_image() { |
73 | sudo qemu-img create -f raw "$IMAGE_FILE" $IMAGE_SIZE |
74 | } |
75 | |
76 | === added file 'media_create/create_partitions.py' |
77 | --- media_create/create_partitions.py 1970-01-01 00:00:00 +0000 |
78 | +++ media_create/create_partitions.py 2010-12-09 20:01:29 +0000 |
79 | @@ -0,0 +1,87 @@ |
80 | +import subprocess |
81 | +import sys |
82 | + |
83 | +from media_create import cmd_runner |
84 | + |
85 | + |
86 | +def run_sfdisk_commands(commands, heads, sectors, cylinders_arg, device, |
87 | + as_root=True): |
88 | + """Run the given commands under sfdisk. |
89 | + |
90 | + :param commands: A string of sfdisk commands; each on a separate line. |
91 | + :return: A 2-tuple containing the subprocess' stdout and stderr. |
92 | + """ |
93 | + args = ['sfdisk', |
94 | + '-D', |
95 | + '-H', str(heads), |
96 | + '-S', str(sectors)] |
97 | + if cylinders_arg: |
98 | + args.append(cylinders_arg) |
99 | + args.append(device) |
100 | + # XXX: There's some stuff duplicated here from cmd_runner.run() but I |
101 | + # don't see an easy way to consolidate them as a single function, so I'll |
102 | + # leave it for later. |
103 | + if as_root: |
104 | + args = args[:] |
105 | + args.insert(0, 'sudo') |
106 | + proc = cmd_runner.Popen( |
107 | + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
108 | + stderr=subprocess.PIPE) |
109 | + return proc.communicate("%s\n" % commands) |
110 | + |
111 | + |
112 | +def create_partitions(board, device, fat_size, heads, sectors, cylinders_arg): |
113 | + """Partition the given device according to the board requirements. |
114 | + |
115 | + :param board: A string with the board type (e.g. beagle, panda, etc) |
116 | + :param device: A string containing the path to the device to partition. |
117 | + :param fat_size: The type of FATs used in the boot partition (16 or 32). |
118 | + :param heads: Number of heads to use in the disk geometry of |
119 | + partitions. |
120 | + :param sectors: Number of sectors to use in the disk geometry of |
121 | + partitions. |
122 | + :param cylinders_arg: A string of the form "-C NN" containing the number |
123 | + of cylinders to use in the disk geometry of partitions. |
124 | + """ |
125 | + stdout = [] |
126 | + stderr = [] |
127 | + is_block_device = device.startswith('/dev/') |
128 | + if is_block_device: |
129 | + # Overwrite any existing partition tables with a fresh one. |
130 | + cmd_runner.run( |
131 | + ['parted', '-s', device, 'mklabel', 'msdos'], as_root=True) |
132 | + |
133 | + if fat_size == 32: |
134 | + partition_type = '0x0C' |
135 | + else: |
136 | + partition_type = '0x0E' |
137 | + |
138 | + if board == 'mx51evk': |
139 | + # Create a one cylinder partition for fixed-offset bootloader data at |
140 | + # the beginning of the image (size is one cylinder, so 8224768 bytes |
141 | + # with the first sector for MBR). |
142 | + out, err = run_sfdisk_commands( |
143 | + ',1,0xDA', heads, sectors, cylinders_arg, device) |
144 | + stdout.append(out) |
145 | + stderr.append(err) |
146 | + |
147 | + # Create a VFAT or FAT16 partition of 9 cylinders (74027520 bytes, ~70 |
148 | + # MiB), followed by a Linux-type partition containing the rest of the free |
149 | + # space. |
150 | + sfdisk_cmd = ',9,%s,*\n,,,-' % partition_type |
151 | + out, err = run_sfdisk_commands( |
152 | + sfdisk_cmd, heads, sectors, cylinders_arg, device) |
153 | + stdout.append(out) |
154 | + stderr.append(err) |
155 | + return "\n".join(stdout), "\n".join(stderr) |
156 | + |
157 | + |
158 | +if __name__ == "__main__": |
159 | + board, device, fat_size, heads, sectors, cylinders_arg = sys.argv[1:] |
160 | + fat_size = int(fat_size) |
161 | + heads = int(heads) |
162 | + sectors = int(sectors) |
163 | + stdout, stderr = create_partitions( |
164 | + board, device, fat_size, heads, sectors, cylinders_arg) |
165 | + print stdout |
166 | + print stderr |
167 | |
168 | === modified file 'media_create/tests/fixtures.py' |
169 | --- media_create/tests/fixtures.py 2010-12-09 19:17:14 +0000 |
170 | +++ media_create/tests/fixtures.py 2010-12-09 20:01:29 +0000 |
171 | @@ -3,6 +3,7 @@ |
172 | import subprocess |
173 | import tempfile |
174 | |
175 | +from media_create import create_partitions |
176 | from media_create import cmd_runner |
177 | |
178 | |
179 | @@ -62,17 +63,11 @@ |
180 | setattr(self.obj, self.attr_name, self.orig_attr) |
181 | |
182 | |
183 | -class MockDoRun(object): |
184 | - """A mock for do_run() which just stores the args given to it.""" |
185 | +class MockCmdRunnerPopen(object): |
186 | + """A mock for cmd_runner.Popen() which stores the args given to it.""" |
187 | args = None |
188 | def __call__(self, args, **kwargs): |
189 | self.args = args |
190 | - return 0 |
191 | - |
192 | - |
193 | -class MockCmdRunnerPopen(object): |
194 | - def __call__(self, args, **kwargs): |
195 | - self.args = args |
196 | self.returncode = 0 |
197 | return self |
198 | |
199 | @@ -83,7 +78,7 @@ |
200 | class MockCmdRunnerPopenFixture(MockSomethingFixture): |
201 | """A test fixture which mocks cmd_runner.do_run with the given mock. |
202 | |
203 | - If no mock is given, MockDoRun is used. |
204 | + If no mock is given, a MockCmdRunnerPopen instance is used. |
205 | """ |
206 | |
207 | def __init__(self, mock=None): |
208 | @@ -104,3 +99,27 @@ |
209 | |
210 | def tearDown(self): |
211 | os.chdir(self.orig_cwd) |
212 | + |
213 | + |
214 | +class MockCallableWithPositionalArgs(object): |
215 | + """A callable mock which just stores the positional args given to it. |
216 | + |
217 | + Every time an instance of this is "called", it will append a tuple |
218 | + containing the positional arguments given to it to self.calls. |
219 | + """ |
220 | + calls = None |
221 | + return_value = None |
222 | + def __call__(self, *args): |
223 | + if self.calls is None: |
224 | + self.calls = [] |
225 | + self.calls.append(args) |
226 | + return self.return_value |
227 | + |
228 | + |
229 | +class MockRunSfdiskCommandsFixture(MockSomethingFixture): |
230 | + |
231 | + def __init__(self): |
232 | + mock = MockCallableWithPositionalArgs() |
233 | + mock.return_value = ('', '') |
234 | + super(MockRunSfdiskCommandsFixture, self).__init__( |
235 | + create_partitions, 'run_sfdisk_commands', mock) |
236 | |
237 | === modified file 'media_create/tests/test_media_create.py' |
238 | --- media_create/tests/test_media_create.py 2010-12-09 19:17:14 +0000 |
239 | +++ media_create/tests/test_media_create.py 2010-12-09 20:01:29 +0000 |
240 | @@ -4,17 +4,19 @@ |
241 | import string |
242 | import subprocess |
243 | import sys |
244 | -import tempfile |
245 | |
246 | from testtools import TestCase |
247 | |
248 | from hwpack.testing import TestCaseWithFixtures |
249 | |
250 | -from media_create.boot_cmd import create_boot_cmd |
251 | from media_create import cmd_runner |
252 | from media_create import ensure_command |
253 | - |
254 | from media_create import populate_boot |
255 | +from media_create.boot_cmd import create_boot_cmd |
256 | +from media_create.create_partitions import ( |
257 | + create_partitions, |
258 | + run_sfdisk_commands, |
259 | + ) |
260 | from media_create.populate_boot import ( |
261 | make_boot_script, |
262 | make_uImage, |
263 | @@ -31,6 +33,7 @@ |
264 | CreateTarballFixture, |
265 | MockCmdRunnerPopenFixture, |
266 | MockSomethingFixture, |
267 | + MockRunSfdiskCommandsFixture, |
268 | ) |
269 | |
270 | |
271 | @@ -202,7 +205,7 @@ |
272 | def test_get_file_matching(self): |
273 | prefix = ''.join( |
274 | random.choice(string.ascii_lowercase) for x in range(5)) |
275 | - file1 = self._create_temp_file_as_fixture(prefix) |
276 | + file1 = self.createTempFileAsFixture(prefix) |
277 | directory = os.path.dirname(file1) |
278 | self.assertEqual( |
279 | file1, _get_file_matching('%s/%s*' % (directory, prefix))) |
280 | @@ -210,8 +213,8 @@ |
281 | def test_get_file_matching_too_many_files_found(self): |
282 | prefix = ''.join( |
283 | random.choice(string.ascii_lowercase) for x in range(5)) |
284 | - file1 = self._create_temp_file_as_fixture(prefix) |
285 | - file2 = self._create_temp_file_as_fixture(prefix) |
286 | + file1 = self.createTempFileAsFixture(prefix) |
287 | + file2 = self.createTempFileAsFixture(prefix) |
288 | directory = os.path.dirname(file1) |
289 | self.assertRaises( |
290 | ValueError, _get_file_matching, '%s/%s*' % (directory, prefix)) |
291 | @@ -222,23 +225,79 @@ |
292 | |
293 | def test_run_mkimage(self): |
294 | # Create a fake boot script. |
295 | - filename = self._create_temp_file_as_fixture() |
296 | + filename = self.createTempFileAsFixture() |
297 | f = open(filename, 'w') |
298 | f.write("setenv bootcmd 'fatload mmc 0:1 0x80000000 uImage;\nboot") |
299 | f.close() |
300 | |
301 | - img = self._create_temp_file_as_fixture() |
302 | + img = self.createTempFileAsFixture() |
303 | # Use that fake boot script to create a boot loader using mkimage. |
304 | # Send stdout to file as mkimage will print to stdout and we don't |
305 | # want that. |
306 | retval = _run_mkimage( |
307 | 'script', '0', '0', 'boot script', filename, img, |
308 | - stdout=open(self._create_temp_file_as_fixture(), 'w'), |
309 | + stdout=open(self.createTempFileAsFixture(), 'w'), |
310 | as_root=False) |
311 | |
312 | self.assertEqual(0, retval) |
313 | |
314 | - def _create_temp_file_as_fixture(self, prefix='tmp'): |
315 | - _, filename = tempfile.mkstemp(prefix=prefix) |
316 | - self.addCleanup(os.unlink, filename) |
317 | - return filename |
318 | + |
319 | +class TestCreatePartitions(TestCaseWithFixtures): |
320 | + |
321 | + def test_create_partitions_for_mx51evk(self): |
322 | + # For this board we create a one cylinder partition at the beginning. |
323 | + popen_fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
324 | + sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture()) |
325 | + |
326 | + create_partitions('mx51evk', '/dev/sdz', 32, 255, 63, '') |
327 | + |
328 | + self.assertEqual( |
329 | + ['sudo', 'parted', '-s', '/dev/sdz', 'mklabel', 'msdos'], |
330 | + popen_fixture.mock.args) |
331 | + self.assertEqual( |
332 | + [(',1,0xDA', 255, 63, '', '/dev/sdz'), |
333 | + (',9,0x0C,*\n,,,-', 255, 63, '', '/dev/sdz')], |
334 | + sfdisk_fixture.mock.calls) |
335 | + |
336 | + def test_create_partitions_for_beagle(self): |
337 | + popen_fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
338 | + sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture()) |
339 | + |
340 | + create_partitions('beagle', '/dev/sdz', 32, 255, 63, '') |
341 | + |
342 | + self.assertEqual( |
343 | + ['sudo', 'parted', '-s', '/dev/sdz', 'mklabel', 'msdos'], |
344 | + popen_fixture.mock.args) |
345 | + self.assertEqual( |
346 | + [(',9,0x0C,*\n,,,-', 255, 63, '', '/dev/sdz')], |
347 | + sfdisk_fixture.mock.calls) |
348 | + |
349 | + def test_create_partitions_with_img_file(self): |
350 | + popen_fixture = self.useFixture(MockCmdRunnerPopenFixture()) |
351 | + sfdisk_fixture = self.useFixture(MockRunSfdiskCommandsFixture()) |
352 | + |
353 | + tempfile = self.createTempFileAsFixture() |
354 | + create_partitions('beagle', tempfile, 32, 255, 63, '') |
355 | + |
356 | + # popen() was not called as there's no existing partition table for |
357 | + # us to overwrite on the image file. |
358 | + self.assertEqual(None, popen_fixture.mock.args) |
359 | + |
360 | + self.assertEqual( |
361 | + [(',9,0x0C,*\n,,,-', 255, 63, '', tempfile)], |
362 | + sfdisk_fixture.mock.calls) |
363 | + |
364 | + def test_run_sfdisk_commands(self): |
365 | + tempfile = self.createTempFileAsFixture() |
366 | + cmd_runner.run(['qemu-img', 'create', '-f', 'raw', tempfile, '10M'], |
367 | + stdout=subprocess.PIPE) |
368 | + stdout, stderr = run_sfdisk_commands( |
369 | + ',1,0xDA', 5, 63, '', tempfile, as_root=False) |
370 | + self.assertIn('Successfully wrote the new partition table', stdout) |
371 | + |
372 | + def test_run_sfdisk_commands_raises_on_non_zero_returncode(self): |
373 | + tempfile = self.createTempFileAsFixture() |
374 | + self.assertRaises( |
375 | + cmd_runner.SubcommandNonZeroReturnValue, |
376 | + run_sfdisk_commands, |
377 | + ',1,0xDA', 5, 63, '', tempfile, as_root=False) |
Hi,
Looks fine. I agree sharing more code with do_run would be nice.
The board-specific code in their isn't that nice, but I presume you
are deferring removing that for now?
Thanks,
James