backport upstream patches

This commit is contained in:
shixuantong 2023-08-18 14:40:13 +08:00
parent f7a7a356a5
commit 8f58b0e2dc
6 changed files with 600 additions and 1 deletions

View File

@ -0,0 +1,108 @@
From 3e02227236fc860bdf0937b2734b35c373fdb263 Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
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

View File

@ -0,0 +1,107 @@
From d1e237d22deeb7dde724bdc5495b5cbbe8914404 Mon Sep 17 00:00:00 2001
From: Robert Schweikert <rjschwei@suse.com>
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

View File

@ -0,0 +1,113 @@
From 7b04985553ed8b0bc29574608e261ee42280f4d0 Mon Sep 17 00:00:00 2001
From: dermotbradley <dermot_bradley@yahoo.com>
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<X>``, 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

View File

@ -0,0 +1,165 @@
From b711bb6a4db8281a33f9809cff9ba84c9c5fe1e4 Mon Sep 17 00:00:00 2001
From: Steven Stallion <sstallion@gmail.com>
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

View File

@ -0,0 +1,98 @@
From 63d69ccefdca774d90be02a5625f543194163e73 Mon Sep 17 00:00:00 2001
From: Brett Holman <brett.holman@canonical.com>
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

View File

@ -1,6 +1,6 @@
Name: cloud-init
Version: 21.4
Release: 18
Release: 19
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
@ -39,6 +39,11 @@ 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
@ -150,6 +155,9 @@ fi
%exclude /usr/share/doc/*
%changelog
* Fri Aug 18 2023 shixuantong <shixuantong1@huawei.com> - 21.4-19
- backport upstream patches
* Sat Jul 29 2023 Lv Ying <lvying6@huawei.com> - 21.4-18
- backport upstream patches:
https://github.com/canonical/cloud-init/commit/7136109df5a7b3c75dfb05a853fc4485fed25b5f