Merge ~mwhudson/curtin:FR-2656-attach-device-to-action into curtin:master

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Michael Hudson-Doyle
Approved revision: 8bf1bdbd025808845799d20a1dcaae85032e2c60
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~mwhudson/curtin:FR-2656-attach-device-to-action
Merge into: curtin:master
Diff against target: 235 lines (+41/-5)
5 files modified
curtin/block/schemas.py (+5/-2)
curtin/commands/block_meta.py (+14/-0)
curtin/storage_config.py (+8/-3)
doc/topics/storage.rst (+6/-0)
tests/unittests/test_storage_config.py (+8/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Dan Bungert Approve
Review via email: mp+429401@code.launchpad.net

Commit message

storage_config: add 'path' key to all actions that refer to a block device

Disks and partitions already get this, add it to raid, logical volume,
dm_crypt and bcache actions.

Description of the change

This is a bit of an RFC and basically the easiest way to do this. It adds a bunch of attributes that will be accepted by the schema but ignored on the input, which is potentially confusing. We could add a key like 'devpath' or even '_devpath' instead, we could even programmatically mangle the schemas so that it's not valid on input. What do you think?

(This needs doc updates but that can wait until we decide on the above point)

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
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Dan Bungert (dbungert) wrote :

So far so good.

I think the most curtin-like answer is that if a path is provided, it should be correct. So we could have code verifying that. We would then mark such path values as optional and most people can be blissfully unaware of the capability. Or we could log a warning that the values are ignored.

A solution I considered in other areas is that by definition any config output by curtin is valid curtin config, so we could decide to not include it in the schema. However, this is not an answer I love because you wouldn't be able to just feed a config output to curtin back to curtin.

In summary, I recommend we include path in the schema as 'path', and either verify it or something lesser (like the warning that the value is ignored in some cases).

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Right I think it makes sense that you should be able to feed curtin output straight back into curtin. If we are going to move away from that the extra keys that are not valid for curtin should be something very clearly different like "$devpath$" or something but I think that is still pretty ugly. I think you can change the schema to enforce that any unknown attributes have to match a regex (like starting with _ or something) and then document that they are ignored.

I agree that it's most curtinish to check the path values are correct if passed. But that just feels like makework (even though it would probably take less time than adding this comment). I should probably just do it.

Revision history for this message
Dan Bungert (dbungert) :
review: Approve
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

Commit message lints:
- Line #0 has 3 too many characters. Line starts with: "storage_config: attach"...

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/block/schemas.py b/curtin/block/schemas.py
2index 92f88d0..3278331 100644
3--- a/curtin/block/schemas.py
4+++ b/curtin/block/schemas.py
5@@ -67,6 +67,7 @@ BCACHE = {
6 'type': ['string'],
7 'enum': ['writethrough', 'writeback', 'writearound', 'none'],
8 },
9+ 'path': {'type': 'string', 'pattern': _path_dev},
10 },
11 }
12 DASD = {
13@@ -163,6 +164,7 @@ DM_CRYPT = {
14 'volume': {'$ref': '#/definitions/ref_id'},
15 'key': {'$ref': '#/definitions/id'},
16 'keyfile': {'$ref': '#/definitions/id'},
17+ 'path': {'type': 'string', 'pattern': _path_dev},
18 'preserve': {'$ref': '#/definitions/preserve'},
19 'type': {'const': 'dm_crypt'},
20 },
21@@ -213,6 +215,7 @@ LVM_PARTITION = {
22 'size': {'$ref': '#/definitions/size'}, # XXX: This is not used
23 'type': {'const': 'lvm_partition'},
24 'volgroup': {'$ref': '#/definitions/ref_id'},
25+ 'path': {'type': 'string', 'pattern': _path_dev},
26 },
27 }
28 LVM_VOLGROUP = {
29@@ -286,8 +289,7 @@ PARTITION = {
30 'multipath': {'type': 'string'},
31 # Permit path to device as output.
32 # This value is ignored for input.
33- 'path': {'type': 'string',
34- 'pattern': _path_dev},
35+ 'path': {'type': 'string', 'pattern': _path_dev},
36 'name': {'$ref': '#/definitions/name'},
37 'offset': {'$ref': '#/definitions/size'}, # XXX: This is not used
38 'resize': {'type': 'boolean'},
39@@ -335,6 +337,7 @@ RAID = {
40 'name': {'$ref': '#/definitions/name'},
41 'mdname': {'$ref': '#/definitions/name'}, # XXX: Docs need updating
42 'metadata': {'type': ['string', 'number']},
43+ 'path': {'type': 'string', 'pattern': _path_dev},
44 'preserve': {'$ref': '#/definitions/preserve'},
45 'ptable': {'$ref': '#/definitions/ptable'},
46 'wipe': {'$ref': '#/definitions/wipe'},
47diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
48index 45bec55..d01333e 100644
49--- a/curtin/commands/block_meta.py
50+++ b/curtin/commands/block_meta.py
51@@ -851,6 +851,15 @@ def partition_verify_fdasd(disk_path, partnumber, info):
52 raise RuntimeError("dasd partitions do not support flags")
53
54
55+def check_passed_path(info, actual_path):
56+ if 'path' not in info:
57+ return
58+ passed_path = info['path']
59+ if os.path.realpath(passed_path) != os.path.realpath(actual_path):
60+ LOG.warning("%s action %s specified path %s but path was %s" % (
61+ info["type"], info, info['path'], actual_path))
62+
63+
64 def partition_handler(info, storage_config, context):
65 device = info.get('device')
66 size = info.get('size')
67@@ -866,6 +875,7 @@ def partition_handler(info, storage_config, context):
68 partnumber = determine_partition_number(info.get('id'), storage_config)
69 disk_kname = block.path_to_kname(disk)
70 part_path = block.dev_path(block.partition_kname(disk_kname, partnumber))
71+ check_passed_path(info, part_path)
72 context.id_to_device[info['id']] = part_path
73
74 # consider the disks logical sector size when calculating sectors
75@@ -1431,6 +1441,7 @@ def lvm_partition_handler(info, storage_config, context):
76 lvm.lvm_scan()
77
78 lv_path = get_path_to_storage_volume(info['id'], storage_config)
79+ check_passed_path(info, lv_path)
80 context.id_to_device[info['id']] = lv_path
81
82 wipe_mode = info.get('wipe', 'superblock')
83@@ -1466,6 +1477,7 @@ def dm_crypt_handler(info, storage_config, context):
84 if not dm_name:
85 dm_name = info.get('id')
86 dmcrypt_dev = os.path.join("/dev", "mapper", dm_name)
87+ check_passed_path(info, dmcrypt_dev)
88 context.id_to_device[info['id']] = dmcrypt_dev
89 preserve = config.value_as_boolean(info.get('preserve'))
90 if not volume:
91@@ -1606,6 +1618,7 @@ def raid_handler(info, storage_config, context):
92 raidlevel = info.get('raidlevel')
93 spare_devices = info.get('spare_devices')
94 md_devname = block.md_path(info.get('name'))
95+ check_passed_path(info, md_devname)
96 context.id_to_device[info['id']] = md_devname
97 container = info.get('container')
98 metadata = info.get('metadata')
99@@ -1784,6 +1797,7 @@ def bcache_handler(info, storage_config, context):
100 bcache_dev = bcache.create_backing_device(backing_device, cache_device,
101 cache_mode, cset_uuid)
102 # Not sure what to do in the preserve case here.
103+ check_passed_path(info, bcache_dev)
104 context.id_to_device[info['id']] = bcache_dev
105
106 if cache_mode and not backing_device:
107diff --git a/curtin/storage_config.py b/curtin/storage_config.py
108index e9e8991..9c522d8 100644
109--- a/curtin/storage_config.py
110+++ b/curtin/storage_config.py
111@@ -589,10 +589,11 @@ class BcacheParser(ProbertParser):
112 backing_device = backing_data.get('blockdev')
113 cache_device = _find_cache_device(backing_data, self.caching)
114 cache_mode = _cache_mode(backing_data)
115- bcache_name = os.path.basename(_find_bcache_devname(backing_uuid,
116- backing_data, self.blockdev_data))
117+ devname = _find_bcache_devname(backing_uuid,
118+ backing_data, self.blockdev_data)
119+ bcache_name = os.path.basename(devname)
120 bcache_entry = {'type': 'bcache', 'id': 'disk-%s' % bcache_name,
121- 'name': bcache_name}
122+ 'name': bcache_name, 'path': devname}
123
124 if cache_mode:
125 bcache_entry['cache_mode'] = cache_mode
126@@ -911,6 +912,8 @@ class LvmParser(ProbertParser):
127 return {'type': 'lvm_partition',
128 'id': 'lvm-partition-%s' % lv_config['name'],
129 'name': lv_config['name'],
130+ 'path': self.lookup_devname('/dev/{}/{}'.format(
131+ lv_config['volgroup'], lv_config['name'])),
132 'size': lv_config['size'],
133 'volgroup': 'lvm-volgroup-%s' % lv_config['volgroup']}
134
135@@ -1020,6 +1023,7 @@ class DmcryptParser(ProbertParser):
136
137 return {'type': 'dm_crypt',
138 'id': 'dmcrypt-%s' % crypt_name,
139+ 'path': bdev,
140 'volume': bdev_id,
141 'key': '',
142 'dm_name': crypt_name}
143@@ -1058,6 +1062,7 @@ class RaidParser(ProbertParser):
144 'type': 'raid',
145 'id': self.blockdev_to_id(raid_data),
146 'name': raidname,
147+ 'path': devname,
148 'raidlevel': raid_data.get('raidlevel'),
149 }
150
151diff --git a/doc/topics/storage.rst b/doc/topics/storage.rst
152index aab00e6..1225ee0 100644
153--- a/doc/topics/storage.rst
154+++ b/doc/topics/storage.rst
155@@ -72,6 +72,12 @@ commands include:
156 - Zpool Command (``zpool``) **Experimental**
157 - ZFS Command (``zfs``)) **Experimental**
158
159+Any action that refers to a block device (so things like ``partition``
160+and ``dm_crypt`` but not ``lvm_volgroup`` or ``mount``, for example)
161+*can* specify a ``path`` key but aside from ``disk`` actions this is
162+not used to locate the device. A warning will be emitted if the
163+located device does not match the provided path though.
164+
165 Dasd Command
166 ~~~~~~~~~~~~
167
168diff --git a/tests/unittests/test_storage_config.py b/tests/unittests/test_storage_config.py
169index df48a4d..a109dd7 100644
170--- a/tests/unittests/test_storage_config.py
171+++ b/tests/unittests/test_storage_config.py
172@@ -202,6 +202,7 @@ class TestBcacheParser(CiTestCase):
173 'type': 'bcache',
174 'id': 'disk-bcache0',
175 'name': 'bcache0',
176+ 'path': '/dev/bcache0',
177 'backing_device': 'partition-sda3',
178 'cache_device': 'partition-nvme0n1p1',
179 'cache_mode': 'writeback',
180@@ -221,6 +222,7 @@ class TestBcacheParser(CiTestCase):
181 'type': 'bcache',
182 'id': 'disk-bcache0',
183 'name': 'bcache0',
184+ 'path': '/dev/bcache0',
185 'backing_device': 'partition-sda3',
186 'cache_mode': 'writeback',
187 }
188@@ -674,6 +676,7 @@ class TestLvmParser(CiTestCase):
189 expected_dict = {
190 'type': 'lvm_partition',
191 'name': 'my-storage',
192+ 'path': '/dev/dm-2',
193 'id': 'lvm-partition-my-storage',
194 'size': '1073741824B',
195 'volgroup': 'lvm-volgroup-ubuntu-vg',
196@@ -720,6 +723,7 @@ class TestRaidParser(CiTestCase):
197 'type': 'raid',
198 'id': 'raid-md0',
199 'name': 'md0',
200+ 'path': '/dev/md0',
201 'metadata': '1.2',
202 'raidlevel': 'raid5',
203 'devices': ['disk-vde', 'disk-vdf', 'disk-vdg'],
204@@ -744,6 +748,7 @@ class TestRaidParser(CiTestCase):
205 'type': 'raid',
206 'id': 'raid-md127',
207 'name': 'md127',
208+ 'path': '/dev/md127',
209 'metadata': 'imsm',
210 'raidlevel': 'container',
211 'devices': ['disk-nvme0n1', 'disk-nvme1n1'],
212@@ -758,6 +763,7 @@ class TestRaidParser(CiTestCase):
213 'type': 'raid',
214 'id': 'raid-md126',
215 'name': 'md126',
216+ 'path': '/dev/md126',
217 'raidlevel': 'raid0',
218 'container': 'raid-md127',
219 }
220@@ -851,6 +857,7 @@ class TestDmCryptParser(CiTestCase):
221 'type': 'dm_crypt',
222 'id': 'dmcrypt-dmcrypt0',
223 'dm_name': devname,
224+ 'path': '/dev/dm-2',
225 'key': '',
226 'volume': 'lvm-partition-lv3',
227 }
228@@ -1024,6 +1031,7 @@ class TestExtractStorageConfig(CiTestCase):
229 self.assertEqual(1, len(raid_partitions))
230 self.assertEqual({'id': 'raid-md1', 'type': 'raid', 'metadata': '1.2',
231 'raidlevel': 'raid1', 'name': 'md1',
232+ 'path': '/dev/md1',
233 'devices': ['partition-vdb1', 'partition-vdc1'],
234 'spare_devices': []}, raids[0])
235 self.assertEqual({

Subscribers

People subscribed via source and target branches