Merge ~raharper/curtin:ubuntu/xenial-sru-20200130 into curtin:ubuntu/xenial

Proposed by Ryan Harper
Status: Merged
Merged at revision: 1012247082abb716a515d668a923f0a6faffb6d9
Proposed branch: ~raharper/curtin:ubuntu/xenial-sru-20200130
Merge into: curtin:ubuntu/xenial
Diff against target: 16912 lines (+12842/-697)
95 files modified
HACKING.rst (+3/-4)
Makefile (+5/-1)
curtin/__init__.py (+1/-1)
curtin/block/__init__.py (+50/-6)
curtin/block/clear_holders.py (+88/-21)
curtin/block/deps.py (+1/-1)
curtin/block/multipath.py (+74/-2)
curtin/block/schemas.py (+8/-2)
curtin/block/zfs.py (+24/-0)
curtin/commands/block_meta.py (+138/-55)
curtin/commands/curthooks.py (+229/-53)
curtin/commands/install.py (+5/-1)
curtin/deps/__init__.py (+1/-1)
curtin/distro.py (+25/-2)
curtin/net/deps.py (+3/-3)
curtin/storage_config.py (+108/-22)
curtin/udev.py (+15/-7)
curtin/util.py (+23/-8)
debian/changelog (+81/-0)
debian/control (+1/-1)
doc/topics/config.rst (+6/-1)
doc/topics/integration-testing.rst (+13/-6)
doc/topics/storage.rst (+17/-2)
examples/tests/basic-dasd.yaml (+2/-2)
examples/tests/bcache-ceph-nvme-simple.yaml (+107/-0)
examples/tests/bcache-wipe-xfs.yaml (+4/-3)
examples/tests/bridging_network_v2.yaml (+38/-0)
examples/tests/dirty_disks_config.yaml (+9/-1)
examples/tests/network_mtu_networkd.yaml (+93/-0)
examples/tests/reuse-raid-member-partition.yaml (+73/-0)
examples/tests/reuse-raid-member-wipe.yaml (+71/-0)
helpers/common (+84/-13)
tests/data/probert_storage_multipath.json (+1059/-0)
tests/data/probert_storage_raid1_partitions.json (+951/-0)
tests/data/probert_storage_zlp6.json (+6980/-0)
tests/unittests/test_block.py (+59/-0)
tests/unittests/test_block_dasd.py (+1/-1)
tests/unittests/test_block_multipath.py (+87/-6)
tests/unittests/test_block_zfs.py (+26/-0)
tests/unittests/test_clear_holders.py (+131/-11)
tests/unittests/test_commands_block_meta.py (+550/-35)
tests/unittests/test_commands_collect_logs.py (+3/-1)
tests/unittests/test_curthooks.py (+338/-31)
tests/unittests/test_distro.py (+71/-0)
tests/unittests/test_storage_config.py (+152/-10)
tests/unittests/test_udev.py (+15/-14)
tests/unittests/test_util.py (+120/-1)
tests/vmtests/__init__.py (+244/-34)
tests/vmtests/helpers.py (+11/-6)
tests/vmtests/image_sync.py (+10/-3)
tests/vmtests/releases.py (+34/-0)
tests/vmtests/test_apt_config_cmd.py (+1/-8)
tests/vmtests/test_basic.py (+69/-89)
tests/vmtests/test_basic_dasd.py (+7/-16)
tests/vmtests/test_bcache_basic.py (+4/-3)
tests/vmtests/test_bcache_bug1718699.py (+4/-3)
tests/vmtests/test_bcache_ceph.py (+30/-3)
tests/vmtests/test_bcache_partitions.py (+4/-3)
tests/vmtests/test_fs_battery.py (+4/-3)
tests/vmtests/test_iscsi.py (+5/-3)
tests/vmtests/test_journald_reporter.py (+4/-7)
tests/vmtests/test_lvm.py (+14/-21)
tests/vmtests/test_lvm_iscsi.py (+4/-3)
tests/vmtests/test_lvm_raid.py (+3/-3)
tests/vmtests/test_lvm_root.py (+16/-1)
tests/vmtests/test_mdadm_bcache.py (+86/-40)
tests/vmtests/test_mdadm_iscsi.py (+4/-3)
tests/vmtests/test_multipath.py (+5/-5)
tests/vmtests/test_network.py (+3/-3)
tests/vmtests/test_network_alias.py (+4/-3)
tests/vmtests/test_network_bonding.py (+3/-3)
tests/vmtests/test_network_bridging.py (+22/-3)
tests/vmtests/test_network_ipv6.py (+0/-4)
tests/vmtests/test_network_ipv6_static.py (+3/-3)
tests/vmtests/test_network_ipv6_vlan.py (+7/-3)
tests/vmtests/test_network_mtu.py (+41/-13)
tests/vmtests/test_network_static.py (+3/-3)
tests/vmtests/test_network_static_routes.py (+5/-5)
tests/vmtests/test_network_vlan.py (+11/-3)
tests/vmtests/test_nvme.py (+4/-7)
tests/vmtests/test_old_apt_features.py (+3/-2)
tests/vmtests/test_pollinate_useragent.py (+4/-3)
tests/vmtests/test_preserve.py (+4/-3)
tests/vmtests/test_preserve_raid.py (+4/-3)
tests/vmtests/test_raid5_bcache.py (+4/-3)
tests/vmtests/test_reuse_raid_member.py (+63/-0)
tests/vmtests/test_simple.py (+10/-10)
tests/vmtests/test_uefi_basic.py (+8/-7)
tests/vmtests/test_zfsroot.py (+20/-8)
tools/block-discover-to-config (+3/-1)
tools/jenkins-runner (+50/-0)
tools/launch (+10/-0)
tools/vmtest-filter (+12/-1)
tools/vmtest-sync-images (+15/-13)
tools/xkvm (+20/-4)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Needs Fixing
Dan Watkins (community) Approve
Review via email: mp+378357@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Dan Watkins (oddbloke) wrote :

