Fix growvols multipath partition_delimiter detect

On nodes with Fiber Channel multipath, the growvols command is
failing sometimes with the following stack trace:

Traceback (most recent call last):
  File "/usr/local/sbin/growvols", line 650, in <module>
    sys.exit(main(sys.argv))
  File "/usr/local/sbin/growvols", line 541, in main
    partnum = find_next_partnum(devices, disk)
  File "/usr/local/sbin/growvols", line 341, in find_next_partnum
    max_partnum = max(max_partnum, int(dev_name[disk_name_length:]))
ValueError: invalid literal for int() with base 10: 'p1'

The error is related to the kernel naming scheme for disk partitions.
If the disk name is ending with a digit, the kernel adds "pX" (where
X is  the partition number) instead of just "X" when the disk name
ends with a letter.

Consulting the manual page multipath.conf(5), the partition_delimiter
is configurable. If partition_delimiter is <unset> (the default) a
delimiter 'p' is inserted only if the map name ends in a digit. If
however the user configured a partition_delimiter, it will always be
used.

To try to handle both cases, use a regular expression to catch only
the digits at the end of the string.

Authored-By: mcr.opensource@gmail.com
Co-Authored-By: hjensas@redhat.com
Closes-Bug: #2109988
Change-Id: Ifbd58000601b7fc5635dcc2c7a897462000319b0
This commit is contained in:
mcreach 2024-07-12 16:28:01 +02:00 committed by Harald Jensås
parent d6868eaa1a
commit ff5c11c095
3 changed files with 34 additions and 1 deletions

View File

@ -43,6 +43,9 @@ UNIT_BYTES_FORMAT = {
'GiB': 1073741824
}
# Regular expression to get digits at the end of a string
DIGITS_EOF_STR_RE = re.compile(r'\d+$')
# Only create growth partition if there is at least 1GiB available
MIN_DISK_SPACE_BYTES = UNIT_BYTES['GiB']
@ -363,7 +366,15 @@ def find_next_partnum(devices, disk):
if dev_name in seen_devs:
continue
seen_devs.add(dev_name)
max_partnum = max(max_partnum, int(dev_name[disk_name_length:]))
# Multipath device names may have a partition_delimiter between
# the base device name and the partition number. By default, if
# partition_delimiter is unset in the multipath configuration and
# the base name ends in a digit, a 'p' is inserted.
# A regular expression is used to reliably extract trailing
# digits, regardless the presence of a partition delimiter or not.
max_partnum = max(max_partnum,
int(DIGITS_EOF_STR_RE.search(
dev_name[disk_name_length:]).group(0)))
return max_partnum + 1

View File

@ -11,6 +11,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import importlib
import os
import sys
@ -488,6 +489,19 @@ class TestGrowvols(base.BaseTestCase):
disk = growvols.find_disk(opts, devices)
self.assertEqual(6, growvols.find_next_partnum(devices, disk))
def test_find_next_partnum_multipath_partition_delimiter(self):
opts = mock.Mock()
opts.device = 'mpath2'
local_lsblk = copy.deepcopy(LSBLK_MULTIPATH)
local_lsblk = local_lsblk.replace('mpatha1', 'mpath2p1')
local_lsblk = local_lsblk.replace('mpatha2', 'mpath2p2')
local_lsblk = local_lsblk.replace('mpatha3', 'mpath2p3')
local_lsblk = local_lsblk.replace('mpatha4', 'mpath2p4')
local_lsblk = local_lsblk.replace('mpatha', 'mpath2')
devices = list(growvols.parse_shell_vars(local_lsblk))
disk = growvols.find_disk(opts, devices)
self.assertEqual(6, growvols.find_next_partnum(devices, disk))
def test_find_next_device_name(self):
devices = list(growvols.parse_shell_vars(LSBLK))
disk = {

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Fixed an issue in the growvols element where a ``partition_delimiter``
inserted between the device name and the partition number for multipath
devices was not handled correctly. This fix resolves `bug 2109988
<https://bugs.launchpad.net/diskimage-builder/+bug/2109988>`_.