diff --git a/backport-Fix-ds-identify-not-detecting-NoCloud-seed-in-config.patch b/backport-Fix-ds-identify-not-detecting-NoCloud-seed-in-config.patch new file mode 100644 index 0000000000000000000000000000000000000000..e830fbbc4e3ae1c08b82bb5b66b5831859f0bab7 --- /dev/null +++ b/backport-Fix-ds-identify-not-detecting-NoCloud-seed-in-config.patch @@ -0,0 +1,108 @@ +From 3e02227236fc860bdf0937b2734b35c373fdb263 Mon Sep 17 00:00:00 2001 +From: James Falcon +Date: Mon, 25 Apr 2022 15:43:57 -0500 +Subject: [PATCH] Fix ds-identify not detecting NoCloud seed in config (#1381) + +Reference:https://github.com/canonical/cloud-init/commit/3e02227236fc860bdf0937b2734b35c373fdb263 +Conflict:NA + +NoCloud seed config can be defined in /etc/cloud/cloud.cfg[.d]. +However, ds-identify had no means of detecting this config and reported +NOT FOUND. This commit allows ds-identify to detect and report it +properly. + +LP: #1876375 +--- + cloudinit/stages.py | 4 ++-- + tests/unittests/test_ds_identify.py | 25 +++++++++++++++++++++++++ + tools/ds-identify | 5 +++++ + 3 files changed, 32 insertions(+), 2 deletions(-) + +diff --git a/cloudinit/stages.py b/cloudinit/stages.py +index 59b0925..502c060 100644 +--- a/cloudinit/stages.py ++++ b/cloudinit/stages.py +@@ -1045,11 +1045,11 @@ def read_runtime_config(): + def fetch_base_config(): + return util.mergemanydict( + [ +- # builtin config ++ # builtin config, hardcoded in settings.py. + util.get_builtin_cfg(), + # Anything in your conf.d or 'default' cloud.cfg location. + util.read_conf_with_confd(CLOUD_CONFIG), +- # runtime config ++ # runtime config. I.e., /run/cloud-init/cloud.cfg + read_runtime_config(), + # Kernel/cmdline parameters override system config + util.read_conf_from_cmdline(), +diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py +index 43603ea..7342171 100644 +--- a/tests/unittests/test_ds_identify.py ++++ b/tests/unittests/test_ds_identify.py +@@ -1,6 +1,7 @@ + # This file is part of cloud-init. See LICENSE file for license information. + + from collections import namedtuple ++from textwrap import dedent + import copy + import os + from uuid import uuid4 +@@ -610,6 +611,10 @@ class TestDsIdentify(DsIdentifyBase): + """NoCloud is found with uppercase filesystem label.""" + self._test_ds_found('NoCloudUpper') + ++ def test_nocloud_seed_in_cfg(self): ++ """NoCloud seed definition can go in /etc/cloud/cloud.cfg[.d]""" ++ self._test_ds_found("NoCloud-cfg") ++ + def test_nocloud_fatboot(self): + """NoCloud fatboot label - LP: #184166.""" + self._test_ds_found('NoCloud-fatboot') +@@ -912,6 +917,26 @@ VALID_CFG = { + 'dev/vdb': 'pretend iso content for cidata\n', + } + }, ++ "NoCloud-cfg": { ++ "ds": "NoCloud", ++ "files": { ++ # Also include a datasource list of more than just ++ # [NoCloud, None], because that would automatically select ++ # NoCloud without checking ++ "/etc/cloud/cloud.cfg": dedent( ++ """\ ++ datasource_list: [ Azure, Openstack, NoCloud, None ] ++ datasource: ++ NoCloud: ++ user-data: | ++ #cloud-config ++ hostname: footbar ++ meta-data: | ++ instance_id: cloud-image ++ """ ++ ) ++ }, ++ }, + 'NoCloud-fbsd': { + 'ds': 'NoCloud', + 'mocks': [ +diff --git a/tools/ds-identify b/tools/ds-identify +index 30d4b0f..f92c0d3 100755 +--- a/tools/ds-identify ++++ b/tools/ds-identify +@@ -826,6 +826,11 @@ dscheck_NoCloud() { + return ${DS_FOUND} + fi + ++ # This is a bit hacky, but a NoCloud false positive isn't the end of the world ++ if check_config "NoCloud" && check_config "user-data" && check_config "meta-data"; then ++ return ${DS_FOUND} ++ fi ++ + return ${DS_NOT_FOUND} + } + +-- +2.33.0 + + diff --git a/backport-Use-btrfs-enquque-when-available-1926.patch b/backport-Use-btrfs-enquque-when-available-1926.patch new file mode 100644 index 0000000000000000000000000000000000000000..220cfca1b9c285a5812f6d12706fd3f98a4f7adb --- /dev/null +++ b/backport-Use-btrfs-enquque-when-available-1926.patch @@ -0,0 +1,107 @@ +From d1e237d22deeb7dde724bdc5495b5cbbe8914404 Mon Sep 17 00:00:00 2001 +From: Robert Schweikert +Date: Fri, 6 Jan 2023 18:16:28 -0500 +Subject: [PATCH] Use btrfs enquque when available (#1926) + +Reference:https://github.com/canonical/cloud-init/commit/d1e237d22deeb7dde724bdc5495b5cbbe8914404 +Conflict:cc_resizefs:format diffs. + +btrfs has operations that are blocking and when we try to resize a btrfs +filesystem we may be in a race condition with blocking operations. Use the +enqueue feature introduced in btrfs 5.10 to queue our resize request until +resize if possible. + +Before this commit, hitting this race would cause the command to +immediately fail. With this change, the resize is queued and the command +blocks until resize has completed (event driven, with a poll loop of 1m). +--- + cloudinit/config/cc_resizefs.py | 20 ++++++++++++---- + .../test_handler/test_handler_resizefs.py | 23 +++++++++++++++++-- + 2 files changed, 37 insertions(+), 6 deletions(-) + +diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py +index abb812b..61e4fe2 100644 +--- a/cloudinit/config/cc_resizefs.py ++++ b/cloudinit/config/cc_resizefs.py +@@ -63,11 +63,23 @@ def _resize_btrfs(mount_point, devpth): + # solution would be walk the subvolumes and find a rw mounted subvolume. + if (not util.mount_is_read_write(mount_point) and + os.path.isdir("%s/.snapshots" % mount_point)): +- return ('btrfs', 'filesystem', 'resize', 'max', +- '%s/.snapshots' % mount_point) ++ cmd = ['btrfs', 'filesystem', 'resize', 'max', ++ '%s/.snapshots' % mount_point] + else: +- return ('btrfs', 'filesystem', 'resize', 'max', mount_point) +- ++ cmd = ['btrfs', 'filesystem', 'resize', 'max', mount_point] ++ ++ # btrfs has exclusive operations and resize may fail if btrfs is busy ++ # doing one of the operations that prevents resize. As of btrfs 5.10 ++ # the resize operation can be queued ++ btrfs_with_queue = util.Version.from_str("5.10") ++ system_btrfs_ver = util.Version.from_str( ++ subp.subp(["btrfs", "--version"])[0].split("v")[-1].strip() ++ ) ++ if system_btrfs_ver >= btrfs_with_queue: ++ idx = cmd.index("resize") ++ cmd.insert(idx + 1, "--enqueue") ++ ++ return tuple(cmd) + + def _resize_ext(mount_point, devpth): + return ('resize2fs', devpth) +diff --git a/tests/unittests/test_handler/test_handler_resizefs.py b/tests/unittests/test_handler/test_handler_resizefs.py +index 695c7f5..3e77322 100644 +--- a/tests/unittests/test_handler/test_handler_resizefs.py ++++ b/tests/unittests/test_handler/test_handler_resizefs.py +@@ -367,24 +367,43 @@ class TestMaybeGetDevicePathAsWritableBlock(CiTestCase): + + @mock.patch('cloudinit.util.mount_is_read_write') + @mock.patch('cloudinit.config.cc_resizefs.os.path.isdir') +- def test_resize_btrfs_mount_is_ro(self, m_is_dir, m_is_rw): ++ @mock.patch("cloudinit.subp.subp") ++ def test_resize_btrfs_mount_is_ro(self, m_subp, m_is_dir, m_is_rw): + """Do not resize / directly if it is read-only. (LP: #1734787).""" + m_is_rw.return_value = False + m_is_dir.return_value = True ++ m_subp.return_value = ("btrfs-progs v4.19 \n", "") + self.assertEqual( + ('btrfs', 'filesystem', 'resize', 'max', '//.snapshots'), + _resize_btrfs("/", "/dev/sda1")) + + @mock.patch('cloudinit.util.mount_is_read_write') + @mock.patch('cloudinit.config.cc_resizefs.os.path.isdir') +- def test_resize_btrfs_mount_is_rw(self, m_is_dir, m_is_rw): ++ @mock.patch("cloudinit.subp.subp") ++ def test_resize_btrfs_mount_is_rw(self, m_subp, m_is_dir, m_is_rw): + """Do not resize / directly if it is read-only. (LP: #1734787).""" + m_is_rw.return_value = True + m_is_dir.return_value = True ++ m_subp.return_value = ("btrfs-progs v4.19 \n", "") + self.assertEqual( + ('btrfs', 'filesystem', 'resize', 'max', '/'), + _resize_btrfs("/", "/dev/sda1")) + ++ @mock.patch("cloudinit.util.mount_is_read_write") ++ @mock.patch("cloudinit.config.cc_resizefs.os.path.isdir") ++ @mock.patch("cloudinit.subp.subp") ++ def test_resize_btrfs_mount_is_rw_has_queue( ++ self, m_subp, m_is_dir, m_is_rw ++ ): ++ """Queue the resize request if btrfs >= 5.10""" ++ m_is_rw.return_value = True ++ m_is_dir.return_value = True ++ m_subp.return_value = ("btrfs-progs v5.10 \n", "") ++ self.assertEqual( ++ ("btrfs", "filesystem", "resize", "--enqueue", "max", "/"), ++ _resize_btrfs("/", "/dev/sda1"), ++ ) ++ + @mock.patch('cloudinit.util.is_container', return_value=True) + @mock.patch('cloudinit.util.is_FreeBSD') + def test_maybe_get_writable_device_path_zfs_freebsd(self, freebsd, +-- +2.33.0 + + diff --git a/backport-Workaround-net_setup_link-race-with-udev-1655.patch b/backport-Workaround-net_setup_link-race-with-udev-1655.patch new file mode 100644 index 0000000000000000000000000000000000000000..54d094c737ce572a7b0c4215c2c43fd8414a9128 --- /dev/null +++ b/backport-Workaround-net_setup_link-race-with-udev-1655.patch @@ -0,0 +1,52 @@ +From 6b3042e91a03dfd5c1c1498d9c00d6ae765381b6 Mon Sep 17 00:00:00 2001 +From: James Falcon +Date: Mon, 15 Aug 2022 16:35:13 -0500 +Subject: [PATCH 3/3] Workaround net_setup_link race with udev (#1655) + +Reference:https://github.com/canonical/cloud-init/commit/7136109df5a7b3c75dfb05a853fc4485fed25b5f +Conflict:NA + +LP: #1983516 +--- + cloudinit/net/netplan.py | 25 +++++++++++++++++++++---- + 1 file changed, 21 insertions(+), 4 deletions(-) + +diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py +index 894b5db..7d6740d 100644 +--- a/cloudinit/net/netplan.py ++++ b/cloudinit/net/netplan.py +@@ -264,10 +264,27 @@ class Renderer(renderer.Renderer): + LOG.debug("netplan net_setup_link postcmd disabled") + return + setup_lnk = ['udevadm', 'test-builtin', 'net_setup_link'] +- for cmd in [setup_lnk + [SYS_CLASS_NET + iface] +- for iface in get_devicelist() if +- os.path.islink(SYS_CLASS_NET + iface)]: +- subp.subp(cmd, capture=True) ++ ++ # It's possible we can race a udev rename and attempt to run ++ # net_setup_link on a device that no longer exists. When this happens, ++ # we don't know what the device was renamed to, so re-gather the ++ # entire list of devices and try again. ++ last_exception = Exception ++ for _ in range(5): ++ try: ++ for iface in get_devicelist(): ++ if os.path.islink(SYS_CLASS_NET + iface): ++ subp.subp( ++ setup_lnk + [SYS_CLASS_NET + iface], capture=True ++ ) ++ break ++ except subp.ProcessExecutionError as e: ++ last_exception = e ++ else: ++ raise RuntimeError( ++ "'udevadm test-builtin net_setup_link' unable to run " ++ "successfully for all devices." ++ ) from last_exception + + def _render_content(self, network_state: NetworkState): + +-- +2.40.0 + diff --git a/backport-cc_disk_setup-pass-options-in-correct-order-to-utils.patch b/backport-cc_disk_setup-pass-options-in-correct-order-to-utils.patch new file mode 100644 index 0000000000000000000000000000000000000000..a4542fd3448da7c6989ec32e0c94339ab440af4d --- /dev/null +++ b/backport-cc_disk_setup-pass-options-in-correct-order-to-utils.patch @@ -0,0 +1,113 @@ +From 7b04985553ed8b0bc29574608e261ee42280f4d0 Mon Sep 17 00:00:00 2001 +From: dermotbradley +Date: Mon, 14 Nov 2022 23:22:34 +0000 +Subject: [PATCH] cc_disk_setup: pass options in correct order to utils (#1829) + +Reference:https://github.com/canonical/cloud-init/commit/7b04985553ed8b0bc29574608e261ee42280f4d0 +Conflict:(1)schema-cloud-config-v1.json not change. +(2)not change meta, no meta. +(3)test format + +When testing cc_disk_setup it failed with the following error: + + Unexpected error while running command. + Command: ['/sbin/mkfs.ext4', '/dev/sdc1', '-L', 'disk3-fs2'] + Exit code: 1 + Reason: - + Stdout: + Stderr: mke2fs 1.46.5 (30-Dec-2021) + mkfs.ext4: invalid blocks '-L' on device '/dev/sdc1' + +The manpages for mkfs.ext4, mkfs.xfs, and mkswap all indicate that +options should be passed *before* the device name but cc_disk_setup +passed them after the device name - in the case of mkfx.ext4 a +"fs-size" can be passed after the device and that is what the +"-L disk3-fs2" option is being misintepreted as. + +This PR ensures that the device name is passed last. The underlying +issue appears to be due to a different in behaviour between glibc and +musl where glibc "helps" applications by re-ordered command-line +parameters by musl does not[1] as it sticks to POSIX spec. + +This PR also modifies 2 testcases to cater for this change in the code, +adds a note to disk_setup to clarify that when creating a swap partition +a fs_entry also needs to be specified so that mkswap is run, adds to the +examples how to specify a non-default partition type (i.e. for swap), +and modifies the description for disk_setup to clarify this. + +[1] https://wiki.musl-libc.org/functional-differences-from-glibc.html#Miscellaneous_functions_with_GNU_quirks +--- + cloudinit/config/cc_disk_setup.py | 10 +++++++++- + .../unittests/test_handler/test_handler_disk_setup.py | 6 +++--- + 2 files changed, 12 insertions(+), 4 deletions(-) + +diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py +index abdc111..f2ba9c6 100644 +--- a/cloudinit/config/cc_disk_setup.py ++++ b/cloudinit/config/cc_disk_setup.py +@@ -16,6 +16,11 @@ This module is able to configure simple partition tables and filesystems. + for more detail about configuration options for disk setup, see the disk + setup example + ++.. note:: ++ if a swap partition is being created via disk_setup then a fs_entry ++ entry is also needed in order for mkswap to be run, otherwise when swap ++ activation is later attempted it will fail. ++ + For convenience, aliases can be specified for disks using the + ``device_aliases`` config key, which takes a dictionary of alias: path + mappings. There are automatic aliases for ``swap`` and ``ephemeral``, where +@@ -993,6 +998,7 @@ def mkfs(fs_cfg): + # Find the mkfs command + mkfs_cmd = subp.which("mkfs.%s" % fs_type) + if not mkfs_cmd: ++ # for "mkswap" + mkfs_cmd = subp.which("mk%s" % fs_type) + + if not mkfs_cmd: +@@ -1000,7 +1006,7 @@ def mkfs(fs_cfg): + fs_type, fs_type) + return + +- fs_cmd = [mkfs_cmd, device] ++ fs_cmd = [mkfs_cmd] + + if label: + fs_cmd.extend(["-L", label]) +@@ -1015,6 +1021,8 @@ def mkfs(fs_cfg): + if fs_opts: + fs_cmd.extend(fs_opts) + ++ fs_cmd.append(device) ++ + LOG.debug("Creating file system %s on %s", label, device) + LOG.debug(" Using cmd: %s", str(fs_cmd)) + try: +diff --git a/tests/unittests/test_handler/test_handler_disk_setup.py b/tests/unittests/test_handler/test_handler_disk_setup.py +index 2f3a8df..69c7a0d 100644 +--- a/tests/unittests/test_handler/test_handler_disk_setup.py ++++ b/tests/unittests/test_handler/test_handler_disk_setup.py +@@ -235,8 +235,8 @@ class TestMkfsCommandHandling(CiTestCase): + }) + + subp.assert_called_once_with( +- ['/sbin/mkfs.ext4', '/dev/xdb1', +- '-L', 'without_cmd', '-F', 'are', 'added'], ++ ['/sbin/mkfs.ext4', ++ '-L', 'without_cmd', '-F', 'are', 'added', '/dev/xdb1'], + shell=False) + + @mock.patch('cloudinit.config.cc_disk_setup.subp.which') +@@ -254,7 +254,7 @@ class TestMkfsCommandHandling(CiTestCase): + self.assertEqual([mock.call('mkfs.swap'), mock.call('mkswap')], + m_which.call_args_list) + subp.assert_called_once_with( +- ['/sbin/mkswap', '/dev/xdb1', '-L', 'swap', '-f'], shell=False) ++ ['/sbin/mkswap', '-L', 'swap', '-f', '/dev/xdb1'], shell=False) + + # + # vi: ts=4 expandtab +-- +2.33.0 + + diff --git a/backport-mounts-fix-suggested_swapsize-for-64GB-hosts-1569.patch b/backport-mounts-fix-suggested_swapsize-for-64GB-hosts-1569.patch new file mode 100644 index 0000000000000000000000000000000000000000..f72894f2a4ecfb5051174cb1737cd8f6eca2dbf9 --- /dev/null +++ b/backport-mounts-fix-suggested_swapsize-for-64GB-hosts-1569.patch @@ -0,0 +1,165 @@ +From b711bb6a4db8281a33f9809cff9ba84c9c5fe1e4 Mon Sep 17 00:00:00 2001 +From: Steven Stallion +Date: Tue, 12 Jul 2022 09:16:57 -0500 +Subject: [PATCH] mounts: fix suggested_swapsize for > 64GB hosts (#1569) + +Reference:https://github.com/canonical/cloud-init/commit/b711bb6a4db8281a33f9809cff9ba84c9c5fe1e4 +Conflict:(1)tools/.github-cla-signers not change +(2)test format + +When provisioning hosts with more than 64GB of memory, swap was not +created and an error was logged by cloud-init. This was due to a +bug in the "suggested_swapsize" implementation, such that the swap +formula was not being taken into account. + +This commit fixes the bug while also aligning the recommended swap +size to more closely align with the (no hibernation) swap +recommendations available at +https://help.ubuntu.com/community/SwapFaq +--- + cloudinit/config/cc_mounts.py | 42 +++++++++------------------ + cloudinit/config/tests/test_mounts.py | 40 ++++++++++++++++++++++++- + 2 files changed, 52 insertions(+), 30 deletions(-) + +diff --git a/cloudinit/config/cc_mounts.py b/cloudinit/config/cc_mounts.py +index eeb008d..1c6b883 100644 +--- a/cloudinit/config/cc_mounts.py ++++ b/cloudinit/config/cc_mounts.py +@@ -65,6 +65,7 @@ swap file is created. + from string import whitespace + + import logging ++import math + import os + import re + +@@ -81,6 +82,8 @@ NETWORK_NAME_RE = re.compile(NETWORK_NAME_FILTER) + WS = re.compile("[%s]+" % (whitespace)) + FSTAB_PATH = "/etc/fstab" + MNT_COMMENT = "comment=cloudconfig" ++MB = 2**20 ++GB = 2**30 + + LOG = logging.getLogger(__name__) + +@@ -176,13 +179,12 @@ def suggested_swapsize(memsize=None, maxsize=None, fsys=None): + if memsize is None: + memsize = util.read_meminfo()['total'] + +- GB = 2 ** 30 +- sugg_max = 8 * GB ++ sugg_max = memsize * 2 + + info = {'avail': 'na', 'max_in': maxsize, 'mem': memsize} + + if fsys is None and maxsize is None: +- # set max to 8GB default if no filesystem given ++ # set max to default if no filesystem given + maxsize = sugg_max + elif fsys: + statvfs = os.statvfs(fsys) +@@ -200,35 +202,17 @@ def suggested_swapsize(memsize=None, maxsize=None, fsys=None): + + info['max'] = maxsize + +- formulas = [ +- # < 1G: swap = double memory +- (1 * GB, lambda x: x * 2), +- # < 2G: swap = 2G +- (2 * GB, lambda x: 2 * GB), +- # < 4G: swap = memory +- (4 * GB, lambda x: x), +- # < 16G: 4G +- (16 * GB, lambda x: 4 * GB), +- # < 64G: 1/2 M up to max +- (64 * GB, lambda x: x / 2), +- ] +- +- size = None +- for top, func in formulas: +- if memsize <= top: +- size = min(func(memsize), maxsize) +- # if less than 1/2 memory and not much, return 0 +- if size < (memsize / 2) and size < 4 * GB: +- size = 0 +- break +- break +- +- if size is not None: +- size = maxsize ++ if memsize < 4 * GB: ++ minsize = memsize ++ elif memsize < 16 * GB: ++ minsize = 4 * GB ++ else: ++ minsize = round(math.sqrt(memsize / GB)) * GB ++ ++ size = min(minsize, maxsize) + + info['size'] = size + +- MB = 2 ** 20 + pinfo = {} + for k, v in info.items(): + if isinstance(v, int): +diff --git a/cloudinit/config/tests/test_mounts.py b/cloudinit/config/tests/test_mounts.py +index 56510fd..e922122 100644 +--- a/cloudinit/config/tests/test_mounts.py ++++ b/cloudinit/config/tests/test_mounts.py +@@ -1,9 +1,17 @@ + # This file is part of cloud-init. See LICENSE file for license information. ++import math + from unittest import mock ++from collections import namedtuple ++from pytest import approx + + import pytest + +-from cloudinit.config.cc_mounts import create_swapfile ++from cloudinit.config.cc_mounts import ( ++ GB, ++ MB, ++ create_swapfile, ++ suggested_swapsize, ++) + from cloudinit.subp import ProcessExecutionError + + +@@ -59,3 +67,33 @@ class TestCreateSwapfile: + + msg = "fallocate swap creation failed, will attempt with dd" + assert msg in caplog.text ++ ++ # See https://help.ubuntu.com/community/SwapFaq ++ @pytest.mark.parametrize( ++ "memsize,expected", ++ [ ++ (256 * MB, 256 * MB), ++ (512 * MB, 512 * MB), ++ (1 * GB, 1 * GB), ++ (2 * GB, 2 * GB), ++ (4 * GB, 4 * GB), ++ (8 * GB, 4 * GB), ++ (16 * GB, 4 * GB), ++ (32 * GB, 6 * GB), ++ (64 * GB, 8 * GB), ++ (128 * GB, 11 * GB), ++ (256 * GB, 16 * GB), ++ (512 * GB, 23 * GB), ++ ], ++ ) ++ def test_suggested_swapsize(self, memsize, expected, mocker): ++ mock_stat = namedtuple("mock_stat", "f_frsize f_bfree") ++ mocker.patch( ++ "os.statvfs", ++ # Don't care about available disk space for the purposes of this ++ # test ++ return_value=mock_stat(math.inf, math.inf), ++ ) ++ size = suggested_swapsize(memsize, math.inf, "dontcare") ++ assert expected == approx(size) ++ +-- +2.33.0 + + diff --git a/backport-util-Fix-error-path-and-parsing-in-get_proc_ppid.patch b/backport-util-Fix-error-path-and-parsing-in-get_proc_ppid.patch new file mode 100644 index 0000000000000000000000000000000000000000..0a886e57f9ed317ced453cbd5cc23f05b877bab8 --- /dev/null +++ b/backport-util-Fix-error-path-and-parsing-in-get_proc_ppid.patch @@ -0,0 +1,98 @@ +From 93b341c7ab968aedd926ed30cfbd3fa29be62fb3 Mon Sep 17 00:00:00 2001 +From: Brett Holman +Date: Fri, 5 Aug 2022 18:11:06 +0200 +Subject: [PATCH 1/3] util: Fix error path and parsing in get_proc_ppid() + +Reference:https://github.com/canonical/cloud-init/commit/668a68fe577368555ea7f71577fc352494a98c25 +Conflict:NA + +If IOError was thrown, the variable "contents" was +unbound and threw a name error exception. + +Parsing /proc//stat was broken in less common +cases. + +Fix both and add parsing tests. +--- + cloudinit/util.py | 10 ++++----- + tests/unittests/test_util.py | 41 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 46 insertions(+), 5 deletions(-) + +diff --git a/cloudinit/util.py b/cloudinit/util.py +index 0e0fc04..9e530db 100644 +--- a/cloudinit/util.py ++++ b/cloudinit/util.py +@@ -2761,13 +2761,13 @@ def get_proc_ppid(pid): + ppid = 0 + try: + contents = load_file("/proc/%s/stat" % pid, quiet=True) ++ if contents: ++ # see proc.5 for format ++ m = re.search(r"^\d+ \(.+\) [RSDZTtWXxKWP] (\d+)", str(contents)) ++ if m: ++ ppid = int(m.group(1)) + except IOError as e: + LOG.warning('Failed to load /proc/%s/stat. %s', pid, e) +- if contents: +- parts = contents.split(" ", 4) +- # man proc says +- # ppid %d (4) The PID of the parent. +- ppid = int(parts[3]) + return ppid + + # vi: ts=4 expandtab +diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py +index 1185487..3fa5059 100644 +--- a/tests/unittests/test_util.py ++++ b/tests/unittests/test_util.py +@@ -898,6 +898,47 @@ class TestGetProcEnv(helpers.TestCase): + my_ppid = os.getppid() + self.assertEqual(my_ppid, util.get_proc_ppid(my_pid)) + ++ def test_get_proc_ppid_mocked(self): ++ for ppid, proc_data in ( ++ ( ++ 0, ++ "1 (systemd) S 0 1 1 0 -1 4194560 112664 14612195 153 18014" ++ "274 237 756828 152754 20 0 1 0 3 173809664 3736" ++ "18446744073709551615 1 1 0 0 0 0 671173123 4096 1260 0 0 0 17" ++ "8 0 0 0 0 123974 0 0 0 0 0 0 0 0", ++ ), ++ ( ++ 180771, ++ "180781 ([pytest-xdist r) R 180771 180598 167240 34825 " ++ "180598 4194304 128712 7570 0 0 1061 34 8 1 20 0 2 0 6551540 " ++ "351993856 25173 18446744073709551615 93907896635392 " ++ "93907899455533 140725724279536 0 0 0 0 16781312 17642 0 0 0 " ++ "17 1 0 0 0 0 0 93907901810800 93907902095288 93907928788992 " ++ "140725724288007 140725724288074 140725724288074 " ++ "140725724291047 0", ++ ), ++ ( ++ 5620, ++ "8723 (Utility Process) S 5620 5191 5191 0 -1 4194304 3219 " ++ "0 50 0 1045 431 0 0 20 0 3 0 9007 220585984 8758 " ++ "18446744073709551615 94469734690816 94469735319392 " ++ "140728350183632 0 0 0 0 69634 1073745144 0 0 0 17 10 0 0 0 0 " ++ "0 94469735327152 94469735331056 94469763170304 " ++ "140728350189012 140728350189221 140728350189221 " ++ "140728350195661 0", ++ ), ++ ( ++ 4946, ++ "4947 ((sd-pam)) S 4946 4946 4946 0 -1 1077936448 54 0 0 0 " ++ "0 0 0 0 20 0 1 0 4136 175616000 1394 18446744073709551615 1 1" ++ "0 0 0 0 0 4096 0 0 0 0 17 8 0 0 0 0 0 0 0 0 0 0 0 0 0", ++ ), ++ ): ++ with mock.patch( ++ "cloudinit.util.load_file", return_value=proc_data ++ ): ++ assert ppid == util.get_proc_ppid("mocked") ++ + + class TestKernelVersion(): + """test kernel version function""" +-- +2.40.0 + diff --git a/backport-util-Support-Idle-process-state-in-get_proc_ppid-163.patch b/backport-util-Support-Idle-process-state-in-get_proc_ppid-163.patch new file mode 100644 index 0000000000000000000000000000000000000000..4c4903e84f91a7708afe04fa9706e9451b60d8a4 --- /dev/null +++ b/backport-util-Support-Idle-process-state-in-get_proc_ppid-163.patch @@ -0,0 +1,36 @@ +From 631eecf82a4af3ec46b6f2a257849a80a2299bee Mon Sep 17 00:00:00 2001 +From: Brett Holman +Date: Fri, 5 Aug 2022 22:28:53 +0200 +Subject: [PATCH 2/3] util: Support Idle process state in get_proc_ppid() + (#1637) + +Reference:https://github.com/canonical/cloud-init/commit/f51c352e6c6a7d05a61308c188450a1b818eac45 +Conflict:NA +--- + cloudinit/util.py | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/cloudinit/util.py b/cloudinit/util.py +index 9e530db..58f6e27 100644 +--- a/cloudinit/util.py ++++ b/cloudinit/util.py +@@ -2763,9 +2763,15 @@ def get_proc_ppid(pid): + contents = load_file("/proc/%s/stat" % pid, quiet=True) + if contents: + # see proc.5 for format +- m = re.search(r"^\d+ \(.+\) [RSDZTtWXxKWP] (\d+)", str(contents)) ++ m = re.search(r"^\d+ \(.+\) [RSDZTtWXxKPI] (\d+)", str(contents)) + if m: + ppid = int(m.group(1)) ++ else: ++ LOG.warning( ++ "Unable to match parent pid of process pid=%s input: %s", ++ pid, ++ contents, ++ ) + except IOError as e: + LOG.warning('Failed to load /proc/%s/stat. %s', pid, e) + return ppid +-- +2.40.0 + diff --git a/backport-util-add-Version-class.patch b/backport-util-add-Version-class.patch new file mode 100644 index 0000000000000000000000000000000000000000..8f6dd34c91838e68271ca200758c5ea2c9628a20 --- /dev/null +++ b/backport-util-add-Version-class.patch @@ -0,0 +1,98 @@ +From 63d69ccefdca774d90be02a5625f543194163e73 Mon Sep 17 00:00:00 2001 +From: Brett Holman +Date: Sat, 6 Aug 2022 00:11:25 +0200 +Subject: [PATCH] Add Ansible Config Module (#1579) + +Reference:https://github.com/canonical/cloud-init/commit/63d69ccefdca774d90be02a5625f543194163e73 +Conflict:(1)only change cloudinit/util.py. +(2)add 'from collections import namedtuple' + +The intended purpose of this commit is to augment cloud-init's +configuration capabilities during boot. This allows users +to run ansible playbooks against the local node as part of +the boot order. Current Ansible workflows on nodes booted +by cloud-init requires waiting for the booted node before +running the playbook against the online node from the external +control node. In the current state this could potentially +be automated to using phone-home or runcmd modules, however +neither of these options provides an obvious solution for +integration and both require an external service to operate. + +This module enables users to seamlessly integrate cloud-init +and Ansible auto-configuration during boot using ansible-pull, +a less common mode of operation that differs from the normal +mode of operation by installing Ansible locally and running a +playbook pulled from a VCS repository. + +Expected future work in this module includes an option to +configure management nodes. +--- + cloudinit/util.py | 47 +++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 45 insertions(+), 2 deletions(-) + +diff --git a/cloudinit/util.py b/cloudinit/util.py +index 7c47871..8763050 100644 +--- a/cloudinit/util.py ++++ b/cloudinit/util.py +@@ -33,7 +33,8 @@ import sys + import time + from base64 import b64decode, b64encode + from errno import ENOENT +-from functools import lru_cache ++from collections import namedtuple ++from functools import lru_cache, total_ordering + from urllib import parse + from typing import List + +@@ -2776,4 +2777,46 @@ def get_proc_ppid(pid): + LOG.warning('Failed to load /proc/%s/stat. %s', pid, e) + return ppid + +-# vi: ts=4 expandtab ++ ++@total_ordering ++class Version(namedtuple("Version", ["major", "minor", "patch", "rev"])): ++ def __new__(cls, major=-1, minor=-1, patch=-1, rev=-1): ++ """Default of -1 allows us to tiebreak in favor of the most specific ++ number""" ++ return super(Version, cls).__new__(cls, major, minor, patch, rev) ++ ++ @classmethod ++ def from_str(cls, version: str): ++ return cls(*(list(map(int, version.split("."))))) ++ ++ def __gt__(self, other): ++ return 1 == self._compare_version(other) ++ ++ def __eq__(self, other): ++ return ( ++ self.major == other.major ++ and self.minor == other.minor ++ and self.patch == other.patch ++ and self.rev == other.rev ++ ) ++ ++ def _compare_version(self, other) -> int: ++ """ ++ return values: ++ 1: self > v2 ++ -1: self < v2 ++ 0: self == v2 ++ to break a tie between 3.1.N and 3.1, always treat the more ++ specific number as larger ++ """ ++ if self == other: ++ return 0 ++ if self.major > other.major: ++ return 1 ++ if self.minor > other.minor: ++ return 1 ++ if self.patch > other.patch: ++ return 1 ++ if self.rev > other.rev: ++ return 1 ++ return -1 +-- +2.33.0 + + diff --git a/cloud-init.spec b/cloud-init.spec index f296a32b83c3523997054c1754d4b96586c14dc2..e384f98701e9bcda74c795d1647e5259900b674e 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -1,6 +1,6 @@ Name: cloud-init Version: 21.4 -Release: 15 +Release: 16 Summary: the defacto multi-distribution package that handles early initialization of a cloud instance. License: ASL 2.0 or GPLv3 URL: http://launchpad.net/cloud-init @@ -35,6 +35,14 @@ Patch6005: backport-DataSourceVMware-fix-var-use-before-init-1674.patch Patch6006: backport-cc_set_hostname-ignore-var-lib-cloud-data-set-hostna.patch Patch6007: backport-disk_setup-use-byte-string-when-purging-the-partitio.patch Patch6008: backport-util-atomically-update-sym-links-to-avoid-Suppress-F.patch +Patch6009: backport-Workaround-net_setup_link-race-with-udev-1655.patch +Patch6010: backport-util-Fix-error-path-and-parsing-in-get_proc_ppid.patch +Patch6011: backport-util-Support-Idle-process-state-in-get_proc_ppid-163.patch +Patch6012: backport-Use-btrfs-enquque-when-available-1926.patch +Patch6013: backport-mounts-fix-suggested_swapsize-for-64GB-hosts-1569.patch +Patch6014: backport-Fix-ds-identify-not-detecting-NoCloud-seed-in-config.patch +Patch6015: backport-cc_disk_setup-pass-options-in-correct-order-to-utils.patch +Patch6016: backport-util-add-Version-class.patch Patch9000: Fix-the-error-level-logs-displayed-for-the-cloud-init-local-service.patch @@ -146,6 +154,9 @@ fi %exclude /usr/share/doc/* %changelog +* Fri Aug 18 2023 shixuantong - 21.4-16 +- backport upstream patches + * Sat Jul 29 2023 shixuantong - 21.4-15 - remove patch comment