Confirmed that I see the same locally.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/HACKING.rst b/HACKING.rst
2index d87552b..7c26a25 100644
3--- a/HACKING.rst
4+++ b/HACKING.rst
5@@ -15,12 +15,11 @@ Do these things once
6 be listed in the `contributor-agreement-canonical`_ group. Unfortunately
7 there is no easy way to check if an organization or company you are doing
8 work for has signed. If you are unsure or have questions, email
9- `Scott Moser <mailto:scott.moser@canonical.com>`_ and
10- `Ryan Harper <mailto:ryan.harper@canonical.com>`_ or ping smoser or rharper
11- in ``#curtin`` channel via Freenode IRC.
12+ `Josh Powers <mailto:josh.powers@canonical.com>` or ping powersj in
13+ ``#curtin`` channel via Freenode IRC.
14
15 When prompted for 'Project contact' or 'Canonical Project Manager' enter
16- 'David Britton'.
17+ 'Josh Powers'.
18
19 * Configure git with your email and name for commit messages.
20
21diff --git a/Makefile b/Makefile
22index 25db1d0..827102c 100644
23--- a/Makefile
24+++ b/Makefile
25@@ -31,10 +31,14 @@ pyflakes:
26 pyflakes3:
27 @$(CWD)/tools/run-pyflakes3
28
29-unittest:
30+unittest2:
31 nosetests $(coverageopts) $(noseopts) tests/unittests
32+
33+unittest3:
34 nosetests3 $(coverageopts) $(noseopts) tests/unittests
35
36+unittest: unittest2 unittest3
37+
38 schema-validate:
39 @$(CWD)/tools/schema-validate-storage
40
41diff --git a/curtin/__init__.py b/curtin/__init__.py
42index 076c135..142d288 100644
43--- a/curtin/__init__.py
44+++ b/curtin/__init__.py
45@@ -32,6 +32,6 @@ FEATURES = [
46 'HAS_VERSION_MODULE',
47 ]
48
49-__version__ = "19.1"
50+__version__ = "19.3"
51
52 # vi: ts=4 expandtab syntax=python
53diff --git a/curtin/block/__init__.py b/curtin/block/__init__.py
54index 5d1b1bd..f30c5df 100644
55--- a/curtin/block/__init__.py
56+++ b/curtin/block/__init__.py
57@@ -668,6 +668,24 @@ def get_proc_mounts():
58 return mounts
59
60
61+def _get_dev_disk_by_prefix(prefix):
62+ """
63+ Construct a dictionary mapping devname to disk/<prefix> paths
64+
65+ :returns: Dictionary populated by examining /dev/disk/<prefix>/*
66+
67+ {
68+ '/dev/sda': '/dev/disk/<prefix>/virtio-aaaa',
69+ '/dev/sda1': '/dev/disk/<prefix>/virtio-aaaa-part1',
70+ }
71+ """
72+ return {
73+ os.path.realpath(bypfx): bypfx
74+ for bypfx in [os.path.join(prefix, path)
75+ for path in os.listdir(prefix)]
76+ }
77+
78+
79 def get_dev_disk_byid():
80 """
81 Construct a dictionary mapping devname to disk/by-id paths
82@@ -679,12 +697,7 @@ def get_dev_disk_byid():
83 '/dev/sda1': '/dev/disk/by-id/virtio-aaaa-part1',
84 }
85 """
86-
87- prefix = '/dev/disk/by-id'
88- return {
89- os.path.realpath(byid): byid
90- for byid in [os.path.join(prefix, path) for path in os.listdir(prefix)]
91- }
92+ return _get_dev_disk_by_prefix('/dev/disk/by-id')
93
94
95 def disk_to_byid_path(kname):
96@@ -696,6 +709,15 @@ def disk_to_byid_path(kname):
97 return mapping.get(dev_path(kname))
98
99
100+def disk_to_bypath_path(kname):
101+ """"
102+ Return a /dev/disk/by-path path to kname if present.
103+ """
104+
105+ mapping = _get_dev_disk_by_prefix('/dev/disk/by-path')
106+ return mapping.get(dev_path(kname))
107+
108+
109 def get_device_mapper_links(devpath, first=False):
110 """ Return the best devlink to device at devpath. """
111 info = udevadm_info(devpath)
112@@ -892,6 +914,28 @@ def is_online(device):
113 return int(device_size) > 0
114
115
116+def zkey_supported(strict=True):
117+ """ Return True if zkey cmd present and can generate keys, else False."""
118+ LOG.debug('Checking if zkey encryption is supported...')
119+ try:
120+ util.load_kernel_module('pkey')
121+ except util.ProcessExecutionError as err:
122+ msg = "Failed to load 'pkey' kernel module"
123+ LOG.error(msg + ": %s" % err) if strict else LOG.warning(msg)
124+ return False
125+
126+ try:
127+ with tempfile.NamedTemporaryFile() as tf:
128+ util.subp(['zkey', 'generate', tf.name], capture=True)
129+ LOG.debug('zkey encryption supported.')
130+ return True
131+ except util.ProcessExecutionError as err:
132+ msg = "zkey not supported"
133+ LOG.error(msg + ": %s" % err) if strict else LOG.warning(msg)
134+
135+ return False
136+
137+
138 @contextmanager
139 def exclusive_open(path, exclusive=True):
140 """
141diff --git a/curtin/block/clear_holders.py b/curtin/block/clear_holders.py
142index 4a099cd..b691841 100644
143--- a/curtin/block/clear_holders.py
144+++ b/curtin/block/clear_holders.py
145@@ -166,14 +166,27 @@ def shutdown_mdadm(device):
146
147 blockdev = block.sysfs_to_devpath(device)
148
149- LOG.info('Wiping superblock on raid device: %s', device)
150- _wipe_superblock(blockdev, exclusive=False)
151-
152+ LOG.info('Discovering raid devices and spares for %s', device)
153 md_devs = (
154 mdadm.md_get_devices_list(blockdev) +
155 mdadm.md_get_spares_list(blockdev))
156 mdadm.set_sync_action(blockdev, action="idle")
157 mdadm.set_sync_action(blockdev, action="frozen")
158+
159+ LOG.info('Wiping superblock on raid device: %s', device)
160+ try:
161+ _wipe_superblock(blockdev, exclusive=False)
162+ except ValueError as e:
163+ # if the array is not functional, writes to the device may fail
164+ # and _wipe_superblock will raise ValueError for short writes
165+ # which happens on inactive raid volumes. In that case we
166+ # shouldn't give up yet as we still want to disassemble
167+ # array and wipe members. Other errors such as IOError or OSError
168+ # are unwelcome and will stop deployment.
169+ LOG.debug('Non-fatal error writing to array device %s, '
170+ 'proceeding with shutdown: %s', blockdev, e)
171+
172+ LOG.info('Removing raid array members: %s', md_devs)
173 for mddev in md_devs:
174 try:
175 mdadm.fail_device(blockdev, mddev)
176@@ -272,7 +285,26 @@ def wipe_superblock(device):
177 else:
178 bcache._stop_device(stop_path)
179
180- _wipe_superblock(blockdev)
181+ # the blockdev (e.g. /dev/sda2) may be a multipath partition which can
182+ # only be wiped via its device mapper device (e.g. /dev/dm-4)
183+ # check for this and determine the correct device mapper value to use.
184+ mp_dev = None
185+ mp_support = multipath.multipath_supported()
186+ if mp_support:
187+ parent, partnum = block.get_blockdev_for_partition(blockdev)
188+ parent_mpath_id = multipath.find_mpath_id_by_path(parent)
189+ if parent_mpath_id is not None:
190+ # construct multipath dmsetup id
191+ # <mpathid>-part%d -> /dev/dm-1
192+ mp_id, mp_dev = multipath.find_mpath_id_by_parent(parent_mpath_id,
193+ partnum=partnum)
194+ # if we don't find a mapping then the mp partition has already been
195+ # wiped/removed
196+ if mp_dev:
197+ LOG.debug('Found multipath device over %s, wiping holder %s',
198+ blockdev, mp_dev)
199+
200+ _wipe_superblock(mp_dev if mp_dev else blockdev)
201
202 # if we had partitions, make sure they've been removed
203 if partitions:
204@@ -295,16 +327,19 @@ def wipe_superblock(device):
205 device, attempt + 1, len(retries), wait)
206 time.sleep(wait)
207
208- # multipath partitions are separate block devices (disks)
209- if multipath.is_mpath_partition(blockdev):
210- multipath.remove_partition(blockdev)
211- # multipath devices must be hidden to utilize a single member (path)
212- elif multipath.is_mpath_device(blockdev):
213- mp_id = multipath.find_mpath_id(blockdev)
214- if mp_id:
215- multipath.remove_map(mp_id)
216- else:
217- raise RuntimeError('Failed to find multipath id for %s' % blockdev)
218+ if mp_support:
219+ # multipath partitions are separate block devices (disks)
220+ if mp_dev or multipath.is_mpath_partition(blockdev):
221+ multipath.remove_partition(mp_dev if mp_dev else blockdev)
222+ # multipath devices must be hidden to utilize a single member (path)
223+ elif multipath.is_mpath_device(blockdev):
224+ mp_id = multipath.find_mpath_id(blockdev)
225+ multipath.remove_partition(blockdev)
226+ if mp_id:
227+ multipath.remove_map(mp_id)
228+ else:
229+ raise RuntimeError(
230+ 'Failed to find multipath id for %s' % blockdev)
231
232
233 def _wipe_superblock(blockdev, exclusive=True, strict=True):
234@@ -456,25 +491,39 @@ def plan_shutdown_holder_trees(holders_trees):
235 if not isinstance(holders_trees, (list, tuple)):
236 holders_trees = [holders_trees]
237
238+ # sort the trees to ensure we generate a consistent plan
239+ holders_trees = sorted(holders_trees, key=lambda x: x['device'])
240+
241+ def htree_level(tree):
242+ if len(tree['holders']) == 0:
243+ return 0
244+ return 1 + sum(htree_level(holder) for holder in tree['holders'])
245+
246 def flatten_holders_tree(tree, level=0):
247 """
248 add entries from holders tree to registry with level key corresponding
249 to how many layers from raw disks the current device is at
250 """
251 device = tree['device']
252+ device_level = htree_level(tree)
253
254 # always go with highest level if current device has been
255 # encountered already. since the device and everything above it is
256 # re-added to the registry it ensures that any increase of level
257 # required here will propagate down the tree
258 # this handles a scenario like mdadm + bcache, where the backing
259- # device for bcache is a 3nd level item like mdadm, but the cache
260+ # device for bcache is a 3rd level item like mdadm, but the cache
261 # device is 1st level (disk) or second level (partition), ensuring
262 # that the bcache item is always considered higher level than
263 # anything else regardless of whether it was added to the tree via
264 # the cache device or backing device first
265 if device in reg:
266- level = max(reg[device]['level'], level)
267+ level = max(reg[device]['level'], level) + 1
268+
269+ else:
270+ # first time device to registry, assume the larger value of the
271+ # current level or the length of its dependencies.
272+ level = max(device_level, level)
273
274 reg[device] = {'level': level, 'device': device,
275 'dev_type': tree['dev_type']}
276@@ -487,10 +536,26 @@ def plan_shutdown_holder_trees(holders_trees):
277 for holders_tree in holders_trees:
278 flatten_holders_tree(holders_tree)
279
280- # return list of entry dicts with highest level first, then dev_type
281+ def devtype_order(dtype):
282+ """Return the order in which we want to clear device types, higher
283+ value should be cleared first.
284+
285+ :param: dtype: string. A device types name from the holders registry,
286+ see _define_handlers_registry()
287+ :returns: integer
288+ """
289+ dev_type_order = [
290+ 'disk', 'partition', 'bcache', 'lvm', 'raid', 'crypt']
291+ return 1 + dev_type_order.index(dtype)
292+
293+ # return list of entry dicts with greatest htree depth. The 'level' value
294+ # indicates the number of additional devices that are "below" this device.
295+ # Devices must be cleared in descending 'level' value. For devices which
296+ # have the same 'level' value, we sort within the 'level' by devtype order.
297 return [reg[k]
298- for k in sorted(reg, key=lambda x: (reg[x]['level'] * -1,
299- reg[x]['dev_type']))]
300+ for k in sorted(reg, reverse=True,
301+ key=lambda x: (reg[x]['level'],
302+ devtype_order(reg[x]['dev_type'])))]
303
304
305 def format_holders_tree(holders_tree):
306@@ -534,8 +599,10 @@ def assert_clear(base_paths):
307 valid = ('disk', 'partition')
308 if not isinstance(base_paths, (list, tuple)):
309 base_paths = [base_paths]
310- base_paths = [block.sys_block_path(path) for path in base_paths]
311- for holders_tree in [gen_holders_tree(p) for p in base_paths]:
312+ base_paths = [block.sys_block_path(path, strict=False)
313+ for path in base_paths]
314+ for holders_tree in [gen_holders_tree(p)
315+ for p in base_paths if os.path.exists(p)]:
316 if any(holder_type not in valid and path not in base_paths
317 for (holder_type, path) in get_holder_types(holders_tree)):
318 raise OSError('Storage not clear, remaining:\n{}'
319diff --git a/curtin/block/deps.py b/curtin/block/deps.py
320index 930f764..38581a8 100644
321--- a/curtin/block/deps.py
322+++ b/curtin/block/deps.py
323@@ -58,7 +58,7 @@ def detect_required_packages_mapping(osfamily=DISTROS.debian):
324 distro_mapping = {
325 DISTROS.debian: {
326 'bcache': ['bcache-tools'],
327- 'btrfs': ['btrfs-tools'],
328+ 'btrfs': ['^btrfs-(progs|tools)$'],
329 'ext2': ['e2fsprogs'],
330 'ext3': ['e2fsprogs'],
331 'ext4': ['e2fsprogs'],
332diff --git a/curtin/block/multipath.py b/curtin/block/multipath.py
333index d1e8441..8ce0509 100644
334--- a/curtin/block/multipath.py
335+++ b/curtin/block/multipath.py
336@@ -11,6 +11,7 @@ SHOW_MAPS_FMT = "name=%n multipath='%w' sysfs='%d' paths='%N'"
337
338
339 def _extract_mpath_data(cmd, show_verb):
340+ """ Parse output from specifed command output via load_shell_content."""
341 data, _err = util.subp(cmd, capture=True)
342 result = []
343 for line in data.splitlines():
344@@ -23,16 +24,34 @@ def _extract_mpath_data(cmd, show_verb):
345
346
347 def show_paths():
348+ """ Query multipathd for paths output and return a dict of the values."""
349 cmd = ['multipathd', 'show', 'paths', 'raw', 'format', SHOW_PATHS_FMT]
350 return _extract_mpath_data(cmd, 'paths')
351
352
353 def show_maps():
354+ """ Query multipathd for maps output and return a dict of the values."""
355 cmd = ['multipathd', 'show', 'maps', 'raw', 'format', SHOW_MAPS_FMT]
356 return _extract_mpath_data(cmd, 'maps')
357
358
359+def dmname_to_blkdev_mapping():
360+ """ Use dmsetup ls output to build a dict of DM_NAME, /dev/dm-x values."""
361+ data, _err = util.subp(['dmsetup', 'ls', '-o', 'blkdevname'], capture=True)
362+ mapping = {}
363+ if data and data.strip() != "No devices found":
364+ LOG.debug('multipath: dmsetup ls output:\n%s', data)
365+ for line in data.splitlines():
366+ if line:
367+ dm_name, blkdev = line.split('\t')
368+ # (dm-1) -> /dev/dm-1
369+ mapping[dm_name] = '/dev/' + blkdev.strip('()')
370+
371+ return mapping
372+
373+
374 def is_mpath_device(devpath):
375+ """ Check if devpath is a multipath device, returns boolean. """
376 info = udev.udevadm_info(devpath)
377 if info.get('DM_UUID', '').startswith('mpath-'):
378 return True
379@@ -41,6 +60,7 @@ def is_mpath_device(devpath):
380
381
382 def is_mpath_member(devpath):
383+ """ Check if a device is a multipath member (a path), returns boolean. """
384 try:
385 util.subp(['multipath', '-c', devpath], capture=True)
386 return True
387@@ -49,6 +69,7 @@ def is_mpath_member(devpath):
388
389
390 def is_mpath_partition(devpath):
391+ """ Check if a device is a multipath partition, returns boolean. """
392 if devpath.startswith('/dev/dm-'):
393 if 'DM_PART' in udev.udevadm_info(devpath):
394 LOG.debug("%s is multipath device partition", devpath)
395@@ -58,6 +79,7 @@ def is_mpath_partition(devpath):
396
397
398 def mpath_partition_to_mpath_id(devpath):
399+ """ Return the mpath id of a multipath partition. """
400 info = udev.udevadm_info(devpath)
401 if 'DM_MPATH' in info:
402 return info['DM_MPATH']
403@@ -66,9 +88,10 @@ def mpath_partition_to_mpath_id(devpath):
404
405
406 def remove_partition(devpath, retries=10):
407+ """ Remove a multipath partition mapping. """
408 LOG.debug('multipath: removing multipath partition: %s', devpath)
409 for _ in range(0, retries):
410- util.subp(['dmsetup', 'remove', devpath], rcs=[0, 1])
411+ util.subp(['dmsetup', 'remove', '--force', '--retry', devpath])
412 udev.udevadm_settle()
413 if not os.path.exists(devpath):
414 return
415@@ -77,10 +100,11 @@ def remove_partition(devpath, retries=10):
416
417
418 def remove_map(map_id, retries=10):
419+ """ Remove a multipath device mapping. """
420 LOG.debug('multipath: removing multipath map: %s', map_id)
421 devpath = '/dev/mapper/%s' % map_id
422 for _ in range(0, retries):
423- util.subp(['multipath', '-f', map_id], rcs=[0, 1])
424+ util.subp(['multipath', '-v3', '-R3', '-f', map_id], rcs=[0, 1])
425 udev.udevadm_settle()
426 if not os.path.exists(devpath):
427 return
428@@ -89,6 +113,7 @@ def remove_map(map_id, retries=10):
429
430
431 def find_mpath_members(multipath_id, paths=None):
432+ """ Return a list of device path for each member of aspecified mpath_id."""
433 if not paths:
434 paths = show_paths()
435
436@@ -98,6 +123,7 @@ def find_mpath_members(multipath_id, paths=None):
437
438
439 def find_mpath_id(devpath, maps=None):
440+ """ Return the mpath_id associated with a specified device path. """
441 if not maps:
442 maps = show_maps()
443
444@@ -109,3 +135,49 @@ def find_mpath_id(devpath, maps=None):
445 return mpmap['multipath']
446
447 return None
448+
449+
450+def find_mpath_id_by_path(devpath, paths=None):
451+ """ Return the mpath_id associated with a specified device path. """
452+ if not paths:
453+ paths = show_paths()
454+
455+ for path in paths:
456+ if devpath == '/dev/' + path['device']:
457+ return path['multipath']
458+
459+ return None
460+
461+
462+def find_mpath_id_by_parent(multipath_id, partnum=None):
463+ """ Return the mpath_id associated with a specified device path. """
464+ devmap = dmname_to_blkdev_mapping()
465+ LOG.debug('multipath: dm_name blk map: %s', devmap)
466+ dm_name = multipath_id
467+ if partnum:
468+ dm_name += "-part%d" % int(partnum)
469+
470+ return (dm_name, devmap.get(dm_name))
471+
472+
473+def multipath_supported():
474+ """Return a boolean indicating if multipath is supported."""
475+ try:
476+ multipath_assert_supported()
477+ return True
478+ except RuntimeError:
479+ return False
480+
481+
482+def multipath_assert_supported():
483+ """ Determine if the runtime system supports multipath.
484+ returns: True if system supports multipath
485+ raises: RuntimeError: if system does not support multipath
486+ """
487+ missing_progs = [p for p in ('multipath', 'multipathd')
488+ if not util.which(p)]
489+ if missing_progs:
490+ raise RuntimeError(
491+ "Missing multipath utils: %s" % ','.join(missing_progs))
492+
493+# vi: ts=4 expandtab syntax=python
494diff --git a/curtin/block/schemas.py b/curtin/block/schemas.py
495index fb7507d..fc7e522 100644
496--- a/curtin/block/schemas.py
497+++ b/curtin/block/schemas.py
498@@ -7,6 +7,8 @@ _path_nondev = r'(^/$|^(/[^/]+)+$)'
499 _fstypes = ['btrfs', 'ext2', 'ext3', 'ext4', 'fat', 'fat12', 'fat16', 'fat32',
500 'iso9660', 'vfat', 'jfs', 'ntfs', 'reiserfs', 'swap', 'xfs',
501 'zfsroot']
502+_ptables = ['dos', 'gpt', 'msdos', 'vtoc']
503+_ptable_unsupported = 'unsupported'
504
505 definitions = {
506 'id': {'type': 'string'},
507@@ -14,7 +16,7 @@ definitions = {
508 'devices': {'type': 'array', 'items': {'$ref': '#/definitions/ref_id'}},
509 'name': {'type': 'string'},
510 'preserve': {'type': 'boolean'},
511- 'ptable': {'type': 'string', 'enum': ['dos', 'gpt', 'msdos']},
512+ 'ptable': {'type': 'string', 'enum': _ptables + [_ptable_unsupported]},
513 'size': {'type': ['string', 'number'],
514 'minimum': 1,
515 'pattern': r'^([1-9]\d*(.\d+)?|\d+.\d+)(K|M|G|T)?B?'},
516@@ -119,6 +121,7 @@ DISK = {
517 'properties': {
518 'id': {'$ref': '#/definitions/id'},
519 'name': {'$ref': '#/definitions/name'},
520+ 'multipath': {'type': 'string'},
521 'preserve': {'$ref': '#/definitions/preserve'},
522 'wipe': {'$ref': '#/definitions/wipe'},
523 'type': {'const': 'disk'},
524@@ -135,7 +138,7 @@ DISK = {
525 'type': 'string',
526 'oneOf': [
527 {'pattern': r'^0x(\d|[a-zA-Z])+'},
528- {'pattern': r'^nvme\.(\d|[a-zA-Z]-)+'}],
529+ {'pattern': r'^(nvme|eui)\.([-0-9a-zA-Z])+'}],
530 },
531 'grub_device': {
532 'type': ['boolean', 'integer'],
533@@ -265,6 +268,7 @@ PARTITION = {
534 'additionalProperties': False,
535 'properties': {
536 'id': {'$ref': '#/definitions/id'},
537+ 'multipath': {'type': 'string'},
538 'name': {'$ref': '#/definitions/name'},
539 'offset': {'$ref': '#/definitions/size'}, # XXX: This is not used
540 'preserve': {'$ref': '#/definitions/preserve'},
541@@ -296,6 +300,8 @@ RAID = {
542 'devices': {'$ref': '#/definitions/devices'},
543 'name': {'$ref': '#/definitions/name'},
544 'mdname': {'$ref': '#/definitions/name'}, # XXX: Docs need updating
545+ 'metadata': {'type': ['string', 'number']},
546+ 'preserve': {'$ref': '#/definitions/preserve'},
547 'ptable': {'$ref': '#/definitions/ptable'},
548 'spare_devices': {'$ref': '#/definitions/devices'},
549 'type': {'const': 'raid'},
550diff --git a/curtin/block/zfs.py b/curtin/block/zfs.py
551index dce15c3..25751d3 100644
552--- a/curtin/block/zfs.py
553+++ b/curtin/block/zfs.py
554@@ -281,4 +281,28 @@ def device_to_poolname(devname):
555 if vdev_type == 'zfs_member' and label:
556 return label
557
558+
559+def get_zpool_from_config(cfg):
560+ """Parse a curtin storage config and return a list
561+ of zpools that were created.
562+ """
563+ if not cfg:
564+ return []
565+
566+ if 'storage' not in cfg:
567+ return []
568+
569+ zpools = []
570+ sconfig = cfg['storage']['config']
571+ for item in sconfig:
572+ if item['type'] == 'zpool':
573+ zpools.append(item['pool'])
574+ elif item['type'] == 'format':
575+ if item['fstype'] == 'zfsroot':
576+ # curtin.commands.blockmeta sets pool='rpool' for zfsroot
577+ zpools.append('rpool')
578+
579+ return zpools
580+
581+
582 # vi: ts=4 expandtab syntax=python
583diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
584index 79493fc..f287bd4 100644
585--- a/curtin/commands/block_meta.py
586+++ b/curtin/commands/block_meta.py
587@@ -2,6 +2,7 @@
588
589 from collections import OrderedDict, namedtuple
590 from curtin import (block, config, paths, util)
591+from curtin.block import schemas
592 from curtin.block import (bcache, clear_holders, dasd, iscsi, lvm, mdadm, mkfs,
593 zfs)
594 from curtin import distro
595@@ -32,6 +33,7 @@ SIMPLE = 'simple'
596 SIMPLE_BOOT = 'simple-boot'
597 CUSTOM = 'custom'
598 BCACHE_REGISTRATION_RETRY = [0.2] * 60
599+PTABLE_UNSUPPORTED = schemas._ptable_unsupported
600
601 CMD_ARGUMENTS = (
602 ((('-D', '--devices'),
603@@ -58,7 +60,7 @@ CMD_ARGUMENTS = (
604 @logged_time("BLOCK_META")
605 def block_meta(args):
606 # main entry point for the block-meta command.
607- state = util.load_command_environment()
608+ state = util.load_command_environment(strict=True)
609 cfg = config.load_command_config(args, state)
610 dd_images = util.get_dd_images(cfg.get('sources', {}))
611
612@@ -67,7 +69,7 @@ def block_meta(args):
613 if devices is None:
614 devices = []
615 if 'storage' in cfg:
616- devices = get_disk_paths_from_storage_config(
617+ devices = get_device_paths_from_storage_config(
618 extract_storage_ordered_dict(cfg))
619 if len(devices) == 0:
620 devices = cfg.get('block-meta', {}).get('devices', [])
621@@ -258,7 +260,7 @@ def make_dname_byid(path, error_msg=None, info=None):
622
623
624 def make_dname(volume, storage_config):
625- state = util.load_command_environment()
626+ state = util.load_command_environment(strict=True)
627 rules_dir = os.path.join(state['scratch'], "rules.d")
628 vol = storage_config.get(volume)
629 path = get_path_to_storage_volume(volume, storage_config)
630@@ -404,9 +406,8 @@ def get_path_to_storage_volume(volume, storage_config):
631 try:
632 if not vol_value:
633 continue
634- if disk_key == 'serial':
635+ if disk_key in ['wwn', 'serial']:
636 volume_path = block.lookup_disk(vol_value)
637- break
638 elif disk_key == 'path':
639 if vol_value.startswith('iscsi:'):
640 i = iscsi.ensure_disk_connected(vol_value)
641@@ -416,21 +417,18 @@ def get_path_to_storage_volume(volume, storage_config):
642 # sys/class/block access is valid. ie, there are no
643 # udev generated values in sysfs
644 volume_path = os.path.realpath(vol_value)
645- break
646- elif disk_key == 'wwn':
647- by_wwn = '/dev/disk/by-id/wwn-%s' % vol.get('wwn')
648- volume_path = os.path.realpath(by_wwn)
649- break
650 elif disk_key == 'device_id':
651 dasd_device = dasd.DasdDevice(vol_value)
652 volume_path = dasd_device.devname
653- break
654 except ValueError:
655- pass
656+ continue
657+ # verify path exists otherwise try the next key
658+ if not os.path.exists(volume_path):
659+ volume_path = None
660
661- if not volume_path:
662- raise ValueError("serial, wwn or path to block dev must be \
663- specified to identify disk")
664+ if volume_path is None:
665+ raise ValueError("Failed to find storage volume id='%s' config: %s"
666+ % (vol['id'], vol))
667
668 elif vol.get('type') == "lvm_partition":
669 # For lvm partitions, a directory in /dev/ should be present with the
670@@ -536,7 +534,7 @@ def disk_handler(info, storage_config):
671
672 if config.value_as_boolean(info.get('preserve')):
673 # Handle preserve flag, verifying if ptable specified in config
674- if config.value_as_boolean(ptable):
675+ if config.value_as_boolean(ptable) and ptable != PTABLE_UNSUPPORTED:
676 current_ptable = block.get_part_table_type(disk)
677 if not ((ptable in _dos_names and current_ptable in _dos_names) or
678 (ptable == 'gpt' and current_ptable == 'gpt')):
679@@ -868,6 +866,47 @@ def mount_data(info, storage_config):
680 spec, path, fstype, ",".join(options), freq, passno, volume_path)
681
682
683+def _get_volume_type(device_path):
684+ lsblock = block._lsblock([device_path])
685+ kname = block.path_to_kname(device_path)
686+ return lsblock[kname]['TYPE']
687+
688+
689+def get_volume_spec(device_path):
690+ """
691+ Return the most reliable spec for a device per Ubuntu FSTAB wiki
692+
693+ https://wiki.ubuntu.com/FSTAB
694+ """
695+ info = udevadm_info(path=device_path)
696+ block_type = _get_volume_type(device_path)
697+
698+ devlinks = []
699+ if 'raid' in block_type:
700+ devlinks = [link for link in info['DEVLINKS']
701+ if os.path.basename(link).startswith('md-uuid-')]
702+ elif block_type in ['crypt', 'lvm', 'mpath']:
703+ devlinks = [link for link in info['DEVLINKS']
704+ if os.path.basename(link).startswith('dm-uuid-')]
705+ elif block_type in ['disk', 'part']:
706+ if device_path.startswith('/dev/bcache'):
707+ devlinks = [link for link in info['DEVLINKS']
708+ if link.startswith('/dev/bcache/by-uuid')]
709+ # on s390x prefer by-path links which are stable and unique.
710+ if platform.machine() == 's390x':
711+ devlinks = [link for link in info['DEVLINKS']
712+ if link.startswith('/dev/disk/by-path')]
713+ if len(devlinks) == 0:
714+ # use FS UUID if present
715+ devlinks = [link for link in info['DEVLINKS']
716+ if '/by-uuid' in link]
717+ if len(devlinks) == 0 and block_type == 'part':
718+ devlinks = [link for link in info['DEVLINKS']
719+ if '/by-partuuid' in link]
720+
721+ return devlinks[0] if len(devlinks) else device_path
722+
723+
724 def fstab_line_for_data(fdata):
725 """Return a string representing fdata in /etc/fstab format.
726
727@@ -883,8 +922,7 @@ def fstab_line_for_data(fdata):
728 if fdata.spec is None:
729 if not fdata.device:
730 raise ValueError("FstabData missing both spec and device.")
731- uuid = block.get_volume_uuid(fdata.device)
732- spec = ("UUID=%s" % uuid) if uuid else fdata.device
733+ spec = get_volume_spec(fdata.device)
734 else:
735 spec = fdata.spec
736
737@@ -896,8 +934,20 @@ def fstab_line_for_data(fdata):
738 else:
739 options = fdata.options
740
741- return ' '.join((spec, path, fdata.fstype, options,
742- fdata.freq, fdata.passno)) + "\n"
743+ if path != "none":
744+ # prefer provided spec over device
745+ device = fdata.spec if fdata.spec else None
746+ # if not provided a spec, derive device from calculated spec value
747+ if not device:
748+ device = fdata.device if fdata.device else spec
749+ comment = "# %s was on %s during curtin installation" % (path, device)
750+ else:
751+ comment = None
752+
753+ entry = ' '.join((spec, path, fdata.fstype, options,
754+ fdata.freq, fdata.passno)) + "\n"
755+ line = '\n'.join([comment, entry] if comment else [entry])
756+ return line
757
758
759 def mount_fstab_data(fdata, target=None):
760@@ -960,7 +1010,7 @@ def mount_handler(info, storage_config):
761 Mount specified device under target at 'path' and generate
762 fstab entry.
763 """
764- state = util.load_command_environment()
765+ state = util.load_command_environment(strict=True)
766 mount_apply(mount_data(info, storage_config),
767 target=state.get('target'), fstab=state.get('fstab'))
768
769@@ -1050,7 +1100,7 @@ def lvm_partition_handler(info, storage_config):
770
771
772 def dm_crypt_handler(info, storage_config):
773- state = util.load_command_environment()
774+ state = util.load_command_environment(strict=True)
775 volume = info.get('volume')
776 keysize = info.get('keysize')
777 cipher = info.get('cipher')
778@@ -1062,6 +1112,7 @@ def dm_crypt_handler(info, storage_config):
779 dm_name = info.get('id')
780
781 volume_path = get_path_to_storage_volume(volume, storage_config)
782+ volume_byid_path = block.disk_to_byid_path(volume_path)
783
784 if 'keyfile' in info:
785 if 'key' in info:
786@@ -1077,16 +1128,45 @@ def dm_crypt_handler(info, storage_config):
787 else:
788 raise ValueError("encryption key or keyfile must be specified")
789
790- cmd = ["cryptsetup"]
791- if cipher:
792- cmd.extend(["--cipher", cipher])
793- if keysize:
794- cmd.extend(["--key-size", keysize])
795- cmd.extend(["luksFormat", volume_path, keyfile])
796-
797- util.subp(cmd)
798+ # if zkey is available, attempt to generate and use it; if it's not
799+ # available or fails to setup properly, fallback to normal cryptsetup
800+ # passing strict=False downgrades log messages to warnings
801+ zkey_used = None
802+ if block.zkey_supported(strict=False):
803+ volume_name = "%s:%s" % (volume_byid_path, dm_name)
804+ LOG.debug('Attempting to setup zkey for %s', volume_name)
805+ luks_type = 'luks2'
806+ gen_cmd = ['zkey', 'generate', '--xts', '--volume-type', luks_type,
807+ '--sector-size', '4096', '--name', dm_name,
808+ '--description',
809+ "curtin generated zkey for %s" % volume_name,
810+ '--volumes', volume_name]
811+ run_cmd = ['zkey', 'cryptsetup', '--run', '--volumes',
812+ volume_byid_path, '--batch-mode', '--key-file', keyfile]
813+ try:
814+ util.subp(gen_cmd, capture=True)
815+ util.subp(run_cmd, capture=True)
816+ zkey_used = os.path.join(os.path.split(state['fstab'])[0],
817+ "zkey_used")
818+ # mark in state that we used zkey
819+ util.write_file(zkey_used, "1")
820+ except util.ProcessExecutionError as e:
821+ LOG.exception(e)
822+ msg = 'Setup of zkey on %s failed, fallback to cryptsetup.'
823+ LOG.error(msg % volume_path)
824+
825+ if not zkey_used:
826+ LOG.debug('Using cryptsetup on %s', volume_path)
827+ luks_type = "luks"
828+ cmd = ["cryptsetup"]
829+ if cipher:
830+ cmd.extend(["--cipher", cipher])
831+ if keysize:
832+ cmd.extend(["--key-size", keysize])
833+ cmd.extend(["luksFormat", volume_path, keyfile])
834+ util.subp(cmd)
835
836- cmd = ["cryptsetup", "open", "--type", "luks", volume_path, dm_name,
837+ cmd = ["cryptsetup", "open", "--type", luks_type, volume_path, dm_name,
838 "--key-file", keyfile]
839
840 util.subp(cmd)
841@@ -1097,11 +1177,11 @@ def dm_crypt_handler(info, storage_config):
842 # A crypttab will be created in the same directory as the fstab in the
843 # configuration. This will then be copied onto the system later
844 if state['fstab']:
845- crypt_tab_location = os.path.join(os.path.split(state['fstab'])[0],
846- "crypttab")
847+ state_dir = os.path.dirname(state['fstab'])
848+ crypt_tab_location = os.path.join(state_dir, "crypttab")
849 uuid = block.get_volume_uuid(volume_path)
850- with open(crypt_tab_location, "a") as fp:
851- fp.write("%s UUID=%s none luks\n" % (dm_name, uuid))
852+ util.write_file(crypt_tab_location,
853+ "%s UUID=%s none luks\n" % (dm_name, uuid), omode="a")
854 else:
855 LOG.info("fstab configuration is not present in environment, so \
856 cannot locate an appropriate directory to write crypttab in \
857@@ -1109,7 +1189,7 @@ def dm_crypt_handler(info, storage_config):
858
859
860 def raid_handler(info, storage_config):
861- state = util.load_command_environment()
862+ state = util.load_command_environment(strict=True)
863 devices = info.get('devices')
864 raidlevel = info.get('raidlevel')
865 spare_devices = info.get('spare_devices')
866@@ -1166,11 +1246,10 @@ def raid_handler(info, storage_config):
867 # The file must also be written onto the running system to enable it to run
868 # mdadm --assemble and continue installation
869 if state['fstab']:
870- mdadm_location = os.path.join(os.path.split(state['fstab'])[0],
871- "mdadm.conf")
872+ state_dir = os.path.dirname(state['fstab'])
873+ mdadm_location = os.path.join(state_dir, "mdadm.conf")
874 mdadm_scan_data = mdadm.mdadm_detail_scan()
875- with open(mdadm_location, "w") as fp:
876- fp.write(mdadm_scan_data)
877+ util.write_file(mdadm_location, mdadm_scan_data)
878 else:
879 LOG.info("fstab configuration is not present in the environment, so \
880 cannot locate an appropriate directory to write mdadm.conf in, \
881@@ -1448,7 +1527,7 @@ def zpool_handler(info, storage_config):
882 """
883 zfs.zfs_assert_supported()
884
885- state = util.load_command_environment()
886+ state = util.load_command_environment(strict=True)
887
888 # extract /dev/disk/by-id paths for each volume used
889 vdevs = [get_path_to_storage_volume(v, storage_config)
890@@ -1487,7 +1566,7 @@ def zfs_handler(info, storage_config):
891 """
892 zfs.zfs_assert_supported()
893
894- state = util.load_command_environment()
895+ state = util.load_command_environment(strict=True)
896 poolname = get_poolname(info, storage_config)
897 volume = info.get('volume')
898 properties = info.get('properties', {})
899@@ -1505,20 +1584,24 @@ def zfs_handler(info, storage_config):
900 util.write_file(state['fstab'], fstab_entry, omode='a')
901
902
903-def get_disk_paths_from_storage_config(storage_config):
904- """Returns a list of disk paths in a storage config filtering out
905- preserved or disks which do not have wipe configuration.
906+def get_device_paths_from_storage_config(storage_config):
907+ """Returns a list of device paths in a storage config filtering out
908+ preserved or devices which do not have wipe configuration.
909
910 :param: storage_config: Ordered dict of storage configation
911 """
912- def get_path_if_present(disk, config):
913- return get_path_to_storage_volume(disk, config)
914-
915- return [get_path_if_present(k, storage_config)
916- for (k, v) in storage_config.items()
917- if v.get('type') == 'disk' and
918- config.value_as_boolean(v.get('wipe')) and
919- not config.value_as_boolean(v.get('preserve'))]
920+ dpaths = []
921+ for (k, v) in storage_config.items():
922+ if v.get('type') in ['disk', 'partition']:
923+ if config.value_as_boolean(v.get('wipe')):
924+ if config.value_as_boolean(v.get('preserve')):
925+ continue
926+ try:
927+ dpaths.append(
928+ get_path_to_storage_volume(k, storage_config))
929+ except Exception:
930+ pass
931+ return dpaths
932
933
934 def zfsroot_update_storage_config(storage_config):
935@@ -1655,7 +1738,7 @@ def meta_custom(args):
936 'zpool': zpool_handler,
937 }
938
939- state = util.load_command_environment()
940+ state = util.load_command_environment(strict=True)
941 cfg = config.load_command_config(args, state)
942
943 storage_config_dict = extract_storage_ordered_dict(cfg)
944@@ -1689,7 +1772,7 @@ def meta_simple(args):
945 """Creates a root partition. If args.mode == SIMPLE_BOOT, it will also
946 create a separate /boot partition.
947 """
948- state = util.load_command_environment()
949+ state = util.load_command_environment(strict=True)
950 cfg = config.load_command_config(args, state)
951 if args.target is not None:
952 state['target'] = args.target
953diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
954index 75f5083..542557c 100644
955--- a/curtin/commands/curthooks.py
956+++ b/curtin/commands/curthooks.py
957@@ -104,6 +104,68 @@ def disable_overlayroot(cfg, target):
958 shutil.move(local_conf, local_conf + ".old")
959
960
961+def _update_initramfs_tools(machine=None):
962+ """ Return a list of binary names used to update an initramfs.
963+
964+ On some architectures there are helper binaries that are also
965+ used and will be included in the list.
966+ """
967+ tools = ['update-initramfs']
968+ if not machine:
969+ machine = platform.machine()
970+ if machine == 's390x':
971+ tools.append('zipl')
972+ elif machine == 'aarch64':
973+ tools.append('flash-kernel')
974+ return tools
975+
976+
977+def disable_update_initramfs(cfg, target, machine=None):
978+ """ Find update-initramfs tools in target and change their name. """
979+ with util.ChrootableTarget(target) as in_chroot:
980+ for tool in _update_initramfs_tools(machine=machine):
981+ found = util.which(tool, target=target)
982+ if found:
983+ LOG.debug('Diverting original %s in target.', tool)
984+ rename = found + '.curtin-disabled'
985+ divert = ['dpkg-divert', '--add', '--rename',
986+ '--divert', rename, found]
987+ in_chroot.subp(divert)
988+
989+ # create a dummy update-initramfs which just returns true;
990+ # this handles postinstall scripts which make invoke $tool
991+ # directly
992+ util.write_file(target + found,
993+ content="#!/bin/true\n# diverted by curtin",
994+ mode=0o755)
995+
996+
997+def update_initramfs_is_disabled(target):
998+ """ Return a bool indicating if initramfs tooling is disabled. """
999+ disabled = []
1000+ with util.ChrootableTarget(target) as in_chroot:
1001+ out, _err = in_chroot.subp(['dpkg-divert', '--list'], capture=True)
1002+ disabled = [divert for divert in out.splitlines()
1003+ if divert.endswith('.curtin-disabled')]
1004+ return len(disabled) > 0
1005+
1006+
1007+def enable_update_initramfs(cfg, target, machine=None):
1008+ """ Enable initramfs update tools by restoring their original name. """
1009+ if update_initramfs_is_disabled(target):
1010+ with util.ChrootableTarget(target) as in_chroot:
1011+ for tool in _update_initramfs_tools(machine=machine):
1012+ LOG.info('Restoring %s in target for initrd updates.', tool)
1013+ found = util.which(tool, target=target)
1014+ if not found:
1015+ continue
1016+ # remove the diverted
1017+ util.del_file(target + found)
1018+ # un-divert and restore original file
1019+ in_chroot.subp(
1020+ ['dpkg-divert', '--rename', '--remove', found])
1021+
1022+
1023 def setup_zipl(cfg, target):
1024 if platform.machine() != 's390x':
1025 return
1026@@ -262,6 +324,11 @@ def get_flash_kernel_pkgs(arch=None, uefi=None):
1027
1028
1029 def setup_kernel_img_conf(target):
1030+ # kernel-img.conf only needed on release prior to 19.10
1031+ lsb_info = distro.lsb_release(target=target)
1032+ if tuple(map(int, lsb_info['release'].split('.'))) >= (19, 10):
1033+ return
1034+
1035 kernel_img_conf_vars = {
1036 'bootloader': 'no',
1037 'inboot': 'yes',
1038@@ -420,12 +487,25 @@ def setup_grub(cfg, target, osfamily=DISTROS.debian):
1039
1040 if storage_cfg_odict:
1041 storage_grub_devices = []
1042- for item_id, item in storage_cfg_odict.items():
1043- if not item.get('grub_device'):
1044- continue
1045- LOG.debug("checking: %s", item)
1046- storage_grub_devices.append(
1047- get_path_to_storage_volume(item_id, storage_cfg_odict))
1048+ if util.is_uefi_bootable():
1049+ # Curtin only supports creating one EFI system partition. Thus the
1050+ # grub_device can only be the default system partition mounted at
1051+ # /boot/efi.
1052+ for item_id, item in storage_cfg_odict.items():
1053+ if item.get('path') == '/boot/efi':
1054+ efi_dev_id = storage_cfg_odict[item['device']]['volume']
1055+ LOG.debug("checking: %s", item)
1056+ storage_grub_devices.append(get_path_to_storage_volume(
1057+ efi_dev_id, storage_cfg_odict))
1058+ break
1059+ else:
1060+ for item_id, item in storage_cfg_odict.items():
1061+ if not item.get('grub_device'):
1062+ continue
1063+ LOG.debug("checking: %s", item)
1064+ storage_grub_devices.append(
1065+ get_path_to_storage_volume(item_id, storage_cfg_odict))
1066+
1067 if len(storage_grub_devices) > 0:
1068 grubcfg['install_devices'] = storage_grub_devices
1069
1070@@ -509,10 +589,7 @@ def setup_grub(cfg, target, osfamily=DISTROS.debian):
1071 LOG.debug("installing grub to %s [replace_default=%s]",
1072 instdevs, replace_default)
1073
1074- # rhel lvm uses /run during grub configuration
1075- chroot_mounts = (["/dev", "/proc", "/sys", "/run"]
1076- if osfamily == DISTROS.redhat else None)
1077- with util.ChrootableTarget(target, mounts=chroot_mounts):
1078+ with util.ChrootableTarget(target):
1079 args = ['install-grub']
1080 if util.is_uefi_bootable():
1081 args.append("--uefi")
1082@@ -529,18 +606,66 @@ def setup_grub(cfg, target, osfamily=DISTROS.debian):
1083 join_stdout_err = ['sh', '-c', 'exec "$0" "$@" 2>&1']
1084 out, _err = util.subp(
1085 join_stdout_err + args + instdevs, env=env, capture=True)
1086- LOG.debug("%s\n%s\n", args, out)
1087+ LOG.debug("%s\n%s\n", args + instdevs, out)
1088
1089 if util.is_uefi_bootable() and grubcfg.get('update_nvram', True):
1090 uefi_reorder_loaders(grubcfg, target)
1091
1092
1093 def update_initramfs(target=None, all_kernels=False):
1094- cmd = ['update-initramfs', '-u']
1095- if all_kernels:
1096- cmd.extend(['-k', 'all'])
1097- with util.ChrootableTarget(target) as in_chroot:
1098- in_chroot.subp(cmd)
1099+ """ Invoke update-initramfs in the target path.
1100+
1101+ Look up the installed kernel versions in the target
1102+ to ensure that an initrd get created or updated as needed.
1103+ This allows curtin to invoke update-initramfs exactly once
1104+ at the end of the install instead of multiple calls.
1105+ """
1106+ if update_initramfs_is_disabled(target):
1107+ return
1108+
1109+ # We keep the all_kernels flag for callers, the implementation
1110+ # now will operate correctly on all kernels present in the image
1111+ # which is almost always exactly one.
1112+ #
1113+ # Ideally curtin should be able to use update-initramfs -k all
1114+ # however, update-initramfs expects to be able to find out which
1115+ # versions of kernels are installed by using values from the
1116+ # kernel package invoking update-initramfs -c <kernel version>.
1117+ # With update-initramfs diverted, nothing captures the kernel
1118+ # version strings in the place where update-initramfs expects
1119+ # to find this information. Instead, curtin will examine
1120+ # /boot to see what kernels and initramfs are installed and
1121+ # either create or update as needed.
1122+ #
1123+ # This loop below will examine the contents of target's
1124+ # /boot and pattern match for kernel files. On Ubuntu this
1125+ # is in the form of /boot/vmlinu[xz]-<uname -r version>.
1126+ #
1127+ # For each kernel, we extract the version string and then
1128+ # construct the name of of the initrd file that *would*
1129+ # have been created when the kernel package was installed
1130+ # if curtin had not diverted update-initramfs to prevent
1131+ # duplicate initrd creation.
1132+ #
1133+ # if the initrd file exists, then we only need to invoke
1134+ # update-initramfs's -u (update) method. If the file does
1135+ # not exist, then we need to run the -c (create) method.
1136+ boot = paths.target_path(target, 'boot')
1137+ for kernel in sorted(glob.glob(boot + '/vmlinu*-*')):
1138+ kfile = os.path.basename(kernel)
1139+ # handle vmlinux or vmlinuz
1140+ kprefix = kfile.split('-')[0]
1141+ version = kfile.replace(kprefix + '-', '')
1142+ initrd = kernel.replace(kprefix, 'initrd.img')
1143+ # -u == update, -c == create
1144+ mode = '-u' if os.path.exists(initrd) else '-c'
1145+ cmd = ['update-initramfs', mode, '-k', version]
1146+ with util.ChrootableTarget(target) as in_chroot:
1147+ in_chroot.subp(cmd)
1148+ if not os.path.exists(initrd):
1149+ files = os.listdir(target + '/boot')
1150+ LOG.debug('Failed to find initrd %s', initrd)
1151+ LOG.debug('Files in target /boot: %s', files)
1152
1153
1154 def copy_fstab(fstab, target):
1155@@ -548,7 +673,10 @@ def copy_fstab(fstab, target):
1156 LOG.warn("fstab variable not in state, not copying fstab")
1157 return
1158
1159- shutil.copy(fstab, os.path.sep.join([target, 'etc/fstab']))
1160+ content = util.load_file(fstab)
1161+ header = distro.fstab_header()
1162+ util.write_file(os.path.sep.join([target, 'etc/fstab']),
1163+ content="%s\n%s" % (header, content))
1164
1165
1166 def copy_crypttab(crypttab, target):
1167@@ -594,6 +722,28 @@ def copy_zpool_cache(zpool_cache, target):
1168 shutil.copy(zpool_cache, os.path.sep.join([target, 'etc/zfs']))
1169
1170
1171+def copy_zkey_repository(zkey_repository, target,
1172+ target_repo='etc/zkey/repository'):
1173+ if not zkey_repository:
1174+ LOG.warn("zkey repository path must be specified, not copying")
1175+ return
1176+
1177+ tdir = os.path.sep.join([target, target_repo])
1178+ if not os.path.exists(tdir):
1179+ util.ensure_dir(tdir)
1180+
1181+ files_copied = []
1182+ for src in os.listdir(zkey_repository):
1183+ source_path = os.path.join(zkey_repository, src)
1184+ target_path = os.path.join(tdir, src)
1185+ if not os.path.exists(target_path):
1186+ shutil.copy2(source_path, target_path)
1187+ files_copied.append(target_path)
1188+
1189+ LOG.debug('Imported zkey repo %s with files: %s',
1190+ zkey_repository, files_copied)
1191+
1192+
1193 def apply_networking(target, state):
1194 netconf = state.get('network_config')
1195 interfaces = state.get('interfaces')
1196@@ -862,12 +1012,21 @@ def install_missing_packages(cfg, target, osfamily=DISTROS.debian):
1197 # UEFI requires grub-efi-{arch}. If a signed version of that package
1198 # exists then it will be installed.
1199 if util.is_uefi_bootable():
1200- uefi_pkgs = []
1201+ uefi_pkgs = ['efibootmgr']
1202 if osfamily == DISTROS.redhat:
1203 # centos/redhat doesn't support 32-bit?
1204- uefi_pkgs.extend(['grub2-efi-x64-modules'])
1205+ if 'grub2-efi-x64-modules' not in installed_packages:
1206+ # Previously Curtin only supported unsigned GRUB due to an
1207+ # upstream bug. By default lp:maas-image-builder and
1208+ # packer-maas have grub preinstalled. If grub2-efi-x64-modules
1209+ # is already in the image use unsigned grub so the install
1210+ # doesn't require Internet access. If grub is missing use the
1211+ # signed version.
1212+ uefi_pkgs.extend(['grub2-efi-x64', 'shim-x64'])
1213 elif osfamily == DISTROS.debian:
1214 arch = util.get_architecture()
1215+ if arch == 'i386':
1216+ arch = 'ia32'
1217 uefi_pkgs.append('grub-efi-%s' % arch)
1218
1219 # Architecture might support a signed UEFI loader
1220@@ -1143,40 +1302,43 @@ def redhat_upgrade_cloud_init(netcfg, target=None, osfamily=DISTROS.redhat):
1221 cloud_init_yum_repo = (
1222 paths.target_path(target,
1223 'etc/yum.repos.d/curtin-cloud-init.repo'))
1224+ rhel_ver = distro.rpm_get_dist_id(target)
1225 # Inject cloud-init daily yum repo
1226 util.write_file(cloud_init_yum_repo,
1227- content=cloud_init_repo(
1228- distro.rpm_get_dist_id(target)))
1229+ content=cloud_init_repo(rhel_ver))
1230
1231- # we separate the installation of repository packages (epel,
1232+ # ensure up-to-date ca-certificates to handle https mirror
1233+ # connections for epel and cloud-init-el.
1234+ packages = ['ca-certificates']
1235+
1236+ if int(rhel_ver) < 8:
1237+ # cloud-init in RHEL < 8 requires EPEL for dependencies.
1238+ packages += ['epel-release']
1239+ # RHEL8+ no longer ships bridge-utils. This does not effect
1240+ # bridge configuration. Only install on RHEL < 8 if not
1241+ # available, do not upgrade.
1242+ with util.ChrootableTarget(target) as in_chroot:
1243+ try:
1244+ in_chroot.subp(['rpm', '-q', 'bridge-utils'],
1245+ capture=False, rcs=[0])
1246+ except util.ProcessExecutionError:
1247+ LOG.debug(
1248+ 'Image missing bridge-utils package, installing')
1249+ packages += ['bridge-utils']
1250+
1251+ packages += ['cloud-init-el-release', 'cloud-init']
1252+
1253+ # We separate the installation of repository packages (epel,
1254 # cloud-init-el-release) as we need a new invocation of yum
1255 # to read the newly installed repo files.
1256-
1257- # ensure up-to-date ca-certificates to handle https mirror
1258- # connections
1259- distro.install_packages(['ca-certificates'], target=target,
1260- osfamily=osfamily)
1261- distro.install_packages(['epel-release'], target=target,
1262- osfamily=osfamily)
1263- distro.install_packages(['cloud-init-el-release'], target=target,
1264- osfamily=osfamily)
1265- distro.install_packages(['cloud-init'], target=target,
1266- osfamily=osfamily)
1267+ for package in packages:
1268+ distro.install_packages(
1269+ [package], target=target, osfamily=osfamily)
1270
1271 # remove cloud-init el-stable bootstrap repo config as the
1272 # cloud-init-el-release package points to the correct repo
1273 util.del_file(cloud_init_yum_repo)
1274
1275- # install bridge-utils if needed
1276- with util.ChrootableTarget(target) as in_chroot:
1277- try:
1278- in_chroot.subp(['rpm', '-q', 'bridge-utils'],
1279- capture=False, rcs=[0])
1280- except util.ProcessExecutionError:
1281- LOG.debug('Image missing bridge-utils package, installing')
1282- distro.install_packages(['bridge-utils'], target=target,
1283- osfamily=osfamily)
1284-
1285 LOG.info('Passing network configuration through to target')
1286 net.render_netconfig_passthrough(target, netconfig={'network': netcfg})
1287
1288@@ -1255,6 +1417,7 @@ def builtin_curthooks(cfg, target, state):
1289 LOG.info('Running curtin builtin curthooks')
1290 stack_prefix = state.get('report_stack_prefix', '')
1291 state_etcd = os.path.split(state['fstab'])[0]
1292+ machine = platform.machine()
1293
1294 distro_info = distro.get_distroinfo(target=target)
1295 if not distro_info:
1296@@ -1269,6 +1432,7 @@ def builtin_curthooks(cfg, target, state):
1297 description="configuring apt configuring apt"):
1298 do_apt_config(cfg, target)
1299 disable_overlayroot(cfg, target)
1300+ disable_update_initramfs(cfg, target, machine)
1301
1302 # LP: #1742560 prevent zfs-dkms from being installed (Xenial)
1303 if distro.lsb_release(target=target)['codename'] == 'xenial':
1304@@ -1361,12 +1525,6 @@ def builtin_curthooks(cfg, target, state):
1305 description="enabling selinux autorelabel mode"):
1306 redhat_apply_selinux_autorelabel(target)
1307
1308- with events.ReportEventStack(
1309- name=stack_prefix + '/updating-initramfs-configuration',
1310- reporting_enabled=True, level="INFO",
1311- description="updating initramfs configuration"):
1312- redhat_update_initramfs(target, cfg)
1313-
1314 with events.ReportEventStack(
1315 name=stack_prefix + '/pollinate-user-agent',
1316 reporting_enabled=True, level="INFO",
1317@@ -1379,6 +1537,13 @@ def builtin_curthooks(cfg, target, state):
1318 if os.path.exists(zpool_cache):
1319 copy_zpool_cache(zpool_cache, target)
1320
1321+ zkey_repository = '/etc/zkey/repository'
1322+ zkey_used = os.path.join(os.path.split(state['fstab'])[0], "zkey_used")
1323+ if all(map(os.path.exists, [zkey_repository, zkey_used])):
1324+ distro.install_packages(['s390-tools-zkey'], target=target,
1325+ osfamily=osfamily)
1326+ copy_zkey_repository(zkey_repository, target)
1327+
1328 # If a crypttab file was created by block_meta than it needs to be
1329 # copied onto the target system, and update_initramfs() needs to be
1330 # run, so that the cryptsetup hooks are properly configured on the
1331@@ -1395,17 +1560,28 @@ def builtin_curthooks(cfg, target, state):
1332 if os.path.isdir(udev_rules_d):
1333 copy_dname_rules(udev_rules_d, target)
1334
1335+ with events.ReportEventStack(
1336+ name=stack_prefix + '/updating-initramfs-configuration',
1337+ reporting_enabled=True, level="INFO",
1338+ description="updating initramfs configuration"):
1339+ if osfamily == DISTROS.debian:
1340+ # re-enable update_initramfs
1341+ enable_update_initramfs(cfg, target, machine)
1342+ update_initramfs(target, all_kernels=True)
1343+ elif osfamily == DISTROS.redhat:
1344+ redhat_update_initramfs(target, cfg)
1345+
1346 # As a rule, ARMv7 systems don't use grub. This may change some
1347 # day, but for now, assume no. They do require the initramfs
1348 # to be updated, and this also triggers boot loader setup via
1349 # flash-kernel.
1350- machine = platform.machine()
1351 if (machine.startswith('armv7') or
1352 machine.startswith('s390x') or
1353 machine.startswith('aarch64') and not util.is_uefi_bootable()):
1354- update_initramfs(target)
1355- else:
1356- setup_grub(cfg, target, osfamily=osfamily)
1357+ return
1358+
1359+ # all other paths lead to grub
1360+ setup_grub(cfg, target, osfamily=osfamily)
1361
1362
1363 def curthooks(args):
1364diff --git a/curtin/commands/install.py b/curtin/commands/install.py
1365index ad17508..a3471f6 100644
1366--- a/curtin/commands/install.py
1367+++ b/curtin/commands/install.py
1368@@ -11,7 +11,7 @@ import subprocess
1369 import sys
1370 import tempfile
1371
1372-from curtin.block import iscsi
1373+from curtin.block import iscsi, zfs
1374 from curtin import config
1375 from curtin import distro
1376 from curtin import util
1377@@ -505,6 +505,10 @@ def cmd_install(args):
1378 if iscsi.get_iscsi_disks_from_config(cfg):
1379 iscsi.restart_iscsi_service()
1380
1381+ for pool in zfs.get_zpool_from_config(cfg):
1382+ LOG.debug('Exporting ZFS zpool %s', pool)
1383+ zfs.zpool_export(pool)
1384+
1385 shutil.rmtree(workingd.top)
1386
1387 apply_power_state(cfg.get('power_state'))
1388diff --git a/curtin/deps/__init__.py b/curtin/deps/__init__.py
1389index 37c4469..714ef18 100644
1390--- a/curtin/deps/__init__.py
1391+++ b/curtin/deps/__init__.py
1392@@ -25,7 +25,7 @@ REQUIRED_EXECUTABLES = [
1393 ('lvcreate', 'lvm2'),
1394 ('mdadm', 'mdadm'),
1395 ('mkfs.vfat', 'dosfstools'),
1396- ('mkfs.btrfs', 'btrfs-tools'),
1397+ ('mkfs.btrfs', '^btrfs-(progs|tools)$'),
1398 ('mkfs.ext4', 'e2fsprogs'),
1399 ('mkfs.xfs', 'xfsprogs'),
1400 ('partprobe', 'parted'),
1401diff --git a/curtin/distro.py b/curtin/distro.py
1402index 9714515..ed178bd 100644
1403--- a/curtin/distro.py
1404+++ b/curtin/distro.py
1405@@ -5,6 +5,7 @@ import os
1406 import re
1407 import shutil
1408 import tempfile
1409+import textwrap
1410
1411 from .paths import target_path
1412 from .util import (
1413@@ -284,7 +285,13 @@ def run_yum_command(mode, args=None, opts=None, env=None, target=None,
1414 if opts is None:
1415 opts = []
1416
1417- cmd = ['yum'] + defopts + opts + [mode] + args
1418+ # dnf is a drop in replacement for yum. On newer RH based systems yum
1419+ # is just a sym link to dnf.
1420+ if which('dnf', target=target):
1421+ cmd = ['dnf']
1422+ else:
1423+ cmd = ['yum']
1424+ cmd += defopts + opts + [mode] + args
1425 if not execute:
1426 return env, cmd
1427
1428@@ -311,8 +318,14 @@ def yum_install(mode, packages=None, opts=None, env=None, target=None,
1429 raise ValueError(
1430 'Unsupported mode "%s" for yum package install/upgrade' % mode)
1431
1432+ # dnf is a drop in replacement for yum. On newer RH based systems yum
1433+ # is just a sym link to dnf.
1434+ if which('dnf', target=target):
1435+ cmd = ['dnf']
1436+ else:
1437+ cmd = ['yum']
1438 # download first, then install/upgrade from cache
1439- cmd = ['yum'] + defopts + opts + [mode]
1440+ cmd += defopts + opts + [mode]
1441 dl_opts = ['--downloadonly', '--setopt=keepcache=1']
1442 inst_opts = ['--cacheonly']
1443
1444@@ -509,4 +522,14 @@ def get_package_version(pkg, target=None, semx=None):
1445 return None
1446
1447
1448+def fstab_header():
1449+ return textwrap.dedent("""\
1450+# /etc/fstab: static file system information.
1451+#
1452+# Use 'blkid' to print the universally unique identifier for a
1453+# device; this may be used with UUID= as a more robust way to name devices
1454+# that works even if disks are added and removed. See fstab(5).
1455+#
1456+# <file system> <mount point> <type> <options> <dump> <pass>""")
1457+
1458 # vi: ts=4 expandtab syntax=python
1459diff --git a/curtin/net/deps.py b/curtin/net/deps.py
1460index b98961d..cbebae9 100644
1461--- a/curtin/net/deps.py
1462+++ b/curtin/net/deps.py
1463@@ -47,11 +47,11 @@ def detect_required_packages_mapping(osfamily=DISTROS.debian):
1464 distro_mapping = {
1465 DISTROS.debian: {
1466 'bond': ['ifenslave'],
1467- 'bonds': [],
1468+ 'bonds': ['ifenslave'],
1469 'bridge': ['bridge-utils'],
1470- 'bridges': [],
1471+ 'bridges': ['bridge-utils'],
1472 'vlan': ['vlan'],
1473- 'vlans': []},
1474+ 'vlans': ['vlan']},
1475 DISTROS.redhat: {
1476 'bond': [],
1477 'bonds': [],
1478diff --git a/curtin/storage_config.py b/curtin/storage_config.py
1479index a79f30b..4244b3e 100644
1480--- a/curtin/storage_config.py
1481+++ b/curtin/storage_config.py
1482@@ -304,7 +304,8 @@ def merge_config_trees_to_list(config_trees):
1483 max_level = level
1484 item_cfg = tree[top_item_id]
1485 if top_item_id in reg:
1486- raise ValueError('Duplicate id: %s' % top_item_id)
1487+ LOG.warning('Dropping Duplicate id: %s' % top_item_id)
1488+ continue
1489 reg[top_item_id] = {'level': level, 'config': item_cfg}
1490
1491 def sort_level(configs):
1492@@ -388,15 +389,15 @@ class ProbertParser(object):
1493 data = {}
1494 self.class_data = data
1495 else:
1496- raise ValueError('probe_data missing %s data' %
1497- self.probe_data_key)
1498+ LOG.warning('probe_data missing %s data', self.probe_data_key)
1499+ self.class_data = {}
1500
1501 # We keep a reference to the blockdev_data on the superclass
1502 # as each specific parser has common needs to reference
1503 # this data separate from the BlockdevParser class.
1504- self.blockdev_data = self.probe_data.get('blockdev')
1505+ self.blockdev_data = self.probe_data.get('blockdev', {})
1506 if not self.blockdev_data:
1507- raise ValueError('probe_data missing valid "blockdev" data')
1508+ LOG.warning('probe_data missing valid "blockdev" data')
1509
1510 def parse(self):
1511 raise NotImplementedError()
1512@@ -420,14 +421,76 @@ class ProbertParser(object):
1513
1514 return None
1515
1516+ def is_mpath(self, blockdev):
1517+ if blockdev.get('DM_MULTIPATH_DEVICE_PATH') == "1":
1518+ return True
1519+
1520+ return bool('mpath-' in blockdev.get('DM_UUID', ''))
1521+
1522+ def get_mpath_name(self, blockdev):
1523+ mpath_data = self.probe_data.get('multipath')
1524+ if not mpath_data:
1525+ return None
1526+
1527+ bd_name = blockdev['DEVNAME']
1528+ if blockdev['DEVTYPE'] == 'partition':
1529+ bd_name = self.partition_parent_devname(blockdev)
1530+ bd_name = os.path.basename(bd_name)
1531+ for path in mpath_data['paths']:
1532+ if bd_name == path['device']:
1533+ rv = path['multipath']
1534+ return rv
1535+
1536+ def find_mpath_member(self, blockdev):
1537+ if blockdev.get('DM_MULTIPATH_DEVICE_PATH') == "1":
1538+ # find all other DM_MULTIPATH_DEVICE_PATH devs with same serial
1539+ serial = blockdev.get('ID_SERIAL')
1540+ members = sorted([os.path.basename(dev['DEVNAME'])
1541+ for dev in self.blockdev_data.values()
1542+ if dev.get("ID_SERIAL", "") == serial and
1543+ dev['DEVTYPE'] == blockdev['DEVTYPE']])
1544+ # [/dev/sda, /dev/sdb]
1545+ # [/dev/sda1, /dev/sda2, /dev/sdb1, /dev/sdb2]
1546+
1547+ else:
1548+ dm_mpath = blockdev.get('DM_MPATH')
1549+ dm_uuid = blockdev.get('DM_UUID')
1550+ dm_part = blockdev.get('DM_PART')
1551+
1552+ if dm_mpath:
1553+ multipath = dm_mpath
1554+ else:
1555+ # part1-mpath-30000000000000064
1556+ # mpath-30000000000000064
1557+ # mpath-36005076306ffd6b60000000000002406
1558+ match = re.search(r'mpath\-([a-zA-Z]*|\d*)+$', dm_uuid)
1559+ if not match:
1560+ LOG.debug('Failed to extract multipath ID pattern from '
1561+ 'DM_UUID value: "%s"', dm_uuid)
1562+ return None
1563+ # remove leading 'mpath-'
1564+ multipath = match.group(0)[6:]
1565+ mpath_data = self.probe_data.get('multipath')
1566+ if not mpath_data:
1567+ return None
1568+ members = sorted([path['device'] for path in mpath_data['paths']
1569+ if path['multipath'] == multipath])
1570+
1571+ # append partition number if present
1572+ if dm_part:
1573+ members = [member + dm_part for member in members]
1574+
1575+ if len(members):
1576+ return members[0]
1577+
1578+ return None
1579+
1580 def blockdev_to_id(self, blockdev):
1581 """ Examine a blockdev dictionary and return a tuple of curtin
1582 storage type and name that can be used as a value for
1583 storage_config ids (opaque reference to other storage_config
1584 elements).
1585 """
1586- def is_mpath(blockdev):
1587- return bool(blockdev.get('DM_UUID', '').startswith('mpath-'))
1588
1589 def is_dmcrypt(blockdev):
1590 return bool(blockdev.get('DM_UUID', '').startswith('CRYPT-LUKS'))
1591@@ -441,8 +504,15 @@ class ProbertParser(object):
1592 if 'DM_LV_NAME' in blockdev:
1593 devtype = 'lvm-partition'
1594 name = blockdev['DM_LV_NAME']
1595- elif is_mpath(blockdev):
1596- name = blockdev['DM_UUID']
1597+ elif self.is_mpath(blockdev):
1598+ # extract a multipath member device
1599+ member = self.find_mpath_member(blockdev)
1600+ if member:
1601+ name = member
1602+ else:
1603+ name = blockdev['DM_UUID']
1604+ if 'DM_PART' in blockdev:
1605+ devtype = 'partition'
1606 elif is_dmcrypt(blockdev):
1607 devtype = 'dmcrypt'
1608 name = blockdev['DM_NAME']
1609@@ -505,7 +575,8 @@ class BcacheParser(ProbertParser):
1610 msg = ('Invalid "blockdev" value for cache device '
1611 'uuid=%s' % cset_uuid)
1612 if not cset_uuid:
1613- raise ValueError(msg)
1614+ LOG.warning(msg)
1615+ return None
1616
1617 for devuuid, config in cache_data.items():
1618 cache = _sb_get(config, 'cset.uuid')
1619@@ -518,6 +589,8 @@ class BcacheParser(ProbertParser):
1620 by_uuid = '/dev/bcache/by-uuid/' + uuid
1621 label = _sb_get(backing_data, 'dev.label')
1622 for devname, data in blockdev_data.items():
1623+ if not devname:
1624+ continue
1625 if devname.startswith('/dev/bcache'):
1626 # DEVLINKS is a space separated list
1627 devlinks = data.get('DEVLINKS', '').split()
1628@@ -525,7 +598,7 @@ class BcacheParser(ProbertParser):
1629 return devname
1630 if label:
1631 return label
1632- raise ValueError('Failed to find bcache %s ' % (by_uuid))
1633+ LOG.warning('Failed to find bcache %s ' % (by_uuid))
1634
1635 def _cache_mode(dev_data):
1636 # "1 [writeback]" -> "writeback"
1637@@ -535,12 +608,14 @@ class BcacheParser(ProbertParser):
1638
1639 return None
1640
1641+ if not self.blockdev_data:
1642+ return None
1643+
1644 backing_device = backing_data.get('blockdev')
1645 cache_device = _find_cache_device(backing_data, self.caching)
1646 cache_mode = _cache_mode(backing_data)
1647- bcache_name = os.path.basename(
1648- _find_bcache_devname(backing_uuid, backing_data,
1649- self.blockdev_data))
1650+ bcache_name = os.path.basename(_find_bcache_devname(backing_uuid,
1651+ backing_data, self.blockdev_data))
1652 bcache_entry = {'type': 'bcache', 'id': 'disk-%s' % bcache_name,
1653 'name': bcache_name}
1654
1655@@ -571,9 +646,10 @@ class BlockdevParser(ProbertParser):
1656 errors = []
1657
1658 for devname, data in self.blockdev_data.items():
1659- # skip composed devices here
1660+ # skip composed devices here, except partitions
1661 if data.get('DEVPATH', '').startswith('/devices/virtual'):
1662- continue
1663+ if data.get('DEVTYPE', '') != "partition":
1664+ continue
1665 entry = self.asdict(data)
1666 if entry:
1667 try:
1668@@ -660,6 +736,9 @@ class BlockdevParser(ProbertParser):
1669 'type': blockdev_data['DEVTYPE'],
1670 'id': self.blockdev_to_id(blockdev_data),
1671 }
1672+ if blockdev_data.get('DM_MULTIPATH_DEVICE_PATH') == "1":
1673+ mpath_name = self.get_mpath_name(blockdev_data)
1674+ entry['multipath'] = mpath_name
1675
1676 # default disks to gpt
1677 if entry['type'] == 'disk':
1678@@ -669,8 +748,12 @@ class BlockdevParser(ProbertParser):
1679 # set wwn, serial, and path
1680 entry.update(uniq_ids)
1681
1682- # default to gpt if not present
1683- entry['ptable'] = blockdev_data.get('ID_PART_TABLE_TYPE', 'gpt')
1684+ if 'ID_PART_TABLE_TYPE' in blockdev_data:
1685+ ptype = blockdev_data['ID_PART_TABLE_TYPE']
1686+ if ptype in schemas._ptables:
1687+ entry['ptable'] = ptype
1688+ else:
1689+ entry['ptable'] = schemas._ptable_unsupported
1690 return entry
1691
1692 if entry['type'] == 'partition':
1693@@ -683,7 +766,8 @@ class BlockdevParser(ProbertParser):
1694 part = None
1695 for pentry in ptable['partitions']:
1696 node = pentry['node']
1697- if node.lstrip(parent_devname) == attrs['partition']:
1698+ node_p = node.replace(parent_devname, '')
1699+ if node_p.replace('p', '') == attrs['partition']:
1700 part = pentry
1701 break
1702
1703@@ -919,7 +1003,6 @@ class RaidParser(ProbertParser):
1704 Collects storage config type: raid for valid
1705 data and returns tuple of lists, configs, errors.
1706 """
1707-
1708 configs = []
1709 errors = []
1710 for devname, data in self.class_data.items():
1711@@ -1103,7 +1186,7 @@ class ZfsParser(ProbertParser):
1712 return (zpool_configs + zfs_configs, errors)
1713
1714
1715-def extract_storage_config(probe_data):
1716+def extract_storage_config(probe_data, strict=False):
1717 """ Examine a probert storage dictionary and extract a curtin
1718 storage configuration that would recreate all of the
1719 storage devices present in the provided data.
1720@@ -1155,7 +1238,10 @@ def extract_storage_config(probe_data):
1721 for e in errors:
1722 LOG.exception('Validation error: %s\n' % e)
1723 if len(errors) > 0:
1724- raise RuntimeError("Extract storage config does not validate.")
1725+ errmsg = "Extract storage config does not validate."
1726+ LOG.warning(errmsg)
1727+ if strict:
1728+ raise RuntimeError(errmsg)
1729
1730 # build and merge probed data into a valid storage config by
1731 # generating a config tree for each item in the probed data
1732diff --git a/curtin/udev.py b/curtin/udev.py
1733index 106a7e7..e2e3dd0 100644
1734--- a/curtin/udev.py
1735+++ b/curtin/udev.py
1736@@ -1,6 +1,8 @@
1737 # This file is part of curtin. See LICENSE file for copyright and license info.
1738
1739+import shlex
1740 import os
1741+
1742 from curtin import util
1743 from curtin.log import logged_call
1744
1745@@ -73,7 +75,7 @@ def udevadm_info(path=None):
1746 if not path:
1747 raise ValueError('Invalid path: "%s"' % path)
1748
1749- info_cmd = ['udevadm', 'info', '--query=property', path]
1750+ info_cmd = ['udevadm', 'info', '--query=property', '--export', path]
1751 output, _ = util.subp(info_cmd, capture=True)
1752
1753 # strip for trailing empty line
1754@@ -87,13 +89,19 @@ def udevadm_info(path=None):
1755 if not value:
1756 value = None
1757 if value:
1758- # devlinks is a list of paths separated by space
1759- # convert to a list for easy use
1760- if key == 'DEVLINKS':
1761- info[key] = value.split()
1762+ # preserve spaces in values to match udev database
1763+ parsed = shlex.split(value)
1764+ if ' ' not in value:
1765+ info[key] = parsed[0]
1766 else:
1767- # preserve spaces in values, to match udev database
1768- info[key] = value
1769+ # special case some known entries with spaces, e.g. ID_SERIAL
1770+ # and DEVLINKS, see tests/unittests/test_udev.py
1771+ if key == "DEVLINKS":
1772+ info[key] = shlex.split(parsed[0])
1773+ elif key == 'ID_SERIAL':
1774+ info[key] = parsed[0]
1775+ else:
1776+ info[key] = parsed
1777
1778 return info
1779
1780diff --git a/curtin/util.py b/curtin/util.py
1781index 1ef17c0..eb2228f 100644
1782--- a/curtin/util.py
1783+++ b/curtin/util.py
1784@@ -632,12 +632,13 @@ class ChrootableTarget(object):
1785 if mounts is not None:
1786 self.mounts = mounts
1787 else:
1788- self.mounts = ["/dev", "/proc", "/sys"]
1789+ self.mounts = ["/dev", "/proc", "/run", "/sys"]
1790 self.umounts = []
1791 self.disabled_daemons = False
1792 self.allow_daemons = allow_daemons
1793 self.sys_resolvconf = sys_resolvconf
1794 self.rconf_d = None
1795+ self.rc_tmp = None
1796
1797 def __enter__(self):
1798 for p in self.mounts:
1799@@ -656,14 +657,20 @@ class ChrootableTarget(object):
1800 rtd = None
1801 try:
1802 rtd = tempfile.mkdtemp(dir=target_etc)
1803- tmp = os.path.join(rtd, "resolv.conf")
1804- os.rename(rconf, tmp)
1805+ if os.path.lexists(rconf):
1806+ self.rc_tmp = os.path.join(rtd, "resolv.conf")
1807+ os.rename(rconf, self.rc_tmp)
1808 self.rconf_d = rtd
1809 shutil.copy("/etc/resolv.conf", rconf)
1810 except Exception:
1811 if rtd:
1812+ # if we renamed, but failed later we need to restore
1813+ if self.rc_tmp and os.path.lexists(self.rc_tmp):
1814+ os.rename(os.path.join(self.rconf_d, "resolv.conf"),
1815+ rconf)
1816 shutil.rmtree(rtd)
1817 self.rconf_d = None
1818+ self.rc_tmp = None
1819 raise
1820
1821 return self
1822@@ -681,7 +688,8 @@ class ChrootableTarget(object):
1823
1824 rconf = paths.target_path(self.target, "/etc/resolv.conf")
1825 if self.sys_resolvconf and self.rconf_d:
1826- os.rename(os.path.join(self.rconf_d, "resolv.conf"), rconf)
1827+ if self.rc_tmp and os.path.lexists(self.rc_tmp):
1828+ os.rename(os.path.join(self.rconf_d, "resolv.conf"), rconf)
1829 shutil.rmtree(self.rconf_d)
1830
1831 def subp(self, *args, **kwargs):
1832@@ -898,8 +906,9 @@ def sanitize_source(source):
1833 if type(source) is dict:
1834 # already sanitized?
1835 return source
1836- supported = ['tgz', 'dd-tgz', 'dd-tbz', 'dd-txz', 'dd-tar', 'dd-bz2',
1837- 'dd-gz', 'dd-xz', 'dd-raw', 'fsimage', 'fsimage-layered']
1838+ supported = ['tgz', 'dd-tgz', 'tbz', 'dd-tbz', 'txz', 'dd-txz', 'dd-tar',
1839+ 'dd-bz2', 'dd-gz', 'dd-xz', 'dd-raw', 'fsimage',
1840+ 'fsimage-layered']
1841 deftype = 'tgz'
1842 for i in supported:
1843 prefix = i + ":"
1844@@ -907,8 +916,14 @@ def sanitize_source(source):
1845 return {'type': i, 'uri': source[len(prefix):]}
1846
1847 # translate squashfs: to fsimage type.
1848- if source.startswith("squashfs:"):
1849- return {'type': 'fsimage', 'uri': source[len("squashfs:")]}
1850+ if source.startswith("squashfs://"):
1851+ return {'type': 'fsimage', 'uri': source[len("squashfs://"):]}
1852+
1853+ elif source.startswith("squashfs:"):
1854+ LOG.warning("The squashfs: prefix is deprecated and"
1855+ "will be removed in a future release."
1856+ "Please use squashfs:// instead.")
1857+ return {'type': 'fsimage', 'uri': source[len("squashfs:"):]}
1858
1859 if source.endswith("squashfs") or source.endswith("squash"):
1860 return {'type': 'fsimage', 'uri': source}
1861diff --git a/debian/changelog b/debian/changelog
1862index 92272dc..dd9a490 100644
1863--- a/debian/changelog
1864+++ b/debian/changelog
1865@@ -1,3 +1,84 @@
1866+curtin (19.3-17-g50ffca46-0ubuntu1~16.04.1) xenial; urgency=medium
1867+
1868+ * New upstream snapshot. (LP: #1861452)
1869+ - clear-holders: ensure we wipe device even if multipath enabled not not mp
1870+ - block_meta: use reliable fs_spec entries for block devices
1871+ - multipath: handle removal of multipath partitions correctly
1872+ - vmtests: skip Focal deploying Centos70 ScsiBasic
1873+ - vmtests: fix network mtu tests, separating ifupdown vs networkd
1874+ - doc: Fix kexec documentation bug. [Mike Pontillo]
1875+ - vmtests: Add Focal Fossa
1876+ - centos: Add centos/rhel 8 support, enable UEFI Secure Boot [Lee Trager]
1877+ - Bump XFS /boot skip-by date out a while
1878+ - vmtest: Fix a missing unset of OUTPUT_FSTAB
1879+ - curthooks: handle s390x/aarch64 kernel install hooks
1880+ - clear-holders: handle arbitrary order of devices to clear
1881+ - curthooks: only run update-initramfs in target once
1882+ - test_network_mtu: bump fixby date for MTU tests
1883+ - block-discover: don't skip partitions on virtual devices
1884+ - block-discover: handle partial probe data
1885+ - Fix parsing of squashfs: uri prefix for installation sources [Daniel Fox]
1886+ - Release 19.3
1887+ - Update HACKING.rst with Josh Powers contact info.
1888+ - t/jenkins-runner: replace $EPOCHSECONDS with 'date +%s' [Paride Legovini]
1889+ - curthooks: skip setup_kernel_img_conf on eoan and newer
1890+ - block_meta: use lookup for wwn, fix fallback from wwn, serial, path
1891+ - vmtest: Adjust TestScsiBasic to use dnames to find correct disk
1892+ - schema: Add ptable value 'unsupported'
1893+ - tools/xkvm: add -nographic to speed up devopt query
1894+ - test_block_dasd: fix random_device_id to only generate valid IDs
1895+ - vmtest: update skip_if_arch message
1896+ - Add skip_by_date to eoan ipv6 vlan test
1897+ - storage_config: interpret value, not presence, of
1898+ DM_MULTIPATH_DEVICE_PATH [Michael Hudson-Doyle]
1899+ - vmtest: Add skip_by_date for test_ip_output on eoan + vlans
1900+ - block-schema: update raid schema for preserve and metadata
1901+ - dasd: update partition table value to 'vtoc'
1902+ - clear-holders: increase the level for devices with holders by one
1903+ - tests: mock timestamp used in collect-log file creation
1904+ - ChrootableTarget: mount /run to resolve lvm/mdadm issues which
1905+ require it.
1906+ - block-discover: handle multipath disks
1907+ - Handle partial raid on partitions
1908+ - install: export zpools if present in the storage-config
1909+ - block-schema: allow 'mac' as partition table type
1910+ - jenkins-runner: disable the lockfile timeout by default [Paride Legovini]
1911+ - curthooks: use correct grub-efi package name on i386
1912+ - vmtest-sync-images: remove unused imports [Paride Legovini]
1913+ - vmtests: use file locking on the images [Paride Legovini]
1914+ - vmtest: enable arm64 [Paride Legovini]
1915+ - Make the vmtests/test_basic test suite run on ppc64el [Paride Legovini]
1916+ - vmtests: separate arch and target_arch in tests [Paride Legovini]
1917+ - vmtests: new decorator: skip_if_arch [Paride Legovini]
1918+ - vmtests: increase the VM memory for Bionic
1919+ - vmtests: Skip Eoan ZFS Root tests until bug fix is complete
1920+ - util: add support for 'tbz', 'txz' tar format types to sanitize_source
1921+ - net: ensure eni helper tools install if given netplan config
1922+ - d/control: update Depends for new probert package names
1923+ [Dimitri John Ledkov]
1924+ - vmtest: fix typo in EoanBcacheBasic test name
1925+ - storage schema: Update nvme wwn regex to allow for nvme wwid format
1926+ - Allow EUI-64 formatted WWNs for disks and accept NVMe partition naming
1927+ [Reed Slaby]
1928+ - Makefile: split Python 2 and Python 3 unittest targets apart
1929+ - Switch to the new btrfs-progs package name, with btrfs-tools fallback.
1930+ [Dimitri John Ledkov]
1931+ - vmtest: bump ram size on Zfs tests for Disco and Eoan
1932+ - vmtest: drop Cosmic test cases
1933+ - unittests: mock subp in ChrootableTarget _exit_ test cases
1934+ - util.ChrootableTarget: skip rename of resolv.conf if not present
1935+ in target
1936+ - Release 19.2
1937+ - storage_config: a missing ID_PART_TABLE_TYPE key means no ptable
1938+ [Michael Hudson-Doyle]
1939+ - vmtests: drop skip_by_date decorators for bug 1813228
1940+ - block: Add opportunistic zkey encryption if supported
1941+ - vmtests: add support for CURTIN_VMTEST_APT_PROXY
1942+ - vmtests: add use of CURTIN_VMTEST_ prefixed envvars to image sync
1943+ - vmtest: dont raise SkipTest in class definition
1944+
1945+ -- Ryan Harper <ryan.harper@canonical.com> Thu, 30 Jan 2020 13:55:17 -0600
1946+
1947 curtin (19.1-7-g37a7a0f4-0ubuntu1~16.04.1) xenial; urgency=medium
1948
1949 * d/control: drop probert dependency for Xenial (LP: #1831757)
1950diff --git a/debian/control b/debian/control
1951index 305bbd7..97e496a 100644
1952--- a/debian/control
1953+++ b/debian/control
1954@@ -29,7 +29,7 @@ Package: curtin
1955 Architecture: all
1956 Priority: extra
1957 Depends: bcache-tools,
1958- btrfs-tools,
1959+ btrfs-progs | btrfs-tools,
1960 dosfstools,
1961 file,
1962 gdisk,
1963diff --git a/doc/topics/config.rst b/doc/topics/config.rst
1964index b7cca43..59e71f3 100644
1965--- a/doc/topics/config.rst
1966+++ b/doc/topics/config.rst
1967@@ -393,7 +393,8 @@ Enable rebooting with kexec.
1968
1969 **Example**::
1970
1971- kexec: on
1972+ kexec:
1973+ mode: "on"
1974
1975
1976 multipath
1977@@ -555,6 +556,7 @@ configures the method used to copy the image to the target system.
1978 - **dd-**: Use ``dd`` command to write image to target.
1979 - **cp://**: Use ``rsync`` command to copy source directory to target.
1980 - **file://**: Use ``tar`` command to extract source to target.
1981+- **squashfs://**: Mount squashfs image and copy contents to target.
1982 - **http[s]://**: Use ``wget | tar`` commands to extract source to target.
1983 - **fsimage://** mount filesystem image and copy contents to target.
1984 Local file or url are supported. Filesystem can be any filesystem type
1985@@ -649,6 +651,9 @@ This results in Curtin downloading the following URLs::
1986 sources:
1987 - cp:///
1988
1989+**Example squashfs from NFS mount**::
1990+ sources:
1991+ - squashfs:///media/filesystem.squashfs
1992
1993 **Example Copy from local tarball**::
1994
1995diff --git a/doc/topics/integration-testing.rst b/doc/topics/integration-testing.rst
1996index 0a66421..35a5bee 100644
1997--- a/doc/topics/integration-testing.rst
1998+++ b/doc/topics/integration-testing.rst
1999@@ -124,12 +124,19 @@ Environment Variables
2000
2001 Some environment variables affect the running of vmtest
2002
2003-- ``apt_proxy``:
2004+- ``CURTIN_VMTEST_APT_PROXY``:
2005
2006- test will set apt: { proxy } in the guests to the value of ``apt_proxy``
2007- environment variable. If that is not set it will look at the host's apt
2008- config and read ``Acquire::HTTP::Proxy``. This can be prevented by
2009- setting ``apt_proxy`` to the empty string; in this case no proxy is used.
2010+ test will set apt: { proxy } in the guests to the value of
2011+ ``CURTIN_VMTEST_APT_PROXY`` environment variable. If that is not
2012+ set it will look at the host's apt config and read
2013+ ``Acquire::HTTP::Proxy``. This can be prevented by setting
2014+ ``CURTIN_VMTEST_APT_PROXY`` to the empty string; in this case no
2015+ proxy is used.
2016+
2017+ .. note::
2018+ For compatibility, the ``apt_proxy`` environment variable is
2019+ supported, with the same behaviour as described above. If both
2020+ are present, ``CURTIN_VMTEST_APT_PROXY`` will be preferred.
2021
2022 - ``CURTIN_VMTEST_CURTIN_EXE``: Defaults to ''
2023
2024@@ -226,7 +233,7 @@ Some environment variables affect the running of vmtest
2025 Allow environment to override the number of virtual cpus to allocate
2026 in the target virtual machines.
2027
2028-- ``IMAGE_DIR``: default /srv/images
2029+- ``CURTIN_VMTEST_IMAGE_DIR``: default /srv/images
2030
2031 Vmtest keeps a mirror of maas ephemeral images in this directory.
2032
2033diff --git a/doc/topics/storage.rst b/doc/topics/storage.rst
2034index a1346e9..c85174d 100644
2035--- a/doc/topics/storage.rst
2036+++ b/doc/topics/storage.rst
2037@@ -110,7 +110,7 @@ These ``label`` values are reserved and cannot be used:
2038 **disk_layout**: *cdl, ldl*
2039
2040 The default ``disk_layout`` value is ``cdl``, the compaible disk layout which
2041-allows for up to 3 partitions and a MBR. The ``ldl``, Linux layout has only
2042+allows for up to 3 partitions and a VTOC. The ``ldl``, Linux layout has only
2043 one partition.
2044
2045
2046@@ -125,7 +125,7 @@ one partition.
2047 mode: full
2048 - id: disk0
2049 type: disk
2050- ptable: mbr
2051+ ptable: vtoc
2052 serial: 0X1520
2053 name: root_disk
2054 wipe: superblock
2055@@ -278,6 +278,14 @@ and determine which device to use as a boot disk. In cases where the boot
2056 device is on a special volume, such as a RAID array or a LVM Logical Volume,
2057 it may be necessary to specify the device that will hold the grub bootloader.
2058
2059+**multipath**: *<multipath name or serial>*
2060+
2061+If a disk is a path in a multipath device, it may be included in the
2062+configuration dictionary. Currently the value is informational only.
2063+Curtin already detects whether disks are part of a multipath and selects
2064+one member path to operate upon.
2065+
2066+
2067 **Config Example**::
2068
2069 - id: disk0
2070@@ -374,6 +382,13 @@ For partitions, the udev rule created relies upon disk contents, in this case
2071 the partition entry UUID. This will remain in effect unless the underlying disk
2072 on which the partition resides has the partition table modified or wiped.
2073
2074+**multipath**: *<multipath name or serial>*
2075+
2076+If a partition is found on a multipath device, it may be included in the
2077+configuration dictionary. Currently the value is informational only.
2078+Curtin already detects whether partitions are part of a multipath and selects
2079+one member path to operate upon.
2080+
2081
2082 **Config Example**::
2083
2084diff --git a/examples/tests/basic-dasd.yaml b/examples/tests/basic-dasd.yaml
2085index 62efbf0..3b0ba0d 100644
2086--- a/examples/tests/basic-dasd.yaml
2087+++ b/examples/tests/basic-dasd.yaml
2088@@ -11,14 +11,14 @@ storage:
2089 - id: sda
2090 type: disk
2091 wipe: superblock
2092- ptable: msdos
2093+ ptable: vtoc
2094 model: QEMU HARDDISK
2095 serial: disk-a
2096 grub_device: true
2097 - id: dasdb
2098 type: disk
2099 wipe: superblock
2100- ptable: msdos
2101+ ptable: vtoc
2102 serial: 0X1520
2103 wipe: superblock
2104 - id: sda-part1
2105diff --git a/examples/tests/bcache-ceph-nvme-simple.yaml b/examples/tests/bcache-ceph-nvme-simple.yaml
2106new file mode 100644
2107index 0000000..83cb04c
2108--- /dev/null
2109+++ b/examples/tests/bcache-ceph-nvme-simple.yaml
2110@@ -0,0 +1,107 @@
2111+storage:
2112+ config:
2113+ - grub_device: true
2114+ id: sda
2115+ model: MM1000GBKAL
2116+ name: sda
2117+ ptable: gpt
2118+ serial: disk-a
2119+ type: disk
2120+ wipe: superblock
2121+ - id: sdb
2122+ model: MM1000GBKAL
2123+ name: sdb
2124+ serial: disk-b
2125+ type: disk
2126+ wipe: superblock
2127+ - id: nvme0n1
2128+ model: INTEL SSDPEDME400G4
2129+ name: nvme0n1
2130+ serial: nvme-CVMD552400
2131+ type: disk
2132+ wipe: superblock
2133+ - backing_device: sdb
2134+ cache_device: nvme0n1
2135+ cache_mode: writeback
2136+ id: bcache1
2137+ name: bcache1
2138+ type: bcache
2139+ - device: sda
2140+ id: sda-part1
2141+ name: sda-part1
2142+ number: 1
2143+ offset: 4194304B
2144+ size: 5G
2145+ type: partition
2146+ uuid: 1e27e7af-26dc-4af4-9ef5-aea928204997
2147+ wipe: superblock
2148+ - device: sda
2149+ id: sda-part2
2150+ name: sda-part2
2151+ number: 2
2152+ size: 2G
2153+ type: partition
2154+ uuid: 0040d622-41f1-4596-842f-82d731ba9054
2155+ wipe: superblock
2156+ - device: sda
2157+ id: sda-part3
2158+ name: sda-part3
2159+ number: 3
2160+ size: 2G
2161+ type: partition
2162+ uuid: cb59d827-662c-4da6-b1ef-7967218bd0db
2163+ wipe: superblock
2164+ - backing_device: sda-part3
2165+ cache_device: nvme0n1
2166+ cache_mode: writeback
2167+ id: bcache0
2168+ name: bcache0
2169+ type: bcache
2170+ - fstype: fat32
2171+ id: sda-part1_format
2172+ label: efi
2173+ type: format
2174+ uuid: 27638478-d881-43e5-a93c-1cac7aa60daa
2175+ volume: sda-part1
2176+ - fstype: ext4
2177+ id: sda-part2_format
2178+ label: boot
2179+ type: format
2180+ uuid: cfd11d4f-d77f-4307-b372-b52e81c873f7
2181+ volume: sda-part2
2182+ - fstype: ext4
2183+ id: bcache0_format
2184+ label: root
2185+ type: format
2186+ uuid: 63247841-195c-4939-83e4-cb834d61f95f
2187+ volume: bcache0
2188+ - devices:
2189+ - bcache1
2190+ id: ceph-bcache-vg
2191+ name: ceph-bcache-vg
2192+ type: lvm_volgroup
2193+ - id: ceph-bcache-lv-0
2194+ name: ceph-bcache-lv-0
2195+ size: 3G
2196+ type: lvm_partition
2197+ volgroup: ceph-bcache-vg
2198+ - fstype: xfs
2199+ id: ceph-bcache-lv-0_format
2200+ volume: ceph-bcache-lv-0
2201+ type: format
2202+ - device: bcache0_format
2203+ id: bcache0_mount
2204+ options: ''
2205+ path: /
2206+ type: mount
2207+ - device: sda-part2_format
2208+ id: sda-part2_mount
2209+ options: ''
2210+ path: /boot
2211+ type: mount
2212+ - device: sda-part1_format
2213+ id: sda-part1_mount
2214+ options: ''
2215+ path: /boot/efi
2216+ type: mount
2217+ version: 1
2218diff --git a/examples/tests/bcache-wipe-xfs.yaml b/examples/tests/bcache-wipe-xfs.yaml
2219index 558d442..1a181a8 100644
2220--- a/examples/tests/bcache-wipe-xfs.yaml
2221+++ b/examples/tests/bcache-wipe-xfs.yaml
2222@@ -6,9 +6,10 @@ early_commands:
2223 # would be seen as soon as the disk was partitioned and cause problems
2224 # for curtin's use of the disk.
2225 # This config recreates issue LP: #1718699
2226- 00_blockmeta: [env, -u, OUTPUT_FSTAB, TARGET_MOUNT_POINT=/tmp/my.bdir/target,
2227- WORKING_DIR=/tmp/my.bdir/work.d, curtin, --showtrace, -v,
2228- block-meta, --umount, custom]
2229+ 00_blockmeta: [env, OUTPUT_FSTAB=/tmp/my.bdir/state/fstab,
2230+ TARGET_MOUNT_POINT=/tmp/my.bdir/target,
2231+ WORKING_DIR=/tmp/my.bdir/work.d, curtin, --showtrace, -v,
2232+ block-meta, --umount, custom]
2233 01_clear_holders: [curtin, clear-holders, --preserve, /dev/disk/by-id/virtio-disk-b]
2234 02_quick_erase: [curtin, block-wipe, --mode, superblock, /dev/disk/by-id/virtio-disk-b]
2235
2236diff --git a/examples/tests/bridging_network_v2.yaml b/examples/tests/bridging_network_v2.yaml
2237new file mode 100644
2238index 0000000..b208972
2239--- /dev/null
2240+++ b/examples/tests/bridging_network_v2.yaml
2241@@ -0,0 +1,38 @@
2242+# v2 equivalent of examples/tests/bridging_network.yaml
2243+showtrace: true
2244+network:
2245+ version: 2
2246+ ethernets:
2247+ eth0:
2248+ dhcp4: true
2249+ match:
2250+ macaddress: '52:54:00:12:34:00'
2251+ set-name: eth0
2252+ eth1:
2253+ match:
2254+ macaddress: '52:54:00:12:34:02'
2255+ set-name: eth1
2256+ eth2:
2257+ match:
2258+ macaddress: '52:54:00:12:34:04'
2259+ set-name: eth2
2260+ bridges:
2261+ br0:
2262+ addresses:
2263+ - 192.168.14.2/24
2264+ interfaces:
2265+ - eth1
2266+ - eth2
2267+ parameters:
2268+ ageing-time: 250
2269+ forward-delay: 1
2270+ hello-time: 1
2271+ max-age: 10
2272+ path-cost:
2273+ eth1: 50
2274+ eth2: 75
2275+ port-priority:
2276+ eth1: 28
2277+ eth2: 14
2278+ priority: 22
2279+ stp: false
2280diff --git a/examples/tests/dirty_disks_config.yaml b/examples/tests/dirty_disks_config.yaml
2281index bcf3fbc..2f86f9e 100644
2282--- a/examples/tests/dirty_disks_config.yaml
2283+++ b/examples/tests/dirty_disks_config.yaml
2284@@ -22,6 +22,13 @@ bucket:
2285 done
2286 swapon --show
2287 exit 0
2288+ - &multipath_on |
2289+ #!/bin/sh
2290+ if command -v multipath; then
2291+ multipath -ll
2292+ multipath -r
2293+ multipath -ll
2294+ fi
2295 - &zpool_export |
2296 #!/bin/sh
2297 # disable any rpools to trigger disks with zfs_member label but inactive
2298@@ -75,11 +82,12 @@ early_commands:
2299 # the disks exactly as in this config before the rest of the install
2300 # will just blow it all away. We have clean out other environment
2301 # that could unintentionally mess things up.
2302- 01-blockmeta: [env, -u, OUTPUT_FSTAB,
2303+ 01-blockmeta: [env, OUTPUT_FSTAB=/tmp/my.bdir/state/fstab,
2304 TARGET_MOUNT_POINT=/tmp/my.bdir/target,
2305 WORKING_DIR=/tmp/my.bdir/work.d,
2306 curtin, --showtrace, -v, block-meta, --umount, custom]
2307 02-enable_swaps: [sh, -c, *swapon]
2308+ 02-multipath_on: [sh, -c, *multipath_on]
2309 03-disable_rpool: [sh, -c, *zpool_export]
2310 04-lvm_stop: [sh, -c, *lvm_stop]
2311 05-mdadm_stop: [sh, -c, *mdadm_stop]
2312diff --git a/examples/tests/network_mtu_networkd.yaml b/examples/tests/network_mtu_networkd.yaml
2313new file mode 100644
2314index 0000000..e2ac444
2315--- /dev/null
2316+++ b/examples/tests/network_mtu_networkd.yaml
2317@@ -0,0 +1,93 @@
2318+showtrace: true
2319+network:
2320+ version: 1
2321+ config:
2322+ - type: physical
2323+ name: interface0
2324+ mac_address: "52:54:00:12:34:00"
2325+ subnets:
2326+ - type: static
2327+ address: 192.168.1.2/24
2328+ mtu: 1601
2329+ - type: static
2330+ address: 2001:4800:78ff:1b:be76:4eff:fe06:1000
2331+ netmask: 'ffff:ffff:ffff:ffff::'
2332+ mtu: 1501
2333+ - type: physical
2334+ name: interface1
2335+ mac_address: "52:54:00:12:34:02"
2336+ subnets:
2337+ - type: static
2338+ address: 192.168.2.2/24
2339+ mtu: 1501
2340+ - type: static
2341+ address: 2001:4800:78ff:1b:be76:4eff:fe06:2000
2342+ netmask: 'ffff:ffff:ffff:ffff::'
2343+ mtu: 1501
2344+ - type: physical
2345+ name: interface2
2346+ mac_address: "52:54:00:12:34:04"
2347+ subnets:
2348+ - type: dhcp
2349+ - type: physical
2350+ name: interface4
2351+ mac_address: "52:54:00:12:34:08"
2352+ subnets:
2353+ - type: static
2354+ address: 2001:4800:78ff:1b:be76:4eff:fe06:5000
2355+ netmask: 'ffff:ffff:ffff:ffff::'
2356+ mtu: 8900
2357+ - type: static
2358+ address: 192.168.5.2/24
2359+ mtu: 9000
2360+ - type: physical
2361+ name: interface5
2362+ mac_address: "52:54:00:12:34:0c"
2363+ subnets:
2364+ - type: static
2365+ address: 2001:4800:78ff:1b:be76:4eff:fe06:6000
2366+ netmask: 'ffff:ffff:ffff:ffff::'
2367+ mtu: 1480
2368+ - type: static
2369+ address: 192.168.6.2/24
2370+ mtu: 1480
2371+
2372+write_files:
2373+ capture_mtu:
2374+ path: '/usr/local/bin/capture-mtu'
2375+ permissions: '0755'
2376+ owner: 'root:root'
2377+ content: |
2378+ #!/bin/sh
2379+ for x in 0 1 2 4 5; do
2380+ dev_mtu=$(cat /sys/class/net/interface${x}/mtu)
2381+ ipv6_mtu=$(cat /proc/sys/net/ipv6/conf/interface${x}/mtu)
2382+ sys=$(sysctl net.ipv6.conf.interface${x}.mtu)
2383+ echo "WARK: interface${x} dev_mtu=$dev_mtu proc_ipv6_mtu=$ipv6_mtu"
2384+ echo "WARK: $sys"
2385+ done
2386+
2387+bucket:
2388+ - &networkd_debug |
2389+ #!/bin/sh
2390+ MP=$TARGET_MOUNT_POINT
2391+ mkdir -p "${MP}/etc/systemd/system/systemd-networkd.service.d"
2392+ conf="${MP}/etc/systemd/system/systemd-networkd.service.d/10-debug.conf"
2393+ echo "[Service]" >$conf
2394+ echo "Environment=SYSTEMD_LOG_LEVEL=debug" >>$conf
2395+ echo "ExecStartPost=/usr/local/bin/capture-mtu" >>$conf
2396+ exit 0
2397+
2398+ - &capture_mtu |
2399+ #!/bin/sh -x
2400+ MP=$TARGET_MOUNT_POINT
2401+ for service in cloud-init-local cloud-init cloud-config; do
2402+ mkdir -p "${MP}/etc/systemd/system/${service}.service.d"
2403+ conf="${MP}/etc/systemd/system/${service}.service.d/10-debug.conf"
2404+ echo "[Service]" >$conf
2405+ echo "ExecStartPre=/usr/local/bin/capture-mtu" >>$conf
2406+ done
2407+
2408+late_commands:
2409+ 01-networkd_debug: [sh, -c, *networkd_debug]
2410+ 02-capture_mtu: [sh, -c, *capture_mtu]
2411diff --git a/examples/tests/reuse-raid-member-partition.yaml b/examples/tests/reuse-raid-member-partition.yaml
2412new file mode 100644
2413index 0000000..3fe2d83
2414--- /dev/null
2415+++ b/examples/tests/reuse-raid-member-partition.yaml
2416@@ -0,0 +1,73 @@
2417+showtrace: true
2418+
2419+# The point of this test is to test installing to a partition that used to
2420+# be a RAID member where the other disks that used to be part of the
2421+# RAID are not present (the scenario that the disk was just grabbed
2422+# out of a pile of previously used disks and shoved into a server).
2423+
2424+# So what it does is to create a RAID0 out of two partition from two
2425+# disks, stop the RAID, wipe one of the disks and then install to the
2426+# other, reusing the partition that was part of the RAID.
2427+
2428+bucket:
2429+ - &setup |
2430+ parted /dev/disk/by-id/virtio-disk-a --script -- \
2431+ mklabel gpt \
2432+ mkpart primary 1GiB 2GiB \
2433+ mkpart primary 2GiB 9GiB
2434+ parted /dev/disk/by-id/virtio-disk-b --script -- \
2435+ mklabel gpt \
2436+ mkpart primary 2GiB 9GiB
2437+ udevadm settle
2438+ mdadm --create --metadata 1.2 --level 0 -n 2 /dev/md1 --assume-clean \
2439+ /dev/disk/by-id/virtio-disk-a-part2 /dev/disk/by-id/virtio-disk-b-part1
2440+ udevadm settle
2441+ mdadm --stop /dev/md1
2442+ udevadm settle
2443+ mdadm --zero-superblock /dev/disk/by-id/virtio-disk-b-part1
2444+ wipefs -a /dev/disk/by-id/virtio-disk-b
2445+ udevadm settle
2446+
2447+early_commands:
2448+ 00-setup-raid: [sh, -exuc, *setup]
2449+
2450+storage:
2451+ config:
2452+ - type: disk
2453+ id: id_disk0
2454+ serial: disk-a
2455+ ptable: gpt
2456+ preserve: true
2457+ - type: disk
2458+ id: id_disk1
2459+ serial: disk-b
2460+ - type: partition
2461+ id: id_disk0_part1
2462+ preserve: true
2463+ device: id_disk0
2464+ flag: boot
2465+ number: 1
2466+ size: 1G
2467+ - type: partition
2468+ id: id_disk0_part2
2469+ preserve: true
2470+ device: id_disk0
2471+ number: 2
2472+ size: 7G
2473+ - type: format
2474+ id: id_efi_format
2475+ volume: id_disk0_part1
2476+ fstype: fat32
2477+ - type: format
2478+ id: id_root_format
2479+ volume: id_disk0_part2
2480+ fstype: ext4
2481+ - type: mount
2482+ device: id_root_format
2483+ id: id_root_mount
2484+ path: /
2485+ - type: mount
2486+ id: id_efi_mount
2487+ device: id_efi_format
2488+ path: /boot/efi
2489+ version: 1
2490diff --git a/examples/tests/reuse-raid-member-wipe.yaml b/examples/tests/reuse-raid-member-wipe.yaml
2491new file mode 100644
2492index 0000000..84a2686
2493--- /dev/null
2494+++ b/examples/tests/reuse-raid-member-wipe.yaml
2495@@ -0,0 +1,71 @@
2496+showtrace: true
2497+
2498+# The point of this test is to test installing to a disk that contains
2499+# a partition that used to be a RAID member where the other parts of
2500+# the RAID are not present (the scenario is that the disk was just
2501+# grabbed out of a pile of previously used disks and shoved into a
2502+# server).
2503+
2504+# So what it does is to create a RAID0 out of partitions on two disks,
2505+# stop the RAID, wipe the superblock on one of them and then install
2506+# to the other using a standard partitioning scheme.
2507+
2508+bucket:
2509+ - &setup |
2510+ parted /dev/disk/by-id/virtio-disk-a --script -- \
2511+ mklabel gpt \
2512+ mkpart primary 1GiB 9GiB
2513+ parted /dev/disk/by-id/virtio-disk-b --script -- \
2514+ mklabel gpt \
2515+ mkpart primary 1GiB 9GiB
2516+ udevadm settle
2517+ mdadm --create --metadata 1.2 --level 0 -n 2 /dev/md1 --assume-clean \
2518+ /dev/disk/by-id/virtio-disk-a-part1 /dev/disk/by-id/virtio-disk-b-part1
2519+ udevadm settle
2520+ mdadm --stop /dev/md1
2521+ udevadm settle
2522+ mdadm --zero-superblock /dev/disk/by-id/virtio-disk-b-part1
2523+ wipefs -a /dev/disk/by-id/virtio-disk-b
2524+ udevadm settle
2525+
2526+early_commands:
2527+ 00-setup-raid: [sh, -exuc, *setup]
2528+
2529+storage:
2530+ config:
2531+ - type: disk
2532+ id: id_disk0
2533+ serial: disk-a
2534+ ptable: gpt
2535+ wipe: superblock-recursive
2536+ - type: disk
2537+ id: id_disk1
2538+ serial: disk-b
2539+ - type: partition
2540+ id: id_disk0_part1
2541+ device: id_disk0
2542+ flag: boot
2543+ number: 1
2544+ size: 512M
2545+ - type: partition
2546+ id: id_disk0_part2
2547+ device: id_disk0
2548+ number: 2
2549+ size: 8G
2550+ - type: format
2551+ id: id_efi_format
2552+ volume: id_disk0_part1
2553+ fstype: fat32
2554+ - type: format
2555+ id: id_root_format
2556+ volume: id_disk0_part2
2557+ fstype: ext4
2558+ - type: mount
2559+ device: id_root_format
2560+ id: id_root_mount
2561+ path: /
2562+ - type: mount
2563+ id: id_efi_mount
2564+ device: id_efi_format
2565+ path: /boot/efi
2566+ version: 1
2567diff --git a/helpers/common b/helpers/common
2568index 5928150..6c8e4f0 100644
2569--- a/helpers/common
2570+++ b/helpers/common
2571@@ -679,7 +679,7 @@ install_grub() {
2572
2573 # grub is not the bootloader you are looking for
2574 if [ "${target_arch}" = "s390x" ]; then
2575- return 0;
2576+ return 0;
2577 fi
2578
2579 # set correct grub package
2580@@ -694,7 +694,7 @@ install_grub() {
2581 x86_64)
2582 case $rhel_ver in
2583 6) grub_name="grub";;
2584- 7) grub_name="grub2-pc";;
2585+ 7|8) grub_name="grub2-pc";;
2586 *)
2587 error "Unknown rhel_ver [$rhel_ver]";
2588 return 1;
2589@@ -711,7 +711,10 @@ install_grub() {
2590 case "$target_arch" in
2591 x86_64)
2592 # centos 7+, no centos6 support
2593- grub_name="grub2-efi-x64-modules"
2594+ # grub2-efi-x64 installs a signed grub bootloader while
2595+ # curtin uses grub2-efi-x64-modules to generate grubx64.efi.
2596+ # Either works just check that one of them is installed.
2597+ grub_name="grub2-efi-x64 grub2-efi-x64-modules"
2598 grub_target="x86_64-efi"
2599 ;;
2600 amd64)
2601@@ -739,11 +742,13 @@ install_grub() {
2602 error "failed to check if $grub_name installed";
2603 return 1;
2604 fi
2605- case "$tmp" in
2606- install\ ok\ installed) :;;
2607- *) debug 1 "$grub_name not installed, not doing anything";
2608- return 1;;
2609- esac
2610+ # Check that any of the packages in $grub_name are installed. If
2611+ # grub_name contains multiple packages, as it does for CentOS 7+,
2612+ # only one package has to be installed for this to pass.
2613+ if ! echo $tmp | grep -q 'install ok installed'; then
2614+ debug 1 "$grub_name not installed, not doing anything"
2615+ return 1
2616+ fi
2617
2618 local grub_d="etc/default/grub.d"
2619 # ubuntu writes to /etc/default/grub.d/50-curtin-settings.cfg
2620@@ -822,11 +827,31 @@ install_grub() {
2621 if [ "$update_nvram" -ge 1 ]; then
2622 nvram=""
2623 fi
2624+ if [ "${#grubdevs_new[@]}" -eq 1 ] && [ -f "${grubdevs_new[0]}" ]; then
2625+ # Currently UEFI can only be pointed to one system partition. If
2626+ # for some reason multiple install locations are given only use the
2627+ # first.
2628+ efi_dev="${grubdevs_new[0]}"
2629+ elif [ "${#grubdevs_new[@]}" -gt 1 ]; then
2630+ error "Only one grub device supported on UEFI!"
2631+ exit 1
2632+ else
2633+ # If no storage configuration was given try to determine the system
2634+ # partition.
2635+ efi_dev=$(awk -v "MP=${mp}/boot/efi" '$2 == MP { print $1 }' /proc/mounts)
2636+ fi
2637+ # The partition number of block device name need to be determined here
2638+ # so both getting the UEFI device from Curtin config and discovering it
2639+ # work.
2640+ efi_part_num=$(cat /sys/class/block/$(basename $efi_dev)/partition)
2641+ efi_disk="/dev/$(lsblk -no pkname $efi_dev)"
2642 debug 1 "curtin uefi: installing ${grub_name} to: /boot/efi"
2643 chroot "$mp" env DEBIAN_FRONTEND=noninteractive sh -exc '
2644 echo "before grub-install efiboot settings"
2645 efibootmgr -v || echo "WARN: efibootmgr exited $?"
2646 bootid="$4"
2647+ efi_disk="$5"
2648+ efi_part_num="$6"
2649 grubpost=""
2650 case $bootid in
2651 debian|ubuntu)
2652@@ -836,7 +861,15 @@ install_grub() {
2653 ;;
2654 centos|redhat|rhel)
2655 grubcmd="grub2-install"
2656- grubpost="grub2-mkconfig -o /boot/grub2/grub.cfg"
2657+ # RHEL uses redhat instead of the os_variant rhel for the bootid.
2658+ if [ "$bootid" = "rhel" ]; then
2659+ bootid="redhat"
2660+ fi
2661+ if [ -f /boot/efi/EFI/$bootid/grubx64.efi ]; then
2662+ grubpost="grub2-mkconfig -o /boot/efi/EFI/$bootid/grub.cfg"
2663+ else
2664+ grubpost="grub2-mkconfig -o /boot/grub2/grub.cfg"
2665+ fi
2666 ;;
2667 *)
2668 echo "Unsupported OS: $bootid" 1>&2
2669@@ -852,10 +885,45 @@ install_grub() {
2670 echo "$gi_out" | grep -q -- "$no_nvram" || no_nvram=""
2671 echo "$gi_out" | grep -q -- "--target" || target=""
2672 echo "$gi_out" | grep -q -- "--efi-directory" || efi_dir=""
2673- $grubcmd $target $efi_dir \
2674- --bootloader-id=$bootid --recheck $no_nvram
2675+
2676+ # Do not overwrite grubx64.efi if it already exists. grub-install
2677+ # generates grubx64.efi and overwrites any existing binary in
2678+ # /boot/efi/EFI/$bootid. This binary is not signed and will cause
2679+ # secure boot to fail.
2680+ #
2681+ # CentOS, RHEL, Fedora ship the signed boot loader in the package
2682+ # grub2-efi-x64 which installs the signed boot loader to
2683+ # /boot/efi/EFI/$bootid/grubx64.efi. All Curtin has to do is
2684+ # configure the firmware. This mirrors what Anaconda does.
2685+ #
2686+ # Debian and Ubuntu come with a patched version of grub which
2687+ # add the install flag --uefi-secure-boot which is enabled by
2688+ # default. When enabled if a signed version of grub exists on
2689+ # the filesystem it will be copied into /boot/efi/EFI/$bootid.
2690+ # Stock Ubuntu images do not ship with anything in /boot. Those
2691+ # files are generated by installing a kernel and grub.
2692+ if [ -f /boot/efi/EFI/$bootid/grubx64.efi ]; then
2693+ if [ -z "$no_nvram" ]; then
2694+ # UEFI firmware should be pointed to the shim if available to
2695+ # enable secure boot.
2696+ for boot_uefi in \
2697+ /boot/efi/EFI/$bootid/shimx64.efi \
2698+ /boot/efi/EFI/BOOT/BOOTX64.EFI \
2699+ /boot/efi/EFI/$bootid/grubx64.efi; do
2700+ if [ -f $boot_uefi ]; then
2701+ break
2702+ fi
2703+ done
2704+ loader=$(echo ${boot_uefi##/boot/efi} | sed "s|/|\\\|g")
2705+ efibootmgr --create --write-signature --label $bootid \
2706+ --disk $efi_disk --part $efi_part_num --loader $loader
2707+ fi
2708+ else
2709+ $grubcmd $target $efi_dir \
2710+ --bootloader-id=$bootid --recheck $no_nvram
2711+ fi
2712 [ -z "$grubpost" ] || $grubpost;' \
2713- -- "${grub_name}" "${grub_target}" "$nvram" "$os_variant" </dev/null ||
2714+ -- "$grub_name" "$grub_target" "$nvram" "$os_variant" "$efi_disk" "$efi_part_num" </dev/null ||
2715 { error "failed to install grub!"; return 1; }
2716
2717 chroot "$mp" sh -exc '
2718@@ -885,8 +953,11 @@ install_grub() {
2719 centos|redhat|rhel)
2720 case $bootver in
2721 6) grubcmd="grub-install";;
2722- 7) grubcmd="grub2-install"
2723+ 7|8) grubcmd="grub2-install"
2724 grubpost="grub2-mkconfig -o /boot/grub2/grub.cfg";;
2725+ *)
2726+ echo "Unknown rhel_ver [$bootver]"
2727+ exit 1
2728 esac
2729 ;;
2730 *)
2731diff --git a/tests/data/probert_storage_multipath.json b/tests/data/probert_storage_multipath.json
2732new file mode 100644
2733index 0000000..53d9f93
2734--- /dev/null
2735+++ b/tests/data/probert_storage_multipath.json
2736@@ -0,0 +1,1059 @@
2737+{
2738+ "lvm": {},
2739+ "raid": {},
2740+ "filesystem": {
2741+ "/dev/sr0": {
2742+ "BOOT_SYSTEM_ID": "EL\\x20TORITO\\x20SPECIFICATION",
2743+ "LABEL": "Ubuntu-Server_19.10_amd64",
2744+ "LABEL_ENC": "Ubuntu-Server\\x2019.10\\x20amd64",
2745+ "TYPE": "iso9660",
2746+ "USAGE": "filesystem",
2747+ "UUID": "2019-08-11-07-41-33-00",
2748+ "UUID_ENC": "2019-08-11-07-41-33-00",
2749+ "VERSION": "Joliet Extension"
2750+ },
2751+ "/dev/sda2": {
2752+ "TYPE": "ext4",
2753+ "USAGE": "filesystem",
2754+ "UUID": "80ca57a0-60b7-4328-be41-afedf915effd",
2755+ "UUID_ENC": "80ca57a0-60b7-4328-be41-afedf915effd",
2756+ "VERSION": "1.0"
2757+ },
2758+ "/dev/sdb2": {
2759+ "TYPE": "ext4",
2760+ "USAGE": "filesystem",
2761+ "UUID": "80ca57a0-60b7-4328-be41-afedf915effd",
2762+ "UUID_ENC": "80ca57a0-60b7-4328-be41-afedf915effd",
2763+ "VERSION": "1.0"
2764+ },
2765+ "/dev/dm-2": {
2766+ "TYPE": "ext4",
2767+ "USAGE": "filesystem",
2768+ "UUID": "80ca57a0-60b7-4328-be41-afedf915effd",
2769+ "UUID_ENC": "80ca57a0-60b7-4328-be41-afedf915effd",
2770+ "VERSION": "1.0"
2771+ }
2772+ },
2773+ "mount": [
2774+ {
2775+ "target": "/",
2776+ "source": "/cow",
2777+ "fstype": "overlay",
2778+ "options": "rw,relatime,lowerdir=//installer.squashfs://filesystem.squashfs,upperdir=/cow/upper,workdir=/cow/work",
2779+ "children": [
2780+ {
2781+ "target": "/sys",
2782+ "source": "sysfs",
2783+ "fstype": "sysfs",
2784+ "options": "rw,nosuid,nodev,noexec,relatime",
2785+ "children": [
2786+ {
2787+ "target": "/sys/kernel/security",
2788+ "source": "securityfs",
2789+ "fstype": "securityfs",
2790+ "options": "rw,nosuid,nodev,noexec,relatime"
2791+ },
2792+ {
2793+ "target": "/sys/fs/cgroup",
2794+ "source": "tmpfs",
2795+ "fstype": "tmpfs",
2796+ "options": "ro,nosuid,nodev,noexec,mode=755",
2797+ "children": [
2798+ {
2799+ "target": "/sys/fs/cgroup/unified",
2800+ "source": "cgroup2",
2801+ "fstype": "cgroup2",
2802+ "options": "rw,nosuid,nodev,noexec,relatime,nsdelegate"
2803+ },
2804+ {
2805+ "target": "/sys/fs/cgroup/systemd",
2806+ "source": "cgroup",
2807+ "fstype": "cgroup",
2808+ "options": "rw,nosuid,nodev,noexec,relatime,xattr,name=systemd"
2809+ },
2810+ {
2811+ "target": "/sys/fs/cgroup/cpu,cpuacct",
2812+ "source": "cgroup",
2813+ "fstype": "cgroup",
2814+ "options": "rw,nosuid,nodev,noexec,relatime,cpu,cpuacct"
2815+ },
2816+ {
2817+ "target": "/sys/fs/cgroup/devices",
2818+ "source": "cgroup",
2819+ "fstype": "cgroup",
2820+ "options": "rw,nosuid,nodev,noexec,relatime,devices"
2821+ },
2822+ {
2823+ "target": "/sys/fs/cgroup/perf_event",
2824+ "source": "cgroup",
2825+ "fstype": "cgroup",
2826+ "options": "rw,nosuid,nodev,noexec,relatime,perf_event"
2827+ },
2828+ {
2829+ "target": "/sys/fs/cgroup/hugetlb",
2830+ "source": "cgroup",
2831+ "fstype": "cgroup",
2832+ "options": "rw,nosuid,nodev,noexec,relatime,hugetlb"
2833+ },
2834+ {
2835+ "target": "/sys/fs/cgroup/rdma",
2836+ "source": "cgroup",
2837+ "fstype": "cgroup",
2838+ "options": "rw,nosuid,nodev,noexec,relatime,rdma"
2839+ },
2840+ {
2841+ "target": "/sys/fs/cgroup/net_cls,net_prio",
2842+ "source": "cgroup",
2843+ "fstype": "cgroup",
2844+ "options": "rw,nosuid,nodev,noexec,relatime,net_cls,net_prio"
2845+ },
2846+ {
2847+ "target": "/sys/fs/cgroup/freezer",
2848+ "source": "cgroup",
2849+ "fstype": "cgroup",
2850+ "options": "rw,nosuid,nodev,noexec,relatime,freezer"
2851+ },
2852+ {
2853+ "target": "/sys/fs/cgroup/cpuset",
2854+ "source": "cgroup",
2855+ "fstype": "cgroup",
2856+ "options": "rw,nosuid,nodev,noexec,relatime,cpuset"
2857+ },
2858+ {
2859+ "target": "/sys/fs/cgroup/memory",
2860+ "source": "cgroup",
2861+ "fstype": "cgroup",
2862+ "options": "rw,nosuid,nodev,noexec,relatime,memory"
2863+ },
2864+ {
2865+ "target": "/sys/fs/cgroup/blkio",
2866+ "source": "cgroup",
2867+ "fstype": "cgroup",
2868+ "options": "rw,nosuid,nodev,noexec,relatime,blkio"
2869+ },
2870+ {
2871+ "target": "/sys/fs/cgroup/pids",
2872+ "source": "cgroup",
2873+ "fstype": "cgroup",
2874+ "options": "rw,nosuid,nodev,noexec,relatime,pids"
2875+ }
2876+ ]
2877+ },
2878+ {
2879+ "target": "/sys/fs/pstore",
2880+ "source": "pstore",
2881+ "fstype": "pstore",
2882+ "options": "rw,nosuid,nodev,noexec,relatime"
2883+ },
2884+ {
2885+ "target": "/sys/fs/bpf",
2886+ "source": "bpf",
2887+ "fstype": "bpf",
2888+ "options": "rw,nosuid,nodev,noexec,relatime,mode=700"
2889+ },
2890+ {
2891+ "target": "/sys/kernel/debug",
2892+ "source": "debugfs",
2893+ "fstype": "debugfs",
2894+ "options": "rw,relatime"
2895+ },
2896+ {
2897+ "target": "/sys/kernel/config",
2898+ "source": "configfs",
2899+ "fstype": "configfs",
2900+ "options": "rw,relatime"
2901+ },
2902+ {
2903+ "target": "/sys/fs/fuse/connections",
2904+ "source": "fusectl",
2905+ "fstype": "fusectl",
2906+ "options": "rw,relatime"
2907+ }
2908+ ]
2909+ },
2910+ {
2911+ "target": "/proc",
2912+ "source": "proc",
2913+ "fstype": "proc",
2914+ "options": "rw,nosuid,nodev,noexec,relatime",
2915+ "children": [
2916+ {
2917+ "target": "/proc/sys/fs/binfmt_misc",
2918+ "source": "systemd-1",
2919+ "fstype": "autofs",
2920+ "options": "rw,relatime,fd=28,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=13765"
2921+ }
2922+ ]
2923+ },
2924+ {
2925+ "target": "/dev",
2926+ "source": "udev",
2927+ "fstype": "devtmpfs",
2928+ "options": "rw,nosuid,relatime,size=462516k,nr_inodes=115629,mode=755",
2929+ "children": [
2930+ {
2931+ "target": "/dev/pts",
2932+ "source": "devpts",
2933+ "fstype": "devpts",
2934+ "options": "rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000"
2935+ },
2936+ {
2937+ "target": "/dev/shm",
2938+ "source": "tmpfs",
2939+ "fstype": "tmpfs",
2940+ "options": "rw,nosuid,nodev"
2941+ },
2942+ {
2943+ "target": "/dev/hugepages",
2944+ "source": "hugetlbfs",
2945+ "fstype": "hugetlbfs",
2946+ "options": "rw,relatime,pagesize=2M"
2947+ },
2948+ {
2949+ "target": "/dev/mqueue",
2950+ "source": "mqueue",
2951+ "fstype": "mqueue",
2952+ "options": "rw,relatime"
2953+ }
2954+ ]
2955+ },
2956+ {
2957+ "target": "/run",
2958+ "source": "tmpfs",
2959+ "fstype": "tmpfs",
2960+ "options": "rw,nosuid,noexec,relatime,size=100568k,mode=755",
2961+ "children": [
2962+ {
2963+ "target": "/run/lock",
2964+ "source": "tmpfs",
2965+ "fstype": "tmpfs",
2966+ "options": "rw,nosuid,nodev,noexec,relatime,size=5120k"
2967+ }
2968+ ]
2969+ },
2970+ {
2971+ "target": "/cdrom",
2972+ "source": "/dev/sr0",
2973+ "fstype": "iso9660",
2974+ "options": "ro,noatime,nojoliet,check=s,map=n,blocksize=2048"
2975+ },
2976+ {
2977+ "target": "/rofs",
2978+ "source": "/dev/loop0",
2979+ "fstype": "squashfs",
2980+ "options": "ro,noatime"
2981+ },
2982+ {
2983+ "target": "/usr/lib/modules",
2984+ "source": "/dev/loop2",
2985+ "fstype": "squashfs",
2986+ "options": "ro,relatime"
2987+ },
2988+ {
2989+ "target": "/media/filesystem",
2990+ "source": "/dev/loop0",
2991+ "fstype": "squashfs",
2992+ "options": "ro,relatime"
2993+ },
2994+ {
2995+ "target": "/tmp",
2996+ "source": "tmpfs",
2997+ "fstype": "tmpfs",
2998+ "options": "rw,nosuid,nodev,relatime"
2999+ },
3000+ {
3001+ "target": "/snap/core/7270",
3002+ "source": "/dev/loop3",
3003+ "fstype": "squashfs",
3004+ "options": "ro,nodev,relatime"
3005+ },
3006+ {
3007+ "target": "/snap/subiquity/1093",
3008+ "source": "/dev/loop4",
3009+ "fstype": "squashfs",
3010+ "options": "ro,nodev,relatime"
3011+ }
3012+ ]
3013+ }
3014+ ],
3015+ "multipath": {
3016+ "maps": [
3017+ {
3018+ "multipath": "30000000000000064",
3019+ "sysfs": "dm-0",
3020+ "paths": "2"
3021+ }
3022+ ],
3023+ "paths": [
3024+ {
3025+ "device": "sda",
3026+ "serial": "serial-a",
3027+ "multipath": "mpatha",
3028+ "host_wwnn": "[undef]",
3029+ "target_wwnn": "[undef]",
3030+ "host_wwpn": "[undef]",
3031+ "target_wwpn": "[undef]",
3032+ "host_adapter": "[undef]"
3033+ },
3034+ {
3035+ "device": "sdb",
3036+ "serial": "serial-a",
3037+ "multipath": "mpatha",
3038+ "host_wwnn": "[undef]",
3039+ "target_wwnn": "[undef]",
3040+ "host_wwpn": "[undef]",
3041+ "target_wwpn": "[undef]",
3042+ "host_adapter": "[undef]"
3043+ }
3044+ ]
3045+ },
3046+ "dmcrypt": {},
3047+ "blockdev": {
3048+ "/dev/sr0": {
3049+ "DEVLINKS": "/dev/disk/by-label/Ubuntu-Server\\x2019.10\\x20amd64 /dev/dvd /dev/disk/by-uuid/2019-08-11-07-41-33-00 /dev/disk/by-id/ata-QEMU_DVD-ROM_QM00003 /dev/cdrom /dev/disk/by-path/pci-0000:00:01.1-ata-2",
3050+ "DEVNAME": "/dev/sr0",
3051+ "DEVPATH": "/devices/pci0000:00/0000:00:01.1/ata2/host1/target1:0:0/1:0:0:0/block/sr0",
3052+ "DEVTYPE": "disk",
3053+ "ID_ATA": "1",
3054+ "ID_BUS": "ata",
3055+ "ID_CDROM": "1",
3056+ "ID_CDROM_DVD": "1",
3057+ "ID_CDROM_MEDIA": "1",
3058+ "ID_CDROM_MEDIA_CD": "1",
3059+ "ID_CDROM_MEDIA_SESSION_COUNT": "1",
3060+ "ID_CDROM_MEDIA_TRACK_COUNT": "1",
3061+ "ID_CDROM_MEDIA_TRACK_COUNT_DATA": "1",
3062+ "ID_CDROM_MRW": "1",
3063+ "ID_CDROM_MRW_W": "1",
3064+ "ID_FOR_SEAT": "block-pci-0000_00_01_1-ata-2",
3065+ "ID_FS_BOOT_SYSTEM_ID": "EL\\x20TORITO\\x20SPECIFICATION",
3066+ "ID_FS_LABEL": "Ubuntu-Server_19.10_amd64",
3067+ "ID_FS_LABEL_ENC": "Ubuntu-Server\\x2019.10\\x20amd64",
3068+ "ID_FS_TYPE": "iso9660",
3069+ "ID_FS_USAGE": "filesystem",
3070+ "ID_FS_UUID": "2019-08-11-07-41-33-00",
3071+ "ID_FS_UUID_ENC": "2019-08-11-07-41-33-00",
3072+ "ID_FS_VERSION": "Joliet Extension",
3073+ "ID_MODEL": "QEMU_DVD-ROM",
3074+ "ID_MODEL_ENC": "QEMU\\x20DVD-ROM\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20\\x20",
3075+ "ID_PART_TABLE_TYPE": "dos",
3076+ "ID_PART_TABLE_UUID": "6612a5ed",
3077+ "ID_PATH": "pci-0000:00:01.1-ata-2",
3078+ "ID_PATH_TAG": "pci-0000_00_01_1-ata-2",
3079+ "ID_REVISION": "2.5+",
3080+ "ID_SCSI": "1",
3081+ "ID_SCSI_INQUIRY": "1",
3082+ "ID_SERIAL": "QEMU_DVD-ROM_QM00003",
3083+ "ID_SERIAL_SHORT": "QM00003",
3084+ "ID_TYPE": "cd",
3085+ "ID_VENDOR": "QEMU",
3086+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3087+ "MAJOR": "11",
3088+ "MINOR": "0",
3089+ "SCSI_MODEL": "QEMU_DVD-ROM",
3090+ "SCSI_MODEL_ENC": "QEMU\\x20DVD-ROM\\x20\\x20\\x20\\x20",
3091+ "SCSI_REVISION": "2.5+",
3092+ "SCSI_TPGS": "0",
3093+ "SCSI_TYPE": "cd/dvd",
3094+ "SCSI_VENDOR": "QEMU",
3095+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3096+ "SUBSYSTEM": "block",
3097+ "SYSTEMD_MOUNT_DEVICE_BOUND": "1",
3098+ "TAGS": ":systemd:uaccess:seat:",
3099+ "USEC_INITIALIZED": "1162968",
3100+ "attrs": {
3101+ "alignment_offset": "0",
3102+ "bdi": null,
3103+ "capability": "119",
3104+ "dev": "11:0",
3105+ "device": null,
3106+ "discard_alignment": "0",
3107+ "events": "media_change eject_request",
3108+ "events_async": "",
3109+ "events_poll_msecs": "-1",
3110+ "ext_range": "1",
3111+ "hidden": "0",
3112+ "inflight": " 0 0",
3113+ "range": "1",
3114+ "removable": "1",
3115+ "ro": "0",
3116+ "size": "2881486848",
3117+ "stat": " 3177 16 427476 210179 0 0 0 0 0 6880 207800 0 0 0 0",
3118+ "subsystem": "block",
3119+ "uevent": "MAJOR=11\nMINOR=0\nDEVNAME=sr0\nDEVTYPE=disk"
3120+ },
3121+ "partitiontable": {
3122+ "label": "dos",
3123+ "id": "0x6612a5ed",
3124+ "device": "/dev/sr0",
3125+ "unit": "sectors",
3126+ "partitions": [
3127+ {
3128+ "node": "/dev/sr0p1",
3129+ "start": 0,
3130+ "size": 1406976,
3131+ "type": "0",
3132+ "bootable": true
3133+ },
3134+ {
3135+ "node": "/dev/sr0p2",
3136+ "start": 790864,
3137+ "size": 7936,
3138+ "type": "ef"
3139+ }
3140+ ]
3141+ }
3142+ },
3143+ "/dev/sda": {
3144+ "DEVLINKS": "/dev/disk/by-id/scsi-30000000000000064 /dev/disk/by-path/pci-0000:00:04.0-scsi-0:0:0:0 /dev/disk/by-id/scsi-SQEMU_QEMU_HARDDISK_serial-a /dev/disk/by-id/wwn-0x0000000000000064 /dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_serial-a",
3145+ "DEVNAME": "/dev/sda",
3146+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio0/host2/target2:0:0/2:0:0:0/block/sda",
3147+ "DEVTYPE": "disk",
3148+ "DM_DEL_PART_NODES": "1",
3149+ "DM_MULTIPATH_DEVICE_PATH": "1",
3150+ "ID_BUS": "scsi",
3151+ "ID_FS_TYPE": "mpath_member",
3152+ "ID_MODEL": "QEMU_HARDDISK",
3153+ "ID_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3154+ "ID_PART_TABLE_TYPE": "gpt",
3155+ "ID_PART_TABLE_UUID": "8261cd75-2234-4dbd-920a-d21a7c083c94",
3156+ "ID_PATH": "pci-0000:00:04.0-scsi-0:0:0:0",
3157+ "ID_PATH_TAG": "pci-0000_00_04_0-scsi-0_0_0_0",
3158+ "ID_REVISION": "2.5+",
3159+ "ID_SCSI": "1",
3160+ "ID_SCSI_INQUIRY": "1",
3161+ "ID_SERIAL": "30000000000000064",
3162+ "ID_SERIAL_SHORT": "0000000000000064",
3163+ "ID_TYPE": "disk",
3164+ "ID_VENDOR": "QEMU",
3165+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3166+ "ID_WWN": "0x0000000000000064",
3167+ "ID_WWN_WITH_EXTENSION": "0x0000000000000064",
3168+ "MAJOR": "8",
3169+ "MINOR": "0",
3170+ "MPATH_SBIN_PATH": "/sbin",
3171+ "SCSI_IDENT_LUN_NAA_LOCAL": "0000000000000064",
3172+ "SCSI_IDENT_LUN_VENDOR": "serial-a",
3173+ "SCSI_IDENT_SERIAL": "serial-a",
3174+ "SCSI_MODEL": "QEMU_HARDDISK",
3175+ "SCSI_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3176+ "SCSI_REVISION": "2.5+",
3177+ "SCSI_TPGS": "0",
3178+ "SCSI_TYPE": "disk",
3179+ "SCSI_VENDOR": "QEMU",
3180+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3181+ "SUBSYSTEM": "block",
3182+ "SYSTEMD_READY": "0",
3183+ "TAGS": ":systemd:",
3184+ "USEC_INITIALIZED": "1357719",
3185+ "attrs": {
3186+ "alignment_offset": "0",
3187+ "bdi": null,
3188+ "capability": "50",
3189+ "dev": "8:0",
3190+ "device": null,
3191+ "discard_alignment": "0",
3192+ "events": "",
3193+ "events_async": "",
3194+ "events_poll_msecs": "-1",
3195+ "ext_range": "256",
3196+ "hidden": "0",
3197+ "inflight": " 0 0",
3198+ "range": "16",
3199+ "removable": "0",
3200+ "ro": "0",
3201+ "size": "10737418240",
3202+ "stat": " 820 0 37338 127 0 0 0 0 0 452 4 0 0 0 0",
3203+ "subsystem": "block",
3204+ "uevent": "MAJOR=8\nMINOR=0\nDEVNAME=sda\nDEVTYPE=disk"
3205+ },
3206+ "partitiontable": {
3207+ "label": "gpt",
3208+ "id": "8261CD75-2234-4DBD-920A-D21A7C083C94",
3209+ "device": "/dev/sda",
3210+ "unit": "sectors",
3211+ "firstlba": 34,
3212+ "lastlba": 20971486,
3213+ "partitions": [
3214+ {
3215+ "node": "/dev/sda1",
3216+ "start": 2048,
3217+ "size": 2048,
3218+ "type": "21686148-6449-6E6F-744E-656564454649",
3219+ "uuid": "8088175C-362A-4B46-9603-F3595065FA73"
3220+ },
3221+ {
3222+ "node": "/dev/sda2",
3223+ "start": 4096,
3224+ "size": 20965376,
3225+ "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
3226+ "uuid": "760493AC-7945-44C5-A6BD-58FCD6632EA7"
3227+ }
3228+ ]
3229+ }
3230+ },
3231+ "/dev/sda1": {
3232+ "DEVLINKS": "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_serial-a-part1 /dev/disk/by-id/scsi-30000000000000064-part1 /dev/disk/by-path/pci-0000:00:04.0-scsi-0:0:0:0-part1 /dev/disk/by-id/scsi-SQEMU_QEMU_HARDDISK_serial-a-part1 /dev/disk/by-partuuid/8088175c-362a-4b46-9603-f3595065fa73 /dev/disk/by-id/wwn-0x0000000000000064-part1",
3233+ "DEVNAME": "/dev/sda1",
3234+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio0/host2/target2:0:0/2:0:0:0/block/sda/sda1",
3235+ "DEVTYPE": "partition",
3236+ "DM_MULTIPATH_DEVICE_PATH": "1",
3237+ "ID_BUS": "scsi",
3238+ "ID_FS_TYPE": "mpath_member",
3239+ "ID_MODEL": "QEMU_HARDDISK",
3240+ "ID_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3241+ "ID_PART_ENTRY_DISK": "8:0",
3242+ "ID_PART_ENTRY_NUMBER": "1",
3243+ "ID_PART_ENTRY_OFFSET": "2048",
3244+ "ID_PART_ENTRY_SCHEME": "gpt",
3245+ "ID_PART_ENTRY_SIZE": "2048",
3246+ "ID_PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649",
3247+ "ID_PART_ENTRY_UUID": "8088175c-362a-4b46-9603-f3595065fa73",
3248+ "ID_PART_TABLE_TYPE": "gpt",
3249+ "ID_PART_TABLE_UUID": "8261cd75-2234-4dbd-920a-d21a7c083c94",
3250+ "ID_PATH": "pci-0000:00:04.0-scsi-0:0:0:0",
3251+ "ID_PATH_TAG": "pci-0000_00_04_0-scsi-0_0_0_0",
3252+ "ID_REVISION": "2.5+",
3253+ "ID_SCSI": "1",
3254+ "ID_SCSI_INQUIRY": "1",
3255+ "ID_SERIAL": "30000000000000064",
3256+ "ID_SERIAL_SHORT": "0000000000000064",
3257+ "ID_TYPE": "disk",
3258+ "ID_VENDOR": "QEMU",
3259+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3260+ "ID_WWN": "0x0000000000000064",
3261+ "ID_WWN_WITH_EXTENSION": "0x0000000000000064",
3262+ "MAJOR": "8",
3263+ "MINOR": "1",
3264+ "PARTN": "1",
3265+ "SCSI_IDENT_LUN_NAA_LOCAL": "0000000000000064",
3266+ "SCSI_IDENT_LUN_VENDOR": "serial-a",
3267+ "SCSI_IDENT_SERIAL": "serial-a",
3268+ "SCSI_MODEL": "QEMU_HARDDISK",
3269+ "SCSI_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3270+ "SCSI_REVISION": "2.5+",
3271+ "SCSI_TPGS": "0",
3272+ "SCSI_TYPE": "disk",
3273+ "SCSI_VENDOR": "QEMU",
3274+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3275+ "SUBSYSTEM": "block",
3276+ "SYSTEMD_READY": "0",
3277+ "TAGS": ":systemd:",
3278+ "USEC_INITIALIZED": "1409047",
3279+ "attrs": {
3280+ "alignment_offset": "0",
3281+ "dev": "8:1",
3282+ "discard_alignment": "0",
3283+ "inflight": " 0 0",
3284+ "partition": "1",
3285+ "ro": "0",
3286+ "size": "1048576",
3287+ "start": "2048",
3288+ "stat": " 167 0 1360 14 0 0 0 0 0 172 0 0 0 0 0",
3289+ "subsystem": "block",
3290+ "uevent": "MAJOR=8\nMINOR=1\nDEVNAME=sda1\nDEVTYPE=partition\nPARTN=1"
3291+ }
3292+ },
3293+ "/dev/sda2": {
3294+ "DEVLINKS": "/dev/disk/by-id/wwn-0x0000000000000064-part2 /dev/disk/by-id/scsi-SQEMU_QEMU_HARDDISK_serial-a-part2 /dev/disk/by-uuid/80ca57a0-60b7-4328-be41-afedf915effd /dev/disk/by-partuuid/760493ac-7945-44c5-a6bd-58fcd6632ea7 /dev/disk/by-path/pci-0000:00:04.0-scsi-0:0:0:0-part2 /dev/disk/by-id/scsi-30000000000000064-part2 /dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_serial-a-part2",
3295+ "DEVNAME": "/dev/sda2",
3296+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio0/host2/target2:0:0/2:0:0:0/block/sda/sda2",
3297+ "DEVTYPE": "partition",
3298+ "DM_MULTIPATH_DEVICE_PATH": "1",
3299+ "ID_BUS": "scsi",
3300+ "ID_FS_TYPE": "ext4",
3301+ "ID_FS_USAGE": "filesystem",
3302+ "ID_FS_UUID": "80ca57a0-60b7-4328-be41-afedf915effd",
3303+ "ID_FS_UUID_ENC": "80ca57a0-60b7-4328-be41-afedf915effd",
3304+ "ID_FS_VERSION": "1.0",
3305+ "ID_MODEL": "QEMU_HARDDISK",
3306+ "ID_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3307+ "ID_PART_ENTRY_DISK": "8:0",
3308+ "ID_PART_ENTRY_NUMBER": "2",
3309+ "ID_PART_ENTRY_OFFSET": "4096",
3310+ "ID_PART_ENTRY_SCHEME": "gpt",
3311+ "ID_PART_ENTRY_SIZE": "20965376",
3312+ "ID_PART_ENTRY_TYPE": "0fc63daf-8483-4772-8e79-3d69d8477de4",
3313+ "ID_PART_ENTRY_UUID": "760493ac-7945-44c5-a6bd-58fcd6632ea7",
3314+ "ID_PART_TABLE_TYPE": "gpt",
3315+ "ID_PART_TABLE_UUID": "8261cd75-2234-4dbd-920a-d21a7c083c94",
3316+ "ID_PATH": "pci-0000:00:04.0-scsi-0:0:0:0",
3317+ "ID_PATH_TAG": "pci-0000_00_04_0-scsi-0_0_0_0",
3318+ "ID_REVISION": "2.5+",
3319+ "ID_SCSI": "1",
3320+ "ID_SCSI_INQUIRY": "1",
3321+ "ID_SERIAL": "30000000000000064",
3322+ "ID_SERIAL_SHORT": "0000000000000064",
3323+ "ID_TYPE": "disk",
3324+ "ID_VENDOR": "QEMU",
3325+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3326+ "ID_WWN": "0x0000000000000064",
3327+ "ID_WWN_WITH_EXTENSION": "0x0000000000000064",
3328+ "MAJOR": "8",
3329+ "MINOR": "2",
3330+ "PARTN": "2",
3331+ "SCSI_IDENT_LUN_NAA_LOCAL": "0000000000000064",
3332+ "SCSI_IDENT_LUN_VENDOR": "serial-a",
3333+ "SCSI_IDENT_SERIAL": "serial-a",
3334+ "SCSI_MODEL": "QEMU_HARDDISK",
3335+ "SCSI_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3336+ "SCSI_REVISION": "2.5+",
3337+ "SCSI_TPGS": "0",
3338+ "SCSI_TYPE": "disk",
3339+ "SCSI_VENDOR": "QEMU",
3340+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3341+ "SUBSYSTEM": "block",
3342+ "SYSTEMD_READY": "0",
3343+ "TAGS": ":systemd:",
3344+ "USEC_INITIALIZED": "1364238",
3345+ "attrs": {
3346+ "alignment_offset": "0",
3347+ "dev": "8:2",
3348+ "discard_alignment": "0",
3349+ "inflight": " 0 0",
3350+ "partition": "2",
3351+ "ro": "0",
3352+ "size": "10734272512",
3353+ "start": "4096",
3354+ "stat": " 268 0 17138 61 0 0 0 0 0 156 4 0 0 0 0",
3355+ "subsystem": "block",
3356+ "uevent": "MAJOR=8\nMINOR=2\nDEVNAME=sda2\nDEVTYPE=partition\nPARTN=2"
3357+ }
3358+ },
3359+ "/dev/sdb": {
3360+ "DEVLINKS": "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_serial-a /dev/disk/by-id/scsi-30000000000000064 /dev/disk/by-id/wwn-0x0000000000000064 /dev/disk/by-path/pci-0000:00:04.0-scsi-0:0:1:0 /dev/disk/by-id/scsi-SQEMU_QEMU_HARDDISK_serial-a",
3361+ "DEVNAME": "/dev/sdb",
3362+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio0/host2/target2:0:1/2:0:1:0/block/sdb",
3363+ "DEVTYPE": "disk",
3364+ "DM_DEL_PART_NODES": "1",
3365+ "DM_MULTIPATH_DEVICE_PATH": "1",
3366+ "ID_BUS": "scsi",
3367+ "ID_FS_TYPE": "mpath_member",
3368+ "ID_MODEL": "QEMU_HARDDISK",
3369+ "ID_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3370+ "ID_PART_TABLE_TYPE": "gpt",
3371+ "ID_PART_TABLE_UUID": "8261cd75-2234-4dbd-920a-d21a7c083c94",
3372+ "ID_PATH": "pci-0000:00:04.0-scsi-0:0:1:0",
3373+ "ID_PATH_TAG": "pci-0000_00_04_0-scsi-0_0_1_0",
3374+ "ID_REVISION": "2.5+",
3375+ "ID_SCSI": "1",
3376+ "ID_SCSI_INQUIRY": "1",
3377+ "ID_SERIAL": "30000000000000064",
3378+ "ID_SERIAL_SHORT": "0000000000000064",
3379+ "ID_TYPE": "disk",
3380+ "ID_VENDOR": "QEMU",
3381+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3382+ "ID_WWN": "0x0000000000000064",
3383+ "ID_WWN_WITH_EXTENSION": "0x0000000000000064",
3384+ "MAJOR": "8",
3385+ "MINOR": "16",
3386+ "MPATH_SBIN_PATH": "/sbin",
3387+ "SCSI_IDENT_LUN_NAA_LOCAL": "0000000000000064",
3388+ "SCSI_IDENT_LUN_VENDOR": "serial-a",
3389+ "SCSI_IDENT_SERIAL": "serial-a",
3390+ "SCSI_MODEL": "QEMU_HARDDISK",
3391+ "SCSI_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3392+ "SCSI_REVISION": "2.5+",
3393+ "SCSI_TPGS": "0",
3394+ "SCSI_TYPE": "disk",
3395+ "SCSI_VENDOR": "QEMU",
3396+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3397+ "SUBSYSTEM": "block",
3398+ "SYSTEMD_READY": "0",
3399+ "TAGS": ":systemd:",
3400+ "USEC_INITIALIZED": "1342677",
3401+ "attrs": {
3402+ "alignment_offset": "0",
3403+ "bdi": null,
3404+ "capability": "50",
3405+ "dev": "8:16",
3406+ "device": null,
3407+ "discard_alignment": "0",
3408+ "events": "",
3409+ "events_async": "",
3410+ "events_poll_msecs": "-1",
3411+ "ext_range": "256",
3412+ "hidden": "0",
3413+ "inflight": " 0 0",
3414+ "range": "16",
3415+ "removable": "0",
3416+ "ro": "0",
3417+ "size": "10737418240",
3418+ "stat": " 460 0 20312 70 0 0 0 0 0 280 12 0 0 0 0",
3419+ "subsystem": "block",
3420+ "uevent": "MAJOR=8\nMINOR=16\nDEVNAME=sdb\nDEVTYPE=disk"
3421+ },
3422+ "partitiontable": {
3423+ "label": "gpt",
3424+ "id": "8261CD75-2234-4DBD-920A-D21A7C083C94",
3425+ "device": "/dev/sdb",
3426+ "unit": "sectors",
3427+ "firstlba": 34,
3428+ "lastlba": 20971486,
3429+ "partitions": [
3430+ {
3431+ "node": "/dev/sdb1",
3432+ "start": 2048,
3433+ "size": 2048,
3434+ "type": "21686148-6449-6E6F-744E-656564454649",
3435+ "uuid": "8088175C-362A-4B46-9603-F3595065FA73"
3436+ },
3437+ {
3438+ "node": "/dev/sdb2",
3439+ "start": 4096,
3440+ "size": 20965376,
3441+ "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
3442+ "uuid": "760493AC-7945-44C5-A6BD-58FCD6632EA7"
3443+ }
3444+ ]
3445+ }
3446+ },
3447+ "/dev/sdb1": {
3448+ "DEVLINKS": "/dev/disk/by-path/pci-0000:00:04.0-scsi-0:0:1:0-part1 /dev/disk/by-id/scsi-30000000000000064-part1 /dev/disk/by-partuuid/8088175c-362a-4b46-9603-f3595065fa73 /dev/disk/by-id/scsi-SQEMU_QEMU_HARDDISK_serial-a-part1 /dev/disk/by-id/wwn-0x0000000000000064-part1 /dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_serial-a-part1",
3449+ "DEVNAME": "/dev/sdb1",
3450+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio0/host2/target2:0:1/2:0:1:0/block/sdb/sdb1",
3451+ "DEVTYPE": "partition",
3452+ "DM_MULTIPATH_DEVICE_PATH": "1",
3453+ "ID_BUS": "scsi",
3454+ "ID_FS_TYPE": "mpath_member",
3455+ "ID_MODEL": "QEMU_HARDDISK",
3456+ "ID_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3457+ "ID_PART_ENTRY_DISK": "8:16",
3458+ "ID_PART_ENTRY_NUMBER": "1",
3459+ "ID_PART_ENTRY_OFFSET": "2048",
3460+ "ID_PART_ENTRY_SCHEME": "gpt",
3461+ "ID_PART_ENTRY_SIZE": "2048",
3462+ "ID_PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649",
3463+ "ID_PART_ENTRY_UUID": "8088175c-362a-4b46-9603-f3595065fa73",
3464+ "ID_PART_TABLE_TYPE": "gpt",
3465+ "ID_PART_TABLE_UUID": "8261cd75-2234-4dbd-920a-d21a7c083c94",
3466+ "ID_PATH": "pci-0000:00:04.0-scsi-0:0:1:0",
3467+ "ID_PATH_TAG": "pci-0000_00_04_0-scsi-0_0_1_0",
3468+ "ID_REVISION": "2.5+",
3469+ "ID_SCSI": "1",
3470+ "ID_SCSI_INQUIRY": "1",
3471+ "ID_SERIAL": "30000000000000064",
3472+ "ID_SERIAL_SHORT": "0000000000000064",
3473+ "ID_TYPE": "disk",
3474+ "ID_VENDOR": "QEMU",
3475+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3476+ "ID_WWN": "0x0000000000000064",
3477+ "ID_WWN_WITH_EXTENSION": "0x0000000000000064",
3478+ "MAJOR": "8",
3479+ "MINOR": "17",
3480+ "PARTN": "1",
3481+ "SCSI_IDENT_LUN_NAA_LOCAL": "0000000000000064",
3482+ "SCSI_IDENT_LUN_VENDOR": "serial-a",
3483+ "SCSI_IDENT_SERIAL": "serial-a",
3484+ "SCSI_MODEL": "QEMU_HARDDISK",
3485+ "SCSI_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3486+ "SCSI_REVISION": "2.5+",
3487+ "SCSI_TPGS": "0",
3488+ "SCSI_TYPE": "disk",
3489+ "SCSI_VENDOR": "QEMU",
3490+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3491+ "SUBSYSTEM": "block",
3492+ "SYSTEMD_READY": "0",
3493+ "TAGS": ":systemd:",
3494+ "USEC_INITIALIZED": "1383416",
3495+ "attrs": {
3496+ "alignment_offset": "0",
3497+ "dev": "8:17",
3498+ "discard_alignment": "0",
3499+ "inflight": " 0 0",
3500+ "partition": "1",
3501+ "ro": "0",
3502+ "size": "1048576",
3503+ "start": "2048",
3504+ "stat": " 86 0 712 7 0 0 0 0 0 72 0 0 0 0 0",
3505+ "subsystem": "block",
3506+ "uevent": "MAJOR=8\nMINOR=17\nDEVNAME=sdb1\nDEVTYPE=partition\nPARTN=1"
3507+ }
3508+ },
3509+ "/dev/sdb2": {
3510+ "DEVLINKS": "/dev/disk/by-id/wwn-0x0000000000000064-part2 /dev/disk/by-id/scsi-SQEMU_QEMU_HARDDISK_serial-a-part2 /dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_serial-a-part2 /dev/disk/by-partuuid/760493ac-7945-44c5-a6bd-58fcd6632ea7 /dev/disk/by-path/pci-0000:00:04.0-scsi-0:0:1:0-part2 /dev/disk/by-uuid/80ca57a0-60b7-4328-be41-afedf915effd /dev/disk/by-id/scsi-30000000000000064-part2",
3511+ "DEVNAME": "/dev/sdb2",
3512+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio0/host2/target2:0:1/2:0:1:0/block/sdb/sdb2",
3513+ "DEVTYPE": "partition",
3514+ "DM_MULTIPATH_DEVICE_PATH": "1",
3515+ "ID_BUS": "scsi",
3516+ "ID_FS_TYPE": "ext4",
3517+ "ID_FS_USAGE": "filesystem",
3518+ "ID_FS_UUID": "80ca57a0-60b7-4328-be41-afedf915effd",
3519+ "ID_FS_UUID_ENC": "80ca57a0-60b7-4328-be41-afedf915effd",
3520+ "ID_FS_VERSION": "1.0",
3521+ "ID_MODEL": "QEMU_HARDDISK",
3522+ "ID_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3523+ "ID_PART_ENTRY_DISK": "8:16",
3524+ "ID_PART_ENTRY_NUMBER": "2",
3525+ "ID_PART_ENTRY_OFFSET": "4096",
3526+ "ID_PART_ENTRY_SCHEME": "gpt",
3527+ "ID_PART_ENTRY_SIZE": "20965376",
3528+ "ID_PART_ENTRY_TYPE": "0fc63daf-8483-4772-8e79-3d69d8477de4",
3529+ "ID_PART_ENTRY_UUID": "760493ac-7945-44c5-a6bd-58fcd6632ea7",
3530+ "ID_PART_TABLE_TYPE": "gpt",
3531+ "ID_PART_TABLE_UUID": "8261cd75-2234-4dbd-920a-d21a7c083c94",
3532+ "ID_PATH": "pci-0000:00:04.0-scsi-0:0:1:0",
3533+ "ID_PATH_TAG": "pci-0000_00_04_0-scsi-0_0_1_0",
3534+ "ID_REVISION": "2.5+",
3535+ "ID_SCSI": "1",
3536+ "ID_SCSI_INQUIRY": "1",
3537+ "ID_SERIAL": "30000000000000064",
3538+ "ID_SERIAL_SHORT": "0000000000000064",
3539+ "ID_TYPE": "disk",
3540+ "ID_VENDOR": "QEMU",
3541+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3542+ "ID_WWN": "0x0000000000000064",
3543+ "ID_WWN_WITH_EXTENSION": "0x0000000000000064",
3544+ "MAJOR": "8",
3545+ "MINOR": "18",
3546+ "PARTN": "2",
3547+ "SCSI_IDENT_LUN_NAA_LOCAL": "0000000000000064",
3548+ "SCSI_IDENT_LUN_VENDOR": "serial-a",
3549+ "SCSI_IDENT_SERIAL": "serial-a",
3550+ "SCSI_MODEL": "QEMU_HARDDISK",
3551+ "SCSI_MODEL_ENC": "QEMU\\x20HARDDISK\\x20\\x20\\x20",
3552+ "SCSI_REVISION": "2.5+",
3553+ "SCSI_TPGS": "0",
3554+ "SCSI_TYPE": "disk",
3555+ "SCSI_VENDOR": "QEMU",
3556+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3557+ "SUBSYSTEM": "block",
3558+ "SYSTEMD_READY": "0",
3559+ "TAGS": ":systemd:",
3560+ "USEC_INITIALIZED": "1355435",
3561+ "attrs": {
3562+ "alignment_offset": "0",
3563+ "dev": "8:18",
3564+ "discard_alignment": "0",
3565+ "inflight": " 0 0",
3566+ "partition": "2",
3567+ "ro": "0",
3568+ "size": "10734272512",
3569+ "start": "4096",
3570+ "stat": " 129 0 8728 20 0 0 0 0 0 96 4 0 0 0 0",
3571+ "subsystem": "block",
3572+ "uevent": "MAJOR=8\nMINOR=18\nDEVNAME=sdb2\nDEVTYPE=partition\nPARTN=2"
3573+ }
3574+ },
3575+ "/dev/fd0": {
3576+ "DEVNAME": "/dev/fd0",
3577+ "DEVPATH": "/devices/platform/floppy.0/block/fd0",
3578+ "DEVTYPE": "disk",
3579+ "MAJOR": "2",
3580+ "MINOR": "0",
3581+ "SUBSYSTEM": "block",
3582+ "TAGS": ":systemd:",
3583+ "USEC_INITIALIZED": "1182861",
3584+ "attrs": {
3585+ "alignment_offset": "0",
3586+ "bdi": null,
3587+ "capability": "11",
3588+ "dev": "2:0",
3589+ "device": null,
3590+ "discard_alignment": "0",
3591+ "events": "",
3592+ "events_async": "",
3593+ "events_poll_msecs": "-1",
3594+ "ext_range": "1",
3595+ "hidden": "0",
3596+ "inflight": " 0 0",
3597+ "range": "1",
3598+ "removable": "1",
3599+ "ro": "0",
3600+ "size": "4096",
3601+ "stat": " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
3602+ "subsystem": "block",
3603+ "uevent": "MAJOR=2\nMINOR=0\nDEVNAME=fd0\nDEVTYPE=disk"
3604+ }
3605+ },
3606+ "/dev/dm-0": {
3607+ "DEVLINKS": "/dev/disk/by-id/wwn-0x0000000000000064 /dev/disk/by-id/dm-name-mpatha /dev/disk/by-id/dm-uuid-mpath-30000000000000064 /dev/mapper/mpatha",
3608+ "DEVNAME": "/dev/dm-0",
3609+ "DEVPATH": "/devices/virtual/block/dm-0",
3610+ "DEVTYPE": "disk",
3611+ "DM_NAME": "mpatha",
3612+ "DM_STATE": "ACTIVE",
3613+ "DM_SUSPENDED": "0",
3614+ "DM_TABLE_STATE": "LIVE",
3615+ "DM_TYPE": "scsi",
3616+ "DM_UDEV_DISABLE_LIBRARY_FALLBACK_FLAG": "1",
3617+ "DM_UDEV_PRIMARY_SOURCE_FLAG": "1",
3618+ "DM_UDEV_RULES": "1",
3619+ "DM_UDEV_RULES_VSN": "2",
3620+ "DM_UUID": "mpath-30000000000000064",
3621+ "DM_WWN": "0x0000000000000064",
3622+ "ID_PART_TABLE_TYPE": "gpt",
3623+ "ID_PART_TABLE_UUID": "8261cd75-2234-4dbd-920a-d21a7c083c94",
3624+ "MAJOR": "253",
3625+ "MINOR": "0",
3626+ "MPATH_DEVICE_READY": "1",
3627+ "MPATH_SBIN_PATH": "/sbin",
3628+ "SUBSYSTEM": "block",
3629+ "TAGS": ":systemd:",
3630+ "USEC_INITIALIZED": "9639027",
3631+ "attrs": {
3632+ "alignment_offset": "0",
3633+ "bdi": null,
3634+ "capability": "10",
3635+ "dev": "253:0",
3636+ "discard_alignment": "0",
3637+ "events": "",
3638+ "events_async": "",
3639+ "events_poll_msecs": "-1",
3640+ "ext_range": "1",
3641+ "hidden": "0",
3642+ "inflight": " 0 0",
3643+ "range": "1",
3644+ "removable": "0",
3645+ "ro": "0",
3646+ "size": "10737418240",
3647+ "stat": " 321 0 16720 46 0 0 0 0 0 172 0 0 0 0 0",
3648+ "subsystem": "block",
3649+ "uevent": "MAJOR=253\nMINOR=0\nDEVNAME=dm-0\nDEVTYPE=disk"
3650+ },
3651+ "partitiontable": {
3652+ "label": "gpt",
3653+ "id": "8261CD75-2234-4DBD-920A-D21A7C083C94",
3654+ "device": "/dev/dm-0",
3655+ "unit": "sectors",
3656+ "firstlba": 34,
3657+ "lastlba": 20971486,
3658+ "partitions": [
3659+ {
3660+ "node": "/dev/mapper/mpatha-part1",
3661+ "start": 2048,
3662+ "size": 2048,
3663+ "type": "21686148-6449-6E6F-744E-656564454649",
3664+ "uuid": "8088175C-362A-4B46-9603-F3595065FA73"
3665+ },
3666+ {
3667+ "node": "/dev/mapper/mpatha-part2",
3668+ "start": 4096,
3669+ "size": 20965376,
3670+ "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
3671+ "uuid": "760493AC-7945-44C5-A6BD-58FCD6632EA7"
3672+ }
3673+ ]
3674+ }
3675+ },
3676+ "/dev/dm-1": {
3677+ "DEVLINKS": "/dev/disk/by-id/dm-name-mpatha-part1 /dev/mapper/mpatha-part1 /dev/disk/by-id/wwn-0x0000000000000064-part1 /dev/disk/by-partuuid/8088175c-362a-4b46-9603-f3595065fa73 /dev/disk/by-id/dm-uuid-part1-mpath-30000000000000064",
3678+ "DEVNAME": "/dev/dm-1",
3679+ "DEVPATH": "/devices/virtual/block/dm-1",
3680+ "DEVTYPE": "disk",
3681+ "DM_ACTIVATION": "1",
3682+ "DM_MPATH": "mpatha",
3683+ "DM_NAME": "mpatha-part1",
3684+ "DM_PART": "1",
3685+ "DM_STATE": "ACTIVE",
3686+ "DM_SUBSYSTEM_UDEV_FLAG0": "1",
3687+ "DM_SUSPENDED": "0",
3688+ "DM_TABLE_STATE": "LIVE",
3689+ "DM_TYPE": "scsi",
3690+ "DM_UDEV_DISABLE_LIBRARY_FALLBACK_FLAG": "1",
3691+ "DM_UDEV_PRIMARY_SOURCE_FLAG": "1",
3692+ "DM_UDEV_RULES": "1",
3693+ "DM_UDEV_RULES_VSN": "2",
3694+ "DM_UUID": "part1-mpath-30000000000000064",
3695+ "DM_WWN": "0x0000000000000064",
3696+ "ID_PART_ENTRY_DISK": "253:0",
3697+ "ID_PART_ENTRY_NUMBER": "1",
3698+ "ID_PART_ENTRY_OFFSET": "2048",
3699+ "ID_PART_ENTRY_SCHEME": "gpt",
3700+ "ID_PART_ENTRY_SIZE": "2048",
3701+ "ID_PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649",
3702+ "ID_PART_ENTRY_UUID": "8088175c-362a-4b46-9603-f3595065fa73",
3703+ "MAJOR": "253",
3704+ "MINOR": "1",
3705+ "SUBSYSTEM": "block",
3706+ "TAGS": ":systemd:",
3707+ "USEC_INITIALIZED": "10556653",
3708+ "attrs": {
3709+ "alignment_offset": "0",
3710+ "bdi": null,
3711+ "capability": "10",
3712+ "dev": "253:1",
3713+ "discard_alignment": "0",
3714+ "events": "",
3715+ "events_async": "",
3716+ "events_poll_msecs": "-1",
3717+ "ext_range": "1",
3718+ "hidden": "0",
3719+ "inflight": " 0 0",
3720+ "range": "1",
3721+ "removable": "0",
3722+ "ro": "0",
3723+ "size": "1048576",
3724+ "stat": " 78 0 624 8 0 0 0 0 0 56 8 0 0 0 0",
3725+ "subsystem": "block",
3726+ "uevent": "MAJOR=253\nMINOR=1\nDEVNAME=dm-1\nDEVTYPE=disk"
3727+ }
3728+ },
3729+ "/dev/dm-2": {
3730+ "DEVLINKS": "/dev/mapper/mpatha-part2 /dev/disk/by-id/dm-uuid-part2-mpath-30000000000000064 /dev/disk/by-id/dm-name-mpatha-part2 /dev/disk/by-partuuid/760493ac-7945-44c5-a6bd-58fcd6632ea7 /dev/disk/by-uuid/80ca57a0-60b7-4328-be41-afedf915effd /dev/disk/by-id/wwn-0x0000000000000064-part2",
3731+ "DEVNAME": "/dev/dm-2",
3732+ "DEVPATH": "/devices/virtual/block/dm-2",
3733+ "DEVTYPE": "disk",
3734+ "DM_ACTIVATION": "1",
3735+ "DM_MPATH": "mpatha",
3736+ "DM_NAME": "mpatha-part2",
3737+ "DM_PART": "2",
3738+ "DM_STATE": "ACTIVE",
3739+ "DM_SUBSYSTEM_UDEV_FLAG0": "1",
3740+ "DM_SUSPENDED": "0",
3741+ "DM_TABLE_STATE": "LIVE",
3742+ "DM_TYPE": "scsi",
3743+ "DM_UDEV_DISABLE_LIBRARY_FALLBACK_FLAG": "1",
3744+ "DM_UDEV_PRIMARY_SOURCE_FLAG": "1",
3745+ "DM_UDEV_RULES": "1",
3746+ "DM_UDEV_RULES_VSN": "2",
3747+ "DM_UUID": "part2-mpath-30000000000000064",
3748+ "DM_WWN": "0x0000000000000064",
3749+ "ID_FS_TYPE": "ext4",
3750+ "ID_FS_USAGE": "filesystem",
3751+ "ID_FS_UUID": "80ca57a0-60b7-4328-be41-afedf915effd",
3752+ "ID_FS_UUID_ENC": "80ca57a0-60b7-4328-be41-afedf915effd",
3753+ "ID_FS_VERSION": "1.0",
3754+ "ID_PART_ENTRY_DISK": "253:0",
3755+ "ID_PART_ENTRY_NUMBER": "2",
3756+ "ID_PART_ENTRY_OFFSET": "4096",
3757+ "ID_PART_ENTRY_SCHEME": "gpt",
3758+ "ID_PART_ENTRY_SIZE": "20965376",
3759+ "ID_PART_ENTRY_TYPE": "0fc63daf-8483-4772-8e79-3d69d8477de4",
3760+ "ID_PART_ENTRY_UUID": "760493ac-7945-44c5-a6bd-58fcd6632ea7",
3761+ "MAJOR": "253",
3762+ "MINOR": "2",
3763+ "SUBSYSTEM": "block",
3764+ "TAGS": ":systemd:",
3765+ "USEC_INITIALIZED": "10583306",
3766+ "attrs": {
3767+ "alignment_offset": "0",
3768+ "bdi": null,
3769+ "capability": "10",
3770+ "dev": "253:2",
3771+ "discard_alignment": "0",
3772+ "events": "",
3773+ "events_async": "",
3774+ "events_poll_msecs": "-1",
3775+ "ext_range": "1",
3776+ "hidden": "0",
3777+ "inflight": " 0 0",
3778+ "range": "1",
3779+ "removable": "0",
3780+ "ro": "0",
3781+ "size": "10734272512",
3782+ "stat": " 91 0 8032 4 0 0 0 0 0 52 4 0 0 0 0",
3783+ "subsystem": "block",
3784+ "uevent": "MAJOR=253\nMINOR=2\nDEVNAME=dm-2\nDEVTYPE=disk"
3785+ }
3786+ }
3787+ },
3788+ "bcache": {
3789+ "backing": {},
3790+ "caching": {}
3791+ },
3792+ "zfs": {
3793+ "zpools": {}
3794+ }
3795+}
3796diff --git a/tests/data/probert_storage_raid1_partitions.json b/tests/data/probert_storage_raid1_partitions.json
3797new file mode 100644
3798index 0000000..709da5a
3799--- /dev/null
3800+++ b/tests/data/probert_storage_raid1_partitions.json
3801@@ -0,0 +1,951 @@
3802+{
3803+ "zfs": {
3804+ "zpools": {}
3805+ },
3806+ "dmcrypt": {},
3807+ "blockdev": {
3808+ "/dev/sr0": {
3809+ "DEVLINKS": "/dev/disk/by-label/Ubuntu-Server\\x2020.04\\x20LTS\\x20ppc64 /dev/disk/by-id/scsi-0QEMU_QEMU_CD-ROM_drive-scsi0-0-0-0 /dev/dvd /dev/cdrom /dev/disk/by-path/pci-0000:00:02.0-scsi-0:0:0:0 /dev/disk/by-uuid/2019-11-16-08-09-20-00",
3810+ "DEVNAME": "/dev/sr0",
3811+ "DEVPATH": "/devices/pci0000:00/0000:00:02.0/virtio1/host0/target0:0:0/0:0:0:0/block/sr0",
3812+ "DEVTYPE": "disk",
3813+ "ID_BUS": "scsi",
3814+ "ID_CDROM": "1",
3815+ "ID_CDROM_CD": "1",
3816+ "ID_CDROM_DVD": "1",
3817+ "ID_CDROM_MEDIA": "1",
3818+ "ID_CDROM_MEDIA_DVD": "1",
3819+ "ID_CDROM_MEDIA_SESSION_COUNT": "1",
3820+ "ID_CDROM_MEDIA_STATE": "complete",
3821+ "ID_CDROM_MEDIA_TRACK_COUNT": "1",
3822+ "ID_CDROM_MEDIA_TRACK_COUNT_DATA": "1",
3823+ "ID_CDROM_MRW": "1",
3824+ "ID_CDROM_MRW_W": "1",
3825+ "ID_FOR_SEAT": "block-pci-0000_00_02_0-scsi-0_0_0_0",
3826+ "ID_FS_APPLICATION_ID": "GENISOIMAGE\\x20ISO\\x209660\\x2fHFS\\x20FILESYSTEM\\x20CREATOR\\x20\\x28C\\x29\\x201993\\x20E.YOUNGDALE\\x20\\x28C\\x29\\x201997-2006\\x20J.PEARSON\\x2fJ.SCHILLING\\x20\\x28C\\x29\\x202006-2007\\x20CDRKIT\\x20TEAM",
3827+ "ID_FS_LABEL": "Ubuntu-Server_20.04_LTS_ppc64",
3828+ "ID_FS_LABEL_ENC": "Ubuntu-Server\\x2020.04\\x20LTS\\x20ppc64",
3829+ "ID_FS_SYSTEM_ID": "LINUX",
3830+ "ID_FS_TYPE": "iso9660",
3831+ "ID_FS_USAGE": "filesystem",
3832+ "ID_FS_UUID": "2019-11-16-08-09-20-00",
3833+ "ID_FS_UUID_ENC": "2019-11-16-08-09-20-00",
3834+ "ID_MODEL": "QEMU_CD-ROM",
3835+ "ID_MODEL_ENC": "QEMU\\x20CD-ROM\\x20\\x20\\x20\\x20\\x20",
3836+ "ID_PART_TABLE_TYPE": "mac",
3837+ "ID_PATH": "pci-0000:00:02.0-scsi-0:0:0:0",
3838+ "ID_PATH_TAG": "pci-0000_00_02_0-scsi-0_0_0_0",
3839+ "ID_REVISION": "2.5+",
3840+ "ID_SCSI": "1",
3841+ "ID_SCSI_INQUIRY": "1",
3842+ "ID_SERIAL": "0QEMU_QEMU_CD-ROM_drive-scsi0-0-0-0",
3843+ "ID_SERIAL_SHORT": "drive-scsi0-0-0-0",
3844+ "ID_TYPE": "cd",
3845+ "ID_VENDOR": "QEMU",
3846+ "ID_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3847+ "MAJOR": "11",
3848+ "MINOR": "0",
3849+ "SCSI_MODEL": "QEMU_CD-ROM",
3850+ "SCSI_MODEL_ENC": "QEMU\\x20CD-ROM\\x20\\x20\\x20\\x20\\x20",
3851+ "SCSI_REVISION": "2.5+",
3852+ "SCSI_TPGS": "0",
3853+ "SCSI_TYPE": "cd/dvd",
3854+ "SCSI_VENDOR": "QEMU",
3855+ "SCSI_VENDOR_ENC": "QEMU\\x20\\x20\\x20\\x20",
3856+ "SUBSYSTEM": "block",
3857+ "SYSTEMD_MOUNT_DEVICE_BOUND": "1",
3858+ "TAGS": ":uaccess:seat:systemd:",
3859+ "USEC_INITIALIZED": "626053",
3860+ "attrs": {
3861+ "alignment_offset": "0",
3862+ "bdi": null,
3863+ "capability": "119",
3864+ "dev": "11:0",
3865+ "device": null,
3866+ "discard_alignment": "0",
3867+ "events": "media_change eject_request",
3868+ "events_async": "",
3869+ "events_poll_msecs": "-1",
3870+ "ext_range": "1",
3871+ "hidden": "0",
3872+ "inflight": " 0 0",
3873+ "range": "1",
3874+ "removable": "1",
3875+ "ro": "0",
3876+ "size": "911030272",
3877+ "stat": " 6887 0 1582024 636 0 0 0 0 0 17552 64 0 0 0 0",
3878+ "subsystem": "block",
3879+ "uevent": "MAJOR=11\nMINOR=0\nDEVNAME=sr0\nDEVTYPE=disk"
3880+ },
3881+ "partitiontable": {
3882+ "label": "dos",
3883+ "id": "0x64040020",
3884+ "device": "/dev/sr0",
3885+ "unit": "sectors",
3886+ "partitions": [
3887+ {
3888+ "node": "/dev/sr0p1",
3889+ "start": 0,
3890+ "size": 1778756,
3891+ "type": "96",
3892+ "bootable": true
3893+ },
3894+ {
3895+ "node": "/dev/sr0p2",
3896+ "start": 2129919,
3897+ "size": 1936615684,
3898+ "type": "ff"
3899+ },
3900+ {
3901+ "node": "/dev/sr0p3",
3902+ "start": 1024,
3903+ "size": 0,
3904+ "type": "0"
3905+ },
3906+ {
3907+ "node": "/dev/sr0p4",
3908+ "start": 0,
3909+ "size": 0,
3910+ "type": "0"
3911+ }
3912+ ]
3913+ }
3914+ },
3915+ "/dev/vda": {
3916+ "DEVLINKS": "/dev/disk/by-path/pci-0000:00:04.0 /dev/disk/by-path/virtio-pci-0000:00:04.0",
3917+ "DEVNAME": "/dev/vda",
3918+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio2/block/vda",
3919+ "DEVTYPE": "disk",
3920+ "ID_PART_TABLE_TYPE": "gpt",
3921+ "ID_PART_TABLE_UUID": "c43bafba-6f3f-4ac7-8c3a-3808451ad55e",
3922+ "ID_PATH": "pci-0000:00:04.0",
3923+ "ID_PATH_TAG": "pci-0000_00_04_0",
3924+ "MAJOR": "252",
3925+ "MINOR": "0",
3926+ "SUBSYSTEM": "block",
3927+ "TAGS": ":systemd:",
3928+ "USEC_INITIALIZED": "592200",
3929+ "attrs": {
3930+ "alignment_offset": "0",
3931+ "bdi": null,
3932+ "cache_type": "write back",
3933+ "capability": "50",
3934+ "dev": "252:0",
3935+ "device": null,
3936+ "discard_alignment": "0",
3937+ "events": "",
3938+ "events_async": "",
3939+ "events_poll_msecs": "-1",
3940+ "ext_range": "256",
3941+ "hidden": "0",
3942+ "inflight": " 0 0",
3943+ "range": "16",
3944+ "removable": "0",
3945+ "ro": "0",
3946+ "serial": "",
3947+ "size": "6442450944",
3948+ "stat": " 2923 0 473854 187 1654 3072 402696 427 0 3300 12 0 0 0 0",
3949+ "subsystem": "block",
3950+ "uevent": "MAJOR=252\nMINOR=0\nDEVNAME=vda\nDEVTYPE=disk"
3951+ },
3952+ "partitiontable": {
3953+ "label": "gpt",
3954+ "id": "C43BAFBA-6F3F-4AC7-8C3A-3808451AD55E",
3955+ "device": "/dev/vda",
3956+ "unit": "sectors",
3957+ "firstlba": 34,
3958+ "lastlba": 12582878,
3959+ "partitions": [
3960+ {
3961+ "node": "/dev/vda1",
3962+ "start": 2048,
3963+ "size": 16384,
3964+ "type": "9E1A2D38-C612-4316-AA26-8B49521E5A8B",
3965+ "uuid": "CFECC68C-325E-4066-8F6F-0FE4F57D1AF5"
3966+ },
3967+ {
3968+ "node": "/dev/vda2",
3969+ "start": 18432,
3970+ "size": 2097152,
3971+ "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
3972+ "uuid": "D29ADB04-DF89-4D54-9B50-8DB8EEDAB339"
3973+ }
3974+ ]
3975+ }
3976+ },
3977+ "/dev/vda1": {
3978+ "DEVLINKS": "/dev/disk/by-path/pci-0000:00:04.0-part1 /dev/disk/by-partuuid/cfecc68c-325e-4066-8f6f-0fe4f57d1af5 /dev/disk/by-path/virtio-pci-0000:00:04.0-part1",
3979+ "DEVNAME": "/dev/vda1",
3980+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio2/block/vda/vda1",
3981+ "DEVTYPE": "partition",
3982+ "ID_PART_ENTRY_DISK": "252:0",
3983+ "ID_PART_ENTRY_NUMBER": "1",
3984+ "ID_PART_ENTRY_OFFSET": "2048",
3985+ "ID_PART_ENTRY_SCHEME": "gpt",
3986+ "ID_PART_ENTRY_SIZE": "16384",
3987+ "ID_PART_ENTRY_TYPE": "9e1a2d38-c612-4316-aa26-8b49521e5a8b",
3988+ "ID_PART_ENTRY_UUID": "cfecc68c-325e-4066-8f6f-0fe4f57d1af5",
3989+ "ID_PART_TABLE_TYPE": "gpt",
3990+ "ID_PART_TABLE_UUID": "c43bafba-6f3f-4ac7-8c3a-3808451ad55e",
3991+ "ID_PATH": "pci-0000:00:04.0",
3992+ "ID_PATH_TAG": "pci-0000_00_04_0",
3993+ "ID_SCSI": "1",
3994+ "MAJOR": "252",
3995+ "MINOR": "1",
3996+ "PARTN": "1",
3997+ "SUBSYSTEM": "block",
3998+ "TAGS": ":systemd:",
3999+ "USEC_INITIALIZED": "62680508",
4000+ "attrs": {
4001+ "alignment_offset": "0",
4002+ "dev": "252:1",
4003+ "discard_alignment": "0",
4004+ "inflight": " 0 0",
4005+ "partition": "1",
4006+ "ro": "0",
4007+ "size": "8388608",
4008+ "start": "2048",
4009+ "stat": " 449 0 74496 31 6 3 1152 0 0 620 0 0 0 0 0",
4010+ "subsystem": "block",
4011+ "uevent": "MAJOR=252\nMINOR=1\nDEVNAME=vda1\nDEVTYPE=partition\nPARTN=1"
4012+ }
4013+ },
4014+ "/dev/vda2": {
4015+ "DEVLINKS": "/dev/disk/by-partuuid/d29adb04-df89-4d54-9b50-8db8eedab339 /dev/disk/by-uuid/0a3b5403-d358-4f30-9094-e5a19d60b222 /dev/disk/by-path/pci-0000:00:04.0-part2 /dev/disk/by-path/virtio-pci-0000:00:04.0-part2",
4016+ "DEVNAME": "/dev/vda2",
4017+ "DEVPATH": "/devices/pci0000:00/0000:00:04.0/virtio2/block/vda/vda2",
4018+ "DEVTYPE": "partition",
4019+ "ID_FS_TYPE": "ext4",
4020+ "ID_FS_USAGE": "filesystem",
4021+ "ID_FS_UUID": "0a3b5403-d358-4f30-9094-e5a19d60b222",
4022+ "ID_FS_UUID_ENC": "0a3b5403-d358-4f30-9094-e5a19d60b222",
4023+ "ID_FS_VERSION": "1.0",
4024+ "ID_PART_ENTRY_DISK": "252:0",
4025+ "ID_PART_ENTRY_NUMBER": "2",
4026+ "ID_PART_ENTRY_OFFSET": "18432",
4027+ "ID_PART_ENTRY_SCHEME": "gpt",
4028+ "ID_PART_ENTRY_SIZE": "2097152",
4029+ "ID_PART_ENTRY_TYPE": "0fc63daf-8483-4772-8e79-3d69d8477de4",
4030+ "ID_PART_ENTRY_UUID": "d29adb04-df89-4d54-9b50-8db8eedab339",
4031+ "ID_PART_TABLE_TYPE": "gpt",
4032+ "ID_PART_TABLE_UUID": "c43bafba-6f3f-4ac7-8c3a-3808451ad55e",
4033+ "ID_PATH": "pci-0000:00:04.0",
4034+ "ID_PATH_TAG": "pci-0000_00_04_0",
4035+ "ID_SCSI": "1",
4036+ "MAJOR": "252",
4037+ "MINOR": "2",
4038+ "PARTN": "2",
4039+ "SUBSYSTEM": "block",
4040+ "TAGS": ":systemd:",
4041+ "USEC_INITIALIZED": "62650105",
4042+ "attrs": {
4043+ "alignment_offset": "0",
4044+ "dev": "252:2",
4045+ "discard_alignment": "0",
4046+ "inflight": " 0 0",
4047+ "partition": "2",
4048+ "ro": "0",
4049+ "size": "1073741824",
4050+ "start": "18432",
4051+ "stat": " 322 0 50394 22 1522 2894 310648 343 0 756 12 0 0 0 0",
4052+ "subsystem": "block",
4053+ "uevent": "MAJOR=252\nMINOR=2\nDEVNAME=vda2\nDEVTYPE=partition\nPARTN=2"
4054+ }
4055+ },
4056+ "/dev/vdb": {
4057+ "DEVLINKS": "/dev/disk/by-path/virtio-pci-0000:00:05.0 /dev/disk/by-path/pci-0000:00:05.0",
4058+ "DEVNAME": "/dev/vdb",
4059+ "DEVPATH": "/devices/pci0000:00/0000:00:05.0/virtio3/block/vdb",
4060+ "DEVTYPE": "disk",
4061+ "ID_PART_TABLE_TYPE": "gpt",
4062+ "ID_PART_TABLE_UUID": "0c49cc0b-d527-4a88-a61a-4684bcdf8a5e",
4063+ "ID_PATH": "pci-0000:00:05.0",
4064+ "ID_PATH_TAG": "pci-0000_00_05_0",
4065+ "MAJOR": "252",
4066+ "MINOR": "16",
4067+ "SUBSYSTEM": "block",
4068+ "TAGS": ":systemd:",
4069+ "USEC_INITIALIZED": "600195",
4070+ "attrs": {
4071+ "alignment_offset": "0",
4072+ "bdi": null,
4073+ "cache_type": "write back",
4074+ "capability": "50",
4075+ "dev": "252:16",
4076+ "device": null,
4077+ "discard_alignment": "0",
4078+ "events": "",
4079+ "events_async": "",
4080+ "events_poll_msecs": "-1",
4081+ "ext_range": "256",
4082+ "hidden": "0",
4083+ "inflight": " 0 0",
4084+ "range": "16",
4085+ "removable": "0",
4086+ "ro": "0",
4087+ "serial": "",
4088+ "size": "6442450944",
4089+ "stat": " 20457 64216 10072248 3184 39416 65487 5219881 24281 0 27324 8488 0 0 0 0",
4090+ "subsystem": "block",
4091+ "uevent": "MAJOR=252\nMINOR=16\nDEVNAME=vdb\nDEVTYPE=disk"
4092+ },
4093+ "partitiontable": {
4094+ "label": "gpt",
4095+ "id": "0C49CC0B-D527-4A88-A61A-4684BCDF8A5E",
4096+ "device": "/dev/vdb",
4097+ "unit": "sectors",
4098+ "firstlba": 34,
4099+ "lastlba": 12582878,
4100+ "partitions": [
4101+ {
4102+ "node": "/dev/vdb1",
4103+ "start": 2048,
4104+ "size": 8388608,
4105+ "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
4106+ "uuid": "408078B3-15C1-4315-A376-C2F1E3F13A63"
4107+ }
4108+ ]
4109+ }
4110+ },
4111+ "/dev/vdb1": {
4112+ "DEVLINKS": "/dev/disk/by-partuuid/408078b3-15c1-4315-a376-c2f1e3f13a63 /dev/disk/by-path/virtio-pci-0000:00:05.0-part1 /dev/disk/by-path/pci-0000:00:05.0-part1",
4113+ "DEVNAME": "/dev/vdb1",
4114+ "DEVPATH": "/devices/pci0000:00/0000:00:05.0/virtio3/block/vdb/vdb1",
4115+ "DEVTYPE": "partition",
4116+ "ID_FS_LABEL": "ubuntu-server:1",
4117+ "ID_FS_LABEL_ENC": "ubuntu-server:1",
4118+ "ID_FS_TYPE": "linux_raid_member",
4119+ "ID_FS_USAGE": "raid",
4120+ "ID_FS_UUID": "1e4e3ac4-abd1-ee76-6fdb-871a4d6677af",
4121+ "ID_FS_UUID_ENC": "1e4e3ac4-abd1-ee76-6fdb-871a4d6677af",
4122+ "ID_FS_UUID_SUB": "f1366f7f-319f-81e6-7550-edb94d240727",
4123+ "ID_FS_UUID_SUB_ENC": "f1366f7f-319f-81e6-7550-edb94d240727",
4124+ "ID_FS_VERSION": "1.2",
4125+ "ID_PART_ENTRY_DISK": "252:16",
4126+ "ID_PART_ENTRY_NUMBER": "1",
4127+ "ID_PART_ENTRY_OFFSET": "2048",
4128+ "ID_PART_ENTRY_SCHEME": "gpt",
4129+ "ID_PART_ENTRY_SIZE": "8388608",
4130+ "ID_PART_ENTRY_TYPE": "0fc63daf-8483-4772-8e79-3d69d8477de4",
4131+ "ID_PART_ENTRY_UUID": "408078b3-15c1-4315-a376-c2f1e3f13a63",
4132+ "ID_PART_TABLE_TYPE": "gpt",
4133+ "ID_PART_TABLE_UUID": "0c49cc0b-d527-4a88-a61a-4684bcdf8a5e",
4134+ "ID_PATH": "pci-0000:00:05.0",
4135+ "ID_PATH_TAG": "pci-0000_00_05_0",
4136+ "ID_SCSI": "1",
4137+ "MAJOR": "252",
4138+ "MINOR": "17",
4139+ "PARTN": "1",
4140+ "SUBSYSTEM": "block",
4141+ "TAGS": ":systemd:",
4142+ "USEC_INITIALIZED": "52064605",
4143+ "attrs": {
4144+ "alignment_offset": "0",
4145+ "dev": "252:17",
4146+ "discard_alignment": "0",
4147+ "inflight": " 0 0",
4148+ "partition": "1",
4149+ "ro": "0",
4150+ "size": "4294967296",
4151+ "start": "2048",
4152+ "stat": " 19443 64216 9896724 3125 39280 65397 5207337 24270 0 26204 8488 0 0 0 0",
4153+ "subsystem": "block",
4154+ "uevent": "MAJOR=252\nMINOR=17\nDEVNAME=vdb1\nDEVTYPE=partition\nPARTN=1"
4155+ }
4156+ },
4157+ "/dev/vdc": {
4158+ "DEVLINKS": "/dev/disk/by-path/pci-0000:00:06.0 /dev/disk/by-path/virtio-pci-0000:00:06.0",
4159+ "DEVNAME": "/dev/vdc",
4160+ "DEVPATH": "/devices/pci0000:00/0000:00:06.0/virtio4/block/vdc",
4161+ "DEVTYPE": "disk",
4162+ "ID_PART_TABLE_TYPE": "gpt",
4163+ "ID_PART_TABLE_UUID": "c805b10b-ded9-4519-947c-bcad1d6e81ed",
4164+ "ID_PATH": "pci-0000:00:06.0",
4165+ "ID_PATH_TAG": "pci-0000_00_06_0",
4166+ "MAJOR": "252",
4167+ "MINOR": "32",
4168+ "SUBSYSTEM": "block",
4169+ "TAGS": ":systemd:",
4170+ "USEC_INITIALIZED": "600459",
4171+ "attrs": {
4172+ "alignment_offset": "0",
4173+ "bdi": null,
4174+ "cache_type": "write back",
4175+ "capability": "50",
4176+ "dev": "252:32",
4177+ "device": null,
4178+ "discard_alignment": "0",
4179+ "events": "",
4180+ "events_async": "",
4181+ "events_poll_msecs": "-1",
4182+ "ext_range": "256",
4183+ "hidden": "0",
4184+ "inflight": " 0 0",
4185+ "range": "16",
4186+ "removable": "0",
4187+ "ro": "0",
4188+ "serial": "",
4189+ "size": "6442450944",
4190+ "stat": " 2791 749 264502 172 43737 126620 13598249 40301 0 19796 23016 0 0 0 0",
4191+ "subsystem": "block",
4192+ "uevent": "MAJOR=252\nMINOR=32\nDEVNAME=vdc\nDEVTYPE=disk"
4193+ },
4194+ "partitiontable": {
4195+ "label": "gpt",
4196+ "id": "C805B10B-DED9-4519-947C-BCAD1D6E81ED",
4197+ "device": "/dev/vdc",
4198+ "unit": "sectors",
4199+ "firstlba": 34,
4200+ "lastlba": 12582878,
4201+ "partitions": [
4202+ {
4203+ "node": "/dev/vdc1",
4204+ "start": 2048,
4205+ "size": 8388608,
4206+ "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
4207+ "uuid": "69441F30-8321-4A70-A4CF-6A4B92393CD6"
4208+ }
4209+ ]
4210+ }
4211+ },
4212+ "/dev/vdc1": {
4213+ "DEVLINKS": "/dev/disk/by-partuuid/69441f30-8321-4a70-a4cf-6a4b92393cd6 /dev/disk/by-path/pci-0000:00:06.0-part1 /dev/disk/by-path/virtio-pci-0000:00:06.0-part1",
4214+ "DEVNAME": "/dev/vdc1",
4215+ "DEVPATH": "/devices/pci0000:00/0000:00:06.0/virtio4/block/vdc/vdc1",
4216+ "DEVTYPE": "partition",
4217+ "ID_FS_LABEL": "ubuntu-server:1",
4218+ "ID_FS_LABEL_ENC": "ubuntu-server:1",
4219+ "ID_FS_TYPE": "linux_raid_member",
4220+ "ID_FS_USAGE": "raid",
4221+ "ID_FS_UUID": "1e4e3ac4-abd1-ee76-6fdb-871a4d6677af",
4222+ "ID_FS_UUID_ENC": "1e4e3ac4-abd1-ee76-6fdb-871a4d6677af",
4223+ "ID_FS_UUID_SUB": "52929a1d-d9f0-e3a9-c16e-c40433201114",
4224+ "ID_FS_UUID_SUB_ENC": "52929a1d-d9f0-e3a9-c16e-c40433201114",
4225+ "ID_FS_VERSION": "1.2",
4226+ "ID_PART_ENTRY_DISK": "252:32",
4227+ "ID_PART_ENTRY_NUMBER": "1",
4228+ "ID_PART_ENTRY_OFFSET": "2048",
4229+ "ID_PART_ENTRY_SCHEME": "gpt",
4230+ "ID_PART_ENTRY_SIZE": "8388608",
4231+ "ID_PART_ENTRY_TYPE": "0fc63daf-8483-4772-8e79-3d69d8477de4",
4232+ "ID_PART_ENTRY_UUID": "69441f30-8321-4a70-a4cf-6a4b92393cd6",
4233+ "ID_PART_TABLE_TYPE": "gpt",
4234+ "ID_PART_TABLE_UUID": "c805b10b-ded9-4519-947c-bcad1d6e81ed",
4235+ "ID_PATH": "pci-0000:00:06.0",
4236+ "ID_PATH_TAG": "pci-0000_00_06_0",
4237+ "ID_SCSI": "1",
4238+ "MAJOR": "252",
4239+ "MINOR": "33",
4240+ "PARTN": "1",
4241+ "SUBSYSTEM": "block",
4242+ "TAGS": ":systemd:",
4243+ "USEC_INITIALIZED": "52548543",
4244+ "attrs": {
4245+ "alignment_offset": "0",
4246+ "dev": "252:33",
4247+ "discard_alignment": "0",
4248+ "inflight": " 0 0",
4249+ "partition": "1",
4250+ "ro": "0",
4251+ "size": "4294967296",
4252+ "start": "2048",
4253+ "stat": " 1756 749 86802 112 43601 126530 13585705 40291 0 18404 23016 0 0 0 0",
4254+ "subsystem": "block",
4255+ "uevent": "MAJOR=252\nMINOR=33\nDEVNAME=vdc1\nDEVTYPE=partition\nPARTN=1"
4256+ }
4257+ },
4258+ "/dev/vdd": {
4259+ "DEVLINKS": "/dev/disk/by-path/virtio-pci-0000:00:07.0 /dev/disk/by-uuid/00c629d6-06ab-4dfd-b21e-c3186f34105d /dev/disk/by-path/pci-0000:00:07.0",
4260+ "DEVNAME": "/dev/vdd",
4261+ "DEVPATH": "/devices/pci0000:00/0000:00:07.0/virtio5/block/vdd",
4262+ "DEVTYPE": "disk",
4263+ "ID_FS_TYPE": "ext2",
4264+ "ID_FS_USAGE": "filesystem",
4265+ "ID_FS_UUID": "00c629d6-06ab-4dfd-b21e-c3186f34105d",
4266+ "ID_FS_UUID_ENC": "00c629d6-06ab-4dfd-b21e-c3186f34105d",
4267+ "ID_FS_VERSION": "1.0",
4268+ "ID_PATH": "pci-0000:00:07.0",
4269+ "ID_PATH_TAG": "pci-0000_00_07_0",
4270+ "MAJOR": "252",
4271+ "MINOR": "48",
4272+ "SUBSYSTEM": "block",
4273+ "TAGS": ":systemd:",
4274+ "USEC_INITIALIZED": "563993",
4275+ "attrs": {
4276+ "alignment_offset": "0",
4277+ "bdi": null,
4278+ "cache_type": "write back",
4279+ "capability": "50",
4280+ "dev": "252:48",
4281+ "device": null,
4282+ "discard_alignment": "0",
4283+ "events": "",
4284+ "events_async": "",
4285+ "events_poll_msecs": "-1",
4286+ "ext_range": "256",
4287+ "hidden": "0",
4288+ "inflight": " 0 0",
4289+ "range": "16",
4290+ "removable": "0",
4291+ "ro": "1",
4292+ "serial": "",
4293+ "size": "1048576",
4294+ "stat": " 97 0 3367 4 2 0 0 0 0 76 0 0 0 0 0",
4295+ "subsystem": "block",
4296+ "uevent": "MAJOR=252\nMINOR=48\nDEVNAME=vdd\nDEVTYPE=disk"
4297+ }
4298+ },
4299+ "/dev/md1": {
4300+ "DEVLINKS": "/dev/disk/by-id/md-name-ubuntu-server:1 /dev/disk/by-id/md-uuid-1e4e3ac4:abd1ee76:6fdb871a:4d6677af",
4301+ "DEVNAME": "/dev/md1",
4302+ "DEVPATH": "/devices/virtual/block/md1",
4303+ "DEVTYPE": "disk",
4304+ "ID_PART_TABLE_TYPE": "gpt",
4305+ "ID_PART_TABLE_UUID": "18f354ee-3557-4a37-9508-c947edb21b5c",
4306+ "MAJOR": "9",
4307+ "MD_DEVICES": "2",
4308+ "MD_DEVICE_ev_vdb1_DEV": "/dev/vdb1",
4309+ "MD_DEVICE_ev_vdb1_ROLE": "0",
4310+ "MD_DEVICE_ev_vdc1_DEV": "/dev/vdc1",
4311+ "MD_DEVICE_ev_vdc1_ROLE": "1",
4312+ "MD_LEVEL": "raid1",
4313+ "MD_METADATA": "1.2",
4314+ "MD_NAME": "ubuntu-server:1",
4315+ "MD_UUID": "1e4e3ac4:abd1ee76:6fdb871a:4d6677af",
4316+ "MINOR": "1",
4317+ "SUBSYSTEM": "block",
4318+ "SYSTEMD_WANTS": "mdmonitor.service",
4319+ "TAGS": ":systemd:",
4320+ "USEC_INITIALIZED": "52972570",
4321+ "attrs": {
4322+ "alignment_offset": "0",
4323+ "bdi": null,
4324+ "capability": "50",
4325+ "dev": "9:1",
4326+ "discard_alignment": "0",
4327+ "events": "",
4328+ "events_async": "",
4329+ "events_poll_msecs": "-1",
4330+ "ext_range": "256",
4331+ "hidden": "0",
4332+ "inflight": " 0 0",
4333+ "range": "1",
4334+ "removable": "0",
4335+ "ro": "0",
4336+ "size": "4289724416",
4337+ "stat": " 20095 0 1539578 0 105192 0 5220984 0 0 0 0 0 0 0 0",
4338+ "subsystem": "block",
4339+ "uevent": "MAJOR=9\nMINOR=1\nDEVNAME=md1\nDEVTYPE=disk"
4340+ },
4341+ "partitiontable": {
4342+ "label": "gpt",
4343+ "id": "18F354EE-3557-4A37-9508-C947EDB21B5C",
4344+ "device": "/dev/md1",
4345+ "unit": "sectors",
4346+ "firstlba": 34,
4347+ "lastlba": 8378334,
4348+ "partitions": [
4349+ {
4350+ "node": "/dev/md1p1",
4351+ "start": 2048,
4352+ "size": 8370176,
4353+ "type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
4354+ "uuid": "5B3F90C0-2432-45C1-98E2-6E9E6649430E"
4355+ }
4356+ ]
4357+ }
4358+ },
4359+ "/dev/md1p1": {
4360+ "DEVLINKS": "/dev/disk/by-uuid/b312e605-d330-43ad-aa9b-2d503412977d /dev/disk/by-id/md-uuid-1e4e3ac4:abd1ee76:6fdb871a:4d6677af-part1 /dev/disk/by-id/md-name-ubuntu-server:1-part1",
4361+ "DEVNAME": "/dev/md1p1",
4362+ "DEVPATH": "/devices/virtual/block/md1/md1p1",
4363+ "DEVTYPE": "partition",
4364+ "ID_FS_TYPE": "ext4",
4365+ "ID_FS_USAGE": "filesystem",
4366+ "ID_FS_UUID": "b312e605-d330-43ad-aa9b-2d503412977d",
4367+ "ID_FS_UUID_ENC": "b312e605-d330-43ad-aa9b-2d503412977d",
4368+ "ID_FS_VERSION": "1.0",
4369+ "ID_PART_ENTRY_DISK": "9:1",
4370+ "ID_PART_ENTRY_NUMBER": "1",
4371+ "ID_PART_ENTRY_OFFSET": "2048",
4372+ "ID_PART_ENTRY_SCHEME": "gpt",
4373+ "ID_PART_ENTRY_SIZE": "8370176",
4374+ "ID_PART_ENTRY_TYPE": "0fc63daf-8483-4772-8e79-3d69d8477de4",
4375+ "ID_PART_ENTRY_UUID": "5b3f90c0-2432-45c1-98e2-6e9e6649430e",
4376+ "ID_SCSI": "1",
4377+ "MAJOR": "259",
4378+ "MD_DEVICES": "2",
4379+ "MD_DEVICE_ev_vdb1_DEV": "/dev/vdb1",
4380+ "MD_DEVICE_ev_vdb1_ROLE": "0",
4381+ "MD_DEVICE_ev_vdc1_DEV": "/dev/vdc1",
4382+ "MD_DEVICE_ev_vdc1_ROLE": "1",
4383+ "MD_LEVEL": "raid1",
4384+ "MD_METADATA": "1.2",
4385+ "MD_NAME": "ubuntu-server:1",
4386+ "MD_UUID": "1e4e3ac4:abd1ee76:6fdb871a:4d6677af",
4387+ "MINOR": "1",
4388+ "PARTN": "1",
4389+ "SUBSYSTEM": "block",
4390+ "SYSTEMD_WANTS": "mdmonitor.service",
4391+ "TAGS": ":systemd:",
4392+ "USEC_INITIALIZED": "62053949",
4393+ "attrs": {
4394+ "alignment_offset": "0",
4395+ "dev": "259:1",
4396+ "discard_alignment": "0",
4397+ "inflight": " 0 0",
4398+ "partition": "1",
4399+ "ro": "0",
4400+ "size": "4285530112",
4401+ "start": "2048",
4402+ "stat": " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
4403+ "subsystem": "block",
4404+ "uevent": "MAJOR=259\nMINOR=1\nDEVNAME=md1p1\nDEVTYPE=partition\nPARTN=1"
4405+ }
4406+ }
4407+ },
4408+ "bcache": {
4409+ "backing": {},
4410+ "caching": {}
4411+ },
4412+ "lvm": {},
4413+ "mount": [
4414+ {
4415+ "target": "/",
4416+ "source": "/cow",
4417+ "fstype": "overlay",
4418+ "options": "rw,relatime,lowerdir=/installer.squashfs:/filesystem.squashfs,upperdir=/cow/upper,workdir=/cow/work",
4419+ "children": [
4420+ {
4421+ "target": "/sys",
4422+ "source": "sysfs",
4423+ "fstype": "sysfs",
4424+ "options": "rw,nosuid,nodev,noexec,relatime",
4425+ "children": [
4426+ {
4427+ "target": "/sys/kernel/security",
4428+ "source": "securityfs",
4429+ "fstype": "securityfs",
4430+ "options": "rw,nosuid,nodev,noexec,relatime"
4431+ },
4432+ {
4433+ "target": "/sys/fs/cgroup",
4434+ "source": "tmpfs",
4435+ "fstype": "tmpfs",
4436+ "options": "ro,nosuid,nodev,noexec,mode=755",
4437+ "children": [
4438+ {
4439+ "target": "/sys/fs/cgroup/unified",
4440+ "source": "cgroup2",
4441+ "fstype": "cgroup2",
4442+ "options": "rw,nosuid,nodev,noexec,relatime,nsdelegate"
4443+ },
4444+ {
4445+ "target": "/sys/fs/cgroup/systemd",
4446+ "source": "cgroup",
4447+ "fstype": "cgroup",
4448+ "options": "rw,nosuid,nodev,noexec,relatime,xattr,name=systemd"
4449+ },
4450+ {
4451+ "target": "/sys/fs/cgroup/freezer",
4452+ "source": "cgroup",
4453+ "fstype": "cgroup",
4454+ "options": "rw,nosuid,nodev,noexec,relatime,freezer"
4455+ },
4456+ {
4457+ "target": "/sys/fs/cgroup/pids",
4458+ "source": "cgroup",
4459+ "fstype": "cgroup",
4460+ "options": "rw,nosuid,nodev,noexec,relatime,pids"
4461+ },
4462+ {
4463+ "target": "/sys/fs/cgroup/cpuset",
4464+ "source": "cgroup",
4465+ "fstype": "cgroup",
4466+ "options": "rw,nosuid,nodev,noexec,relatime,cpuset"
4467+ },
4468+ {
4469+ "target": "/sys/fs/cgroup/cpu,cpuacct",
4470+ "source": "cgroup",
4471+ "fstype": "cgroup",
4472+ "options": "rw,nosuid,nodev,noexec,relatime,cpu,cpuacct"
4473+ },
4474+ {
4475+ "target": "/sys/fs/cgroup/blkio",
4476+ "source": "cgroup",
4477+ "fstype": "cgroup",
4478+ "options": "rw,nosuid,nodev,noexec,relatime,blkio"
4479+ },
4480+ {
4481+ "target": "/sys/fs/cgroup/net_cls,net_prio",
4482+ "source": "cgroup",
4483+ "fstype": "cgroup",
4484+ "options": "rw,nosuid,nodev,noexec,relatime,net_cls,net_prio"
4485+ },
4486+ {
4487+ "target": "/sys/fs/cgroup/rdma",
4488+ "source": "cgroup",
4489+ "fstype": "cgroup",
4490+ "options": "rw,nosuid,nodev,noexec,relatime,rdma"
4491+ },
4492+ {
4493+ "target": "/sys/fs/cgroup/devices",
4494+ "source": "cgroup",
4495+ "fstype": "cgroup",
4496+ "options": "rw,nosuid,nodev,noexec,relatime,devices"
4497+ },
4498+ {
4499+ "target": "/sys/fs/cgroup/memory",
4500+ "source": "cgroup",
4501+ "fstype": "cgroup",
4502+ "options": "rw,nosuid,nodev,noexec,relatime,memory"
4503+ },
4504+ {
4505+ "target": "/sys/fs/cgroup/perf_event",
4506+ "source": "cgroup",
4507+ "fstype": "cgroup",
4508+ "options": "rw,nosuid,nodev,noexec,relatime,perf_event"
4509+ },
4510+ {
4511+ "target": "/sys/fs/cgroup/hugetlb",
4512+ "source": "cgroup",
4513+ "fstype": "cgroup",
4514+ "options": "rw,nosuid,nodev,noexec,relatime,hugetlb"
4515+ }
4516+ ]
4517+ },
4518+ {
4519+ "target": "/sys/fs/pstore",
4520+ "source": "pstore",
4521+ "fstype": "pstore",
4522+ "options": "rw,nosuid,nodev,noexec,relatime"
4523+ },
4524+ {
4525+ "target": "/sys/fs/bpf",
4526+ "source": "bpf",
4527+ "fstype": "bpf",
4528+ "options": "rw,nosuid,nodev,noexec,relatime,mode=700"
4529+ },
4530+ {
4531+ "target": "/sys/kernel/debug",
4532+ "source": "debugfs",
4533+ "fstype": "debugfs",
4534+ "options": "rw,nosuid,nodev,noexec,relatime"
4535+ },
4536+ {
4537+ "target": "/sys/fs/fuse/connections",
4538+ "source": "fusectl",
4539+ "fstype": "fusectl",
4540+ "options": "rw,nosuid,nodev,noexec,relatime"
4541+ },
4542+ {
4543+ "target": "/sys/kernel/config",
4544+ "source": "configfs",
4545+ "fstype": "configfs",
4546+ "options": "rw,nosuid,nodev,noexec,relatime"
4547+ }
4548+ ]
4549+ },
4550+ {
4551+ "target": "/proc",
4552+ "source": "proc",
4553+ "fstype": "proc",
4554+ "options": "rw,nosuid,nodev,noexec,relatime",
4555+ "children": [
4556+ {
4557+ "target": "/proc/sys/fs/binfmt_misc",
4558+ "source": "systemd-1",
4559+ "fstype": "autofs",
4560+ "options": "rw,relatime,fd=27,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=13520"
4561+ }
4562+ ]
4563+ },
4564+ {
4565+ "target": "/dev",
4566+ "source": "udev",
4567+ "fstype": "devtmpfs",
4568+ "options": "rw,nosuid,relatime,size=956672k,nr_inodes=14948,mode=755",
4569+ "children": [
4570+ {
4571+ "target": "/dev/pts",
4572+ "source": "devpts",
4573+ "fstype": "devpts",
4574+ "options": "rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000"
4575+ },
4576+ {
4577+ "target": "/dev/shm",
4578+ "source": "tmpfs",
4579+ "fstype": "tmpfs",
4580+ "options": "rw,nosuid,nodev"
4581+ },
4582+ {
4583+ "target": "/dev/mqueue",
4584+ "source": "mqueue",
4585+ "fstype": "mqueue",
4586+ "options": "rw,nosuid,nodev,noexec,relatime"
4587+ }
4588+ ]
4589+ },
4590+ {
4591+ "target": "/run",
4592+ "source": "tmpfs",
4593+ "fstype": "tmpfs",
4594+ "options": "rw,nosuid,noexec,relatime,size=206720k,mode=755",
4595+ "children": [
4596+ {
4597+ "target": "/run/lock",
4598+ "source": "tmpfs",
4599+ "fstype": "tmpfs",
4600+ "options": "rw,nosuid,nodev,noexec,relatime,size=5120k"
4601+ }
4602+ ]
4603+ },
4604+ {
4605+ "target": "/cdrom",
4606+ "source": "/dev/sr0",
4607+ "fstype": "iso9660",
4608+ "options": "ro,noatime,nojoliet,check=s,map=n,blocksize=2048"
4609+ },
4610+ {
4611+ "target": "/rofs",
4612+ "source": "/dev/loop0",
4613+ "fstype": "squashfs",
4614+ "options": "ro,noatime"
4615+ },
4616+ {
4617+ "target": "/usr/lib/modules",
4618+ "source": "/dev/loop2",
4619+ "fstype": "squashfs",
4620+ "options": "ro,relatime"
4621+ },
4622+ {
4623+ "target": "/media/filesystem",
4624+ "source": "/dev/loop0",
4625+ "fstype": "squashfs",
4626+ "options": "ro,relatime"
4627+ },
4628+ {
4629+ "target": "/tmp",
4630+ "source": "tmpfs",
4631+ "fstype": "tmpfs",
4632+ "options": "rw,nosuid,nodev,relatime"
4633+ },
4634+ {
4635+ "target": "/subiquity_config",
4636+ "source": "/dev/vdd",
4637+ "fstype": "ext4",
4638+ "options": "ro,relatime"
4639+ },
4640+ {
4641+ "target": "/snap/core/8041",
4642+ "source": "/dev/loop3",
4643+ "fstype": "squashfs",
4644+ "options": "ro,nodev,relatime"
4645+ },
4646+ {
4647+ "target": "/snap/subiquity/1335",
4648+ "source": "/dev/loop4",
4649+ "fstype": "squashfs",
4650+ "options": "ro,nodev,relatime"
4651+ },
4652+ {
4653+ "target": "/target",
4654+ "source": "/dev/md1p1",
4655+ "fstype": "ext4",
4656+ "options": "rw,relatime",
4657+ "children": [
4658+ {
4659+ "target": "/target/run",
4660+ "source": "tmpfs",
4661+ "fstype": "tmpfs",
4662+ "options": "rw,nosuid,noexec,relatime,size=206720k,mode=755"
4663+ },
4664+ {
4665+ "target": "/target/boot",
4666+ "source": "/dev/vda2",
4667+ "fstype": "ext4",
4668+ "options": "rw,relatime"
4669+ },
4670+ {
4671+ "target": "/target/etc/apt",
4672+ "source": "overlay",
4673+ "fstype": "overlay",
4674+ "options": "rw,relatime,lowerdir=/target/etc/apt,upperdir=/tmp/tmp.dFi4ai53r1/upper,workdir=/tmp/tmp.dFi4ai53r1/work"
4675+ },
4676+ {
4677+ "target": "/target/cdrom",
4678+ "source": "/dev/sr0",
4679+ "fstype": "iso9660",
4680+ "options": "ro,noatime,nojoliet,check=s,map=n,blocksize=2048"
4681+ }
4682+ ]
4683+ }
4684+ ]
4685+ }
4686+ ],
4687+ "filesystem": {
4688+ "/dev/sr0": {
4689+ "APPLICATION_ID": "GENISOIMAGE\\x20ISO\\x209660\\x2fHFS\\x20FILESYSTEM\\x20CREATOR\\x20\\x28C\\x29\\x201993\\x20E.YOUNGDALE\\x20\\x28C\\x29\\x201997-2006\\x20J.PEARSON\\x2fJ.SCHILLING\\x20\\x28C\\x29\\x202006-2007\\x20CDRKIT\\x20TEAM",
4690+ "LABEL": "Ubuntu-Server_20.04_LTS_ppc64",
4691+ "LABEL_ENC": "Ubuntu-Server\\x2020.04\\x20LTS\\x20ppc64",
4692+ "SYSTEM_ID": "LINUX",
4693+ "TYPE": "iso9660",
4694+ "USAGE": "filesystem",
4695+ "UUID": "2019-11-16-08-09-20-00",
4696+ "UUID_ENC": "2019-11-16-08-09-20-00"
4697+ },
4698+ "/dev/vda2": {
4699+ "TYPE": "ext4",
4700+ "USAGE": "filesystem",
4701+ "UUID": "0a3b5403-d358-4f30-9094-e5a19d60b222",
4702+ "UUID_ENC": "0a3b5403-d358-4f30-9094-e5a19d60b222",
4703+ "VERSION": "1.0"
4704+ },
4705+ "/dev/vdd": {
4706+ "TYPE": "ext2",
4707+ "USAGE": "filesystem",
4708+ "UUID": "00c629d6-06ab-4dfd-b21e-c3186f34105d",
4709+ "UUID_ENC": "00c629d6-06ab-4dfd-b21e-c3186f34105d",
4710+ "VERSION": "1.0"
4711+ },
4712+ "/dev/md1p1": {
4713+ "TYPE": "ext4",
4714+ "USAGE": "filesystem",
4715+ "UUID": "b312e605-d330-43ad-aa9b-2d503412977d",
4716+ "UUID_ENC": "b312e605-d330-43ad-aa9b-2d503412977d",
4717+ "VERSION": "1.0"
4718+ }
4719+ },
4720+ "raid": {
4721+ "/dev/md1": {
4722+ "DEVLINKS": "/dev/disk/by-id/md-name-ubuntu-server:1 /dev/disk/by-id/md-uuid-1e4e3ac4:abd1ee76:6fdb871a:4d6677af",
4723+ "DEVNAME": "/dev/md1",
4724+ "DEVPATH": "/devices/virtual/block/md1",
4725+ "DEVTYPE": "disk",
4726+ "ID_PART_TABLE_TYPE": "gpt",
4727+ "ID_PART_TABLE_UUID": "18f354ee-3557-4a37-9508-c947edb21b5c",
4728+ "MAJOR": "9",
4729+ "MD_DEVICES": "2",
4730+ "MD_DEVICE_ev_vdb1_DEV": "/dev/vdb1",
4731+ "MD_DEVICE_ev_vdb1_ROLE": "0",
4732+ "MD_DEVICE_ev_vdc1_DEV": "/dev/vdc1",
4733+ "MD_DEVICE_ev_vdc1_ROLE": "1",
4734+ "MD_LEVEL": "raid1",
4735+ "MD_METADATA": "1.2",
4736+ "MD_NAME": "ubuntu-server:1",
4737+ "MD_UUID": "1e4e3ac4:abd1ee76:6fdb871a:4d6677af",
4738+ "MINOR": "1",
4739+ "SUBSYSTEM": "block",
4740+ "SYSTEMD_WANTS": "mdmonitor.service",
4741+ "TAGS": ":systemd:",
4742+ "USEC_INITIALIZED": "52972570",
4743+ "raidlevel": "raid1",
4744+ "devices": [
4745+ "/dev/vdb1",
4746+ "/dev/vdc1"
4747+ ],
4748+ "spare_devices": []
4749+ }
4750+ },
4751+ "multipath": {}
4752+}
4753diff --git a/tests/data/probert_storage_zlp6.json b/tests/data/probert_storage_zlp6.json
4754new file mode 100644
4755index 0000000..b56a02b
4756--- /dev/null
4757+++ b/tests/data/probert_storage_zlp6.json
4758@@ -0,0 +1,6980 @@
4759+{
4760+ "storage": {
4761+ "bcache": {
4762+ "backing": {},
4763+ "caching": {}
4764+ },
4765+ "blockdev": {
4766+ "/dev/dasda": {
4767+ "DEVLINKS": "/dev/disk/by-id/ccw-IBM.750000000DXP71.1500.20 /dev/disk/by-path/ccw-0.0.1520 /dev/disk/by-uuid/3f168f0c-d0c7-4d8b-b23d-92c7476b092b",
4768+ "DEVNAME": "/dev/dasda",
4769+ "DEVPATH": "/devices/css0/0.0.0180/0.0.1520/block/dasda",
4770+ "DEVTYPE": "disk",
4771+ "ID_BUS": "ccw",
4772+ "ID_FS_TYPE": "bcache",
4773+ "ID_FS_UUID": "3f168f0c-d0c7-4d8b-b23d-92c7476b092b",
4774+ "ID_FS_UUID_ENC": "3f168f0c-d0c7-4d8b-b23d-92c7476b092b",
4775+ "ID_PATH": "ccw-0.0.1520",
4776+ "ID_PATH_TAG": "ccw-0_0_1520",
4777+ "ID_TYPE": "disk",
4778+ "ID_UID": "IBM.750000000DXP71.1500.20",
4779+ "ID_XUID": "IBM.750000000DXP71.1500.20",
4780+ "MAJOR": "94",
4781+ "MINOR": "0",
4782+ "MPATH_SBIN_PATH": "/sbin",
4783+ "SUBSYSTEM": "block",
4784+ "TAGS": ":systemd:",
4785+ "USEC_INITIALIZED": "9522332",
4786+ "attrs": {
4787+ "alignment_offset": "0",
4788+ "bdi": null,
4789+ "capability": "10",
4790+ "dev": "94:0",
4791+ "device": null,
4792+ "discard_alignment": "0",
4793+ "ext_range": "4",
4794+ "hidden": "0",
4795+ "inflight": " 0 0",
4796+ "range": "4",
4797+ "removable": "0",
4798+ "ro": "0",
4799+ "size": "59082670080",
4800+ "stat": " 16869 0 1012184 2450 12685 171372 4061544 74710 0 56227200 56332580",
4801+ "subsystem": "block",
4802+ "uevent": "MAJOR=94\nMINOR=0\nDEVNAME=dasda\nDEVTYPE=disk"
4803+ }
4804+ },
4805+ "/dev/dasdb": {
4806+ "DEVLINKS": "/dev/disk/by-id/ccw-IBM.750000000DXP71.1500.1f /dev/disk/by-id/ccw-0X151F /dev/disk/by-path/ccw-0.0.151f",
4807+ "DEVNAME": "/dev/dasdb",
4808+ "DEVPATH": "/devices/css0/0.0.017f/0.0.151f/block/dasdb",
4809+ "DEVTYPE": "disk",
4810+ "ID_BUS": "ccw",
4811+ "ID_PATH": "ccw-0.0.151f",
4812+ "ID_PATH_TAG": "ccw-0_0_151f",
4813+ "ID_SERIAL": "0X151F",
4814+ "ID_TYPE": "disk",
4815+ "ID_UID": "IBM.750000000DXP71.1500.1f",
4816+ "ID_XUID": "IBM.750000000DXP71.1500.1f",
4817+ "MAJOR": "94",
4818+ "MINOR": "4",
4819+ "MPATH_SBIN_PATH": "/sbin",
4820+ "SUBSYSTEM": "block",
4821+ "TAGS": ":systemd:",
4822+ "USEC_INITIALIZED": "9552537",
4823+ "attrs": {
4824+ "alignment_offset": "0",
4825+ "bdi": null,
4826+ "capability": "10",
4827+ "dev": "94:4",
4828+ "device": null,
4829+ "discard_alignment": "0",
4830+ "ext_range": "4",
4831+ "hidden": "0",
4832+ "inflight": " 0 0",
4833+ "range": "4",
4834+ "removable": "0",
4835+ "ro": "0",
4836+ "size": "59082670080",
4837+ "stat": " 2590 0 61872 750 0 0 0 0 0 1514773090 1514775410",
4838+ "subsystem": "block",
4839+ "uevent": "MAJOR=94\nMINOR=4\nDEVNAME=dasdb\nDEVTYPE=disk"
4840+ }
4841+ },
4842+ "/dev/dasdb1": {
4843+ "DEVLINKS": "/dev/disk/by-uuid/45824f3e-2b61-48c6-912f-6142ce21827c /dev/disk/by-id/ccw-IBM.750000000DXP71.1500.1f-part1 /dev/disk/by-path/ccw-0.0.151f-part1 /dev/disk/by-id/ccw-0X151F-part1",
4844+ "DEVNAME": "/dev/dasdb1",
4845+ "DEVPATH": "/devices/css0/0.0.017f/0.0.151f/block/dasdb/dasdb1",
4846+ "DEVTYPE": "partition",
4847+ "ID_BUS": "ccw",
4848+ "ID_FS_TYPE": "ext4",
4849+ "ID_FS_USAGE": "filesystem",
4850+ "ID_FS_UUID": "45824f3e-2b61-48c6-912f-6142ce21827c",
4851+ "ID_FS_UUID_ENC": "45824f3e-2b61-48c6-912f-6142ce21827c",
4852+ "ID_FS_VERSION": "1.0",
4853+ "ID_PATH": "ccw-0.0.151f",
4854+ "ID_PATH_TAG": "ccw-0_0_151f",
4855+ "ID_SCSI": "1",
4856+ "ID_SERIAL": "0X151F",
4857+ "ID_TYPE": "disk",
4858+ "ID_UID": "IBM.750000000DXP71.1500.1f",
4859+ "ID_XUID": "IBM.750000000DXP71.1500.1f",
4860+ "MAJOR": "94",
4861+ "MINOR": "5",
4862+ "PARTN": "1",
4863+ "SUBSYSTEM": "block",
4864+ "TAGS": ":systemd:",
4865+ "USEC_INITIALIZED": "1279563953260",
4866+ "attrs": {
4867+ "alignment_offset": "0",
4868+ "dev": "94:5",
4869+ "discard_alignment": "0",
4870+ "inflight": " 0 0",
4871+ "partition": "1",
4872+ "ro": "0",
4873+ "size": "6144000000",
4874+ "start": "192",
4875+ "stat": " 128 0 5568 160 0 0 0 0 0 280 280",
4876+ "subsystem": "block",
4877+ "uevent": "MAJOR=94\nMINOR=5\nDEVNAME=dasdb1\nDEVTYPE=partition\nPARTN=1"
4878+ }
4879+ },
4880+ "/dev/dasdb2": {
4881+ "DEVLINKS": "/dev/disk/by-id/ccw-0X151F-part2 /dev/disk/by-id/ccw-IBM.750000000DXP71.1500.1f-part2 /dev/disk/by-uuid/07c44238-17da-4548-8f94-046e5c1251d3 /dev/disk/by-path/ccw-0.0.151f-part2",
4882+ "DEVNAME": "/dev/dasdb2",
4883+ "DEVPATH": "/devices/css0/0.0.017f/0.0.151f/block/dasdb/dasdb2",
4884+ "DEVTYPE": "partition",
4885+ "ID_BUS": "ccw",
4886+ "ID_FS_TYPE": "crypto_LUKS",
4887+ "ID_FS_USAGE": "crypto",
4888+ "ID_FS_UUID": "07c44238-17da-4548-8f94-046e5c1251d3",
4889+ "ID_FS_UUID_ENC": "07c44238-17da-4548-8f94-046e5c1251d3",
4890+ "ID_FS_VERSION": "2",
4891+ "ID_PATH": "ccw-0.0.151f",
4892+ "ID_PATH_TAG": "ccw-0_0_151f",
4893+ "ID_SCSI": "1",
4894+ "ID_SERIAL": "0X151F",
4895+ "ID_TYPE": "disk",
4896+ "ID_UID": "IBM.750000000DXP71.1500.1f",
4897+ "ID_XUID": "IBM.750000000DXP71.1500.1f",
4898+ "MAJOR": "94",
4899+ "MINOR": "6",
4900+ "PARTN": "2",
4901+ "SUBSYSTEM": "block",
4902+ "TAGS": ":systemd:",
4903+ "USEC_INITIALIZED": "1279563871011",
4904+ "attrs": {
4905+ "alignment_offset": "0",
4906+ "dev": "94:6",
4907+ "discard_alignment": "0",
4908+ "inflight": " 0 0",
4909+ "partition": "2",
4910+ "ro": "0",
4911+ "size": "52937490432",
4912+ "start": "1500192",
4913+ "stat": " 113 0 1832 200 0 0 0 0 0 230 320",
4914+ "subsystem": "block",
4915+ "uevent": "MAJOR=94\nMINOR=6\nDEVNAME=dasdb2\nDEVTYPE=partition\nPARTN=2"
4916+ }
4917+ },
4918+ "/dev/dasdc": {
4919+ "DEVLINKS": "/dev/disk/by-path/ccw-0.0.1522 /dev/disk/by-id/ccw-IBM.750000000DXP71.1500.22 /dev/disk/by-id/ccw-0X1522",
4920+ "DEVNAME": "/dev/dasdc",
4921+ "DEVPATH": "/devices/css0/0.0.0182/0.0.1522/block/dasdc",
4922+ "DEVTYPE": "disk",
4923+ "ID_BUS": "ccw",
4924+ "ID_PATH": "ccw-0.0.1522",
4925+ "ID_PATH_TAG": "ccw-0_0_1522",
4926+ "ID_SERIAL": "0X1522",
4927+ "ID_TYPE": "disk",
4928+ "ID_UID": "IBM.750000000DXP71.1500.22",
4929+ "ID_XUID": "IBM.750000000DXP71.1500.22",
4930+ "MAJOR": "94",
4931+ "MINOR": "8",
4932+ "MPATH_SBIN_PATH": "/sbin",
4933+ "SUBSYSTEM": "block",
4934+ "TAGS": ":systemd:",
4935+ "USEC_INITIALIZED": "9503072",
4936+ "attrs": {
4937+ "alignment_offset": "0",
4938+ "bdi": null,
4939+ "capability": "10",
4940+ "dev": "94:8",
4941+ "device": null,
4942+ "discard_alignment": "0",
4943+ "ext_range": "4",
4944+ "hidden": "0",
4945+ "inflight": " 0 0",
4946+ "range": "4",
4947+ "removable": "0",
4948+ "ro": "0",
4949+ "size": "59082670080",
4950+ "stat": " 46350 0 3209608 4010 55144 368331 13826720 190440 0 1083958950 1084285940",
4951+ "subsystem": "block",
4952+ "uevent": "MAJOR=94\nMINOR=8\nDEVNAME=dasdc\nDEVTYPE=disk"
4953+ }
4954+ },
4955+ "/dev/dasdc1": {
4956+ "DEVLINKS": "/dev/disk/by-id/ccw-IBM.750000000DXP71.1500.22-part1 /dev/disk/by-id/ccw-0X1522-part1 /dev/disk/by-path/ccw-0.0.1522-part1",
4957+ "DEVNAME": "/dev/dasdc1",
4958+ "DEVPATH": "/devices/css0/0.0.0182/0.0.1522/block/dasdc/dasdc1",
4959+ "DEVTYPE": "partition",
4960+ "ID_BUS": "ccw",
4961+ "ID_PATH": "ccw-0.0.1522",
4962+ "ID_PATH_TAG": "ccw-0_0_1522",
4963+ "ID_SCSI": "1",
4964+ "ID_SERIAL": "0X1522",
4965+ "ID_TYPE": "disk",
4966+ "ID_UID": "IBM.750000000DXP71.1500.22",
4967+ "ID_XUID": "IBM.750000000DXP71.1500.22",
4968+ "MAJOR": "94",
4969+ "MINOR": "9",
4970+ "PARTN": "1",
4971+ "SUBSYSTEM": "block",
4972+ "TAGS": ":systemd:",
4973+ "USEC_INITIALIZED": "1279589828313",
4974+ "attrs": {
4975+ "alignment_offset": "0",
4976+ "dev": "94:9",
4977+ "discard_alignment": "0",
4978+ "inflight": " 0 0",
4979+ "partition": "1",
4980+ "ro": "0",
4981+ "size": "17180786688",
4982+ "start": "192",
4983+ "stat": " 102 0 5328 140 0 0 0 0 0 210 210",
4984+ "subsystem": "block",
4985+ "uevent": "MAJOR=94\nMINOR=9\nDEVNAME=dasdc1\nDEVTYPE=partition\nPARTN=1"
4986+ }
4987+ },
4988+ "/dev/dasdc2": {
4989+ "DEVLINKS": "/dev/disk/by-id/ccw-IBM.750000000DXP71.1500.22-part2 /dev/disk/by-path/ccw-0.0.1522-part2 /dev/disk/by-id/ccw-0X1522-part2",
4990+ "DEVNAME": "/dev/dasdc2",
4991+ "DEVPATH": "/devices/css0/0.0.0182/0.0.1522/block/dasdc/dasdc2",
4992+ "DEVTYPE": "partition",
4993+ "ID_BUS": "ccw",
4994+ "ID_PATH": "ccw-0.0.1522",
4995+ "ID_PATH_TAG": "ccw-0_0_1522",
4996+ "ID_SCSI": "1",
4997+ "ID_SERIAL": "0X1522",
4998+ "ID_TYPE": "disk",
4999+ "ID_UID": "IBM.750000000DXP71.1500.22",
5000+ "ID_XUID": "IBM.750000000DXP71.1500.22",
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches