Merge ~raharper/curtin:fix/dasd-disk-vtoc-preserve into curtin:master

Proposed by Ryan Harper
Status: Merged
Approved by: Ryan Harper
Approved revision: c623f08bfa742f196d901082b57442f2faec5819
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~raharper/curtin:fix/dasd-disk-vtoc-preserve
Merge into: curtin:master
Diff against target: 206 lines (+109/-9)
5 files modified
curtin/block/__init__.py (+13/-1)
curtin/block/schemas.py (+3/-2)
curtin/commands/block_meta.py (+9/-6)
tests/unittests/test_block.py (+11/-0)
tests/unittests/test_commands_block_meta.py (+73/-0)
Reviewer Review Type Date Requested Status
Chad Smith Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+381785@code.launchpad.net

Commit message

block-meta: handle preserve with vtoc ptable

DASD devices have a 'vtoc' partition table type. When we're handling
a disk config with 'preserve': True we raised a ValueError expecting
either 'gpt' or 'msdos'. Fix this by refactoring the code to check
the expected ptable value and adding a check_vtoc_signature method.

LP: #1871158

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) :
review: Needs Information
Revision history for this message
Ryan Harper (raharper) wrote :

Thanks, I'll update.

Revision history for this message
Chad Smith (chad.smith) :
Revision history for this message
Ryan Harper (raharper) :
8da06ac... by Ryan Harper

Consolidate ptable values into variables; drop unneeded ValueError

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

ValueError: ('Invalid partition table type: %s in %s', None, {'id': 'sparedisk_id', 'type': 'disk', 'serial': 'disk-b', 'name': 'sparedisk', 'wipe': 'superblock'})
[ 63.818482] cloud-init[1063]: ('Invalid partition table type: %s in %s', None, {'id': 'sparedisk_id', 'type': 'disk', 'serial': 'disk-b', 'name': 'sparedisk', 'wipe': 'superblock'})

c11a97e... by Ryan Harper

Handle no ptable value in disk config

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
c623f08... by Ryan Harper

Fix assertEqual on call_count

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Joshua Powers (powersj) wrote :

From IRC:

<blackboxsw> rharper: in merge #381785 per your change +PTABLES_SUPPORTED = schemas._ptables the 'rub' was that the variable name is schemas._ptables instead of schemas.PTABLES_SUPPORTED and schemas._ptables_valid instead of schemas.PTABLES_VALID (no need to declare the 2 variables inside curtin/commands/block_meta.py)

