Merge lp:~ltrager/maas/lp1685361 into lp:~maas-committers/maas/trunk
- lp1685361
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Blake Rouse | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 6007 | ||||
Proposed branch: | lp:~ltrager/maas/lp1685361 | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
949 lines (+293/-323) 8 files modified
src/metadataserver/api.py (+10/-4) src/metadataserver/builtin_scripts/badblocks.py (+18/-6) src/metadataserver/builtin_scripts/smartctl.py (+14/-5) src/metadataserver/tests/test_api.py (+3/-1) src/metadataserver/user_data/templates/snippets/maas_run_remote_scripts.py (+14/-8) src/metadataserver/user_data/templates/snippets/tests/test_maas_run_remote_scripts.py (+19/-0) src/provisioningserver/refresh/node_info_scripts.py (+8/-1) src/provisioningserver/refresh/tests/test_node_info_scripts.py (+207/-298) |
||||
To merge this branch: | bzr merge lp:~ltrager/maas/lp1685361 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Review via email: mp+322993@code.launchpad.net |
Commit message
Fix running commissioning and testing on Trusty
* The new Python script runner used paramater expansion before named arguments which is a feature available in Python 3.5+.
* 00-maas-
* If a commissioning script failed the node goes into failed commissioning and invalidates the hosts OAUTH tokens. The runner was still running and sending test results. Now if a commissioning script fails no test scripts are run and the metadata server marks all pending tests as aborted.
Description of the change
MAAS can now use Trusty for commissioning and testing. The smartctl and badblock tests used the SERIAL column to help users identity drive. The trusty version of lsblk does not support outputting this column. The tests now try with the COLUMN argument, then try again without it as they can still run without that information. The ntp test is failing due to the ntp package not being installed. That means ntp isn't being configured, I'm not sure if that is a bug or unsupported on Trusty. If its unsupported I can fix it by install ntp in the test. The stress-ng tests also fail due to unsupported arguments used with stress-ng. I could look into switching to using the snap for stress-ng.
Preview Diff
1 | === modified file 'src/metadataserver/api.py' |
2 | --- src/metadataserver/api.py 2017-04-06 16:08:19 +0000 |
3 | +++ src/metadataserver/api.py 2017-04-22 03:04:16 +0000 |
4 | @@ -495,12 +495,18 @@ |
5 | } |
6 | target_status = signaling_statuses.get(status) |
7 | |
8 | - if target_status is not None: |
9 | - node.status_expires = None |
10 | - |
11 | - # Recalculate tags when commissioning ends. |
12 | if target_status in [NODE_STATUS.READY, NODE_STATUS.TESTING]: |
13 | + # Recalculate tags when commissioning ends. |
14 | populate_tags_for_single_node(Tag.objects.all(), node) |
15 | + elif (target_status == NODE_STATUS.FAILED_COMMISSIONING and |
16 | + node.current_testing_script_set is not None): |
17 | + # If commissioning failed testing doesn't run, mark any pending |
18 | + # scripts as aborted. |
19 | + qs = node.current_testing_script_set.scriptresult_set.filter( |
20 | + status=SCRIPT_STATUS.PENDING) |
21 | + for script_result in qs: |
22 | + script_result.status = SCRIPT_STATUS.ABORTED |
23 | + script_result.save(update_fields=['status']) |
24 | |
25 | return target_status |
26 | |
27 | |
28 | === modified file 'src/metadataserver/builtin_scripts/badblocks.py' |
29 | --- src/metadataserver/builtin_scripts/badblocks.py 2017-03-29 23:38:04 +0000 |
30 | +++ src/metadataserver/builtin_scripts/badblocks.py 2017-04-22 03:04:16 +0000 |
31 | @@ -77,11 +77,20 @@ |
32 | iscsi_drives = re.findall( |
33 | 'Attached scsi disk (?P<disk>\w+)', output.decode('utf-8')) |
34 | |
35 | - lsblk_output = check_output( |
36 | - [ |
37 | - 'lsblk', '--exclude', '1,2,7', '-d', '-P', '-o', |
38 | - 'NAME,RO,MODEL,SERIAL', |
39 | - ], timeout=TIMEOUT).decode('utf-8') |
40 | + try: |
41 | + lsblk_output = check_output( |
42 | + [ |
43 | + 'lsblk', '--exclude', '1,2,7', '-d', '-P', '-o', |
44 | + 'NAME,RO,MODEL,SERIAL', |
45 | + ], timeout=TIMEOUT).decode('utf-8') |
46 | + except CalledProcessError: |
47 | + # The SERIAL column is unsupported in the Trusty version of lsblk. Try |
48 | + # again without it. |
49 | + lsblk_output = check_output( |
50 | + [ |
51 | + 'lsblk', '--exclude', '1,2,7', '-d', '-P', '-o', |
52 | + 'NAME,RO,MODEL', |
53 | + ], timeout=TIMEOUT).decode('utf-8') |
54 | drives = [] |
55 | for line in lsblk_output.splitlines(): |
56 | drive = {} |
57 | @@ -115,7 +124,10 @@ |
58 | dashes = '-' * int((80.0 - (2 + len(thread.drive['PATH']))) / 2) |
59 | print('%s %s %s' % (dashes, thread.drive['PATH'], dashes)) |
60 | print('Model: %s' % thread.drive['MODEL']) |
61 | - print('Serial: %s' % thread.drive['SERIAL']) |
62 | + # The SERIAL column is only available in newer versions of lsblk. This |
63 | + # can be removed with Trusty support. |
64 | + if 'SERIAL' in thread.drive: |
65 | + print('Serial: %s' % thread.drive['SERIAL']) |
66 | print() |
67 | |
68 | if thread.returncode != 0: |
69 | |
70 | === modified file 'src/metadataserver/builtin_scripts/smartctl.py' |
71 | --- src/metadataserver/builtin_scripts/smartctl.py 2017-03-30 00:45:53 +0000 |
72 | +++ src/metadataserver/builtin_scripts/smartctl.py 2017-04-22 03:04:16 +0000 |
73 | @@ -148,11 +148,20 @@ |
74 | if m is not None: |
75 | drives.append(drive_with_device_type) |
76 | |
77 | - all_drives = check_output( |
78 | - [ |
79 | - 'lsblk', '--exclude', '1,2,7', '-d', '-l', '-o', |
80 | - 'NAME,MODEL,SERIAL', '-x', 'NAME', |
81 | - ], timeout=TIMEOUT, stderr=DEVNULL).decode('utf-8').splitlines() |
82 | + try: |
83 | + all_drives = check_output( |
84 | + [ |
85 | + 'lsblk', '--exclude', '1,2,7', '-d', '-l', '-o', |
86 | + 'NAME,MODEL,SERIAL', '-x', 'NAME', |
87 | + ], timeout=TIMEOUT, stderr=DEVNULL).decode('utf-8').splitlines() |
88 | + except CalledProcessError: |
89 | + # The SERIAL column and sorting(-x) are unsupported in the Trusty |
90 | + # version of lsblk. Try again without them. |
91 | + all_drives = check_output( |
92 | + [ |
93 | + 'lsblk', '--exclude', '1,2,7', '-d', '-l', '-o', |
94 | + 'NAME,MODEL', |
95 | + ], timeout=TIMEOUT, stderr=DEVNULL).decode('utf-8').splitlines() |
96 | supported_drives = iscsi_drives + [ |
97 | drive[0].split('/')[-1] for drive in drives] |
98 | unsupported_drives = [ |
99 | |
100 | === modified file 'src/metadataserver/tests/test_api.py' |
101 | --- src/metadataserver/tests/test_api.py 2017-04-06 16:08:19 +0000 |
102 | +++ src/metadataserver/tests/test_api.py 2017-04-22 03:04:16 +0000 |
103 | @@ -1435,7 +1435,7 @@ |
104 | self.assertEqual(http.client.OK, response.status_code) |
105 | self.assertEqual(NODE_STATUS.READY, reload_object(node).status) |
106 | |
107 | - def test_signaling_commissioning_failure_makes_node_failed_Tests(self): |
108 | + def test_signaling_commissioning_failure_makes_node_failed_tests(self): |
109 | node = factory.make_Node( |
110 | status=NODE_STATUS.COMMISSIONING, with_empty_script_sets=True) |
111 | client = make_node_client(node=node) |
112 | @@ -1443,6 +1443,8 @@ |
113 | self.assertEqual(http.client.OK, response.status_code) |
114 | self.assertEqual( |
115 | NODE_STATUS.FAILED_COMMISSIONING, reload_object(node).status) |
116 | + for script_result in node.current_testing_script_set: |
117 | + self.assertEqual(SCRIPT_STATUS.ABORTED, script_result.status) |
118 | |
119 | def test_signaling_commissioning_failure_does_not_populate_tags(self): |
120 | populate_tags_for_single_node = self.patch( |
121 | |
122 | === modified file 'src/metadataserver/user_data/templates/snippets/maas_run_remote_scripts.py' |
123 | --- src/metadataserver/user_data/templates/snippets/maas_run_remote_scripts.py 2017-04-13 01:15:22 +0000 |
124 | +++ src/metadataserver/user_data/templates/snippets/maas_run_remote_scripts.py 2017-04-22 03:04:16 +0000 |
125 | @@ -103,8 +103,9 @@ |
126 | timeout_seconds = script.get('timeout_seconds') |
127 | |
128 | signal_wrapper( |
129 | - **args, error='Starting %s [%d/%d]' % ( |
130 | - script['name'], i, len(scripts))) |
131 | + error='Starting %s [%d/%d]' % ( |
132 | + script['name'], i, len(scripts)), |
133 | + **args) |
134 | |
135 | script_path = os.path.join(scripts_dir, script['path']) |
136 | combined_path = os.path.join(out_dir, script['name']) |
137 | @@ -142,8 +143,9 @@ |
138 | stderr_name: result, |
139 | } |
140 | signal_wrapper( |
141 | - **args, error='Failed to execute %s [%d/%d]: %d' % ( |
142 | - script['name'], i, total_scripts, args['exit_status'])) |
143 | + error='Failed to execute %s [%d/%d]: %d' % ( |
144 | + script['name'], i, total_scripts, args['exit_status']), |
145 | + **args) |
146 | except TimeoutExpired: |
147 | fail_count += 1 |
148 | args['status'] = 'TIMEDOUT' |
149 | @@ -153,9 +155,10 @@ |
150 | stderr_name: open(stderr_path, 'rb').read(), |
151 | } |
152 | signal_wrapper( |
153 | - **args, error='Timeout(%s) expired on %s [%d/%d]' % ( |
154 | + error='Timeout(%s) expired on %s [%d/%d]' % ( |
155 | str(timedelta(seconds=timeout_seconds)), script['name'], i, |
156 | - total_scripts)) |
157 | + total_scripts), |
158 | + **args) |
159 | else: |
160 | if proc.returncode != 0: |
161 | fail_count += 1 |
162 | @@ -166,8 +169,9 @@ |
163 | stderr_name: open(stderr_path, 'rb').read(), |
164 | } |
165 | signal_wrapper( |
166 | - **args, error='Finished %s [%d/%d]: %d' % ( |
167 | - script['name'], i, len(scripts), args['exit_status'])) |
168 | + error='Finished %s [%d/%d]: %d' % ( |
169 | + script['name'], i, len(scripts), args['exit_status']), |
170 | + **args) |
171 | |
172 | # Signal failure after running commissioning or testing scripts so MAAS |
173 | # transisitions the node into FAILED_COMMISSIONING or FAILED_TESTING. |
174 | @@ -188,6 +192,8 @@ |
175 | if commissioning_scripts is not None: |
176 | fail_count += run_scripts( |
177 | url, creds, scripts_dir, out_dir, commissioning_scripts) |
178 | + if fail_count != 0: |
179 | + return |
180 | |
181 | testing_scripts = scripts.get('testing_scripts') |
182 | if testing_scripts is not None: |
183 | |
184 | === modified file 'src/metadataserver/user_data/templates/snippets/tests/test_maas_run_remote_scripts.py' |
185 | --- src/metadataserver/user_data/templates/snippets/tests/test_maas_run_remote_scripts.py 2017-04-13 01:19:43 +0000 |
186 | +++ src/metadataserver/user_data/templates/snippets/tests/test_maas_run_remote_scripts.py 2017-04-22 03:04:16 +0000 |
187 | @@ -21,6 +21,7 @@ |
188 | MockAnyCall, |
189 | MockCalledOnce, |
190 | MockCalledOnceWith, |
191 | + MockNotCalled, |
192 | ) |
193 | from maastesting.testcase import MAASTestCase |
194 | from snippets import maas_run_remote_scripts |
195 | @@ -117,6 +118,24 @@ |
196 | mock_signal, |
197 | MockAnyCall(None, None, 'OK', 'All scripts successfully ran')) |
198 | |
199 | + def test_run_scripts_from_metadata_doesnt_run_tests_on_commiss_fail(self): |
200 | + scripts_dir = self.useFixture(TempDirectory()).path |
201 | + mock_run_scripts = self.patch(maas_run_remote_scripts, 'run_scripts') |
202 | + mock_run_scripts.return_value = random.randint(1, 100) |
203 | + mock_signal = self.patch(maas_run_remote_scripts, 'signal') |
204 | + index_json = self.make_index_json(scripts_dir) |
205 | + |
206 | + # Don't need to give the url, creds, or out_dir as we're not running |
207 | + # the scripts and sending the results. |
208 | + run_scripts_from_metadata(None, None, scripts_dir, None) |
209 | + |
210 | + self.assertThat( |
211 | + mock_run_scripts, |
212 | + MockCalledOnceWith( |
213 | + None, None, scripts_dir, None, |
214 | + index_json['commissioning_scripts'])) |
215 | + self.assertThat(mock_signal, MockNotCalled()) |
216 | + |
217 | def test_run_scripts_from_metadata_does_nothing_on_empty(self): |
218 | scripts_dir = self.useFixture(TempDirectory()).path |
219 | self.patch(maas_run_remote_scripts, 'run_scripts') |
220 | |
221 | === modified file 'src/provisioningserver/refresh/node_info_scripts.py' |
222 | --- src/provisioningserver/refresh/node_info_scripts.py 2017-04-06 00:39:19 +0000 |
223 | +++ src/provisioningserver/refresh/node_info_scripts.py 2017-04-22 03:04:16 +0000 |
224 | @@ -457,7 +457,7 @@ |
225 | blockdevs = [] |
226 | block_list = check_output(( |
227 | "lsblk", "--exclude", "1,2,7", "-d", "-P", |
228 | - "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")) |
229 | + "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")) |
230 | block_list = block_list.decode("utf-8") |
231 | for blockdev in block_list.splitlines(): |
232 | tokens = shlex.split(blockdev) |
233 | @@ -467,6 +467,13 @@ |
234 | current_block[k] = v.strip() |
235 | blockdevs.append(current_block) |
236 | |
237 | + # Sort drives by MAJ:MIN so MAAS picks the correct boot drive. |
238 | + # lsblk -x MAJ:MIN can't be used as the -x flag only appears in |
239 | + # lsblk 2.71.1 or newer which is unavailable on Trusty. See LP:1673724 |
240 | + blockdevs = sorted( |
241 | + blockdevs, |
242 | + key=lambda blockdev: [int(i) for i in blockdev['MAJ:MIN'].split(':')]) |
243 | + |
244 | # Grab the device path, serial number, and sata connection. |
245 | UDEV_MAPPINGS = { |
246 | "DEVNAME": "PATH", |
247 | |
248 | === modified file 'src/provisioningserver/refresh/tests/test_node_info_scripts.py' |
249 | --- src/provisioningserver/refresh/tests/test_node_info_scripts.py 2017-04-05 14:35:05 +0000 |
250 | +++ src/provisioningserver/refresh/tests/test_node_info_scripts.py 2017-04-22 03:04:16 +0000 |
251 | @@ -338,16 +338,19 @@ |
252 | @typed |
253 | def make_lsblk_output( |
254 | self, name=None, read_only=False, removable=False, |
255 | - model=None, rotary=True) -> bytes: |
256 | + model=None, rotary=True, maj_min=None) -> bytes: |
257 | if name is None: |
258 | name = factory.make_name('name') |
259 | if model is None: |
260 | model = factory.make_name('model') |
261 | + if maj_min is None: |
262 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
263 | read_only = "1" if read_only else "0" |
264 | removable = "1" if removable else "0" |
265 | rotary = "1" if rotary else "0" |
266 | - output = 'NAME="%s" RO="%s" RM="%s" MODEL="%s" ROTA="%s"' % ( |
267 | - name, read_only, removable, model, rotary) |
268 | + output = ( |
269 | + 'NAME="%s" RO="%s" RM="%s" MODEL="%s" ROTA="%s" MAJ:MIN="%s"' % ( |
270 | + name, read_only, removable, model, rotary, '%s:%s' % maj_min)) |
271 | return output.encode("ascii") |
272 | |
273 | @typed |
274 | @@ -390,13 +393,37 @@ |
275 | gather_physical_block_devices(dev_disk_byid=dev_disk_byid) |
276 | return json.loads(output.getvalue()) |
277 | |
278 | + def make_output( |
279 | + self, name, maj_min, model, serial, size, block_size, |
280 | + drive_path=None, device_id_path=None, rotary=True, |
281 | + removable=False, read_only=False, sata=True): |
282 | + if drive_path is None: |
283 | + drive_path = '/dev/%s' % name |
284 | + ret = { |
285 | + 'NAME': name, |
286 | + 'PATH': drive_path, |
287 | + 'MAJ:MIN': '%s:%s' % maj_min, |
288 | + 'RO': '1' if read_only else '0', |
289 | + 'RM': '1' if removable else '0', |
290 | + 'MODEL': model, |
291 | + 'ROTA': '1' if rotary else '0', |
292 | + 'SATA': '1' if sata else '0', |
293 | + 'SERIAL': serial, |
294 | + 'SIZE': str(size), |
295 | + 'BLOCK_SIZE': str(block_size), |
296 | + 'RPM': '5400', |
297 | + } |
298 | + if device_id_path is not None: |
299 | + ret['ID_PATH'] = device_id_path |
300 | + return ret |
301 | + |
302 | def test__calls_lsblk(self): |
303 | check_output = self.patch(subprocess, "check_output") |
304 | check_output.return_value = b"" |
305 | self.call_gather_physical_block_devices() |
306 | self.assertThat(check_output, MockCalledOnceWith(( |
307 | "lsblk", "--exclude", "1,2,7", "-d", "-P", |
308 | - "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN"))) |
309 | + "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN"))) |
310 | |
311 | def test__returns_empty_list_when_no_disks(self): |
312 | check_output = self.patch(subprocess, "check_output") |
313 | @@ -416,7 +443,7 @@ |
314 | self.assertThat(check_output, MockCallsMatch( |
315 | call(( |
316 | "lsblk", "--exclude", "1,2,7", "-d", "-P", |
317 | - "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")), |
318 | + "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")), |
319 | call(("udevadm", "info", "-q", "all", "-n", name)))) |
320 | |
321 | def test__returns_empty_list_when_cdrom_only(self): |
322 | @@ -448,7 +475,7 @@ |
323 | self.assertThat(check_output, MockCallsMatch( |
324 | call(( |
325 | "lsblk", "--exclude", "1,2,7", "-d", "-P", |
326 | - "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")), |
327 | + "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")), |
328 | call(("udevadm", "info", "-q", "all", "-n", name)), |
329 | call(("sudo", "-n", "blockdev", "--getsize64", "/dev/%s" % name)), |
330 | call(("sudo", "-n", "blockdev", "--getbsz", "/dev/%s" % name)))) |
331 | @@ -471,48 +498,52 @@ |
332 | self.assertThat(check_output, MockCallsMatch( |
333 | call(( |
334 | "lsblk", "--exclude", "1,2,7", "-d", "-P", |
335 | - "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")), |
336 | + "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")), |
337 | call(("udevadm", "info", "-q", "all", "-n", name)), |
338 | call(("blockdev", "--getsize64", "/dev/%s" % name)), |
339 | call(("blockdev", "--getbsz", "/dev/%s" % name)))) |
340 | |
341 | - def test__returns_block_device(self): |
342 | - name = factory.make_name('name') |
343 | - model = factory.make_name('model') |
344 | - serial = factory.make_name('serial') |
345 | - size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
346 | - block_size = random.choice([512, 1024, 4096]) |
347 | - check_output = self.patch(subprocess, "check_output") |
348 | - |
349 | + def test__returns_sorted_block_devices(self): |
350 | + output = [] |
351 | + check_output_side_effects = [] |
352 | # Create simulated /dev tree |
353 | devroot = self.make_dir() |
354 | os.mkdir(os.path.join(devroot, 'disk')) |
355 | byidroot = os.path.join(devroot, 'disk', 'by_id') |
356 | os.mkdir(byidroot) |
357 | - os.mknod(os.path.join(devroot, name)) |
358 | - os.symlink(os.path.join(devroot, name), |
359 | - os.path.join(byidroot, 'deviceid')) |
360 | - |
361 | - check_output.side_effect = [ |
362 | - self.make_lsblk_output(name=name, model=model), |
363 | - self.make_udevadm_output(name, serial=serial, dev=devroot), |
364 | - b'%d' % size, |
365 | - b'%d' % block_size, |
366 | + for _ in range(3): |
367 | + name = factory.make_name('name') |
368 | + model = factory.make_name('model') |
369 | + serial = factory.make_name('serial') |
370 | + size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
371 | + block_size = random.choice([512, 1024, 4096]) |
372 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
373 | + |
374 | + # Create simulated /dev tree |
375 | + drive_path = os.path.join(devroot, name) |
376 | + os.mknod(drive_path) |
377 | + device_id_path = os.path.join( |
378 | + byidroot, factory.make_name('deviceid')) |
379 | + os.symlink(drive_path, device_id_path) |
380 | + |
381 | + check_output_side_effects += [ |
382 | + self.make_lsblk_output( |
383 | + name=name, model=model, maj_min=maj_min), |
384 | + self.make_udevadm_output(name, serial=serial, dev=devroot), |
385 | + b'%d' % size, |
386 | + b'%d' % block_size, |
387 | ] |
388 | - self.assertEqual([{ |
389 | - "NAME": name, |
390 | - "PATH": os.path.join(devroot, name), |
391 | - "ID_PATH": os.path.join(byidroot, 'deviceid'), |
392 | - "RO": "0", |
393 | - "RM": "0", |
394 | - "MODEL": model, |
395 | - "ROTA": "1", |
396 | - "SATA": "1", |
397 | - "SERIAL": serial, |
398 | - "SIZE": "%s" % size, |
399 | - "BLOCK_SIZE": "%s" % block_size, |
400 | - "RPM": "5400", |
401 | - }], self.call_gather_physical_block_devices(byidroot)) |
402 | + output.append( |
403 | + self.make_output( |
404 | + name, maj_min, model, serial, size, block_size, |
405 | + drive_path, device_id_path)) |
406 | + |
407 | + check_output = self.patch(subprocess, "check_output") |
408 | + check_output.side_effect = check_output_side_effects |
409 | + |
410 | + for ref, out in zip( |
411 | + output, self.call_gather_physical_block_devices(byidroot)): |
412 | + self.assertDictEqual(ref, out) |
413 | |
414 | def test__removes_duplicate_block_device_same_serial_and_model(self): |
415 | """Multipath disks get multiple IDs, but same serial/model is same |
416 | @@ -522,107 +553,51 @@ |
417 | serial = factory.make_name('serial') |
418 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
419 | block_size = random.choice([512, 1024, 4096]) |
420 | - check_output = self.patch(subprocess, "check_output") |
421 | - |
422 | - name2 = factory.make_name('name') |
423 | - |
424 | - # Create simulated /dev tree. |
425 | - devroot = self.make_dir() |
426 | - os.mkdir(os.path.join(devroot, 'disk')) |
427 | - byidroot = os.path.join(devroot, 'disk', 'by_id') |
428 | - os.mkdir(byidroot) |
429 | - |
430 | - os.mknod(os.path.join(devroot, name)) |
431 | - os.symlink(os.path.join(devroot, name), |
432 | - os.path.join(byidroot, 'deviceid')) |
433 | - |
434 | - os.mknod(os.path.join(devroot, name2)) |
435 | - os.symlink(os.path.join(devroot, name2), |
436 | - os.path.join(byidroot, 'deviceid2')) |
437 | - |
438 | - check_output.side_effect = [ |
439 | - b"\n".join([ |
440 | - self.make_lsblk_output(name=name, model=model), |
441 | - self.make_lsblk_output(name=name2, model=model)]), |
442 | - self.make_udevadm_output(name, serial=serial, dev=devroot), |
443 | - self.make_udevadm_output(name2, serial=serial, dev=devroot), |
444 | - b'%d' % size, |
445 | - b'%d' % block_size, |
446 | - b'%d' % size, |
447 | - b'%d' % block_size, |
448 | - ] |
449 | - |
450 | - self.assertEqual([{ |
451 | - "NAME": name, |
452 | - "PATH": os.path.join(devroot, name), |
453 | - "ID_PATH": os.path.join(byidroot, 'deviceid'), |
454 | - "RO": "0", |
455 | - "RM": "0", |
456 | - "MODEL": model, |
457 | - "ROTA": "1", |
458 | - "SATA": "1", |
459 | - "SERIAL": serial, |
460 | - "SIZE": "%s" % size, |
461 | - "BLOCK_SIZE": "%s" % block_size, |
462 | - "RPM": "5400", |
463 | - }], self.call_gather_physical_block_devices(byidroot)) |
464 | - |
465 | - def test__removes_duplicate_block_device_same_serial_blank_model(self): |
466 | - """Multipath disks get multiple IDs, but same serial is same device.""" |
467 | - name = factory.make_name('name') |
468 | - model = "" |
469 | - serial = factory.make_name('serial') |
470 | - size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
471 | - block_size = random.choice([512, 1024, 4096]) |
472 | - check_output = self.patch(subprocess, "check_output") |
473 | - |
474 | - name2 = factory.make_name('name') |
475 | - |
476 | - # Create simulated /dev tree. |
477 | - devroot = self.make_dir() |
478 | - os.mkdir(os.path.join(devroot, 'disk')) |
479 | - byidroot = os.path.join(devroot, 'disk', 'by_id') |
480 | - os.mkdir(byidroot) |
481 | - |
482 | - os.mknod(os.path.join(devroot, name)) |
483 | - os.symlink(os.path.join(devroot, name), |
484 | - os.path.join(byidroot, 'deviceid')) |
485 | - |
486 | - os.mknod(os.path.join(devroot, name2)) |
487 | - os.symlink(os.path.join(devroot, name2), |
488 | - os.path.join(byidroot, 'deviceid2')) |
489 | - |
490 | - check_output.side_effect = [ |
491 | - b"\n".join([ |
492 | - self.make_lsblk_output(name=name, model=model), |
493 | - self.make_lsblk_output(name=name2, model=model)]), |
494 | - self.make_udevadm_output(name, serial=serial, dev=devroot), |
495 | - self.make_udevadm_output(name2, serial=serial, dev=devroot), |
496 | - b'%d' % size, |
497 | - b'%d' % block_size, |
498 | - b'%d' % size, |
499 | - b'%d' % block_size, |
500 | - ] |
501 | - |
502 | - self.assertEqual([{ |
503 | - "NAME": name, |
504 | - "PATH": os.path.join(devroot, name), |
505 | - "ID_PATH": os.path.join(byidroot, 'deviceid'), |
506 | - "RO": "0", |
507 | - "RM": "0", |
508 | - "MODEL": model, |
509 | - "ROTA": "1", |
510 | - "SATA": "1", |
511 | - "SERIAL": serial, |
512 | - "SIZE": "%s" % size, |
513 | - "BLOCK_SIZE": "%s" % block_size, |
514 | - "RPM": "5400", |
515 | - }], self.call_gather_physical_block_devices(byidroot)) |
516 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
517 | + check_output = self.patch(subprocess, "check_output") |
518 | + |
519 | + name2 = factory.make_name('name') |
520 | + |
521 | + # Create simulated /dev tree. |
522 | + devroot = self.make_dir() |
523 | + os.mkdir(os.path.join(devroot, 'disk')) |
524 | + byidroot = os.path.join(devroot, 'disk', 'by_id') |
525 | + os.mkdir(byidroot) |
526 | + |
527 | + drive_path = os.path.join(devroot, name) |
528 | + os.mknod(drive_path) |
529 | + device_id_path = os.path.join(byidroot, 'deviceid') |
530 | + os.symlink(os.path.join(devroot, name), device_id_path) |
531 | + |
532 | + os.mknod(os.path.join(devroot, name2)) |
533 | + device_id_path2 = os.path.join(byidroot, 'deviceid2') |
534 | + os.symlink(os.path.join(devroot, name2), device_id_path2) |
535 | + |
536 | + check_output.side_effect = [ |
537 | + b"\n".join([ |
538 | + self.make_lsblk_output( |
539 | + name=name, model=model, maj_min=maj_min), |
540 | + self.make_lsblk_output( |
541 | + name=name2, model=model, maj_min=maj_min)]), |
542 | + self.make_udevadm_output(name, serial=serial, dev=devroot), |
543 | + self.make_udevadm_output(name2, serial=serial, dev=devroot), |
544 | + b'%d' % size, |
545 | + b'%d' % block_size, |
546 | + b'%d' % size, |
547 | + b'%d' % block_size, |
548 | + ] |
549 | + |
550 | + self.assertItemsEqual( |
551 | + [self.make_output( |
552 | + name, maj_min, model, serial, size, block_size, drive_path, |
553 | + device_id_path)], |
554 | + self.call_gather_physical_block_devices(byidroot)) |
555 | |
556 | def test__keeps_block_device_same_serial_different_model(self): |
557 | """Multipath disks get multiple IDs, but same serial is same device.""" |
558 | name = factory.make_name('name') |
559 | model = factory.make_name('model') |
560 | + maj_min = (0, 0) |
561 | serial = factory.make_name('serial') |
562 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
563 | block_size = random.choice([512, 1024, 4096]) |
564 | @@ -630,6 +605,7 @@ |
565 | |
566 | name2 = factory.make_name('name') |
567 | model2 = factory.make_name('model') |
568 | + maj_min2 = (1, 1) |
569 | |
570 | # Create simulated /dev tree. |
571 | devroot = self.make_dir() |
572 | @@ -637,18 +613,23 @@ |
573 | byidroot = os.path.join(devroot, 'disk', 'by_id') |
574 | os.mkdir(byidroot) |
575 | |
576 | - os.mknod(os.path.join(devroot, name)) |
577 | - os.symlink(os.path.join(devroot, name), |
578 | - os.path.join(byidroot, 'deviceid')) |
579 | + drive_path = os.path.join(devroot, name) |
580 | + os.mknod(drive_path) |
581 | + device_id_path = os.path.join(byidroot, 'deviceid') |
582 | + os.symlink(os.path.join(devroot, name), device_id_path) |
583 | |
584 | - os.mknod(os.path.join(devroot, name2)) |
585 | - os.symlink(os.path.join(devroot, name2), |
586 | - os.path.join(byidroot, 'deviceid2')) |
587 | + drive_path2 = os.path.join(devroot, name2) |
588 | + os.mknod(drive_path2) |
589 | + device_id_path2 = os.path.join(byidroot, 'deviceid2') |
590 | + os.symlink(os.path.join(devroot, name2), device_id_path2) |
591 | |
592 | check_output.side_effect = [ |
593 | b"\n".join([ |
594 | - self.make_lsblk_output(name=name, model=model), |
595 | - self.make_lsblk_output(name=name2, model=model2)]), |
596 | + self.make_lsblk_output( |
597 | + name=name, model=model, maj_min=maj_min), |
598 | + self.make_lsblk_output( |
599 | + name=name2, model=model2, maj_min=maj_min2) |
600 | + ]), |
601 | self.make_udevadm_output(name, serial=serial, dev=devroot), |
602 | self.make_udevadm_output(name2, serial=serial, dev=devroot), |
603 | b'%d' % size, |
604 | @@ -657,44 +638,29 @@ |
605 | b'%d' % block_size, |
606 | ] |
607 | |
608 | - self.assertEqual([{ |
609 | - "NAME": name, |
610 | - "PATH": os.path.join(devroot, name), |
611 | - "ID_PATH": os.path.join(byidroot, 'deviceid'), |
612 | - "RO": "0", |
613 | - "RM": "0", |
614 | - "MODEL": model, |
615 | - "ROTA": "1", |
616 | - "SATA": "1", |
617 | - "SERIAL": serial, |
618 | - "SIZE": "%s" % size, |
619 | - "BLOCK_SIZE": "%s" % block_size, |
620 | - "RPM": "5400", |
621 | - }, { |
622 | - "NAME": name2, |
623 | - "PATH": os.path.join(devroot, name2), |
624 | - "ID_PATH": os.path.join(byidroot, 'deviceid2'), |
625 | - "RO": "0", |
626 | - "RM": "0", |
627 | - "MODEL": model2, |
628 | - "ROTA": "1", |
629 | - "SATA": "1", |
630 | - "SERIAL": serial, |
631 | - "SIZE": "%s" % size, |
632 | - "BLOCK_SIZE": "%s" % block_size, |
633 | - "RPM": "5400", |
634 | - }], self.call_gather_physical_block_devices(byidroot)) |
635 | + for ref, out in zip( |
636 | + [ |
637 | + self.make_output( |
638 | + name, maj_min, model, serial, size, block_size, |
639 | + drive_path, device_id_path), |
640 | + self.make_output( |
641 | + name2, maj_min2, model2, serial, size, block_size, |
642 | + drive_path2, device_id_path2), |
643 | + ], self.call_gather_physical_block_devices(byidroot)): |
644 | + self.assertDictEqual(ref, out) |
645 | |
646 | def test__keeps_block_device_blank_serial_same_model(self): |
647 | """Multipath disks get multiple IDs, but same serial is same device.""" |
648 | name = factory.make_name('name') |
649 | model = factory.make_name('model') |
650 | + maj_min = (0, 0) |
651 | serial = '' |
652 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
653 | block_size = random.choice([512, 1024, 4096]) |
654 | check_output = self.patch(subprocess, "check_output") |
655 | |
656 | name2 = factory.make_name('name') |
657 | + maj_min2 = (1, 1) |
658 | |
659 | # Create simulated /dev tree. |
660 | devroot = self.make_dir() |
661 | @@ -702,18 +668,22 @@ |
662 | byidroot = os.path.join(devroot, 'disk', 'by_id') |
663 | os.mkdir(byidroot) |
664 | |
665 | - os.mknod(os.path.join(devroot, name)) |
666 | - os.symlink(os.path.join(devroot, name), |
667 | - os.path.join(byidroot, 'deviceid')) |
668 | + drive_path = os.path.join(devroot, name) |
669 | + os.mknod(drive_path) |
670 | + device_id_path = os.path.join(byidroot, 'deviceid') |
671 | + os.symlink(os.path.join(devroot, name), device_id_path) |
672 | |
673 | - os.mknod(os.path.join(devroot, name2)) |
674 | - os.symlink(os.path.join(devroot, name2), |
675 | - os.path.join(byidroot, 'deviceid2')) |
676 | + drive_path2 = os.path.join(devroot, name2) |
677 | + os.mknod(drive_path2) |
678 | + device_id_path2 = os.path.join(byidroot, 'deviceid2') |
679 | + os.symlink(os.path.join(devroot, name2), device_id_path2) |
680 | |
681 | check_output.side_effect = [ |
682 | b"\n".join([ |
683 | - self.make_lsblk_output(name=name, model=model), |
684 | - self.make_lsblk_output(name=name2, model=model)]), |
685 | + self.make_lsblk_output( |
686 | + name=name, model=model, maj_min=maj_min), |
687 | + self.make_lsblk_output( |
688 | + name=name2, model=model, maj_min=maj_min2)]), |
689 | self.make_udevadm_output(name, serial=serial, dev=devroot), |
690 | self.make_udevadm_output(name2, serial=serial, dev=devroot), |
691 | b'%d' % size, |
692 | @@ -722,38 +692,22 @@ |
693 | b'%d' % block_size, |
694 | ] |
695 | |
696 | - self.assertEqual([{ |
697 | - "NAME": name, |
698 | - "PATH": os.path.join(devroot, name), |
699 | - "ID_PATH": os.path.join(byidroot, 'deviceid'), |
700 | - "RO": "0", |
701 | - "RM": "0", |
702 | - "MODEL": model, |
703 | - "ROTA": "1", |
704 | - "SATA": "1", |
705 | - "SERIAL": serial, |
706 | - "SIZE": "%s" % size, |
707 | - "BLOCK_SIZE": "%s" % block_size, |
708 | - "RPM": "5400", |
709 | - }, { |
710 | - "NAME": name2, |
711 | - "PATH": os.path.join(devroot, name2), |
712 | - "ID_PATH": os.path.join(byidroot, 'deviceid2'), |
713 | - "RO": "0", |
714 | - "RM": "0", |
715 | - "MODEL": model, |
716 | - "ROTA": "1", |
717 | - "SATA": "1", |
718 | - "SERIAL": serial, |
719 | - "SIZE": "%s" % size, |
720 | - "BLOCK_SIZE": "%s" % block_size, |
721 | - "RPM": "5400", |
722 | - }], self.call_gather_physical_block_devices(byidroot)) |
723 | + for ref, out in zip( |
724 | + [ |
725 | + self.make_output( |
726 | + name, maj_min, model, serial, size, block_size, |
727 | + drive_path, device_id_path), |
728 | + self.make_output( |
729 | + name2, maj_min2, model, serial, size, block_size, |
730 | + drive_path2, device_id_path2), |
731 | + ], self.call_gather_physical_block_devices(byidroot)): |
732 | + self.assertDictEqual(ref, out) |
733 | |
734 | def test__returns_block_device_without_id_path(self): |
735 | """Block devices without by-id links should not have ID_PATH key""" |
736 | name = factory.make_name('name') |
737 | model = factory.make_name('model') |
738 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
739 | serial = factory.make_name('serial') |
740 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
741 | block_size = random.choice([512, 1024, 4096]) |
742 | @@ -764,158 +718,113 @@ |
743 | os.mkdir(os.path.join(devroot, 'disk')) |
744 | byidroot = os.path.join(devroot, 'disk', 'by_id') |
745 | os.mkdir(byidroot) |
746 | - os.mknod(os.path.join(devroot, name)) |
747 | + drive_path = os.path.join(devroot, name) |
748 | + os.mknod(drive_path) |
749 | |
750 | check_output.side_effect = [ |
751 | - self.make_lsblk_output(name=name, model=model), |
752 | + self.make_lsblk_output(name=name, model=model, maj_min=maj_min), |
753 | self.make_udevadm_output(name, serial=serial, dev=devroot), |
754 | b'%d' % size, |
755 | b'%d' % block_size, |
756 | ] |
757 | - self.assertEqual([{ |
758 | - "NAME": name, |
759 | - "PATH": os.path.join(devroot, name), |
760 | - "RO": "0", |
761 | - "RM": "0", |
762 | - "MODEL": model, |
763 | - "ROTA": "1", |
764 | - "SATA": "1", |
765 | - "SERIAL": serial, |
766 | - "SIZE": "%s" % size, |
767 | - "BLOCK_SIZE": "%s" % block_size, |
768 | - "RPM": "5400", |
769 | - }], self.call_gather_physical_block_devices(byidroot)) |
770 | + for ref, out in zip( |
771 | + [ |
772 | + self.make_output( |
773 | + name, maj_min, model, serial, size, block_size, |
774 | + drive_path), |
775 | + ], self.call_gather_physical_block_devices(byidroot)): |
776 | + self.assertDictEqual(ref, out) |
777 | |
778 | def test__returns_block_device_readonly(self): |
779 | name = factory.make_name('name') |
780 | model = factory.make_name('model') |
781 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
782 | serial = factory.make_name('serial') |
783 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
784 | block_size = random.choice([512, 1024, 4096]) |
785 | check_output = self.patch(subprocess, "check_output") |
786 | check_output.side_effect = [ |
787 | - self.make_lsblk_output(name=name, model=model, read_only=True), |
788 | + self.make_lsblk_output( |
789 | + name=name, model=model, read_only=True, maj_min=maj_min), |
790 | self.make_udevadm_output(name, serial=serial), |
791 | b'%d' % size, |
792 | b'%d' % block_size, |
793 | ] |
794 | - self.assertEqual([{ |
795 | - "NAME": name, |
796 | - "PATH": "/dev/%s" % name, |
797 | - "RO": "1", |
798 | - "RM": "0", |
799 | - "MODEL": model, |
800 | - "ROTA": "1", |
801 | - "SATA": "1", |
802 | - "SERIAL": serial, |
803 | - "SIZE": "%s" % size, |
804 | - "BLOCK_SIZE": "%s" % block_size, |
805 | - "RPM": "5400", |
806 | - }], self.call_gather_physical_block_devices()) |
807 | + for ref, out in zip( |
808 | + [ |
809 | + self.make_output( |
810 | + name, maj_min, model, serial, size, |
811 | + block_size, read_only=True), |
812 | + ], self.call_gather_physical_block_devices()): |
813 | + self.assertDictEqual(ref, out) |
814 | |
815 | def test__returns_block_device_ssd(self): |
816 | name = factory.make_name('name') |
817 | model = factory.make_name('model') |
818 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
819 | serial = factory.make_name('serial') |
820 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
821 | block_size = random.choice([512, 1024, 4096]) |
822 | check_output = self.patch(subprocess, "check_output") |
823 | check_output.side_effect = [ |
824 | - self.make_lsblk_output(name=name, model=model, rotary=False), |
825 | + self.make_lsblk_output( |
826 | + name=name, model=model, rotary=False, maj_min=maj_min), |
827 | self.make_udevadm_output(name, serial=serial), |
828 | b'%d' % size, |
829 | b'%d' % block_size, |
830 | ] |
831 | - self.assertEqual([{ |
832 | - "NAME": name, |
833 | - "PATH": "/dev/%s" % name, |
834 | - "RO": "0", |
835 | - "RM": "0", |
836 | - "MODEL": model, |
837 | - "ROTA": "0", |
838 | - "SATA": "1", |
839 | - "SERIAL": serial, |
840 | - "SIZE": "%s" % size, |
841 | - "BLOCK_SIZE": "%s" % block_size, |
842 | - "RPM": "5400", |
843 | - }], self.call_gather_physical_block_devices()) |
844 | + for ref, out in zip( |
845 | + [ |
846 | + self.make_output( |
847 | + name, maj_min, model, serial, size, block_size, |
848 | + rotary=False), |
849 | + ], self.call_gather_physical_block_devices()): |
850 | + self.assertDictEqual(ref, out) |
851 | |
852 | def test__returns_block_device_not_sata(self): |
853 | name = factory.make_name('name') |
854 | model = factory.make_name('model') |
855 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
856 | serial = factory.make_name('serial') |
857 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
858 | block_size = random.choice([512, 1024, 4096]) |
859 | check_output = self.patch(subprocess, "check_output") |
860 | check_output.side_effect = [ |
861 | - self.make_lsblk_output(name=name, model=model), |
862 | + self.make_lsblk_output(name=name, model=model, maj_min=maj_min), |
863 | self.make_udevadm_output(name, serial=serial, sata=False), |
864 | b'%d' % size, |
865 | b'%d' % block_size, |
866 | ] |
867 | - self.assertEqual([{ |
868 | - "NAME": name, |
869 | - "PATH": "/dev/%s" % name, |
870 | - "RO": "0", |
871 | - "RM": "0", |
872 | - "MODEL": model, |
873 | - "ROTA": "1", |
874 | - "SATA": "0", |
875 | - "SERIAL": serial, |
876 | - "SIZE": "%s" % size, |
877 | - "BLOCK_SIZE": "%s" % block_size, |
878 | - "RPM": "5400", |
879 | - }], self.call_gather_physical_block_devices()) |
880 | + for ref, out in zip( |
881 | + [ |
882 | + self.make_output( |
883 | + name, maj_min, model, serial, size, block_size, |
884 | + sata=False), |
885 | + ], self.call_gather_physical_block_devices()): |
886 | + self.assertDictEqual(ref, out) |
887 | |
888 | def test__returns_block_device_removable(self): |
889 | name = factory.make_name('name') |
890 | model = factory.make_name('model') |
891 | + maj_min = (random.randint(0, 255), random.randint(0, 255)) |
892 | serial = factory.make_name('serial') |
893 | size = random.randint(3000 * 1000, 1000 * 1000 * 1000) |
894 | block_size = random.choice([512, 1024, 4096]) |
895 | check_output = self.patch(subprocess, "check_output") |
896 | check_output.side_effect = [ |
897 | - self.make_lsblk_output(name=name, model=model, removable=True), |
898 | + self.make_lsblk_output( |
899 | + name=name, model=model, removable=True, maj_min=maj_min), |
900 | self.make_udevadm_output(name, serial=serial), |
901 | b'%d' % size, |
902 | b'%d' % block_size, |
903 | ] |
904 | - self.assertEqual([{ |
905 | - "NAME": name, |
906 | - "PATH": "/dev/%s" % name, |
907 | - "RO": "0", |
908 | - "RM": "1", |
909 | - "MODEL": model, |
910 | - "ROTA": "1", |
911 | - "SATA": "1", |
912 | - "SERIAL": serial, |
913 | - "SIZE": "%s" % size, |
914 | - "BLOCK_SIZE": "%s" % block_size, |
915 | - "RPM": "5400", |
916 | - }], self.call_gather_physical_block_devices()) |
917 | - |
918 | - def test__returns_multiple_block_devices_in_order(self): |
919 | - names = [factory.make_name('name') for _ in range(3)] |
920 | - lsblk = [ |
921 | - self.make_lsblk_output(name=name) |
922 | - for name in names |
923 | - ] |
924 | - call_outputs = [] |
925 | - call_outputs.append(b"\n".join(lsblk)) |
926 | - for name in names: |
927 | - call_outputs.append(self.make_udevadm_output(name)) |
928 | - for name in names: |
929 | - call_outputs.append( |
930 | - b"%d" % random.randint(1000 * 1000, 1000 * 1000 * 1000)) |
931 | - call_outputs.append( |
932 | - b"%d" % random.choice([512, 1024, 4096])) |
933 | - check_output = self.patch(subprocess, "check_output") |
934 | - check_output.side_effect = call_outputs |
935 | - device_names = [ |
936 | - block_info['NAME'] |
937 | - for block_info in self.call_gather_physical_block_devices() |
938 | - ] |
939 | - self.assertEqual(names, device_names) |
940 | + for ref, out in zip( |
941 | + [ |
942 | + self.make_output( |
943 | + name, maj_min, model, serial, size, block_size, |
944 | + removable=True), |
945 | + ], self.call_gather_physical_block_devices()): |
946 | + self.assertDictEqual(ref, out) |
947 | |
948 | def test__quietly_exits_in_container(self): |
949 | script_dir = self.useFixture(TempDirectory()).path |
Looks good. Thanks for fixing this issue glad to see it work on Trusty. Did you fix this in 2.1 as well? For the block device listing that is.