Merge ~mwhudson/curtin:v2-disk-identification into curtin:master
- Git
- lp:~mwhudson/curtin
- v2-disk-identification
- Merge into master
Status: | Merged |
---|---|
Approved by: | Michael Hudson-Doyle |
Approved revision: | 8aae411a5ffe9057bf3c5fc9d02f1237bf3cf14a |
Merge reported by: | Server Team CI bot |
Merged at revision: | not available |
Proposed branch: | ~mwhudson/curtin:v2-disk-identification |
Merge into: | curtin:master |
Diff against target: |
553 lines (+384/-44) 7 files modified
curtin/block/multipath.py (+4/-3) curtin/commands/block_meta.py (+130/-39) curtin/udev.py (+9/-0) examples/tests/multipath-reuse.yaml (+1/-1) examples/tests/multipath.yaml (+1/-1) requirements.txt (+1/-0) tests/unittests/test_commands_block_meta.py (+238/-0) |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Dan Bungert | Approve | ||
Review via email: mp+415700@code.launchpad.net |
Commit message
block_meta: all fields on a disk action must match with v2 config
This is for https:/
similar reports.
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
- 886eb19... by Michael Hudson-Doyle
-
add pyudev to requirements
- 4642442... by Michael Hudson-Doyle
-
oops
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:24b8cf341ce
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Dan Bungert (dbungert) wrote : | # |
The log comment is the only reason I'm marking this as needs fixing.
- a2ac4e3... by Michael Hudson-Doyle
-
respond to review
Michael Hudson-Doyle (mwhudson) : | # |
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:b0a25cd3e02
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 7f839a7... by Michael Hudson-Doyle
-
have v2 get_path_
to_storage_ volume promote multipath members to multipath device more generally
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:ce1259ecf20
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 8aae411... by Michael Hudson-Doyle
-
update an example
Dan Bungert (dbungert) wrote : | # |
Looks like this was fixed a while ago.
Michael Hudson-Doyle (mwhudson) wrote : | # |
The reason I haven't merged this is that it broke some vmtests. Now that jenkins works again (?) I can iterate on it again I guess.
Michael Hudson-Doyle (mwhudson) wrote : | # |
On reflection (and trying to help another user who has run into this problem) I think we should merge this and deal with any fallout as it happens. Generally speaking the new behaviour is better.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:8aae411a5ff
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Dan Bungert (dbungert) wrote : | # |
+1 to merge in spite of known vmtest issues.
Preview Diff
1 | diff --git a/curtin/block/multipath.py b/curtin/block/multipath.py |
2 | index 0f9170e..8b1267a 100644 |
3 | --- a/curtin/block/multipath.py |
4 | +++ b/curtin/block/multipath.py |
5 | @@ -186,13 +186,14 @@ def find_mpath_partitions(mpath_id): |
6 | if mp_id.startswith(mpath_id + '-')) |
7 | |
8 | |
9 | -def get_mpath_id_from_device(device): |
10 | +def get_mpath_id_from_device(device, info=None): |
11 | # /dev/dm-X |
12 | - if is_mpath_device(device) or is_mpath_partition(device): |
13 | + if info is None: |
14 | info = udev.udevadm_info(device) |
15 | + if is_mpath_device(device, info) or is_mpath_partition(device, info): |
16 | return info.get('DM_NAME') |
17 | # /dev/sdX |
18 | - if is_mpath_member(device): |
19 | + if is_mpath_member(device, info): |
20 | return find_mpath_id_by_path(device) |
21 | |
22 | return None |
23 | diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py |
24 | index cef0e49..74c491c 100644 |
25 | --- a/curtin/commands/block_meta.py |
26 | +++ b/curtin/commands/block_meta.py |
27 | @@ -13,8 +13,13 @@ from curtin.storage_config import (extract_storage_ordered_dict, |
28 | |
29 | |
30 | from . import populate_one_subcmd |
31 | -from curtin.udev import (compose_udev_equality, udevadm_settle, |
32 | - udevadm_trigger, udevadm_info) |
33 | +from curtin.udev import ( |
34 | + compose_udev_equality, |
35 | + udev_all_block_device_properties, |
36 | + udevadm_info, |
37 | + udevadm_settle, |
38 | + udevadm_trigger, |
39 | + ) |
40 | |
41 | import glob |
42 | import json |
43 | @@ -25,6 +30,7 @@ import sys |
44 | import tempfile |
45 | import time |
46 | |
47 | + |
48 | FstabData = namedtuple( |
49 | "FstabData", ('spec', 'path', 'fstype', 'options', 'freq', 'passno', |
50 | 'device')) |
51 | @@ -429,6 +435,122 @@ def get_poolname(info, storage_config): |
52 | return poolname |
53 | |
54 | |
55 | +def v1_get_path_to_disk(vol): |
56 | + volume_path = None |
57 | + for disk_key in ['wwn', 'serial', 'device_id', 'path']: |
58 | + vol_value = vol.get(disk_key) |
59 | + try: |
60 | + if not vol_value: |
61 | + continue |
62 | + if disk_key in ['wwn', 'serial']: |
63 | + volume_path = block.lookup_disk(vol_value) |
64 | + elif disk_key == 'path': |
65 | + if vol_value.startswith('iscsi:'): |
66 | + i = iscsi.ensure_disk_connected(vol_value) |
67 | + volume_path = os.path.realpath(i.devdisk_path) |
68 | + else: |
69 | + # resolve any symlinks to the dev_kname so |
70 | + # sys/class/block access is valid. ie, there are no |
71 | + # udev generated values in sysfs |
72 | + volume_path = os.path.realpath(vol_value) |
73 | + # convert /dev/sdX to /dev/mapper/mpathX value |
74 | + if multipath.is_mpath_member(volume_path): |
75 | + volume_path = '/dev/mapper/' + ( |
76 | + multipath.get_mpath_id_from_device(volume_path)) |
77 | + elif disk_key == 'device_id': |
78 | + dasd_device = dasd.DasdDevice(vol_value) |
79 | + volume_path = dasd_device.devname |
80 | + except ValueError: |
81 | + continue |
82 | + # verify path exists otherwise try the next key |
83 | + if os.path.exists(volume_path): |
84 | + break |
85 | + else: |
86 | + volume_path = None |
87 | + |
88 | + if volume_path is None: |
89 | + raise ValueError("Failed to find storage volume id='%s' config: %s" |
90 | + % (vol['id'], vol)) |
91 | + |
92 | + return volume_path |
93 | + |
94 | + |
95 | +def v2_get_path_to_disk(vol): |
96 | + all_disks = [] |
97 | + link2dev = {} |
98 | + |
99 | + for dev in udev_all_block_device_properties(): |
100 | + if 'DM_PART' in dev or 'PARTN' in dev: |
101 | + continue |
102 | + for link in dev.get('DEVLINKS', '').split(): |
103 | + link2dev[link] = dev |
104 | + link2dev[dev['DEVNAME']] = dev |
105 | + all_disks.append(dev) |
106 | + |
107 | + def disks_matching(key, value): |
108 | + return [dev for dev in all_disks if key in dev and dev[key] == value] |
109 | + |
110 | + def disk_by_path(path): |
111 | + return link2dev[path] |
112 | + |
113 | + def disk_by_keys(val, *keys): |
114 | + for key in keys: |
115 | + devs = disks_matching(key, val) |
116 | + if devs: |
117 | + return devs |
118 | + return [] |
119 | + |
120 | + cands = [] |
121 | + |
122 | + def add_cands(*devs): |
123 | + new_devs = [] |
124 | + for dev in devs: |
125 | + if multipath.is_mpath_member(dev['DEVNAME'], dev): |
126 | + mpath_id = multipath.get_mpath_id_from_device( |
127 | + dev['DEVNAME'], dev) |
128 | + dev = link2dev['/dev/mapper/' + mpath_id] |
129 | + new_devs.append(dev) |
130 | + cands.append(set([dev['DEVNAME'] for dev in new_devs])) |
131 | + |
132 | + if 'wwn' in vol: |
133 | + add_cands(*disk_by_keys(vol['wwn'], 'DM_WWN', 'ID_WWN')) |
134 | + if 'serial' in vol: |
135 | + add_cands( |
136 | + *disk_by_keys( |
137 | + vol['serial'], 'DM_SERIAL', 'ID_SERIAL', 'ID_SERIAL_SHORT')) |
138 | + if 'device_id' in vol: |
139 | + dasd_device = dasd.DasdDevice(vol['device_id']) |
140 | + cands.append(set([dasd_device.devname])) |
141 | + volume_path = dasd_device.devname |
142 | + # verify path exists otherwise try the next key |
143 | + if os.path.exists(volume_path): |
144 | + cands.append(set([volume_path])) |
145 | + if 'path' in vol: |
146 | + path = vol['path'] |
147 | + if path.startswith('iscsi:'): |
148 | + i = iscsi.ensure_disk_connected(path) |
149 | + path = i.devdisk_path |
150 | + dev = disk_by_path(path) |
151 | + if dev is not None: |
152 | + add_cands(dev) |
153 | + else: |
154 | + cands.append(set()) |
155 | + |
156 | + LOG.debug('found candidate disks %s', cands) |
157 | + |
158 | + cands = set.intersection(*cands) |
159 | + |
160 | + if len(cands) == 0: |
161 | + raise ValueError("Failed to find storage volume id='%s' config: %s" |
162 | + % (vol['id'], vol)) |
163 | + if len(cands) > 1: |
164 | + raise ValueError( |
165 | + "Storage volume id='%s' config: %s identified multiple devices" |
166 | + % (vol['id'], vol)) |
167 | + |
168 | + return cands.pop() |
169 | + |
170 | + |
171 | def get_path_to_storage_volume(volume, storage_config): |
172 | # Get path to block device for volume. Volume param should refer to id of |
173 | # volume in storage config |
174 | @@ -455,41 +577,10 @@ def get_path_to_storage_volume(volume, storage_config): |
175 | elif vol.get('type') == "disk": |
176 | # Get path to block device for disk. Device_id param should refer |
177 | # to id of device in storage config |
178 | - volume_path = None |
179 | - for disk_key in ['wwn', 'serial', 'device_id', 'path']: |
180 | - vol_value = vol.get(disk_key) |
181 | - try: |
182 | - if not vol_value: |
183 | - continue |
184 | - if disk_key in ['wwn', 'serial']: |
185 | - volume_path = block.lookup_disk(vol_value) |
186 | - elif disk_key == 'path': |
187 | - if vol_value.startswith('iscsi:'): |
188 | - i = iscsi.ensure_disk_connected(vol_value) |
189 | - volume_path = os.path.realpath(i.devdisk_path) |
190 | - else: |
191 | - # resolve any symlinks to the dev_kname so |
192 | - # sys/class/block access is valid. ie, there are no |
193 | - # udev generated values in sysfs |
194 | - volume_path = os.path.realpath(vol_value) |
195 | - # convert /dev/sdX to /dev/mapper/mpathX value |
196 | - if multipath.is_mpath_member(volume_path): |
197 | - volume_path = '/dev/mapper/' + ( |
198 | - multipath.get_mpath_id_from_device(volume_path)) |
199 | - elif disk_key == 'device_id': |
200 | - dasd_device = dasd.DasdDevice(vol_value) |
201 | - volume_path = dasd_device.devname |
202 | - except ValueError: |
203 | - continue |
204 | - # verify path exists otherwise try the next key |
205 | - if os.path.exists(volume_path): |
206 | - break |
207 | - else: |
208 | - volume_path = None |
209 | - |
210 | - if volume_path is None: |
211 | - raise ValueError("Failed to find storage volume id='%s' config: %s" |
212 | - % (vol['id'], vol)) |
213 | + if getattr(storage_config, 'version', 1) > 1: |
214 | + volume_path = v2_get_path_to_disk(vol) |
215 | + else: |
216 | + volume_path = v1_get_path_to_disk(vol) |
217 | |
218 | elif vol.get('type') == "lvm_partition": |
219 | # For lvm partitions, a directory in /dev/ should be present with the |
220 | @@ -2067,8 +2158,8 @@ def meta_custom(args): |
221 | |
222 | storage_config_dict = extract_storage_ordered_dict(cfg) |
223 | |
224 | - version = cfg['storage']['version'] |
225 | - if version > 1: |
226 | + storage_config_dict.version = cfg['storage']['version'] |
227 | + if storage_config_dict.version > 1: |
228 | from curtin.commands.block_meta_v2 import ( |
229 | disk_handler_v2, |
230 | partition_handler_v2, |
231 | diff --git a/curtin/udev.py b/curtin/udev.py |
232 | index 2b7a46e..fa4f940 100644 |
233 | --- a/curtin/udev.py |
234 | +++ b/curtin/udev.py |
235 | @@ -129,4 +129,13 @@ def udevadm_info(path=None): |
236 | return info |
237 | |
238 | |
239 | +def udev_all_block_device_properties(): |
240 | + import pyudev |
241 | + props = [] |
242 | + c = pyudev.Context() |
243 | + for device in c.list_devices(subsystem='block'): |
244 | + props.append(dict(device.properties)) |
245 | + return props |
246 | + |
247 | + |
248 | # vi: ts=4 expandtab syntax=python |
249 | diff --git a/examples/tests/multipath-reuse.yaml b/examples/tests/multipath-reuse.yaml |
250 | index f008848..2f2b5b1 100644 |
251 | --- a/examples/tests/multipath-reuse.yaml |
252 | +++ b/examples/tests/multipath-reuse.yaml |
253 | @@ -23,7 +23,7 @@ storage: |
254 | - id: sda |
255 | type: disk |
256 | ptable: msdos |
257 | - serial: 'IPR-0 1234567890' |
258 | + serial: 'IPR-0_1234567890' |
259 | name: mpath_a |
260 | grub_device: true |
261 | multipath: mpatha |
262 | diff --git a/examples/tests/multipath.yaml b/examples/tests/multipath.yaml |
263 | index a3b536f..d0bd358 100644 |
264 | --- a/examples/tests/multipath.yaml |
265 | +++ b/examples/tests/multipath.yaml |
266 | @@ -7,7 +7,7 @@ storage: |
267 | - id: sda |
268 | type: disk |
269 | ptable: msdos |
270 | - serial: 'IPR-0 1234567890' |
271 | + serial: 'IPR-0_1234567890' |
272 | name: mpath_a |
273 | wipe: superblock |
274 | grub_device: true |
275 | diff --git a/requirements.txt b/requirements.txt |
276 | index 6afa3b8..d8d1a3d 100644 |
277 | --- a/requirements.txt |
278 | +++ b/requirements.txt |
279 | @@ -1,3 +1,4 @@ |
280 | +pyudev |
281 | pyyaml |
282 | oauthlib |
283 | # For validation of storate configuration format |
284 | diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py |
285 | index bd0ddf6..7067bc0 100644 |
286 | --- a/tests/unittests/test_commands_block_meta.py |
287 | +++ b/tests/unittests/test_commands_block_meta.py |
288 | @@ -35,6 +35,11 @@ class TestGetPathToStorageVolume(CiTestCase): |
289 | self.add_patch(basepath + 'devsync', 'm_devsync') |
290 | self.add_patch(basepath + 'util.subp', 'm_subp') |
291 | self.add_patch(basepath + 'multipath.is_mpath_member', 'm_mp') |
292 | + self.m_mp.return_value = False |
293 | + self.add_patch( |
294 | + basepath + 'multipath.get_mpath_id_from_device', 'm_get_mpath_id') |
295 | + self.add_patch( |
296 | + basepath + 'udev_all_block_device_properties', 'm_udev_all') |
297 | |
298 | def test_block_lookup_called_with_disk_wwn(self): |
299 | volume = 'mydisk' |
300 | @@ -69,6 +74,7 @@ class TestGetPathToStorageVolume(CiTestCase): |
301 | self.assertEqual(expected_calls, self.m_lookup.call_args_list) |
302 | |
303 | def test_fallback_to_path_when_lookup_wwn_serial_fail(self): |
304 | + self.m_mp.return_value = False |
305 | volume = 'mydisk' |
306 | wwn = self.random_string() |
307 | serial = self.random_string() |
308 | @@ -87,6 +93,7 @@ class TestGetPathToStorageVolume(CiTestCase): |
309 | self.assertEqual(path, result) |
310 | |
311 | def test_block_lookup_not_called_with_wwn_or_serial_keys(self): |
312 | + self.m_mp.return_value = False |
313 | volume = 'mydisk' |
314 | path = "/%s/%s" % (self.random_string(), self.random_string()) |
315 | cfg = {'id': volume, 'type': 'disk', 'path': path} |
316 | @@ -118,6 +125,237 @@ class TestGetPathToStorageVolume(CiTestCase): |
317 | self.assertEqual(expected_calls, self.m_lookup.call_args_list) |
318 | self.m_exists.assert_has_calls([call(path)]) |
319 | |
320 | + def test_v2_match_serial(self): |
321 | + serial = self.random_string() |
322 | + devname = self.random_string() |
323 | + self.m_udev_all.return_value = [ |
324 | + {'DEVNAME': devname, 'ID_SERIAL': serial}, |
325 | + ] |
326 | + disk_id = self.random_string() |
327 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial} |
328 | + s_cfg = OrderedDict({disk_id: cfg}) |
329 | + s_cfg.version = 2 |
330 | + self.assertEqual( |
331 | + devname, |
332 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
333 | + |
334 | + def test_v2_match_serial_short(self): |
335 | + serial = self.random_string() |
336 | + serial_short = self.random_string() |
337 | + devname = self.random_string() |
338 | + self.m_udev_all.return_value = [{ |
339 | + 'DEVNAME': devname, |
340 | + 'ID_SERIAL': serial, |
341 | + 'ID_SERIAL_SHORT': serial_short, |
342 | + }] |
343 | + disk_id = self.random_string() |
344 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial_short} |
345 | + s_cfg = OrderedDict({disk_id: cfg}) |
346 | + s_cfg.version = 2 |
347 | + self.assertEqual( |
348 | + devname, |
349 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
350 | + |
351 | + def test_v2_match_serial_skips_partition(self): |
352 | + serial = self.random_string() |
353 | + devname = self.random_string() |
354 | + self.m_udev_all.return_value = [ |
355 | + {'DEVNAME': devname, 'ID_SERIAL': serial}, |
356 | + {'DEVNAME': devname+'p1', 'ID_SERIAL': serial, 'PARTN': "1"}, |
357 | + ] |
358 | + disk_id = self.random_string() |
359 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial} |
360 | + s_cfg = OrderedDict({disk_id: cfg}) |
361 | + s_cfg.version = 2 |
362 | + self.assertEqual( |
363 | + devname, |
364 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
365 | + |
366 | + def test_v2_match_serial_prefer_mpath(self): |
367 | + serial = self.random_string() |
368 | + devname1 = self.random_string() |
369 | + devname2 = self.random_string() |
370 | + devname_dm = self.random_string() |
371 | + self.m_udev_all.return_value = [ |
372 | + {'DEVNAME': devname1, 'ID_SERIAL': serial}, |
373 | + {'DEVNAME': devname2, 'ID_SERIAL': serial}, |
374 | + {'DEVNAME': devname_dm, 'DM_SERIAL': serial}, |
375 | + ] |
376 | + disk_id = self.random_string() |
377 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial} |
378 | + s_cfg = OrderedDict({disk_id: cfg}) |
379 | + s_cfg.version = 2 |
380 | + self.assertEqual( |
381 | + devname_dm, |
382 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
383 | + |
384 | + def test_v2_match_serial_mpath_skip_partition(self): |
385 | + serial = self.random_string() |
386 | + devname1 = self.random_string() |
387 | + devname2 = self.random_string() |
388 | + devname_dm = self.random_string() |
389 | + self.m_udev_all.return_value = [ |
390 | + {'DEVNAME': devname1, 'ID_SERIAL': serial}, |
391 | + {'DEVNAME': devname2, 'ID_SERIAL': serial}, |
392 | + {'DEVNAME': devname_dm, 'DM_SERIAL': serial}, |
393 | + {'DEVNAME': devname_dm+'p1', 'DM_SERIAL': serial, 'DM_PART': '1'}, |
394 | + ] |
395 | + disk_id = self.random_string() |
396 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial} |
397 | + s_cfg = OrderedDict({disk_id: cfg}) |
398 | + s_cfg.version = 2 |
399 | + self.assertEqual( |
400 | + devname_dm, |
401 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
402 | + |
403 | + def test_v2_match_wwn(self): |
404 | + wwn = self.random_string() |
405 | + devname = self.random_string() |
406 | + self.m_udev_all.return_value = [ |
407 | + {'DEVNAME': devname, 'ID_WWN': wwn}, |
408 | + ] |
409 | + disk_id = self.random_string() |
410 | + cfg = {'id': disk_id, 'type': 'disk', 'wwn': wwn} |
411 | + s_cfg = OrderedDict({disk_id: cfg}) |
412 | + s_cfg.version = 2 |
413 | + self.assertEqual( |
414 | + devname, |
415 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
416 | + |
417 | + def test_v2_match_wwn_prefer_mpath(self): |
418 | + wwn = self.random_string() |
419 | + devname1 = self.random_string() |
420 | + devname2 = self.random_string() |
421 | + devname_dm = self.random_string() |
422 | + self.m_udev_all.return_value = [ |
423 | + {'DEVNAME': devname1, 'ID_WWN': wwn}, |
424 | + {'DEVNAME': devname2, 'ID_WWN': wwn}, |
425 | + {'DEVNAME': devname_dm, 'DM_WWN': wwn}, |
426 | + ] |
427 | + disk_id = self.random_string() |
428 | + cfg = {'id': disk_id, 'type': 'disk', 'wwn': wwn} |
429 | + s_cfg = OrderedDict({disk_id: cfg}) |
430 | + s_cfg.version = 2 |
431 | + self.assertEqual( |
432 | + devname_dm, |
433 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
434 | + |
435 | + def test_v2_match_serial_and_wwn(self): |
436 | + serial = self.random_string() |
437 | + wwn = self.random_string() |
438 | + devname = self.random_string() |
439 | + self.m_udev_all.return_value = [ |
440 | + {'DEVNAME': devname, 'ID_SERIAL': serial, 'ID_WWN': wwn}, |
441 | + ] |
442 | + disk_id = self.random_string() |
443 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial, 'wwn': wwn} |
444 | + s_cfg = OrderedDict({disk_id: cfg}) |
445 | + s_cfg.version = 2 |
446 | + self.assertEqual( |
447 | + devname, |
448 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
449 | + |
450 | + def test_v2_different_serial_same_wwn(self): |
451 | + serial1 = self.random_string() |
452 | + serial2 = self.random_string() |
453 | + wwn = self.random_string() |
454 | + devname1 = self.random_string() |
455 | + devname2 = self.random_string() |
456 | + self.m_udev_all.return_value = [ |
457 | + {'DEVNAME': devname1, 'ID_SERIAL': serial1, 'ID_WWN': wwn}, |
458 | + {'DEVNAME': devname2, 'ID_SERIAL': serial2, 'ID_WWN': wwn}, |
459 | + ] |
460 | + disk_id = self.random_string() |
461 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial2, 'wwn': wwn} |
462 | + s_cfg = OrderedDict({disk_id: cfg}) |
463 | + s_cfg.version = 2 |
464 | + self.assertEqual( |
465 | + devname2, |
466 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
467 | + |
468 | + def test_v2_fails_duplicate_wwn(self): |
469 | + serial1 = self.random_string() |
470 | + serial2 = self.random_string() |
471 | + wwn = self.random_string() |
472 | + devname1 = self.random_string() |
473 | + devname2 = self.random_string() |
474 | + self.m_udev_all.return_value = [ |
475 | + {'DEVNAME': devname1, 'ID_SERIAL': serial1, 'ID_WWN': wwn}, |
476 | + {'DEVNAME': devname2, 'ID_SERIAL': serial2, 'ID_WWN': wwn}, |
477 | + ] |
478 | + disk_id = self.random_string() |
479 | + cfg = {'id': disk_id, 'type': 'disk', 'wwn': wwn} |
480 | + s_cfg = OrderedDict({disk_id: cfg}) |
481 | + s_cfg.version = 2 |
482 | + with self.assertRaises(ValueError): |
483 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg) |
484 | + |
485 | + def test_v2_match_path(self): |
486 | + self.m_mp.return_value = False |
487 | + devname = self.random_string() |
488 | + self.m_udev_all.return_value = [ |
489 | + {'DEVNAME': devname}, |
490 | + ] |
491 | + disk_id = self.random_string() |
492 | + cfg = {'id': disk_id, 'type': 'disk', 'path': devname} |
493 | + s_cfg = OrderedDict({disk_id: cfg}) |
494 | + s_cfg.version = 2 |
495 | + self.assertEqual( |
496 | + devname, |
497 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
498 | + |
499 | + def test_v2_match_path_link(self): |
500 | + self.m_mp.return_value = False |
501 | + devname = self.random_string() |
502 | + link1 = self.random_string() |
503 | + link2 = self.random_string() |
504 | + links = link1 + ' ' + link2 |
505 | + self.m_udev_all.return_value = [ |
506 | + {'DEVNAME': devname, 'DEVLINKS': links}, |
507 | + ] |
508 | + disk_id = self.random_string() |
509 | + cfg = {'id': disk_id, 'type': 'disk', 'path': link1} |
510 | + s_cfg = OrderedDict({disk_id: cfg}) |
511 | + s_cfg.version = 2 |
512 | + self.assertEqual( |
513 | + devname, |
514 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
515 | + |
516 | + def test_v2_match_path_prefers_multipath(self): |
517 | + self.m_mp.return_value = True |
518 | + self.m_get_mpath_id.return_value = 'mpatha' |
519 | + devname1 = self.random_string() |
520 | + devname2 = self.random_string() |
521 | + self.m_udev_all.return_value = [ |
522 | + {'DEVNAME': devname1, 'DEVLINKS': ''}, |
523 | + {'DEVNAME': devname2, 'DEVLINKS': '/dev/mapper/mpatha'}, |
524 | + ] |
525 | + disk_id = self.random_string() |
526 | + cfg = {'id': disk_id, 'type': 'disk', 'path': devname1} |
527 | + s_cfg = OrderedDict({disk_id: cfg}) |
528 | + s_cfg.version = 2 |
529 | + self.assertEqual( |
530 | + devname2, |
531 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
532 | + |
533 | + def test_v2_match_serial_prefers_multipath(self): |
534 | + self.m_mp.return_value = True |
535 | + self.m_get_mpath_id.return_value = 'mpatha' |
536 | + devname1 = self.random_string() |
537 | + devname2 = self.random_string() |
538 | + serial = self.random_string() |
539 | + self.m_udev_all.return_value = [ |
540 | + {'DEVNAME': devname1, 'DEVLINKS': '', 'ID_SERIAL': serial}, |
541 | + {'DEVNAME': devname2, 'DEVLINKS': '/dev/mapper/mpatha'}, |
542 | + ] |
543 | + disk_id = self.random_string() |
544 | + cfg = {'id': disk_id, 'type': 'disk', 'serial': serial} |
545 | + s_cfg = OrderedDict({disk_id: cfg}) |
546 | + s_cfg.version = 2 |
547 | + self.assertEqual( |
548 | + devname2, |
549 | + block_meta.get_path_to_storage_volume(disk_id, s_cfg)) |
550 | + |
551 | |
552 | class TestBlockMetaSimple(CiTestCase): |
553 | def setUp(self): |
FAILED: Continuous integration, rev:900934b0fc5 00955cfb26bc407 3e0cd47030e2ef /jenkins. ubuntu. com/server/ job/curtin- ci/240/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-amd64/ 240/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-arm64/ 240/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-ppc64el/ 240/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-s390x/ 240/
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/curtin- ci/240/ /rebuild
https:/