Merge lp:~strikov-deactivatedaccount/curtin/multipath-2 into lp:~curtin-dev/curtin/trunk

Proposed by Scott Moser
Status: Merged
Merged at revision: 211
Proposed branch: lp:~strikov-deactivatedaccount/curtin/multipath-2
Merge into: lp:~curtin-dev/curtin/trunk
Diff against target: 141 lines (+95/-0)
3 files modified
curtin/block/__init__.py (+53/-0)
curtin/commands/block_meta.py (+5/-0)
curtin/commands/curthooks.py (+37/-0)
To merge this branch: bzr merge lp:~strikov-deactivatedaccount/curtin/multipath-2
Reviewer Review Type Date Requested Status
curtin developers Pending
Review via email: mp+261121@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

So, some thoughts here.
a.) we're using 'strip' on the output of /lib/udev/scsi_id which is less than ideal. that was done to drop the trailing '\n'. the result is that we'd recognize ' DISK 1 ' and 'DISK 1' as the same device and take the "enable_multipath" path. Both not very likely and not very big deal, but odd. I guess probably we should:
  if len(scsi_id) and scsi_id[-1] == '\n':
     scsi_id = scsi_id[0:-1]

  not a big deal, but worth fixing

b.) the other hting i'm concerned about is if the image / environment that we're installing in has multipath-tools installed, it may see that /dev/sda (and /dev/sde) is used by /dev/dm-0. and then choose not to install to it, or not realize that and fail to install.

in that scenario, i think the first install possibly works, while subsequent ones fail.
we may need a way to un-setup the dm devices so we can install to the "raw" ones.

Revision history for this message
Scott Moser (smoser) wrote :

oh yeah, and 'c'
i'm not sure if this works or not in python3.
a test would be nice to make sure.

211. By Oleg Strikov

block: multipath: Do not strip scsi_id, just remove trailing newlines.

Two devices may have similar scsi_ids with the only difference in the
exact amount of spaces in the beginning or in the end. By using strip()
we treat these devices as multipath ones. That's wrong because multipath
assumes identical scsi_ids of the devices.

212. By Oleg Strikov

block_meta: Stop all multipath devices before installing the system.

Multipath devices might be automatically assembled if multipath-tools
package is available in the installation environment. We need to stop
all multipath devices to exclusively use one of paths as a target disk.

213. By Oleg Strikov

block, block_meta: Stylistic changes.

214. By Oleg Strikov

curthooks: multipath: Few changes to the way multipath.conf is generated.