Revision history for this message
Chad Smith (chad.smith) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/block/__init__.py b/curtin/block/__init__.py
2index 6e5354e..aa5c6f7 100644
3--- a/curtin/block/__init__.py
4+++ b/curtin/block/__init__.py
5@@ -922,7 +922,8 @@ def get_part_table_type(device):
6 # signature, because a gpt formatted disk usually has a valid mbr to
7 # protect the disk from being modified by older partitioning tools
8 return ('gpt' if check_efi_signature(device) else
9- 'dos' if check_dos_signature(device) else None)
10+ 'dos' if check_dos_signature(device) else
11+ 'vtoc' if check_vtoc_signature(device) else None)
12
13
14 def check_dos_signature(device):
15@@ -959,6 +960,17 @@ def check_efi_signature(device):
16 offset=sector_size) == b'EFI PART'))
17
18
19+def check_vtoc_signature(device):
20+ """ check if the specified device has a vtoc partition table. """
21+ devname = dev_path(path_to_kname(device))
22+ try:
23+ util.subp(['fdasd', '--table', devname])
24+ except util.ProcessExecutionError:
25+ return False
26+
27+ return True
28+
29+
30 def is_extended_partition(device):
31 """
32 check if the specified device path is a dos extended partition
33diff --git a/curtin/block/schemas.py b/curtin/block/schemas.py
34index edca9e6..4de1daa 100644
35--- a/curtin/block/schemas.py
36+++ b/curtin/block/schemas.py
37@@ -7,8 +7,9 @@ _path_nondev = r'(^/$|^(/[^/]+)+$)'
38 _fstypes = ['btrfs', 'ext2', 'ext3', 'ext4', 'fat', 'fat12', 'fat16', 'fat32',
39 'iso9660', 'vfat', 'jfs', 'ntfs', 'reiserfs', 'swap', 'xfs',
40 'zfsroot']
41-_ptables = ['dos', 'gpt', 'msdos', 'vtoc']
42 _ptable_unsupported = 'unsupported'
43+_ptables = ['dos', 'gpt', 'msdos', 'vtoc']
44+_ptables_valid = _ptables + [_ptable_unsupported]
45
46 definitions = {
47 'id': {'type': 'string'},
48@@ -16,7 +17,7 @@ definitions = {
49 'devices': {'type': 'array', 'items': {'$ref': '#/definitions/ref_id'}},
50 'name': {'type': 'string'},
51 'preserve': {'type': 'boolean'},
52- 'ptable': {'type': 'string', 'enum': _ptables + [_ptable_unsupported]},
53+ 'ptable': {'type': 'string', 'enum': _ptables_valid},
54 'size': {'type': ['string', 'number'],
55 'minimum': 1,
56 'pattern': r'^([1-9]\d*(.\d+)?|\d+.\d+)(K|M|G|T)?B?'},
57diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
58index 6c6bfc7..6c145e3 100644
59--- a/curtin/commands/block_meta.py
60+++ b/curtin/commands/block_meta.py
61@@ -34,6 +34,8 @@ SIMPLE = 'simple'
62 SIMPLE_BOOT = 'simple-boot'
63 CUSTOM = 'custom'
64 PTABLE_UNSUPPORTED = schemas._ptable_unsupported
65+PTABLES_SUPPORTED = schemas._ptables
66+PTABLES_VALID = schemas._ptables_valid
67
68 SGDISK_FLAGS = {
69 "boot": 'ef00',
70@@ -551,14 +553,17 @@ def dasd_handler(info, storage_config):
71 def disk_handler(info, storage_config):
72 _dos_names = ['dos', 'msdos']
73 ptable = info.get('ptable')
74- disk = get_path_to_storage_volume(info.get('id'), storage_config)
75+ if ptable and ptable not in PTABLES_VALID:
76+ raise ValueError(
77+ 'Invalid partition table type: %s in %s' % (ptable, info))
78
79+ disk = get_path_to_storage_volume(info.get('id'), storage_config)
80 if config.value_as_boolean(info.get('preserve')):
81 # Handle preserve flag, verifying if ptable specified in config
82- if config.value_as_boolean(ptable) and ptable != PTABLE_UNSUPPORTED:
83+ if ptable != PTABLE_UNSUPPORTED:
84 current_ptable = block.get_part_table_type(disk)
85- if not ((ptable in _dos_names and current_ptable in _dos_names) or
86- (ptable == 'gpt' and current_ptable == 'gpt')):
87+ LOG.debug('disk: current ptable type: %s', current_ptable)
88+ if current_ptable not in PTABLES_SUPPORTED:
89 raise ValueError(
90 "disk '%s' does not have correct partition table or "
91 "cannot be read, but preserve is set to true. "
92@@ -583,8 +588,6 @@ def disk_handler(info, storage_config):
93 elif ptable == "vtoc":
94 # ignore dasd partition tables
95 pass
96- else:
97- raise ValueError('invalid partition table type: %s', ptable)
98 holders = clear_holders.get_holders(disk)
99 if len(holders) > 0:
100 LOG.info('Detected block holders on disk %s: %s', disk, holders)
101diff --git a/tests/unittests/test_block.py b/tests/unittests/test_block.py
102index e70503d..8ba8333 100644
103--- a/tests/unittests/test_block.py
104+++ b/tests/unittests/test_block.py
105@@ -567,6 +567,17 @@ class TestPartTableSignature(CiTestCase):
106 (self.assertTrue if expected else self.assertFalse)(
107 block.check_efi_signature(self.blockdev))
108
109+ @mock.patch('curtin.block.util.subp')
110+ def test_check_vtoc_signature_finds_vtoc_returns_true(self, mock_subp):
111+ mock_subp.return_value = ("vtoc.....ok", "")
112+ self.assertTrue(block.check_vtoc_signature(self.blockdev))
113+
114+ @mock.patch('curtin.block.util.subp')
115+ def test_check_vtoc_signature_returns_false_with_no_sig(self, mock_subp):
116+ mock_subp.side_effect = [
117+ util.ProcessExecutionError(stdout="", stderr="", exit_code=1)]
118+ self.assertFalse(block.check_vtoc_signature(self.blockdev))
119+
120
121 class TestNonAscii(CiTestCase):
122 @mock.patch('curtin.block.util.subp')
123diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py
124index 231b2d1..7b4f99c 100644
125--- a/tests/unittests/test_commands_block_meta.py
126+++ b/tests/unittests/test_commands_block_meta.py
127@@ -1212,6 +1212,79 @@ class TestDasdHandler(CiTestCase):
128 self.assertEqual(0, m_dasd_format.call_count)
129
130
131+class TestDiskHandler(CiTestCase):
132+
133+ with_logs = True
134+
135+ @patch('curtin.commands.block_meta.block')
136+ @patch('curtin.commands.block_meta.util')
137+ @patch('curtin.commands.block_meta.get_path_to_storage_volume')
138+ def test_disk_handler_preserves_known_ptable(self, m_getpath, m_util,
139+ m_block):
140+ storage_config = OrderedDict()
141+ info = {'ptable': 'vtoc', 'serial': 'LX260B',
142+ 'preserve': True, 'name': '', 'grub_device': False,
143+ 'device_id': '0.0.260b', 'type': 'disk', 'id': 'disk-dasda'}
144+
145+ disk_path = "/wark/dasda"
146+ m_getpath.return_value = disk_path
147+ m_block.get_part_table_type.return_value = 'vtoc'
148+ m_getpath.return_value = disk_path
149+ block_meta.disk_handler(info, storage_config)
150+ m_getpath.assert_called_with(info['id'], storage_config)
151+ m_block.get_part_table_type.assert_called_with(disk_path)
152+
153+ @patch('curtin.commands.block_meta.block')
154+ @patch('curtin.commands.block_meta.util')
155+ @patch('curtin.commands.block_meta.get_path_to_storage_volume')
156+ def test_disk_handler_allows_unsupported(self, m_getpath, m_util, m_block):
157+ storage_config = OrderedDict()
158+ info = {'ptable': 'unsupported', 'type': 'disk', 'id': 'disk-foobar',
159+ 'preserve': True, 'name': '', 'grub_device': False}
160+
161+ disk_path = "/wark/foobar"
162+ m_getpath.return_value = disk_path
163+ m_block.get_part_table_type.return_value = self.random_string()
164+ m_getpath.return_value = disk_path
165+ block_meta.disk_handler(info, storage_config)
166+ m_getpath.assert_called_with(info['id'], storage_config)
167+ self.assertEqual(0, m_block.get_part_table_type.call_count)
168+
169+ @patch('curtin.commands.block_meta.block')
170+ @patch('curtin.commands.block_meta.util')
171+ @patch('curtin.commands.block_meta.get_path_to_storage_volume')
172+ def test_disk_handler_allows_no_ptable(self, m_getpath, m_util, m_block):
173+ storage_config = OrderedDict()
174+ info = {'type': 'disk', 'id': 'disk-foobar',
175+ 'preserve': True, 'name': '', 'grub_device': False}
176+ self.assertNotIn('ptable', info)
177+ disk_path = "/wark/foobar"
178+ m_getpath.return_value = disk_path
179+ m_block.get_part_table_type.return_value = 'gpt'
180+ m_getpath.return_value = disk_path
181+ block_meta.disk_handler(info, storage_config)
182+ m_getpath.assert_called_with(info['id'], storage_config)
183+ self.assertEqual(1, m_block.get_part_table_type.call_count)
184+
185+ @patch('curtin.commands.block_meta.block')
186+ @patch('curtin.commands.block_meta.util')
187+ @patch('curtin.commands.block_meta.get_path_to_storage_volume')
188+ def test_disk_handler_errors_when_reading_current_ptable(self, m_getpath,
189+ m_util, m_block):
190+ storage_config = OrderedDict()
191+ info = {'ptable': 'gpt', 'type': 'disk', 'id': 'disk-foobar',
192+ 'preserve': True, 'name': '', 'grub_device': False}
193+
194+ disk_path = "/wark/foobar"
195+ m_getpath.return_value = disk_path
196+ m_block.get_part_table_type.return_value = None
197+ m_getpath.return_value = disk_path
198+ with self.assertRaises(ValueError):
199+ block_meta.disk_handler(info, storage_config)
200+ m_getpath.assert_called_with(info['id'], storage_config)
201+ m_block.get_part_table_type.assert_called_with(disk_path)
202+
203+
204 class TestLvmVolgroupHandler(CiTestCase):
205
206 def setUp(self):

Subscribers

People subscribed via source and target branches