Merge lp:~ltrager/maas/lp1685361 into lp:~maas-committers/maas/trunk

Proposed by Lee Trager
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
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-07-block-devices sorted results using the -x flag which is not available in Trusty. Sorted is now done with Python.
  * 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.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

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.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/metadataserver/api.py'
--- src/metadataserver/api.py 2017-04-06 16:08:19 +0000
+++ src/metadataserver/api.py 2017-04-22 03:04:16 +0000
@@ -495,12 +495,18 @@
495 }495 }
496 target_status = signaling_statuses.get(status)496 target_status = signaling_statuses.get(status)
497497
498 if target_status is not None:
499 node.status_expires = None
500
501 # Recalculate tags when commissioning ends.
502 if target_status in [NODE_STATUS.READY, NODE_STATUS.TESTING]:498 if target_status in [NODE_STATUS.READY, NODE_STATUS.TESTING]:
499 # Recalculate tags when commissioning ends.
503 populate_tags_for_single_node(Tag.objects.all(), node)500 populate_tags_for_single_node(Tag.objects.all(), node)
501 elif (target_status == NODE_STATUS.FAILED_COMMISSIONING and
502 node.current_testing_script_set is not None):
503 # If commissioning failed testing doesn't run, mark any pending
504 # scripts as aborted.
505 qs = node.current_testing_script_set.scriptresult_set.filter(
506 status=SCRIPT_STATUS.PENDING)
507 for script_result in qs:
508 script_result.status = SCRIPT_STATUS.ABORTED
509 script_result.save(update_fields=['status'])
504510
505 return target_status511 return target_status
506512
507513
=== modified file 'src/metadataserver/builtin_scripts/badblocks.py'
--- src/metadataserver/builtin_scripts/badblocks.py 2017-03-29 23:38:04 +0000
+++ src/metadataserver/builtin_scripts/badblocks.py 2017-04-22 03:04:16 +0000
@@ -77,11 +77,20 @@
77 iscsi_drives = re.findall(77 iscsi_drives = re.findall(
78 'Attached scsi disk (?P<disk>\w+)', output.decode('utf-8'))78 'Attached scsi disk (?P<disk>\w+)', output.decode('utf-8'))
7979
80 lsblk_output = check_output(80 try:
81 [81 lsblk_output = check_output(
82 'lsblk', '--exclude', '1,2,7', '-d', '-P', '-o',82 [
83 'NAME,RO,MODEL,SERIAL',83 'lsblk', '--exclude', '1,2,7', '-d', '-P', '-o',
84 ], timeout=TIMEOUT).decode('utf-8')84 'NAME,RO,MODEL,SERIAL',
85 ], timeout=TIMEOUT).decode('utf-8')
86 except CalledProcessError:
87 # The SERIAL column is unsupported in the Trusty version of lsblk. Try
88 # again without it.
89 lsblk_output = check_output(
90 [
91 'lsblk', '--exclude', '1,2,7', '-d', '-P', '-o',
92 'NAME,RO,MODEL',
93 ], timeout=TIMEOUT).decode('utf-8')
85 drives = []94 drives = []
86 for line in lsblk_output.splitlines():95 for line in lsblk_output.splitlines():
87 drive = {}96 drive = {}
@@ -115,7 +124,10 @@
115 dashes = '-' * int((80.0 - (2 + len(thread.drive['PATH']))) / 2)124 dashes = '-' * int((80.0 - (2 + len(thread.drive['PATH']))) / 2)
116 print('%s %s %s' % (dashes, thread.drive['PATH'], dashes))125 print('%s %s %s' % (dashes, thread.drive['PATH'], dashes))
117 print('Model: %s' % thread.drive['MODEL'])126 print('Model: %s' % thread.drive['MODEL'])
118 print('Serial: %s' % thread.drive['SERIAL'])127 # The SERIAL column is only available in newer versions of lsblk. This
128 # can be removed with Trusty support.
129 if 'SERIAL' in thread.drive:
130 print('Serial: %s' % thread.drive['SERIAL'])
119 print()131 print()
120132
121 if thread.returncode != 0:133 if thread.returncode != 0:
122134
=== modified file 'src/metadataserver/builtin_scripts/smartctl.py'
--- src/metadataserver/builtin_scripts/smartctl.py 2017-03-30 00:45:53 +0000
+++ src/metadataserver/builtin_scripts/smartctl.py 2017-04-22 03:04:16 +0000
@@ -148,11 +148,20 @@
148 if m is not None:148 if m is not None:
149 drives.append(drive_with_device_type)149 drives.append(drive_with_device_type)
150150
151 all_drives = check_output(151 try:
152 [152 all_drives = check_output(
153 'lsblk', '--exclude', '1,2,7', '-d', '-l', '-o',153 [
154 'NAME,MODEL,SERIAL', '-x', 'NAME',154 'lsblk', '--exclude', '1,2,7', '-d', '-l', '-o',
155 ], timeout=TIMEOUT, stderr=DEVNULL).decode('utf-8').splitlines()155 'NAME,MODEL,SERIAL', '-x', 'NAME',
156 ], timeout=TIMEOUT, stderr=DEVNULL).decode('utf-8').splitlines()
157 except CalledProcessError:
158 # The SERIAL column and sorting(-x) are unsupported in the Trusty
159 # version of lsblk. Try again without them.
160 all_drives = check_output(
161 [
162 'lsblk', '--exclude', '1,2,7', '-d', '-l', '-o',
163 'NAME,MODEL',
164 ], timeout=TIMEOUT, stderr=DEVNULL).decode('utf-8').splitlines()
156 supported_drives = iscsi_drives + [165 supported_drives = iscsi_drives + [
157 drive[0].split('/')[-1] for drive in drives]166 drive[0].split('/')[-1] for drive in drives]
158 unsupported_drives = [167 unsupported_drives = [
159168
=== modified file 'src/metadataserver/tests/test_api.py'
--- src/metadataserver/tests/test_api.py 2017-04-06 16:08:19 +0000
+++ src/metadataserver/tests/test_api.py 2017-04-22 03:04:16 +0000
@@ -1435,7 +1435,7 @@
1435 self.assertEqual(http.client.OK, response.status_code)1435 self.assertEqual(http.client.OK, response.status_code)
1436 self.assertEqual(NODE_STATUS.READY, reload_object(node).status)1436 self.assertEqual(NODE_STATUS.READY, reload_object(node).status)
14371437
1438 def test_signaling_commissioning_failure_makes_node_failed_Tests(self):1438 def test_signaling_commissioning_failure_makes_node_failed_tests(self):
1439 node = factory.make_Node(1439 node = factory.make_Node(
1440 status=NODE_STATUS.COMMISSIONING, with_empty_script_sets=True)1440 status=NODE_STATUS.COMMISSIONING, with_empty_script_sets=True)
1441 client = make_node_client(node=node)1441 client = make_node_client(node=node)
@@ -1443,6 +1443,8 @@
1443 self.assertEqual(http.client.OK, response.status_code)1443 self.assertEqual(http.client.OK, response.status_code)
1444 self.assertEqual(1444 self.assertEqual(
1445 NODE_STATUS.FAILED_COMMISSIONING, reload_object(node).status)1445 NODE_STATUS.FAILED_COMMISSIONING, reload_object(node).status)
1446 for script_result in node.current_testing_script_set:
1447 self.assertEqual(SCRIPT_STATUS.ABORTED, script_result.status)
14461448
1447 def test_signaling_commissioning_failure_does_not_populate_tags(self):1449 def test_signaling_commissioning_failure_does_not_populate_tags(self):
1448 populate_tags_for_single_node = self.patch(1450 populate_tags_for_single_node = self.patch(
14491451
=== modified file 'src/metadataserver/user_data/templates/snippets/maas_run_remote_scripts.py'
--- src/metadataserver/user_data/templates/snippets/maas_run_remote_scripts.py 2017-04-13 01:15:22 +0000
+++ src/metadataserver/user_data/templates/snippets/maas_run_remote_scripts.py 2017-04-22 03:04:16 +0000
@@ -103,8 +103,9 @@
103 timeout_seconds = script.get('timeout_seconds')103 timeout_seconds = script.get('timeout_seconds')
104104
105 signal_wrapper(105 signal_wrapper(
106 **args, error='Starting %s [%d/%d]' % (106 error='Starting %s [%d/%d]' % (
107 script['name'], i, len(scripts)))107 script['name'], i, len(scripts)),
108 **args)
108109
109 script_path = os.path.join(scripts_dir, script['path'])110 script_path = os.path.join(scripts_dir, script['path'])
110 combined_path = os.path.join(out_dir, script['name'])111 combined_path = os.path.join(out_dir, script['name'])
@@ -142,8 +143,9 @@
142 stderr_name: result,143 stderr_name: result,
143 }144 }
144 signal_wrapper(145 signal_wrapper(
145 **args, error='Failed to execute %s [%d/%d]: %d' % (146 error='Failed to execute %s [%d/%d]: %d' % (
146 script['name'], i, total_scripts, args['exit_status']))147 script['name'], i, total_scripts, args['exit_status']),
148 **args)
147 except TimeoutExpired:149 except TimeoutExpired:
148 fail_count += 1150 fail_count += 1
149 args['status'] = 'TIMEDOUT'151 args['status'] = 'TIMEDOUT'
@@ -153,9 +155,10 @@
153 stderr_name: open(stderr_path, 'rb').read(),155 stderr_name: open(stderr_path, 'rb').read(),
154 }156 }
155 signal_wrapper(157 signal_wrapper(
156 **args, error='Timeout(%s) expired on %s [%d/%d]' % (158 error='Timeout(%s) expired on %s [%d/%d]' % (
157 str(timedelta(seconds=timeout_seconds)), script['name'], i,159 str(timedelta(seconds=timeout_seconds)), script['name'], i,
158 total_scripts))160 total_scripts),
161 **args)
159 else:162 else:
160 if proc.returncode != 0:163 if proc.returncode != 0:
161 fail_count += 1164 fail_count += 1
@@ -166,8 +169,9 @@
166 stderr_name: open(stderr_path, 'rb').read(),169 stderr_name: open(stderr_path, 'rb').read(),
167 }170 }
168 signal_wrapper(171 signal_wrapper(
169 **args, error='Finished %s [%d/%d]: %d' % (172 error='Finished %s [%d/%d]: %d' % (
170 script['name'], i, len(scripts), args['exit_status']))173 script['name'], i, len(scripts), args['exit_status']),
174 **args)
171175
172 # Signal failure after running commissioning or testing scripts so MAAS176 # Signal failure after running commissioning or testing scripts so MAAS
173 # transisitions the node into FAILED_COMMISSIONING or FAILED_TESTING.177 # transisitions the node into FAILED_COMMISSIONING or FAILED_TESTING.
@@ -188,6 +192,8 @@
188 if commissioning_scripts is not None:192 if commissioning_scripts is not None:
189 fail_count += run_scripts(193 fail_count += run_scripts(
190 url, creds, scripts_dir, out_dir, commissioning_scripts)194 url, creds, scripts_dir, out_dir, commissioning_scripts)
195 if fail_count != 0:
196 return
191197
192 testing_scripts = scripts.get('testing_scripts')198 testing_scripts = scripts.get('testing_scripts')
193 if testing_scripts is not None:199 if testing_scripts is not None:
194200
=== modified file 'src/metadataserver/user_data/templates/snippets/tests/test_maas_run_remote_scripts.py'
--- src/metadataserver/user_data/templates/snippets/tests/test_maas_run_remote_scripts.py 2017-04-13 01:19:43 +0000
+++ src/metadataserver/user_data/templates/snippets/tests/test_maas_run_remote_scripts.py 2017-04-22 03:04:16 +0000
@@ -21,6 +21,7 @@
21 MockAnyCall,21 MockAnyCall,
22 MockCalledOnce,22 MockCalledOnce,
23 MockCalledOnceWith,23 MockCalledOnceWith,
24 MockNotCalled,
24)25)
25from maastesting.testcase import MAASTestCase26from maastesting.testcase import MAASTestCase
26from snippets import maas_run_remote_scripts27from snippets import maas_run_remote_scripts
@@ -117,6 +118,24 @@
117 mock_signal,118 mock_signal,
118 MockAnyCall(None, None, 'OK', 'All scripts successfully ran'))119 MockAnyCall(None, None, 'OK', 'All scripts successfully ran'))
119120
121 def test_run_scripts_from_metadata_doesnt_run_tests_on_commiss_fail(self):
122 scripts_dir = self.useFixture(TempDirectory()).path
123 mock_run_scripts = self.patch(maas_run_remote_scripts, 'run_scripts')
124 mock_run_scripts.return_value = random.randint(1, 100)
125 mock_signal = self.patch(maas_run_remote_scripts, 'signal')
126 index_json = self.make_index_json(scripts_dir)
127
128 # Don't need to give the url, creds, or out_dir as we're not running
129 # the scripts and sending the results.
130 run_scripts_from_metadata(None, None, scripts_dir, None)
131
132 self.assertThat(
133 mock_run_scripts,
134 MockCalledOnceWith(
135 None, None, scripts_dir, None,
136 index_json['commissioning_scripts']))
137 self.assertThat(mock_signal, MockNotCalled())
138
120 def test_run_scripts_from_metadata_does_nothing_on_empty(self):139 def test_run_scripts_from_metadata_does_nothing_on_empty(self):
121 scripts_dir = self.useFixture(TempDirectory()).path140 scripts_dir = self.useFixture(TempDirectory()).path
122 self.patch(maas_run_remote_scripts, 'run_scripts')141 self.patch(maas_run_remote_scripts, 'run_scripts')
123142
=== modified file 'src/provisioningserver/refresh/node_info_scripts.py'
--- src/provisioningserver/refresh/node_info_scripts.py 2017-04-06 00:39:19 +0000
+++ src/provisioningserver/refresh/node_info_scripts.py 2017-04-22 03:04:16 +0000
@@ -457,7 +457,7 @@
457 blockdevs = []457 blockdevs = []
458 block_list = check_output((458 block_list = check_output((
459 "lsblk", "--exclude", "1,2,7", "-d", "-P",459 "lsblk", "--exclude", "1,2,7", "-d", "-P",
460 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN"))460 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN"))
461 block_list = block_list.decode("utf-8")461 block_list = block_list.decode("utf-8")
462 for blockdev in block_list.splitlines():462 for blockdev in block_list.splitlines():
463 tokens = shlex.split(blockdev)463 tokens = shlex.split(blockdev)
@@ -467,6 +467,13 @@
467 current_block[k] = v.strip()467 current_block[k] = v.strip()
468 blockdevs.append(current_block)468 blockdevs.append(current_block)
469469
470 # Sort drives by MAJ:MIN so MAAS picks the correct boot drive.
471 # lsblk -x MAJ:MIN can't be used as the -x flag only appears in
472 # lsblk 2.71.1 or newer which is unavailable on Trusty. See LP:1673724
473 blockdevs = sorted(
474 blockdevs,
475 key=lambda blockdev: [int(i) for i in blockdev['MAJ:MIN'].split(':')])
476
470 # Grab the device path, serial number, and sata connection.477 # Grab the device path, serial number, and sata connection.
471 UDEV_MAPPINGS = {478 UDEV_MAPPINGS = {
472 "DEVNAME": "PATH",479 "DEVNAME": "PATH",
473480
=== modified file 'src/provisioningserver/refresh/tests/test_node_info_scripts.py'
--- src/provisioningserver/refresh/tests/test_node_info_scripts.py 2017-04-05 14:35:05 +0000
+++ src/provisioningserver/refresh/tests/test_node_info_scripts.py 2017-04-22 03:04:16 +0000
@@ -338,16 +338,19 @@
338 @typed338 @typed
339 def make_lsblk_output(339 def make_lsblk_output(
340 self, name=None, read_only=False, removable=False,340 self, name=None, read_only=False, removable=False,
341 model=None, rotary=True) -> bytes:341 model=None, rotary=True, maj_min=None) -> bytes:
342 if name is None:342 if name is None:
343 name = factory.make_name('name')343 name = factory.make_name('name')
344 if model is None:344 if model is None:
345 model = factory.make_name('model')345 model = factory.make_name('model')
346 if maj_min is None:
347 maj_min = (random.randint(0, 255), random.randint(0, 255))
346 read_only = "1" if read_only else "0"348 read_only = "1" if read_only else "0"
347 removable = "1" if removable else "0"349 removable = "1" if removable else "0"
348 rotary = "1" if rotary else "0"350 rotary = "1" if rotary else "0"
349 output = 'NAME="%s" RO="%s" RM="%s" MODEL="%s" ROTA="%s"' % (351 output = (
350 name, read_only, removable, model, rotary)352 'NAME="%s" RO="%s" RM="%s" MODEL="%s" ROTA="%s" MAJ:MIN="%s"' % (
353 name, read_only, removable, model, rotary, '%s:%s' % maj_min))
351 return output.encode("ascii")354 return output.encode("ascii")
352355
353 @typed356 @typed
@@ -390,13 +393,37 @@
390 gather_physical_block_devices(dev_disk_byid=dev_disk_byid)393 gather_physical_block_devices(dev_disk_byid=dev_disk_byid)
391 return json.loads(output.getvalue())394 return json.loads(output.getvalue())
392395
396 def make_output(
397 self, name, maj_min, model, serial, size, block_size,
398 drive_path=None, device_id_path=None, rotary=True,
399 removable=False, read_only=False, sata=True):
400 if drive_path is None:
401 drive_path = '/dev/%s' % name
402 ret = {
403 'NAME': name,
404 'PATH': drive_path,
405 'MAJ:MIN': '%s:%s' % maj_min,
406 'RO': '1' if read_only else '0',
407 'RM': '1' if removable else '0',
408 'MODEL': model,
409 'ROTA': '1' if rotary else '0',
410 'SATA': '1' if sata else '0',
411 'SERIAL': serial,
412 'SIZE': str(size),
413 'BLOCK_SIZE': str(block_size),
414 'RPM': '5400',
415 }
416 if device_id_path is not None:
417 ret['ID_PATH'] = device_id_path
418 return ret
419
393 def test__calls_lsblk(self):420 def test__calls_lsblk(self):
394 check_output = self.patch(subprocess, "check_output")421 check_output = self.patch(subprocess, "check_output")
395 check_output.return_value = b""422 check_output.return_value = b""
396 self.call_gather_physical_block_devices()423 self.call_gather_physical_block_devices()
397 self.assertThat(check_output, MockCalledOnceWith((424 self.assertThat(check_output, MockCalledOnceWith((
398 "lsblk", "--exclude", "1,2,7", "-d", "-P",425 "lsblk", "--exclude", "1,2,7", "-d", "-P",
399 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")))426 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")))
400427
401 def test__returns_empty_list_when_no_disks(self):428 def test__returns_empty_list_when_no_disks(self):
402 check_output = self.patch(subprocess, "check_output")429 check_output = self.patch(subprocess, "check_output")
@@ -416,7 +443,7 @@
416 self.assertThat(check_output, MockCallsMatch(443 self.assertThat(check_output, MockCallsMatch(
417 call((444 call((
418 "lsblk", "--exclude", "1,2,7", "-d", "-P",445 "lsblk", "--exclude", "1,2,7", "-d", "-P",
419 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")),446 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")),
420 call(("udevadm", "info", "-q", "all", "-n", name))))447 call(("udevadm", "info", "-q", "all", "-n", name))))
421448
422 def test__returns_empty_list_when_cdrom_only(self):449 def test__returns_empty_list_when_cdrom_only(self):
@@ -448,7 +475,7 @@
448 self.assertThat(check_output, MockCallsMatch(475 self.assertThat(check_output, MockCallsMatch(
449 call((476 call((
450 "lsblk", "--exclude", "1,2,7", "-d", "-P",477 "lsblk", "--exclude", "1,2,7", "-d", "-P",
451 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")),478 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")),
452 call(("udevadm", "info", "-q", "all", "-n", name)),479 call(("udevadm", "info", "-q", "all", "-n", name)),
453 call(("sudo", "-n", "blockdev", "--getsize64", "/dev/%s" % name)),480 call(("sudo", "-n", "blockdev", "--getsize64", "/dev/%s" % name)),
454 call(("sudo", "-n", "blockdev", "--getbsz", "/dev/%s" % name))))481 call(("sudo", "-n", "blockdev", "--getbsz", "/dev/%s" % name))))
@@ -471,48 +498,52 @@
471 self.assertThat(check_output, MockCallsMatch(498 self.assertThat(check_output, MockCallsMatch(
472 call((499 call((
473 "lsblk", "--exclude", "1,2,7", "-d", "-P",500 "lsblk", "--exclude", "1,2,7", "-d", "-P",
474 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN", "-x", "MAJ:MIN")),501 "-o", "NAME,RO,RM,MODEL,ROTA,MAJ:MIN")),
475 call(("udevadm", "info", "-q", "all", "-n", name)),502 call(("udevadm", "info", "-q", "all", "-n", name)),
476 call(("blockdev", "--getsize64", "/dev/%s" % name)),503 call(("blockdev", "--getsize64", "/dev/%s" % name)),
477 call(("blockdev", "--getbsz", "/dev/%s" % name))))504 call(("blockdev", "--getbsz", "/dev/%s" % name))))
478505
479 def test__returns_block_device(self):506 def test__returns_sorted_block_devices(self):
480 name = factory.make_name('name')507 output = []
481 model = factory.make_name('model')508 check_output_side_effects = []
482 serial = factory.make_name('serial')
483 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
484 block_size = random.choice([512, 1024, 4096])
485 check_output = self.patch(subprocess, "check_output")
486
487 # Create simulated /dev tree509 # Create simulated /dev tree
488 devroot = self.make_dir()510 devroot = self.make_dir()
489 os.mkdir(os.path.join(devroot, 'disk'))511 os.mkdir(os.path.join(devroot, 'disk'))
490 byidroot = os.path.join(devroot, 'disk', 'by_id')512 byidroot = os.path.join(devroot, 'disk', 'by_id')
491 os.mkdir(byidroot)513 os.mkdir(byidroot)
492 os.mknod(os.path.join(devroot, name))514 for _ in range(3):
493 os.symlink(os.path.join(devroot, name),515 name = factory.make_name('name')
494 os.path.join(byidroot, 'deviceid'))516 model = factory.make_name('model')
495517 serial = factory.make_name('serial')
496 check_output.side_effect = [518 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
497 self.make_lsblk_output(name=name, model=model),519 block_size = random.choice([512, 1024, 4096])
498 self.make_udevadm_output(name, serial=serial, dev=devroot),520 maj_min = (random.randint(0, 255), random.randint(0, 255))
499 b'%d' % size,521
500 b'%d' % block_size,522 # Create simulated /dev tree
523 drive_path = os.path.join(devroot, name)
524 os.mknod(drive_path)
525 device_id_path = os.path.join(
526 byidroot, factory.make_name('deviceid'))
527 os.symlink(drive_path, device_id_path)
528
529 check_output_side_effects += [
530 self.make_lsblk_output(
531 name=name, model=model, maj_min=maj_min),
532 self.make_udevadm_output(name, serial=serial, dev=devroot),
533 b'%d' % size,
534 b'%d' % block_size,
501 ]535 ]
502 self.assertEqual([{536 output.append(
503 "NAME": name,537 self.make_output(
504 "PATH": os.path.join(devroot, name),538 name, maj_min, model, serial, size, block_size,
505 "ID_PATH": os.path.join(byidroot, 'deviceid'),539 drive_path, device_id_path))
506 "RO": "0",540
507 "RM": "0",541 check_output = self.patch(subprocess, "check_output")
508 "MODEL": model,542 check_output.side_effect = check_output_side_effects
509 "ROTA": "1",543
510 "SATA": "1",544 for ref, out in zip(
511 "SERIAL": serial,545 output, self.call_gather_physical_block_devices(byidroot)):
512 "SIZE": "%s" % size,546 self.assertDictEqual(ref, out)
513 "BLOCK_SIZE": "%s" % block_size,
514 "RPM": "5400",
515 }], self.call_gather_physical_block_devices(byidroot))
516547
517 def test__removes_duplicate_block_device_same_serial_and_model(self):548 def test__removes_duplicate_block_device_same_serial_and_model(self):
518 """Multipath disks get multiple IDs, but same serial/model is same549 """Multipath disks get multiple IDs, but same serial/model is same
@@ -522,107 +553,51 @@
522 serial = factory.make_name('serial')553 serial = factory.make_name('serial')
523 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)554 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
524 block_size = random.choice([512, 1024, 4096])555 block_size = random.choice([512, 1024, 4096])
525 check_output = self.patch(subprocess, "check_output")556 maj_min = (random.randint(0, 255), random.randint(0, 255))
526557 check_output = self.patch(subprocess, "check_output")
527 name2 = factory.make_name('name')558
528559 name2 = factory.make_name('name')
529 # Create simulated /dev tree.560
530 devroot = self.make_dir()561 # Create simulated /dev tree.
531 os.mkdir(os.path.join(devroot, 'disk'))562 devroot = self.make_dir()
532 byidroot = os.path.join(devroot, 'disk', 'by_id')563 os.mkdir(os.path.join(devroot, 'disk'))
533 os.mkdir(byidroot)564 byidroot = os.path.join(devroot, 'disk', 'by_id')
534565 os.mkdir(byidroot)
535 os.mknod(os.path.join(devroot, name))566
536 os.symlink(os.path.join(devroot, name),567 drive_path = os.path.join(devroot, name)
537 os.path.join(byidroot, 'deviceid'))568 os.mknod(drive_path)
538569 device_id_path = os.path.join(byidroot, 'deviceid')
539 os.mknod(os.path.join(devroot, name2))570 os.symlink(os.path.join(devroot, name), device_id_path)
540 os.symlink(os.path.join(devroot, name2),571
541 os.path.join(byidroot, 'deviceid2'))572 os.mknod(os.path.join(devroot, name2))
542573 device_id_path2 = os.path.join(byidroot, 'deviceid2')
543 check_output.side_effect = [574 os.symlink(os.path.join(devroot, name2), device_id_path2)
544 b"\n".join([575
545 self.make_lsblk_output(name=name, model=model),576 check_output.side_effect = [
546 self.make_lsblk_output(name=name2, model=model)]),577 b"\n".join([
547 self.make_udevadm_output(name, serial=serial, dev=devroot),578 self.make_lsblk_output(
548 self.make_udevadm_output(name2, serial=serial, dev=devroot),579 name=name, model=model, maj_min=maj_min),
549 b'%d' % size,580 self.make_lsblk_output(
550 b'%d' % block_size,581 name=name2, model=model, maj_min=maj_min)]),
551 b'%d' % size,582 self.make_udevadm_output(name, serial=serial, dev=devroot),
552 b'%d' % block_size,583 self.make_udevadm_output(name2, serial=serial, dev=devroot),
553 ]584 b'%d' % size,
554585 b'%d' % block_size,
555 self.assertEqual([{586 b'%d' % size,
556 "NAME": name,587 b'%d' % block_size,
557 "PATH": os.path.join(devroot, name),588 ]
558 "ID_PATH": os.path.join(byidroot, 'deviceid'),589
559 "RO": "0",590 self.assertItemsEqual(
560 "RM": "0",591 [self.make_output(
561 "MODEL": model,592 name, maj_min, model, serial, size, block_size, drive_path,
562 "ROTA": "1",593 device_id_path)],
563 "SATA": "1",594 self.call_gather_physical_block_devices(byidroot))
564 "SERIAL": serial,
565 "SIZE": "%s" % size,
566 "BLOCK_SIZE": "%s" % block_size,
567 "RPM": "5400",
568 }], self.call_gather_physical_block_devices(byidroot))
569
570 def test__removes_duplicate_block_device_same_serial_blank_model(self):
571 """Multipath disks get multiple IDs, but same serial is same device."""
572 name = factory.make_name('name')
573 model = ""
574 serial = factory.make_name('serial')
575 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
576 block_size = random.choice([512, 1024, 4096])
577 check_output = self.patch(subprocess, "check_output")
578
579 name2 = factory.make_name('name')
580
581 # Create simulated /dev tree.
582 devroot = self.make_dir()
583 os.mkdir(os.path.join(devroot, 'disk'))
584 byidroot = os.path.join(devroot, 'disk', 'by_id')
585 os.mkdir(byidroot)
586
587 os.mknod(os.path.join(devroot, name))
588 os.symlink(os.path.join(devroot, name),
589 os.path.join(byidroot, 'deviceid'))
590
591 os.mknod(os.path.join(devroot, name2))
592 os.symlink(os.path.join(devroot, name2),
593 os.path.join(byidroot, 'deviceid2'))
594
595 check_output.side_effect = [
596 b"\n".join([
597 self.make_lsblk_output(name=name, model=model),
598 self.make_lsblk_output(name=name2, model=model)]),
599 self.make_udevadm_output(name, serial=serial, dev=devroot),
600 self.make_udevadm_output(name2, serial=serial, dev=devroot),
601 b'%d' % size,
602 b'%d' % block_size,
603 b'%d' % size,
604 b'%d' % block_size,
605 ]
606
607 self.assertEqual([{
608 "NAME": name,
609 "PATH": os.path.join(devroot, name),
610 "ID_PATH": os.path.join(byidroot, 'deviceid'),
611 "RO": "0",
612 "RM": "0",
613 "MODEL": model,
614 "ROTA": "1",
615 "SATA": "1",
616 "SERIAL": serial,
617 "SIZE": "%s" % size,
618 "BLOCK_SIZE": "%s" % block_size,
619 "RPM": "5400",
620 }], self.call_gather_physical_block_devices(byidroot))
621595
622 def test__keeps_block_device_same_serial_different_model(self):596 def test__keeps_block_device_same_serial_different_model(self):
623 """Multipath disks get multiple IDs, but same serial is same device."""597 """Multipath disks get multiple IDs, but same serial is same device."""
624 name = factory.make_name('name')598 name = factory.make_name('name')
625 model = factory.make_name('model')599 model = factory.make_name('model')
600 maj_min = (0, 0)
626 serial = factory.make_name('serial')601 serial = factory.make_name('serial')
627 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)602 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
628 block_size = random.choice([512, 1024, 4096])603 block_size = random.choice([512, 1024, 4096])
@@ -630,6 +605,7 @@
630605
631 name2 = factory.make_name('name')606 name2 = factory.make_name('name')
632 model2 = factory.make_name('model')607 model2 = factory.make_name('model')
608 maj_min2 = (1, 1)
633609
634 # Create simulated /dev tree.610 # Create simulated /dev tree.
635 devroot = self.make_dir()611 devroot = self.make_dir()
@@ -637,18 +613,23 @@
637 byidroot = os.path.join(devroot, 'disk', 'by_id')613 byidroot = os.path.join(devroot, 'disk', 'by_id')
638 os.mkdir(byidroot)614 os.mkdir(byidroot)
639615
640 os.mknod(os.path.join(devroot, name))616 drive_path = os.path.join(devroot, name)
641 os.symlink(os.path.join(devroot, name),617 os.mknod(drive_path)
642 os.path.join(byidroot, 'deviceid'))618 device_id_path = os.path.join(byidroot, 'deviceid')
619 os.symlink(os.path.join(devroot, name), device_id_path)
643620
644 os.mknod(os.path.join(devroot, name2))621 drive_path2 = os.path.join(devroot, name2)
645 os.symlink(os.path.join(devroot, name2),622 os.mknod(drive_path2)
646 os.path.join(byidroot, 'deviceid2'))623 device_id_path2 = os.path.join(byidroot, 'deviceid2')
624 os.symlink(os.path.join(devroot, name2), device_id_path2)
647625
648 check_output.side_effect = [626 check_output.side_effect = [
649 b"\n".join([627 b"\n".join([
650 self.make_lsblk_output(name=name, model=model),628 self.make_lsblk_output(
651 self.make_lsblk_output(name=name2, model=model2)]),629 name=name, model=model, maj_min=maj_min),
630 self.make_lsblk_output(
631 name=name2, model=model2, maj_min=maj_min2)
632 ]),
652 self.make_udevadm_output(name, serial=serial, dev=devroot),633 self.make_udevadm_output(name, serial=serial, dev=devroot),
653 self.make_udevadm_output(name2, serial=serial, dev=devroot),634 self.make_udevadm_output(name2, serial=serial, dev=devroot),
654 b'%d' % size,635 b'%d' % size,
@@ -657,44 +638,29 @@
657 b'%d' % block_size,638 b'%d' % block_size,
658 ]639 ]
659640
660 self.assertEqual([{641 for ref, out in zip(
661 "NAME": name,642 [
662 "PATH": os.path.join(devroot, name),643 self.make_output(
663 "ID_PATH": os.path.join(byidroot, 'deviceid'),644 name, maj_min, model, serial, size, block_size,
664 "RO": "0",645 drive_path, device_id_path),
665 "RM": "0",646 self.make_output(
666 "MODEL": model,647 name2, maj_min2, model2, serial, size, block_size,
667 "ROTA": "1",648 drive_path2, device_id_path2),
668 "SATA": "1",649 ], self.call_gather_physical_block_devices(byidroot)):
669 "SERIAL": serial,650 self.assertDictEqual(ref, out)
670 "SIZE": "%s" % size,
671 "BLOCK_SIZE": "%s" % block_size,
672 "RPM": "5400",
673 }, {
674 "NAME": name2,
675 "PATH": os.path.join(devroot, name2),
676 "ID_PATH": os.path.join(byidroot, 'deviceid2'),
677 "RO": "0",
678 "RM": "0",
679 "MODEL": model2,
680 "ROTA": "1",
681 "SATA": "1",
682 "SERIAL": serial,
683 "SIZE": "%s" % size,
684 "BLOCK_SIZE": "%s" % block_size,
685 "RPM": "5400",
686 }], self.call_gather_physical_block_devices(byidroot))
687651
688 def test__keeps_block_device_blank_serial_same_model(self):652 def test__keeps_block_device_blank_serial_same_model(self):
689 """Multipath disks get multiple IDs, but same serial is same device."""653 """Multipath disks get multiple IDs, but same serial is same device."""
690 name = factory.make_name('name')654 name = factory.make_name('name')
691 model = factory.make_name('model')655 model = factory.make_name('model')
656 maj_min = (0, 0)
692 serial = ''657 serial = ''
693 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)658 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
694 block_size = random.choice([512, 1024, 4096])659 block_size = random.choice([512, 1024, 4096])
695 check_output = self.patch(subprocess, "check_output")660 check_output = self.patch(subprocess, "check_output")
696661
697 name2 = factory.make_name('name')662 name2 = factory.make_name('name')
663 maj_min2 = (1, 1)
698664
699 # Create simulated /dev tree.665 # Create simulated /dev tree.
700 devroot = self.make_dir()666 devroot = self.make_dir()
@@ -702,18 +668,22 @@
702 byidroot = os.path.join(devroot, 'disk', 'by_id')668 byidroot = os.path.join(devroot, 'disk', 'by_id')
703 os.mkdir(byidroot)669 os.mkdir(byidroot)
704670
705 os.mknod(os.path.join(devroot, name))671 drive_path = os.path.join(devroot, name)
706 os.symlink(os.path.join(devroot, name),672 os.mknod(drive_path)
707 os.path.join(byidroot, 'deviceid'))673 device_id_path = os.path.join(byidroot, 'deviceid')
674 os.symlink(os.path.join(devroot, name), device_id_path)
708675
709 os.mknod(os.path.join(devroot, name2))676 drive_path2 = os.path.join(devroot, name2)
710 os.symlink(os.path.join(devroot, name2),677 os.mknod(drive_path2)
711 os.path.join(byidroot, 'deviceid2'))678 device_id_path2 = os.path.join(byidroot, 'deviceid2')
679 os.symlink(os.path.join(devroot, name2), device_id_path2)
712680
713 check_output.side_effect = [681 check_output.side_effect = [
714 b"\n".join([682 b"\n".join([
715 self.make_lsblk_output(name=name, model=model),683 self.make_lsblk_output(
716 self.make_lsblk_output(name=name2, model=model)]),684 name=name, model=model, maj_min=maj_min),
685 self.make_lsblk_output(
686 name=name2, model=model, maj_min=maj_min2)]),
717 self.make_udevadm_output(name, serial=serial, dev=devroot),687 self.make_udevadm_output(name, serial=serial, dev=devroot),
718 self.make_udevadm_output(name2, serial=serial, dev=devroot),688 self.make_udevadm_output(name2, serial=serial, dev=devroot),
719 b'%d' % size,689 b'%d' % size,
@@ -722,38 +692,22 @@
722 b'%d' % block_size,692 b'%d' % block_size,
723 ]693 ]
724694
725 self.assertEqual([{695 for ref, out in zip(
726 "NAME": name,696 [
727 "PATH": os.path.join(devroot, name),697 self.make_output(
728 "ID_PATH": os.path.join(byidroot, 'deviceid'),698 name, maj_min, model, serial, size, block_size,
729 "RO": "0",699 drive_path, device_id_path),
730 "RM": "0",700 self.make_output(
731 "MODEL": model,701 name2, maj_min2, model, serial, size, block_size,
732 "ROTA": "1",702 drive_path2, device_id_path2),
733 "SATA": "1",703 ], self.call_gather_physical_block_devices(byidroot)):
734 "SERIAL": serial,704 self.assertDictEqual(ref, out)
735 "SIZE": "%s" % size,
736 "BLOCK_SIZE": "%s" % block_size,
737 "RPM": "5400",
738 }, {
739 "NAME": name2,
740 "PATH": os.path.join(devroot, name2),
741 "ID_PATH": os.path.join(byidroot, 'deviceid2'),
742 "RO": "0",
743 "RM": "0",
744 "MODEL": model,
745 "ROTA": "1",
746 "SATA": "1",
747 "SERIAL": serial,
748 "SIZE": "%s" % size,
749 "BLOCK_SIZE": "%s" % block_size,
750 "RPM": "5400",
751 }], self.call_gather_physical_block_devices(byidroot))
752705
753 def test__returns_block_device_without_id_path(self):706 def test__returns_block_device_without_id_path(self):
754 """Block devices without by-id links should not have ID_PATH key"""707 """Block devices without by-id links should not have ID_PATH key"""
755 name = factory.make_name('name')708 name = factory.make_name('name')
756 model = factory.make_name('model')709 model = factory.make_name('model')
710 maj_min = (random.randint(0, 255), random.randint(0, 255))
757 serial = factory.make_name('serial')711 serial = factory.make_name('serial')
758 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)712 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
759 block_size = random.choice([512, 1024, 4096])713 block_size = random.choice([512, 1024, 4096])
@@ -764,158 +718,113 @@
764 os.mkdir(os.path.join(devroot, 'disk'))718 os.mkdir(os.path.join(devroot, 'disk'))
765 byidroot = os.path.join(devroot, 'disk', 'by_id')719 byidroot = os.path.join(devroot, 'disk', 'by_id')
766 os.mkdir(byidroot)720 os.mkdir(byidroot)
767 os.mknod(os.path.join(devroot, name))721 drive_path = os.path.join(devroot, name)
722 os.mknod(drive_path)
768723
769 check_output.side_effect = [724 check_output.side_effect = [
770 self.make_lsblk_output(name=name, model=model),725 self.make_lsblk_output(name=name, model=model, maj_min=maj_min),
771 self.make_udevadm_output(name, serial=serial, dev=devroot),726 self.make_udevadm_output(name, serial=serial, dev=devroot),
772 b'%d' % size,727 b'%d' % size,
773 b'%d' % block_size,728 b'%d' % block_size,
774 ]729 ]
775 self.assertEqual([{730 for ref, out in zip(
776 "NAME": name,731 [
777 "PATH": os.path.join(devroot, name),732 self.make_output(
778 "RO": "0",733 name, maj_min, model, serial, size, block_size,
779 "RM": "0",734 drive_path),
780 "MODEL": model,735 ], self.call_gather_physical_block_devices(byidroot)):
781 "ROTA": "1",736 self.assertDictEqual(ref, out)
782 "SATA": "1",
783 "SERIAL": serial,
784 "SIZE": "%s" % size,
785 "BLOCK_SIZE": "%s" % block_size,
786 "RPM": "5400",
787 }], self.call_gather_physical_block_devices(byidroot))
788737
789 def test__returns_block_device_readonly(self):738 def test__returns_block_device_readonly(self):
790 name = factory.make_name('name')739 name = factory.make_name('name')
791 model = factory.make_name('model')740 model = factory.make_name('model')
741 maj_min = (random.randint(0, 255), random.randint(0, 255))
792 serial = factory.make_name('serial')742 serial = factory.make_name('serial')
793 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)743 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
794 block_size = random.choice([512, 1024, 4096])744 block_size = random.choice([512, 1024, 4096])
795 check_output = self.patch(subprocess, "check_output")745 check_output = self.patch(subprocess, "check_output")
796 check_output.side_effect = [746 check_output.side_effect = [
797 self.make_lsblk_output(name=name, model=model, read_only=True),747 self.make_lsblk_output(
748 name=name, model=model, read_only=True, maj_min=maj_min),
798 self.make_udevadm_output(name, serial=serial),749 self.make_udevadm_output(name, serial=serial),
799 b'%d' % size,750 b'%d' % size,
800 b'%d' % block_size,751 b'%d' % block_size,
801 ]752 ]
802 self.assertEqual([{753 for ref, out in zip(
803 "NAME": name,754 [
804 "PATH": "/dev/%s" % name,755 self.make_output(
805 "RO": "1",756 name, maj_min, model, serial, size,
806 "RM": "0",757 block_size, read_only=True),
807 "MODEL": model,758 ], self.call_gather_physical_block_devices()):
808 "ROTA": "1",759 self.assertDictEqual(ref, out)
809 "SATA": "1",
810 "SERIAL": serial,
811 "SIZE": "%s" % size,
812 "BLOCK_SIZE": "%s" % block_size,
813 "RPM": "5400",
814 }], self.call_gather_physical_block_devices())
815760
816 def test__returns_block_device_ssd(self):761 def test__returns_block_device_ssd(self):
817 name = factory.make_name('name')762 name = factory.make_name('name')
818 model = factory.make_name('model')763 model = factory.make_name('model')
764 maj_min = (random.randint(0, 255), random.randint(0, 255))
819 serial = factory.make_name('serial')765 serial = factory.make_name('serial')
820 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)766 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
821 block_size = random.choice([512, 1024, 4096])767 block_size = random.choice([512, 1024, 4096])
822 check_output = self.patch(subprocess, "check_output")768 check_output = self.patch(subprocess, "check_output")
823 check_output.side_effect = [769 check_output.side_effect = [
824 self.make_lsblk_output(name=name, model=model, rotary=False),770 self.make_lsblk_output(
771 name=name, model=model, rotary=False, maj_min=maj_min),
825 self.make_udevadm_output(name, serial=serial),772 self.make_udevadm_output(name, serial=serial),
826 b'%d' % size,773 b'%d' % size,
827 b'%d' % block_size,774 b'%d' % block_size,
828 ]775 ]
829 self.assertEqual([{776 for ref, out in zip(
830 "NAME": name,777 [
831 "PATH": "/dev/%s" % name,778 self.make_output(
832 "RO": "0",779 name, maj_min, model, serial, size, block_size,
833 "RM": "0",780 rotary=False),
834 "MODEL": model,781 ], self.call_gather_physical_block_devices()):
835 "ROTA": "0",782 self.assertDictEqual(ref, out)
836 "SATA": "1",
837 "SERIAL": serial,
838 "SIZE": "%s" % size,
839 "BLOCK_SIZE": "%s" % block_size,
840 "RPM": "5400",
841 }], self.call_gather_physical_block_devices())
842783
843 def test__returns_block_device_not_sata(self):784 def test__returns_block_device_not_sata(self):
844 name = factory.make_name('name')785 name = factory.make_name('name')
845 model = factory.make_name('model')786 model = factory.make_name('model')
787 maj_min = (random.randint(0, 255), random.randint(0, 255))
846 serial = factory.make_name('serial')788 serial = factory.make_name('serial')
847 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)789 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
848 block_size = random.choice([512, 1024, 4096])790 block_size = random.choice([512, 1024, 4096])
849 check_output = self.patch(subprocess, "check_output")791 check_output = self.patch(subprocess, "check_output")
850 check_output.side_effect = [792 check_output.side_effect = [
851 self.make_lsblk_output(name=name, model=model),793 self.make_lsblk_output(name=name, model=model, maj_min=maj_min),
852 self.make_udevadm_output(name, serial=serial, sata=False),794 self.make_udevadm_output(name, serial=serial, sata=False),
853 b'%d' % size,795 b'%d' % size,
854 b'%d' % block_size,796 b'%d' % block_size,
855 ]797 ]
856 self.assertEqual([{798 for ref, out in zip(
857 "NAME": name,799 [
858 "PATH": "/dev/%s" % name,800 self.make_output(
859 "RO": "0",801 name, maj_min, model, serial, size, block_size,
860 "RM": "0",802 sata=False),
861 "MODEL": model,803 ], self.call_gather_physical_block_devices()):
862 "ROTA": "1",804 self.assertDictEqual(ref, out)
863 "SATA": "0",
864 "SERIAL": serial,
865 "SIZE": "%s" % size,
866 "BLOCK_SIZE": "%s" % block_size,
867 "RPM": "5400",
868 }], self.call_gather_physical_block_devices())
869805
870 def test__returns_block_device_removable(self):806 def test__returns_block_device_removable(self):
871 name = factory.make_name('name')807 name = factory.make_name('name')
872 model = factory.make_name('model')808 model = factory.make_name('model')
809 maj_min = (random.randint(0, 255), random.randint(0, 255))
873 serial = factory.make_name('serial')810 serial = factory.make_name('serial')
874 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)811 size = random.randint(3000 * 1000, 1000 * 1000 * 1000)
875 block_size = random.choice([512, 1024, 4096])812 block_size = random.choice([512, 1024, 4096])
876 check_output = self.patch(subprocess, "check_output")813 check_output = self.patch(subprocess, "check_output")
877 check_output.side_effect = [814 check_output.side_effect = [
878 self.make_lsblk_output(name=name, model=model, removable=True),815 self.make_lsblk_output(
816 name=name, model=model, removable=True, maj_min=maj_min),
879 self.make_udevadm_output(name, serial=serial),817 self.make_udevadm_output(name, serial=serial),
880 b'%d' % size,818 b'%d' % size,
881 b'%d' % block_size,819 b'%d' % block_size,
882 ]820 ]
883 self.assertEqual([{821 for ref, out in zip(
884 "NAME": name,822 [
885 "PATH": "/dev/%s" % name,823 self.make_output(
886 "RO": "0",824 name, maj_min, model, serial, size, block_size,
887 "RM": "1",825 removable=True),
888 "MODEL": model,826 ], self.call_gather_physical_block_devices()):
889 "ROTA": "1",827 self.assertDictEqual(ref, out)
890 "SATA": "1",
891 "SERIAL": serial,
892 "SIZE": "%s" % size,
893 "BLOCK_SIZE": "%s" % block_size,
894 "RPM": "5400",
895 }], self.call_gather_physical_block_devices())
896
897 def test__returns_multiple_block_devices_in_order(self):
898 names = [factory.make_name('name') for _ in range(3)]
899 lsblk = [
900 self.make_lsblk_output(name=name)
901 for name in names
902 ]
903 call_outputs = []
904 call_outputs.append(b"\n".join(lsblk))
905 for name in names:
906 call_outputs.append(self.make_udevadm_output(name))
907 for name in names:
908 call_outputs.append(
909 b"%d" % random.randint(1000 * 1000, 1000 * 1000 * 1000))
910 call_outputs.append(
911 b"%d" % random.choice([512, 1024, 4096]))
912 check_output = self.patch(subprocess, "check_output")
913 check_output.side_effect = call_outputs
914 device_names = [
915 block_info['NAME']
916 for block_info in self.call_gather_physical_block_devices()
917 ]
918 self.assertEqual(names, device_names)
919828
920 def test__quietly_exits_in_container(self):829 def test__quietly_exits_in_container(self):
921 script_dir = self.useFixture(TempDirectory()).path830 script_dir = self.useFixture(TempDirectory()).path