a) Don't overwrite multipath.conf provided by the installation image.
b) Add header to the file which state that file was generated by curtin.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'curtin/block/__init__.py'
2--- curtin/block/__init__.py 2015-05-13 13:30:56 +0000
3+++ curtin/block/__init__.py 2015-06-05 16:58:55 +0000
4@@ -22,6 +22,7 @@
5 import tempfile
6
7 from curtin import util
8+from curtin.log import LOG
9
10
11 def get_dev_name_entry(devname):
12@@ -179,6 +180,58 @@
13 return ret
14
15
16+def stop_all_unused_multipath_devices():
17+ """
18+ Stop all unused multipath devices.
19+ """
20+ multipath = util.which('multipath')
21+
22+ # Command multipath is not available only when multipath-tools package
23+ # is not installed. Nothing needs to be done in this case because system
24+ # doesn't create multipath devices without this package installed and we
25+ # have nothing to stop.
26+ if not multipath:
27+ return
28+
29+ # Command multipath -F flushes all unused multipath device maps
30+ cmd = [multipath, '-F']
31+ try:
32+ util.subp(cmd)
33+ except util.ProcessExecutionError as e:
34+ LOG.warn("Failed to stop multipath devices: %s", e)
35+
36+
37+def detect_multipath():
38+ """
39+ Figure out if target machine has any multipath devices.
40+ """
41+ # Multipath-tools are not available in the ephemeral image.
42+ # This workaround detects multipath by looking for multiple
43+ # devices with the same scsi id (serial number).
44+ disk_ids = []
45+ bdinfo = _lsblock(['--nodeps'])
46+ for devname, data in bdinfo.items():
47+ # Command scsi_id returns error while running against cdrom devices.
48+ # To prevent getting unexpected errors for some other types of devices
49+ # we ignore everything except hard disks.
50+ if data['TYPE'] != 'disk':
51+ continue
52+
53+ cmd = ['/lib/udev/scsi_id', '--replace-whitespace',
54+ '--whitelisted', '--device=%s' % data['device_path']]
55+ try:
56+ (out, err) = util.subp(cmd, capture=True)
57+ scsi_id = out.rstrip('\n')
58+ # ignore empty ids because they are meaningless
59+ if scsi_id:
60+ disk_ids.append(scsi_id)
61+ except util.ProcessExecutionError as e:
62+ LOG.warn("Failed to get disk id: %s", e)
63+
64+ duplicates_found = (len(disk_ids) != len(set(disk_ids)))
65+ return duplicates_found
66+
67+
68 def get_root_device(dev, fpath="curtin"):
69 """
70 Get root partition for specified device, based on presence of /curtin.
71
72=== modified file 'curtin/commands/block_meta.py'
73--- curtin/commands/block_meta.py 2015-05-07 03:58:10 +0000
74+++ curtin/commands/block_meta.py 2015-06-05 16:58:55 +0000
75@@ -146,6 +146,11 @@
76 # Remove duplicates but maintain ordering.
77 devices = list(OrderedDict.fromkeys(devices))
78
79+ # Multipath devices might be automatically assembled if multipath-tools
80+ # package is available in the installation environment. We need to stop
81+ # all multipath devices to exclusively use one of paths as a target disk.
82+ block.stop_all_unused_multipath_devices()
83+
84 if len(devices) == 0:
85 devices = block.get_installable_blockdevs()
86 LOG.warn("'%s' mode, no devices given. unused list: %s",
87
88=== modified file 'curtin/commands/curthooks.py'
89--- curtin/commands/curthooks.py 2015-03-11 17:03:33 +0000
90+++ curtin/commands/curthooks.py 2015-06-05 16:58:55 +0000
91@@ -419,6 +419,41 @@
92 maxsize=maxsize)
93
94
95+def detect_and_handle_multipath(cfg, target):
96+ DEFAULT_MULTIPATH_PACKAGES = ['multipath-tools-boot']
97+ mpcfg = cfg.get('multipath', {})
98+ mpmode = mpcfg.get('mode', 'auto')
99+ mppkgs = mpcfg.get('packages', DEFAULT_MULTIPATH_PACKAGES)
100+ if isinstance(mppkgs, str):
101+ mppkgs = [mppkgs]
102+
103+ if mpmode == 'disabled':
104+ return
105+
106+ if mpmode == 'auto' and not block.detect_multipath():
107+ return
108+
109+ util.install_packages(mppkgs, target=target)
110+
111+ multipath_cfg_path = os.path.sep.join([target, '/etc/multipath.conf'])
112+ # We don't want to overwrite multipath.conf file provided by the image.
113+ if os.path.isfile(multipath_cfg_path):
114+ return
115+
116+ # Without user_friendly_names option enabled system fails to boot
117+ # if any of the disks has spaces in its name. Package multipath-tools
118+ # has bug opened for this issue (LP: 1432062) but it was not fixed yet.
119+ multipath_cfg_content = '\n'.join(
120+ ['# This file was generated by curtin while installing the system.',
121+ 'defaults {',
122+ ' user_friendly_names yes',
123+ '}',
124+ ''])
125+ util.write_file(multipath_cfg_path, content=multipath_cfg_content)
126+ # Initrams needs to be regenerated to include updated multipath.cfg
127+ update_initramfs(target)
128+
129+
130 def curthooks(args):
131 state = util.load_command_environment()
132
133@@ -452,6 +487,8 @@
134 copy_interfaces(state.get('interfaces'), target)
135 copy_fstab(state.get('fstab'), target)
136
137+ detect_and_handle_multipath(cfg, target)
138+
139 # As a rule, ARMv7 systems don't use grub. This may change some
140 # day, but for now, assume no. They do require the initramfs
141 # to be updated, and this also triggers boot loader setup via

Subscribers

People subscribed via source and target branches