Compare commits

...

No commits in common. "c8" and "c8-beta" have entirely different histories.
c8 ... c8-beta

12 changed files with 1 additions and 1807 deletions

View File

@ -1,65 +0,0 @@
From 9da40a7e46e40eb090538f9d8a5aa6049fbbc5b8 Mon Sep 17 00:00:00 2001
From: Ani Sinha <anisinha@redhat.com>
Date: Tue, 12 Mar 2024 12:52:10 +0530
Subject: [PATCH] Retain exit code in cloud-init status for recoverable errors
RH-Author: Ani Sinha <None>
RH-MergeRequest: 126: Retain exit code in cloud-init status for recoverable errors
RH-Jira: RHEL-28817
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Commit: [1/1] 8c45ffe77ed8e964c35af4705d65daaf8282038f
Version 23.4 of cloud-init changed the status code reported by cloud-init for
recoverable errors from 0 to 2. Please see the commit
70acb7f2a30d58 ("Add support for cloud-init "degraded" state (#4500)")
This change has the potential to break customers who are expecting a 0 status
and where warnings can be expected. Hence, revert the status code from 2 to 0
even in case of recoverable errors. This retains the old behavior and hence
avoids breaking scripts and software stack that expects 0 on the end user side.
Cannonical has made a similar change downstream for similar reasons. Please see
https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/2048522
and the corresponding downstream patch:
https://github.com/canonical/cloud-init/pull/4747/commits/adce34bfd214e4eecdf87329486f30f0898dd303
This patch has limited risk as it narrowly only restores the old status
code for recoverable errors and does not modify anything else.
X-downstream-only: true
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
cloudinit/cmd/status.py | 2 +-
tests/unittests/cmd/test_status.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cloudinit/cmd/status.py b/cloudinit/cmd/status.py
index f5ee9c11..849c80bc 100644
--- a/cloudinit/cmd/status.py
+++ b/cloudinit/cmd/status.py
@@ -225,7 +225,7 @@ def handle_status_args(name, args) -> int:
return 1
# Recoverable error
elif details.status in UXAppStatusDegradedMap.values():
- return 2
+ return 0
return 0
diff --git a/tests/unittests/cmd/test_status.py b/tests/unittests/cmd/test_status.py
index 6c85a59a..567b517a 100644
--- a/tests/unittests/cmd/test_status.py
+++ b/tests/unittests/cmd/test_status.py
@@ -636,7 +636,7 @@ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
},
None,
MyArgs(long=False, wait=False, format="json"),
- 2,
+ 0,
{
"boot_status_code": "enabled-by-kernel-cmdline",
"datasource": "nocloud",
--
2.39.3

View File

@ -1,108 +0,0 @@
From 42aad98557bb62ae693f38e5f1e137bcc44f6046 Mon Sep 17 00:00:00 2001
From: Ani Sinha <anisinha@redhat.com>
Date: Tue, 5 Mar 2024 12:42:26 +0530
Subject: [PATCH] Revert "systemd: Standardize cloud-init systemd enablement
(#4399)"
RH-Author: Ani Sinha <None>
RH-MergeRequest: 124: Revert "systemd: Standardize cloud-init systemd enablement (#4399)"
RH-Jira: RHEL-21290
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Commit: [1/1] 10da53e761e25ff7d254a4cfb8fb1fd18de8b4ed
This reverts commit ec7dde8041d4023b09324e84abe37dc766ebbaf6.
'ConditionEnvironment' clause is not available in RHEL 8.10 systemd and adding
the feature would be complicated. Hence reverting the patch seems to be the
right thing to do as it was a simple enhancement to make sure all distros use
systemd's kernel commandline and file conditionals. We only care about RHEL so
the change should not affect us.
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
systemd/cloud-config.service.tmpl | 3 ++-
systemd/cloud-final.service.tmpl | 3 ++-
systemd/cloud-init-local.service.tmpl | 3 ++-
systemd/cloud-init.service.tmpl | 3 ++-
systemd/cloud-init.target | 3 ---
5 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/systemd/cloud-config.service.tmpl b/systemd/cloud-config.service.tmpl
index 31d9d983..76e50ae1 100644
--- a/systemd/cloud-config.service.tmpl
+++ b/systemd/cloud-config.service.tmpl
@@ -5,9 +5,10 @@ After=network-online.target cloud-config.target
After=snapd.seeded.service
Before=systemd-user-sessions.service
Wants=network-online.target cloud-config.target
+{% if variant == "rhel" %}
ConditionPathExists=!/etc/cloud/cloud-init.disabled
ConditionKernelCommandLine=!cloud-init=disabled
-ConditionEnvironment=!KERNEL_CMDLINE=cloud-init=disabled
+{% endif %}
[Service]
Type=oneshot
diff --git a/systemd/cloud-final.service.tmpl b/systemd/cloud-final.service.tmpl
index bcf8b009..85f423ac 100644
--- a/systemd/cloud-final.service.tmpl
+++ b/systemd/cloud-final.service.tmpl
@@ -7,9 +7,10 @@ After=multi-user.target
Before=apt-daily.service
{% endif %}
Wants=network-online.target cloud-config.service
+{% if variant == "rhel" %}
ConditionPathExists=!/etc/cloud/cloud-init.disabled
ConditionKernelCommandLine=!cloud-init=disabled
-ConditionEnvironment=!KERNEL_CMDLINE=cloud-init=disabled
+{% endif %}
[Service]
diff --git a/systemd/cloud-init-local.service.tmpl b/systemd/cloud-init-local.service.tmpl
index 3a1ca7fa..6f3f9d8d 100644
--- a/systemd/cloud-init-local.service.tmpl
+++ b/systemd/cloud-init-local.service.tmpl
@@ -26,9 +26,10 @@ Before=sysinit.target
Conflicts=shutdown.target
{% endif %}
RequiresMountsFor=/var/lib/cloud
+{% if variant == "rhel" %}
ConditionPathExists=!/etc/cloud/cloud-init.disabled
ConditionKernelCommandLine=!cloud-init=disabled
-ConditionEnvironment=!KERNEL_CMDLINE=cloud-init=disabled
+{% endif %}
[Service]
Type=oneshot
diff --git a/systemd/cloud-init.service.tmpl b/systemd/cloud-init.service.tmpl
index bf91164a..26d2e39c 100644
--- a/systemd/cloud-init.service.tmpl
+++ b/systemd/cloud-init.service.tmpl
@@ -38,9 +38,10 @@ Conflicts=shutdown.target
Before=shutdown.target
Conflicts=shutdown.target
{% endif %}
+{% if variant == "rhel" %}
ConditionPathExists=!/etc/cloud/cloud-init.disabled
ConditionKernelCommandLine=!cloud-init=disabled
-ConditionEnvironment=!KERNEL_CMDLINE=cloud-init=disabled
+{% endif %}
[Service]
Type=oneshot
diff --git a/systemd/cloud-init.target b/systemd/cloud-init.target
index 30450f7f..760dfee5 100644
--- a/systemd/cloud-init.target
+++ b/systemd/cloud-init.target
@@ -10,6 +10,3 @@
[Unit]
Description=Cloud-init target
After=multi-user.target
-ConditionPathExists=!/etc/cloud/cloud-init.disabled
-ConditionKernelCommandLine=!cloud-init=disabled
-ConditionEnvironment=!KERNEL_CMDLINE=cloud-init=disabled
--
2.39.3

View File

@ -1,207 +0,0 @@
From 1024e43b58ce84bb6c6d8bd89785704e04560b2a Mon Sep 17 00:00:00 2001
From: Florian Apolloner <florian@apolloner.eu>
Date: Fri, 5 Jan 2024 19:07:12 +0100
Subject: [PATCH 2/3] feat: apply global DNS to interfaces in network-manager
(#4723)
RH-Author: Cathy Avery <cavery@redhat.com>
RH-MergeRequest: 128: Fixes for cloud-init fails to configure DNS/search domains for network-config v1
RH-Jira: RHEL-27134
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Commit: [2/2] 73d27116735e853fbaa38942390721dd78bc6241
Sometimes DNS settings in cloud configs are specified globally and
not per interface / subnet. This results in a configuration without
proper nameservers. This was fixed for netplan in d29eeccd and is
now also applied to the network-manager renderer.
Co-authored-by: James Falcon <james.falcon@canonical.com>
(cherry picked from commit 0d787d0a262f70ff848b315633742aa8fc45a1de)
Signed-off-by: Cathy Avery <cavery@redhat.com>
---
cloudinit/net/network_manager.py | 52 ++++++++++++++---------
tests/unittests/net/test_net_rendering.py | 3 ++
tests/unittests/test_net.py | 11 +++++
tools/.github-cla-signers | 1 +
4 files changed, 47 insertions(+), 20 deletions(-)
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
index bd6e6d75..0ba210b7 100644
--- a/cloudinit/net/network_manager.py
+++ b/cloudinit/net/network_manager.py
@@ -246,7 +246,7 @@ class NMConnection:
"""
return addr.replace("-", ":").upper()
- def render_interface(self, iface, renderer):
+ def render_interface(self, iface, network_state, renderer):
"""
Integrate information from network state interface information
into the connection. Most of the work is done here.
@@ -311,7 +311,6 @@ class NMConnection:
found_dns_search = []
# Deal with Layer 3 configuration
- use_top_level_dns = "dns" in iface
for subnet in iface["subnets"]:
family = "ipv6" if subnet_is_ipv6(subnet) else "ipv4"
@@ -322,26 +321,39 @@ class NMConnection:
self.config[family]["gateway"] = subnet["gateway"]
for route in subnet["routes"]:
self._add_route(route)
- if not use_top_level_dns and "dns_nameservers" in subnet:
- for nameserver in subnet["dns_nameservers"]:
- found_nameservers.append(nameserver)
- if not use_top_level_dns and "dns_search" in subnet:
- found_dns_search.append(subnet["dns_search"])
+ # Add subnet-level DNS
+ if "dns_nameservers" in subnet:
+ found_nameservers.extend(subnet["dns_nameservers"])
+ if "dns_search" in subnet:
+ found_dns_search.extend(subnet["dns_search"])
if family == "ipv4" and "mtu" in subnet:
ipv4_mtu = subnet["mtu"]
- # Now add our DNS search domains. We add them later because we
- # only want them if an IP family has already been defined
- if use_top_level_dns:
- for nameserver in iface["dns"]["nameservers"]:
- self._add_nameserver(nameserver)
- if iface["dns"]["search"]:
- self._add_dns_search(iface["dns"]["search"])
- else:
- for nameserver in found_nameservers:
- self._add_nameserver(nameserver)
- for dns_search in found_dns_search:
- self._add_dns_search(dns_search)
+ # Add interface-level DNS
+ if "dns" in iface:
+ found_nameservers += [
+ dns
+ for dns in iface["dns"]["nameservers"]
+ if dns not in found_nameservers
+ ]
+ found_dns_search += [
+ search
+ for search in iface["dns"]["search"]
+ if search not in found_dns_search
+ ]
+
+ # We prefer any interface-specific DNS entries, but if we do not
+ # have any, add the global DNS to the connection
+ if not found_nameservers and network_state.dns_nameservers:
+ found_nameservers = network_state.dns_nameservers
+ if not found_dns_search and network_state.dns_searchdomains:
+ found_dns_search = network_state.dns_searchdomains
+
+ # Write out all DNS entries to the connection
+ for nameserver in found_nameservers:
+ self._add_nameserver(nameserver)
+ if found_dns_search:
+ self._add_dns_search(found_dns_search)
# we do not want to set may-fail to false for both ipv4 and ipv6 dhcp
# at the at the same time. This will make the network configuration
@@ -457,7 +469,7 @@ class Renderer(renderer.Renderer):
# Now render the actual interface configuration
for iface in network_state.iter_interfaces():
conn = self.connections[iface["name"]]
- conn.render_interface(iface, self)
+ conn.render_interface(iface, network_state, self)
# And finally write the files
for con_id, conn in self.connections.items():
diff --git a/tests/unittests/net/test_net_rendering.py b/tests/unittests/net/test_net_rendering.py
index 06feab89..f340ffc1 100644
--- a/tests/unittests/net/test_net_rendering.py
+++ b/tests/unittests/net/test_net_rendering.py
@@ -88,6 +88,9 @@ def _check_network_manager(network_state: NetworkState, tmp_path: Path):
"test_name, renderers",
[("no_matching_mac_v2", Renderer.Netplan | Renderer.NetworkManager)],
)
+@pytest.mark.xfail(
+ reason="v2 interface-specific DNS errantly gets applied globally"
+)
def test_convert(test_name, renderers, tmp_path):
network_config = safeyaml.load(
Path(ARTIFACT_DIR, f"{test_name}.yaml").read_text()
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index 678ec39b..e010eb6b 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -645,6 +645,7 @@ method=manual
may-fail=false
address1=172.19.1.34/22
route1=0.0.0.0/0,172.19.3.254
+dns=172.19.0.12;
""".lstrip(),
),
@@ -2769,6 +2770,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
[ipv4]
method=auto
may-fail=false
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
+ dns-search=barley.maas;wark.maas;foobar.maas;
"""
),
@@ -2794,6 +2797,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
method=manual
may-fail=false
address1=192.168.200.7/24
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
+ dns-search=barley.maas;wark.maas;foobar.maas;
"""
),
@@ -2818,6 +2823,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
[ipv4]
method=auto
may-fail=false
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
+ dns-search=barley.maas;wark.maas;foobar.maas;
"""
),
@@ -2902,12 +2909,15 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
method=manual
may-fail=false
address1=192.168.14.2/24
+ dns=8.8.8.8;4.4.4.4;8.8.4.4;
+ dns-search=barley.maas;wark.maas;foobar.maas;
[ipv6]
method=manual
may-fail=false
address1=2001:1::1/64
route1=::/0,2001:4800:78ff:1b::1
+ dns-search=barley.maas;wark.maas;foobar.maas;
"""
),
@@ -2962,6 +2972,7 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
[ipv6]
method=auto
may-fail=false
+ dns-search=barley.maas;wark.maas;foobar.maas;
"""
),
diff --git a/tools/.github-cla-signers b/tools/.github-cla-signers
index dbdb9cfa..f4da0989 100644
--- a/tools/.github-cla-signers
+++ b/tools/.github-cla-signers
@@ -13,6 +13,7 @@ andrewbogott
andrewlukoshko
ani-sinha
antonyc
+apollo13
aswinrajamannar
bdrung
beantaxi
--
2.41.0

View File

@ -1,350 +0,0 @@
From 773501c6d2b52a5623b5fed3c5534d41aa6488fa Mon Sep 17 00:00:00 2001
From: Ani Sinha <anisinha@redhat.com>
Date: Thu, 20 Jun 2024 22:27:03 +0530
Subject: [PATCH] feat(sysconfig): Add DNS from interface config to resolv.conf
(#5401)
RH-Author: xiachen <xiachen@redhat.com>
RH-MergeRequest: 140: feat(sysconfig): Add DNS from interface config to resolv.conf (#5401)
RH-Jira: RHEL-46013
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Commit: [1/1] b9f492627cdae3bf356f388eb0870241793a7f99
sysconfig renderer currently only uses global dns and search domain
configuration in order to populate /etc/resolv.conf. This means it ignores
interface specific dns configuration completely. This means, when global dns
information is absent and only interface specific dns configuration is present,
/etc/resolv.conf will not have complete dns information. Fix this so that
per interface dns information is also taken into account along with global dns
configuration in order to populate /etc/resolv.conf.
Fixes: GH-5400
Signed-off-by: Ani Sinha <anisinha@redhat.com>
(cherry picked from commit 1b8030e0c7fd6fbff7e38ad1e3e6266ae50c83a5)
---
cloudinit/net/sysconfig.py | 52 +++++++++-
tests/unittests/test_net.py | 183 +++++++++++++++++++++++++++++++++++-
2 files changed, 229 insertions(+), 6 deletions(-)
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index f01c4236..42eb2be3 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -824,20 +824,62 @@ class Renderer(renderer.Renderer):
@staticmethod
def _render_dns(network_state, existing_dns_path=None):
- # skip writing resolv.conf if network_state doesn't include any input.
+
+ found_nameservers = []
+ found_dns_search = []
+
+ for iface in network_state.iter_interfaces():
+ for subnet in iface["subnets"]:
+ # Add subnet-level DNS
+ if "dns_nameservers" in subnet:
+ found_nameservers.extend(subnet["dns_nameservers"])
+ if "dns_search" in subnet:
+ found_dns_search.extend(subnet["dns_search"])
+
+ # Add interface-level DNS
+ if "dns" in iface:
+ found_nameservers += [
+ dns
+ for dns in iface["dns"]["nameservers"]
+ if dns not in found_nameservers
+ ]
+ found_dns_search += [
+ search
+ for search in iface["dns"]["search"]
+ if search not in found_dns_search
+ ]
+
+ # When both global and interface specific entries are present,
+ # use them both to generate /etc/resolv.conf eliminating duplicate
+ # entries. Otherwise use global or interface specific entries whichever
+ # is provided.
+ if network_state.dns_nameservers:
+ found_nameservers += [
+ nameserver
+ for nameserver in network_state.dns_nameservers
+ if nameserver not in found_nameservers
+ ]
+ if network_state.dns_searchdomains:
+ found_dns_search += [
+ search
+ for search in network_state.dns_searchdomains
+ if search not in found_dns_search
+ ]
+
+ # skip writing resolv.conf if no dns information is provided in conf.
if not any(
[
- len(network_state.dns_nameservers),
- len(network_state.dns_searchdomains),
+ len(found_nameservers),
+ len(found_dns_search),
]
):
return None
content = resolv_conf.ResolvConf("")
if existing_dns_path and os.path.isfile(existing_dns_path):
content = resolv_conf.ResolvConf(util.load_file(existing_dns_path))
- for nameserver in network_state.dns_nameservers:
+ for nameserver in found_nameservers:
content.add_nameserver(nameserver)
- for searchdomain in network_state.dns_searchdomains:
+ for searchdomain in found_dns_search:
content.add_search_domain(searchdomain)
header = _make_header(";")
content_str = str(content)
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index e010eb6b..86ba398d 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -516,6 +516,8 @@ OS_SAMPLES = [
}
],
"ip_address": "172.19.1.34",
+ "dns_search": ["testweb.com"],
+ "dns_nameservers": ["172.19.0.13"],
"id": "network0",
}
],
@@ -550,7 +552,9 @@ STARTMODE=auto
"""
; Created by cloud-init automatically, do not edit.
;
+nameserver 172.19.0.13
nameserver 172.19.0.12
+search testweb.com
""".lstrip(),
),
(
@@ -581,6 +585,8 @@ dns = none
BOOTPROTO=none
DEFROUTE=yes
DEVICE=eth0
+DNS1=172.19.0.13
+DOMAIN=testweb.com
GATEWAY=172.19.3.254
HWADDR=fa:16:3e:ed:9a:59
IPADDR=172.19.1.34
@@ -595,7 +601,173 @@ USERCTL=no
"""
; Created by cloud-init automatically, do not edit.
;
+nameserver 172.19.0.13
nameserver 172.19.0.12
+search testweb.com
+""".lstrip(),
+ ),
+ (
+ "etc/NetworkManager/conf.d/99-cloud-init.conf",
+ """
+# Created by cloud-init automatically, do not edit.
+#
+[main]
+dns = none
+""".lstrip(),
+ ),
+ (
+ "etc/udev/rules.d/70-persistent-net.rules",
+ "".join(
+ [
+ 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
+ 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n',
+ ]
+ ),
+ ),
+ ],
+ "expected_network_manager": [
+ (
+ "".join(
+ [
+ "etc/NetworkManager/system-connections",
+ "/cloud-init-eth0.nmconnection",
+ ]
+ ),
+ """
+# Generated by cloud-init. Changes will be lost.
+
+[connection]
+id=cloud-init eth0
+uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
+autoconnect-priority=120
+type=ethernet
+
+[user]
+org.freedesktop.NetworkManager.origin=cloud-init
+
+[ethernet]
+mac-address=FA:16:3E:ED:9A:59
+
+[ipv4]
+method=manual
+may-fail=false
+address1=172.19.1.34/22
+route1=0.0.0.0/0,172.19.3.254
+dns=172.19.0.13;
+dns-search=testweb.com;
+
+""".lstrip(),
+ ),
+ ],
+ },
+ {
+ "in_data": {
+ "services": [
+ {
+ "type": "dns",
+ "address": "172.19.0.12",
+ "search": ["example1.com", "example2.com"],
+ }
+ ],
+ "networks": [
+ {
+ "network_id": "dacd568d-5be6-4786-91fe-750c374b78b4",
+ "type": "ipv4",
+ "netmask": "255.255.252.0",
+ "link": "eth0",
+ "routes": [
+ {
+ "netmask": "0.0.0.0",
+ "network": "0.0.0.0",
+ "gateway": "172.19.3.254",
+ }
+ ],
+ "ip_address": "172.19.1.34",
+ "dns_search": ["example3.com"],
+ "dns_nameservers": ["172.19.0.12"],
+ "id": "network0",
+ }
+ ],
+ "links": [
+ {
+ "ethernet_mac_address": "fa:16:3e:ed:9a:59",
+ "mtu": None,
+ "type": "physical",
+ "id": "eth0",
+ },
+ ],
+ },
+ "in_macs": {
+ "fa:16:3e:ed:9a:59": "eth0",
+ },
+ "out_sysconfig_opensuse": [
+ (
+ "etc/sysconfig/network/ifcfg-eth0",
+ """
+# Created by cloud-init automatically, do not edit.
+#
+BOOTPROTO=static
+IPADDR=172.19.1.34
+LLADDR=fa:16:3e:ed:9a:59
+NETMASK=255.255.252.0
+STARTMODE=auto
+""".lstrip(),
+ ),
+ (
+ "etc/resolv.conf",
+ """
+; Created by cloud-init automatically, do not edit.
+;
+nameserver 172.19.0.12
+search example3.com example1.com example2.com
+""".lstrip(),
+ ),
+ (
+ "etc/NetworkManager/conf.d/99-cloud-init.conf",
+ """
+# Created by cloud-init automatically, do not edit.
+#
+[main]
+dns = none
+""".lstrip(),
+ ),
+ (
+ "etc/udev/rules.d/85-persistent-net-cloud-init.rules",
+ "".join(
+ [
+ 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ',
+ 'ATTR{address}=="fa:16:3e:ed:9a:59", NAME="eth0"\n',
+ ]
+ ),
+ ),
+ ],
+ "out_sysconfig_rhel": [
+ (
+ "etc/sysconfig/network-scripts/ifcfg-eth0",
+ """
+# Created by cloud-init automatically, do not edit.
+#
+BOOTPROTO=none
+DEFROUTE=yes
+DEVICE=eth0
+DNS1=172.19.0.12
+DOMAIN=example3.com
+GATEWAY=172.19.3.254
+HWADDR=fa:16:3e:ed:9a:59
+IPADDR=172.19.1.34
+NETMASK=255.255.252.0
+ONBOOT=yes
+TYPE=Ethernet
+USERCTL=no
+""".lstrip(),
+ ),
+ (
+ "etc/resolv.conf",
+ """
+; Created by cloud-init automatically, do not edit.
+;
+nameserver 172.19.0.12
+search example3.com example1.com example2.com
""".lstrip(),
),
(
@@ -646,6 +818,7 @@ may-fail=false
address1=172.19.1.34/22
route1=0.0.0.0/0,172.19.3.254
dns=172.19.0.12;
+dns-search=example3.com;
""".lstrip(),
),
@@ -653,7 +826,13 @@ dns=172.19.0.12;
},
{
"in_data": {
- "services": [{"type": "dns", "address": "172.19.0.12"}],
+ "services": [
+ {
+ "type": "dns",
+ "address": "172.19.0.12",
+ "search": "example.com",
+ }
+ ],
"networks": [
{
"network_id": "public-ipv4",
@@ -714,6 +893,7 @@ STARTMODE=auto
; Created by cloud-init automatically, do not edit.
;
nameserver 172.19.0.12
+search example.com
""".lstrip(),
),
(
@@ -761,6 +941,7 @@ USERCTL=no
; Created by cloud-init automatically, do not edit.
;
nameserver 172.19.0.12
+search example.com
""".lstrip(),
),
(
--
2.45.1

View File

@ -1,45 +0,0 @@
From b424877c0e7673466e7bd354c1eed4db908ebab3 Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Thu, 18 Apr 2024 20:27:27 -0500
Subject: [PATCH] fix: Add subnet ipv4/ipv6 to network schema (#5191)
RH-Author: Ani Sinha <anisinha@redhat.com>
RH-MergeRequest: 143: fix: Add subnet ipv4/ipv6 to network schema (#5191)
RH-Jira: RHEL-54155
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Commit: [1/1] d4c7beb80b8c67df6b6fc04db8d3b93ed82dd067
These are used by our openstack network_data.json parsing code and
get used by the sysconfig renderer.
Fixes GH-4911
(cherry picked from commit 0b1ca174095e3ad685e6d6649bb08aafb19a95b9)
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
cloudinit/config/schemas/schema-network-config-v1.json | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/cloudinit/config/schemas/schema-network-config-v1.json b/cloudinit/config/schemas/schema-network-config-v1.json
index 64c492a4..f485c784 100644
--- a/cloudinit/config/schemas/schema-network-config-v1.json
+++ b/cloudinit/config/schemas/schema-network-config-v1.json
@@ -523,6 +523,14 @@
"items": {
"$ref": "#/$defs/anyOf_type_route"
}
+ },
+ "ipv4": {
+ "type": "boolean",
+ "description": "Indicate if the subnet is IPv4. If not specified, it will be inferred from the subnet type or address. This exists for compatibility with OpenStack's ``network_data.json`` when rendered through sysconfig."
+ },
+ "ipv6": {
+ "type": "boolean",
+ "description": "Indicate if the subnet is IPv6. If not specified, it will be inferred from the subnet type or address. This is exists for compatibility with OpenStack's ``network_data.json`` when rendered through sysconfig."
}
}
},
--
2.45.1

View File

@ -1,66 +0,0 @@
From 4e5b1ed68014b81ca2ef2f07675f2a43cf03c5c3 Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Tue, 26 Mar 2024 15:55:50 -0500
Subject: [PATCH] fix: Always use single datasource if specified (#5098)
RH-Author: Ani Sinha <anisinha@redhat.com>
RH-MergeRequest: 135: fix: Always use single datasource if specified (#5098)
RH-Jira: RHEL-36701
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Commit: [1/1] 491f053f7d758f1a0ca8918d1449cc2f7838291f
This change may require a user to add `None` to the `datasource_list`
defined in `/etc/cloud/cloud.cfg[.d]` if they have a customized
datasource_list and want the DataSourceNone fallback behavior.
ds-identify would automatically append "None" to the datasource_list
if a single entry was provided in /etc/cloud/cloud.cfg[.d].
This wasn't a problem in the past as the python code would detect
a single datasource along with None as an indication to automatically
use that datasource. Since the python code no longer does that,
we should ensure that one specified datasource results in one specified
datasource after ds-identify has run.
Fixes GH-5091
(cherry picked from commit cdbbd17ae400e432d13f674c18a6f5c873fa328b)
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
tests/unittests/test_ds_identify.py | 2 +-
tools/ds-identify | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
index ba0bf779..acbf3f03 100644
--- a/tests/unittests/test_ds_identify.py
+++ b/tests/unittests/test_ds_identify.py
@@ -522,7 +522,7 @@ class TestDsIdentify(DsIdentifyBase):
mydata = copy.deepcopy(VALID_CFG["Ec2-hvm"])
cfgpath = "etc/cloud/cloud.cfg.d/myds.cfg"
mydata["files"][cfgpath] = 'datasource_list: ["NoCloud"]\n'
- self._check_via_dict(mydata, rc=RC_FOUND, dslist=["NoCloud", DS_NONE])
+ self._check_via_dict(mydata, rc=RC_FOUND, dslist=["NoCloud"])
def test_configured_list_with_none(self):
"""When datasource_list already contains None, None is not added.
diff --git a/tools/ds-identify b/tools/ds-identify
index ec2cc18a..6e49ded3 100755
--- a/tools/ds-identify
+++ b/tools/ds-identify
@@ -1865,7 +1865,11 @@ _main() {
# if there is only a single entry in $DI_DSLIST
if [ $# -eq 1 ] || [ $# -eq 2 -a "$2" = "None" ] ; then
debug 1 "single entry in datasource_list ($DI_DSLIST) use that."
- found "$@"
+ if [ $# -eq 1 ]; then
+ write_result "datasource_list: [ $1 ]"
+ else
+ found "$@"
+ fi
return
fi
--
2.45.1

View File

@ -1,247 +0,0 @@
From cfbe83d4a869ab20d385b5058031df0364483bda Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Thu, 18 Jul 2024 09:04:54 -0400
Subject: [PATCH] fix: Clean cache if no datasource fallback (#5499)
RH-Author: Ani Sinha <anisinha@redhat.com>
RH-MergeRequest: 141: fix: Clean cache if no datasource fallback (#5499)
RH-Jira: RHEL-49742
RH-Acked-by: xiachen <xiachen@redhat.com>
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Commit: [1/1] 64a79c1a6bd06c280aed85032bb55cc60ec1fc2e
9929a00 added the ability to used a cached datasource when none is
found. This was supposed to be per-datasource, but the lack of cache
cleaning got applied universally. This commit makes it so cache will be
cleaned as it was before if fallback isn't implemented in datasource.
Fixes GH-5486
(cherry picked from commit 550c685c98551f65c30832b186fe091721b48477)
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
cloudinit/stages.py | 1 +
.../assets/DataSourceNoCacheNetworkOnly.py | 23 ++++
.../assets/DataSourceNoCacheWithFallback.py | 29 +++++
.../datasources/test_caching.py | 115 ++++++++++++++++++
tests/integration_tests/instances.py | 4 +-
5 files changed, 171 insertions(+), 1 deletion(-)
create mode 100644 tests/integration_tests/assets/DataSourceNoCacheNetworkOnly.py
create mode 100644 tests/integration_tests/assets/DataSourceNoCacheWithFallback.py
create mode 100644 tests/integration_tests/datasources/test_caching.py
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 0b795624..ace94c9a 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -378,6 +378,7 @@ class Init:
ds,
)
else:
+ util.del_file(self.paths.instance_link)
raise e
self.datasource = ds
# Ensure we adjust our path members datasource
diff --git a/tests/integration_tests/assets/DataSourceNoCacheNetworkOnly.py b/tests/integration_tests/assets/DataSourceNoCacheNetworkOnly.py
new file mode 100644
index 00000000..54a7bab3
--- /dev/null
+++ b/tests/integration_tests/assets/DataSourceNoCacheNetworkOnly.py
@@ -0,0 +1,23 @@
+import logging
+
+from cloudinit import sources
+
+LOG = logging.getLogger(__name__)
+
+
+class DataSourceNoCacheNetworkOnly(sources.DataSource):
+ def _get_data(self):
+ LOG.debug("TEST _get_data called")
+ return True
+
+
+datasources = [
+ (
+ DataSourceNoCacheNetworkOnly,
+ (sources.DEP_FILESYSTEM, sources.DEP_NETWORK),
+ ),
+]
+
+
+def get_datasource_list(depends):
+ return sources.list_from_depends(depends, datasources)
diff --git a/tests/integration_tests/assets/DataSourceNoCacheWithFallback.py b/tests/integration_tests/assets/DataSourceNoCacheWithFallback.py
new file mode 100644
index 00000000..fdfc473f
--- /dev/null
+++ b/tests/integration_tests/assets/DataSourceNoCacheWithFallback.py
@@ -0,0 +1,29 @@
+import logging
+import os
+
+from cloudinit import sources
+
+LOG = logging.getLogger(__name__)
+
+
+class DataSourceNoCacheWithFallback(sources.DataSource):
+ def _get_data(self):
+ if os.path.exists("/ci-test-firstboot"):
+ LOG.debug("TEST _get_data called")
+ return True
+ return False
+
+ def check_if_fallback_is_allowed(self):
+ return True
+
+
+datasources = [
+ (
+ DataSourceNoCacheWithFallback,
+ (sources.DEP_FILESYSTEM,),
+ ),
+]
+
+
+def get_datasource_list(depends):
+ return sources.list_from_depends(depends, datasources)
diff --git a/tests/integration_tests/datasources/test_caching.py b/tests/integration_tests/datasources/test_caching.py
new file mode 100644
index 00000000..33e4b671
--- /dev/null
+++ b/tests/integration_tests/datasources/test_caching.py
@@ -0,0 +1,115 @@
+import pytest
+
+from tests.integration_tests import releases, util
+from tests.integration_tests.instances import IntegrationInstance
+
+
+def setup_custom_datasource(client: IntegrationInstance, datasource_name: str):
+ client.write_to_file(
+ "/etc/cloud/cloud.cfg.d/99-imds.cfg",
+ f"datasource_list: [ {datasource_name}, None ]\n"
+ "datasource_pkg_list: [ cisources ]",
+ )
+ assert client.execute(
+ "mkdir -p /usr/lib/python3/dist-packages/cisources"
+ )
+ client.push_file(
+ util.ASSETS_DIR / f"DataSource{datasource_name}.py",
+ "/usr/lib/python3/dist-packages/cisources/"
+ f"DataSource{datasource_name}.py",
+ )
+
+
+def verify_no_cache_boot(client: IntegrationInstance):
+ log = client.read_from_file("/var/log/cloud-init.log")
+ util.verify_ordered_items_in_text(
+ [
+ "No local datasource found",
+ "running 'init'",
+ "no cache found",
+ "Detected platform",
+ "TEST _get_data called",
+ ],
+ text=log,
+ )
+ util.verify_clean_boot(client)
+
+
+@pytest.mark.skipif(
+ not releases.IS_UBUNTU,
+ reason="hardcoded dist-packages directory",
+)
+def test_no_cache_network_only(client: IntegrationInstance):
+ """Test cache removal per boot. GH-5486
+
+ This tests the CloudStack password reset use case. The expectation is:
+ - Metadata is fetched in network timeframe only
+ - Because `check_instance_id` is not defined, no cached datasource
+ is found in the init-local phase, but the cache is used in the
+ remaining phases due to existance of /run/cloud-init/.instance-id
+ - Because `check_if_fallback_is_allowed` is not defined, cloud-init
+ does NOT fall back to the pickled datasource, and will
+ instead delete the cache during the init-local phase
+ - Metadata is therefore fetched every boot in the network phase
+ """
+ setup_custom_datasource(client, "NoCacheNetworkOnly")
+
+ # Run cloud-init as if first boot
+ assert client.execute("cloud-init clean --logs")
+ client.restart()
+
+ verify_no_cache_boot(client)
+
+ # Clear the log without clean and run cloud-init for subsequent boot
+ assert client.execute("echo '' > /var/log/cloud-init.log")
+ client.restart()
+
+ verify_no_cache_boot(client)
+
+
+@pytest.mark.skipif(
+ not releases.IS_UBUNTU,
+ reason="hardcoded dist-packages directory",
+)
+def test_no_cache_with_fallback(client: IntegrationInstance):
+ """Test we use fallback when defined and no cache available."""
+ setup_custom_datasource(client, "NoCacheWithFallback")
+
+ # Run cloud-init as if first boot
+ assert client.execute("cloud-init clean --logs")
+ # Used by custom datasource
+ client.execute("touch /ci-test-firstboot")
+ client.restart()
+
+ log = client.read_from_file("/var/log/cloud-init.log")
+ util.verify_ordered_items_in_text(
+ [
+ "no cache found",
+ "Detected platform",
+ "TEST _get_data called",
+ "running 'init'",
+ "restored from cache with run check",
+ "running 'modules:config'",
+ ],
+ text=log,
+ )
+ util.verify_clean_boot(client)
+
+ # Clear the log without clean and run cloud-init for subsequent boot
+ assert client.execute("echo '' > /var/log/cloud-init.log")
+ client.execute("rm /ci-test-firstboot")
+ client.restart()
+
+ log = client.read_from_file("/var/log/cloud-init.log")
+ util.verify_ordered_items_in_text(
+ [
+ "cache invalid in datasource",
+ "Detected platform",
+ "Restored fallback datasource from checked cache",
+ "running 'init'",
+ "restored from cache with run check",
+ "running 'modules:config'",
+ ],
+ text=log,
+ )
+ util.verify_clean_boot(client)
diff --git a/tests/integration_tests/instances.py b/tests/integration_tests/instances.py
index 3fc6558a..23c0dc98 100644
--- a/tests/integration_tests/instances.py
+++ b/tests/integration_tests/instances.py
@@ -88,7 +88,9 @@ class IntegrationInstance:
# First push to a temporary directory because of permissions issues
tmp_path = _get_tmp_path()
self.instance.push_file(str(local_path), tmp_path)
- assert self.execute("mv {} {}".format(tmp_path, str(remote_path))).ok
+ assert self.execute(
+ "mv {} {}".format(tmp_path, str(remote_path))
+ ), f"Failed to push {tmp_path} to {remote_path}"
def read_from_file(self, remote_path) -> str:
result = self.execute("cat {}".format(remote_path))
--
2.39.3

View File

@ -1,391 +0,0 @@
From 65207b6778fa97ff450a9200c28e4770c9128854 Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Tue, 2 Jan 2024 11:29:17 -0600
Subject: [PATCH 1/3] fix: Correct v2 NetworkManager route rendering (#4637)
RH-Author: Cathy Avery <cavery@redhat.com>
RH-MergeRequest: 128: Fixes for cloud-init fails to configure DNS/search domains for network-config v1
RH-Jira: RHEL-27134
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Commit: [1/2] abfebdde6b8b914d5a7de8853beca1fe206a5b23
fix: Correct v2 NetworkManager route rendering
Because network v2 route defintions can have mixed v4 and v6 routes, we
need to determine the IP family per route rather than per subnet.
Similar, ensure dns-search is rendered correctly.
Fixes GH-4518
(cherry picked from commit c2c100e8c9fd8709539b3ab2b0ee34c66ba3f2f7)
Signed-off-by: Cathy Avery <cavery@redhat.com>
---
cloudinit/net/__init__.py | 2 +
cloudinit/net/network_manager.py | 87 +++++++++-------
tests/unittests/test_net.py | 165 ++++++++++++++++++++++++++++++-
3 files changed, 219 insertions(+), 35 deletions(-)
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
index c0888f52..65e7ff33 100644
--- a/cloudinit/net/__init__.py
+++ b/cloudinit/net/__init__.py
@@ -1287,6 +1287,8 @@ def subnet_is_ipv6(subnet) -> bool:
"""Common helper for checking network_state subnets for ipv6."""
# 'static6', 'dhcp6', 'ipv6_dhcpv6-stateful', 'ipv6_dhcpv6-stateless' or
# 'ipv6_slaac'
+ # This function is inappropriate for v2-based routes as routes defined
+ # under v2 subnets can contain ipv4 and ipv6 simultaneously
if subnet["type"].endswith("6") or subnet["type"] in IPV6_DYNAMIC_TYPES:
# This is a request either static6 type or DHCPv6.
return True
diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py
index 76a0ac15..bd6e6d75 100644
--- a/cloudinit/net/network_manager.py
+++ b/cloudinit/net/network_manager.py
@@ -12,10 +12,15 @@ import itertools
import logging
import os
import uuid
-from typing import Optional
+from typing import List, Optional
from cloudinit import subp, util
-from cloudinit.net import is_ipv6_address, renderer, subnet_is_ipv6
+from cloudinit.net import (
+ is_ipv6_address,
+ is_ipv6_network,
+ renderer,
+ subnet_is_ipv6,
+)
from cloudinit.net.network_state import NetworkState
from cloudinit.net.sysconfig import available_nm_ifcfg_rh
@@ -158,11 +163,11 @@ class NMConnection:
if self.config[family]["method"] == "auto" and method == "manual":
return
- if (
- subnet_type == "ipv6_dhcpv6-stateful"
- or subnet_type == "ipv6_dhcpv6-stateless"
- or subnet_type == "ipv6_slaac"
- ):
+ if subnet_type in [
+ "ipv6_dhcpv6-stateful",
+ "ipv6_dhcpv6-stateless",
+ "ipv6_slaac",
+ ]:
# set ipv4 method to 'disabled' to align with sysconfig renderer.
self._set_default("ipv4", "method", "disabled")
@@ -174,7 +179,8 @@ class NMConnection:
Adds a numbered property, such as address<n> or route<n>, ensuring
the appropriate value gets used for <n>.
"""
-
+ if not self.config.has_section(section):
+ self.config[section] = {}
for index in itertools.count(1):
key = f"{key_prefix}{index}"
if not self.config.has_option(section, key):
@@ -189,40 +195,37 @@ class NMConnection:
value = subnet["address"] + "/" + str(subnet["prefix"])
self._add_numbered(family, "address", value)
- def _add_route(self, family, route):
- """
- Adds a ipv[46].route<n> property.
- """
-
+ def _add_route(self, route):
+ """Adds a ipv[46].route<n> property."""
+ # Because network v2 route definitions can have mixed v4 and v6
+ # routes, determine the family per route based on the gateway
+ family = "ipv6" if is_ipv6_network(route["gateway"]) else "ipv4"
value = route["network"] + "/" + str(route["prefix"])
if "gateway" in route:
value = value + "," + route["gateway"]
self._add_numbered(family, "route", value)
- def _add_nameserver(self, dns):
+ def _add_nameserver(self, dns: str) -> None:
"""
Extends the ipv[46].dns property with a name server.
"""
-
- # FIXME: the subnet contains IPv4 and IPv6 name server mixed
- # together. We might be getting an IPv6 name server while
- # we're dealing with an IPv4 subnet. Sort this out by figuring
- # out the correct family and making sure a valid section exist.
family = "ipv6" if is_ipv6_address(dns) else "ipv4"
- self._set_default(family, "method", "disabled")
-
- self._set_default(family, "dns", "")
- self.config[family]["dns"] = self.config[family]["dns"] + dns + ";"
+ if self.config.has_section(family):
+ self._set_default(family, "dns", "")
+ self.config[family]["dns"] = self.config[family]["dns"] + dns + ";"
- def _add_dns_search(self, family, dns_search):
+ def _add_dns_search(self, dns_search: List[str]) -> None:
"""
Extends the ipv[46].dns-search property with a name server.
"""
-
- self._set_default(family, "dns-search", "")
- self.config[family]["dns-search"] = (
- self.config[family]["dns-search"] + ";".join(dns_search) + ";"
- )
+ for family in ["ipv4", "ipv6"]:
+ if self.config.has_section(family):
+ self._set_default(family, "dns-search", "")
+ self.config[family]["dns-search"] = (
+ self.config[family]["dns-search"]
+ + ";".join(dns_search)
+ + ";"
+ )
def con_uuid(self):
"""
@@ -304,8 +307,11 @@ class NMConnection:
device_mtu = iface["mtu"]
ipv4_mtu = None
+ found_nameservers = []
+ found_dns_search = []
# Deal with Layer 3 configuration
+ use_top_level_dns = "dns" in iface
for subnet in iface["subnets"]:
family = "ipv6" if subnet_is_ipv6(subnet) else "ipv4"
@@ -315,15 +321,28 @@ class NMConnection:
if "gateway" in subnet:
self.config[family]["gateway"] = subnet["gateway"]
for route in subnet["routes"]:
- self._add_route(family, route)
- if "dns_nameservers" in subnet:
+ self._add_route(route)
+ if not use_top_level_dns and "dns_nameservers" in subnet:
for nameserver in subnet["dns_nameservers"]:
- self._add_nameserver(nameserver)
- if "dns_search" in subnet:
- self._add_dns_search(family, subnet["dns_search"])
+ found_nameservers.append(nameserver)
+ if not use_top_level_dns and "dns_search" in subnet:
+ found_dns_search.append(subnet["dns_search"])
if family == "ipv4" and "mtu" in subnet:
ipv4_mtu = subnet["mtu"]
+ # Now add our DNS search domains. We add them later because we
+ # only want them if an IP family has already been defined
+ if use_top_level_dns:
+ for nameserver in iface["dns"]["nameservers"]:
+ self._add_nameserver(nameserver)
+ if iface["dns"]["search"]:
+ self._add_dns_search(iface["dns"]["search"])
+ else:
+ for nameserver in found_nameservers:
+ self._add_nameserver(nameserver)
+ for dns_search in found_dns_search:
+ self._add_dns_search(dns_search)
+
# we do not want to set may-fail to false for both ipv4 and ipv6 dhcp
# at the at the same time. This will make the network configuration
# work only when both ipv4 and ipv6 dhcp succeeds. This may not be
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index e52c2497..678ec39b 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -2934,9 +2934,9 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
may-fail=false
address1=192.168.0.2/24
gateway=192.168.0.1
+ address2=192.168.2.10/24
dns=192.168.0.10;10.23.23.134;
dns-search=barley.maas;sacchromyces.maas;brettanomyces.maas;
- address2=192.168.2.10/24
"""
),
@@ -4114,6 +4114,148 @@ iface bond0 inet6 static
"""
),
},
+ "v2-mixed-routes": {
+ "expected_network_manager": {
+ "cloud-init-eth0.nmconnection": textwrap.dedent(
+ """\
+ # Generated by cloud-init. Changes will be lost.
+
+ [connection]
+ id=cloud-init eth0
+ uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
+ autoconnect-priority=120
+ type=ethernet
+ interface-name=eth0
+
+ [user]
+ org.freedesktop.NetworkManager.origin=cloud-init
+
+ [ethernet]
+
+ [ipv4]
+ method=auto
+ may-fail=true
+ route1=169.254.42.42/32,62.210.0.1
+ route2=169.254.42.43/32,62.210.0.2
+ address1=192.168.1.20/16
+ dns=8.8.8.8;
+ dns-search=lab;home;
+
+ [ipv6]
+ route1=::/0,fe80::dc00:ff:fe20:186
+ route2=fe80::dc00:ff:fe20:188/64,fe80::dc00:ff:fe20:187
+ method=auto
+ may-fail=true
+ address1=2001:bc8:1210:232:dc00:ff:fe20:185/64
+ dns=FEDC::1;
+ dns-search=lab;home;
+
+ """
+ )
+ },
+ "yaml": textwrap.dedent(
+ """\
+ version: 2
+ ethernets:
+ eth0:
+ dhcp4: true
+ dhcp6: true
+ nameservers:
+ search: [lab, home]
+ addresses: [8.8.8.8, "FEDC::1"]
+ routes:
+ - to: 169.254.42.42/32
+ via: 62.210.0.1
+ - via: fe80::dc00:ff:fe20:186
+ to: ::/0
+ - to: 169.254.42.43/32
+ via: 62.210.0.2
+ - via: fe80::dc00:ff:fe20:187
+ to: fe80::dc00:ff:fe20:188
+ addresses:
+ - 192.168.1.20/16
+ - 2001:bc8:1210:232:dc00:ff:fe20:185/64
+ """
+ ),
+ },
+ "v2-dns-no-if-ips": {
+ "expected_network_manager": {
+ "cloud-init-eth0.nmconnection": textwrap.dedent(
+ """\
+ # Generated by cloud-init. Changes will be lost.
+
+ [connection]
+ id=cloud-init eth0
+ uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
+ autoconnect-priority=120
+ type=ethernet
+ interface-name=eth0
+
+ [user]
+ org.freedesktop.NetworkManager.origin=cloud-init
+
+ [ethernet]
+
+ [ipv4]
+ method=auto
+ may-fail=true
+ dns=8.8.8.8;
+ dns-search=lab;home;
+
+ [ipv6]
+ method=auto
+ may-fail=true
+ dns=FEDC::1;
+ dns-search=lab;home;
+
+ """
+ )
+ },
+ "yaml": textwrap.dedent(
+ """\
+ version: 2
+ ethernets:
+ eth0:
+ dhcp4: true
+ dhcp6: true
+ nameservers:
+ search: [lab, home]
+ addresses: [8.8.8.8, "FEDC::1"]
+ """
+ ),
+ },
+ "v2-dns-no-dhcp": {
+ "expected_network_manager": {
+ "cloud-init-eth0.nmconnection": textwrap.dedent(
+ """\
+ # Generated by cloud-init. Changes will be lost.
+
+ [connection]
+ id=cloud-init eth0
+ uuid=1dd9a779-d327-56e1-8454-c65e2556c12c
+ autoconnect-priority=120
+ type=ethernet
+ interface-name=eth0
+
+ [user]
+ org.freedesktop.NetworkManager.origin=cloud-init
+
+ [ethernet]
+
+ """
+ )
+ },
+ "yaml": textwrap.dedent(
+ """\
+ version: 2
+ ethernets:
+ eth0:
+ nameservers:
+ search: [lab, home]
+ addresses: [8.8.8.8, "FEDC::1"]
+ """
+ ),
+ },
}
@@ -6214,6 +6356,27 @@ class TestNetworkManagerRendering(CiTestCase):
entry[self.expected_name], self.expected_conf_d, found
)
+ def test_v2_mixed_routes(self):
+ entry = NETWORK_CONFIGS["v2-mixed-routes"]
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
+ self._compare_files_to_expected(
+ entry[self.expected_name], self.expected_conf_d, found
+ )
+
+ def test_v2_dns_no_ips(self):
+ entry = NETWORK_CONFIGS["v2-dns-no-if-ips"]
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
+ self._compare_files_to_expected(
+ entry[self.expected_name], self.expected_conf_d, found
+ )
+
+ def test_v2_dns_no_dhcp(self):
+ entry = NETWORK_CONFIGS["v2-dns-no-dhcp"]
+ found = self._render_and_read(network_config=yaml.load(entry["yaml"]))
+ self._compare_files_to_expected(
+ entry[self.expected_name], self.expected_conf_d, found
+ )
+
@mock.patch(
"cloudinit.net.is_openvswitch_internal_interface",
--
2.41.0

View File

@ -1,156 +0,0 @@
From d17e05b1709e3b7148e889512282603f7399c857 Mon Sep 17 00:00:00 2001
From: PengpengSun <40026211+PengpengSun@users.noreply.github.com>
Date: Fri, 29 Mar 2024 22:39:13 +0800
Subject: [PATCH] fix: Fall back to cached local ds if no valid ds found
(#4997)
RH-Author: Ani Sinha <anisinha@redhat.com>
RH-MergeRequest: 133: fix: Fall back to cached local ds if no valid ds found (#4997)
RH-Jira: RHEL-32841
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] df9c6fda66dee9622725ff2d52e64999796324b8
Rebooting an instance which has finished VMware guest
customization with DataSourceVMware will load
DataSourceNone due to metadata is NOT available.
This is mostly a re-post of PR#229, few differences are:
1. Let ds decide if fallback is allowed, not always fall back
to previous cached LOCAL ds.
2. No comparing instance-id of cached ds with previous instance-id
due to I think they are always identical.
Fixes GH-3402
(cherry picked from commit 9929a00580d50afc60bf4e0fb9f2f39d4f797b4b)
Signed-off-by: Ani Sinha <anisinha@redhat.com>
Conflicts:
cloudinit/sources/__init__.py
Conflicts because of changes in upstream source coming from
30d5e9a3358f4cbaced ("refactor: Use _unpickle rather than hasattr() in sources")
---
cloudinit/sources/DataSourceVMware.py | 14 +++++++++-
cloudinit/sources/__init__.py | 14 ++++++++++
cloudinit/stages.py | 40 +++++++++++++++++----------
3 files changed, 53 insertions(+), 15 deletions(-)
diff --git a/cloudinit/sources/DataSourceVMware.py b/cloudinit/sources/DataSourceVMware.py
index 1591121d..2d5d42eb 100644
--- a/cloudinit/sources/DataSourceVMware.py
+++ b/cloudinit/sources/DataSourceVMware.py
@@ -197,7 +197,7 @@ class DataSourceVMware(sources.DataSource):
break
if not self.data_access_method:
- LOG.error("failed to find a valid data access method")
+ LOG.debug("failed to find a valid data access method")
return False
LOG.info("using data access method %s", self._get_subplatform())
@@ -291,6 +291,18 @@ class DataSourceVMware(sources.DataSource):
self.metadata["instance-id"] = str(id_file.read()).rstrip().lower()
return self.metadata["instance-id"]
+ def check_if_fallback_is_allowed(self):
+ if (
+ self.data_access_method
+ and self.data_access_method == DATA_ACCESS_METHOD_IMC
+ and is_vmware_platform()
+ ):
+ LOG.debug(
+ "Cache fallback is allowed for : %s", self._get_subplatform()
+ )
+ return True
+ return False
+
def get_public_ssh_keys(self):
for key_name in (
"public-keys-data",
diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py
index c207b5ed..453801be 100644
--- a/cloudinit/sources/__init__.py
+++ b/cloudinit/sources/__init__.py
@@ -312,6 +312,10 @@ class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta):
self.vendordata2_raw = None
if not hasattr(self, "skip_hotplug_detect"):
self.skip_hotplug_detect = False
+
+ if not hasattr(self, "check_if_fallback_is_allowed"):
+ setattr(self, "check_if_fallback_is_allowed", lambda: False)
+
if hasattr(self, "userdata") and self.userdata is not None:
# If userdata stores MIME data, on < python3.6 it will be
# missing the 'policy' attribute that exists on >=python3.6.
@@ -914,6 +918,16 @@ class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta):
# quickly (local check only) if self.instance_id is still
return False
+ def check_if_fallback_is_allowed(self):
+ """check_if_fallback_is_allowed()
+ Checks if a cached ds is allowed to be restored when no valid ds is
+ found in local mode by checking instance-id and searching valid data
+ through ds list.
+
+ @return True if a ds allows fallback, False otherwise.
+ """
+ return False
+
@staticmethod
def _determine_dsmode(candidates, default=None, valid=None):
# return the first candidate that is non None, warn if not valid
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 3b6405f5..0b795624 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -353,20 +353,32 @@ class Init:
LOG.debug(myrep.description)
if not ds:
- util.del_file(self.paths.instance_link)
- (cfg_list, pkg_list) = self._get_datasources()
- # Deep copy so that user-data handlers can not modify
- # (which will affect user-data handlers down the line...)
- (ds, dsname) = sources.find_source(
- self.cfg,
- self.distro,
- self.paths,
- copy.deepcopy(self.ds_deps),
- cfg_list,
- pkg_list,
- self.reporter,
- )
- LOG.info("Loaded datasource %s - %s", dsname, ds)
+ try:
+ cfg_list, pkg_list = self._get_datasources()
+ # Deep copy so that user-data handlers can not modify
+ # (which will affect user-data handlers down the line...)
+ ds, dsname = sources.find_source(
+ self.cfg,
+ self.distro,
+ self.paths,
+ copy.deepcopy(self.ds_deps),
+ cfg_list,
+ pkg_list,
+ self.reporter,
+ )
+ util.del_file(self.paths.instance_link)
+ LOG.info("Loaded datasource %s - %s", dsname, ds)
+ except sources.DataSourceNotFoundException as e:
+ if existing != "check":
+ raise e
+ ds = self._restore_from_cache()
+ if ds and ds.check_if_fallback_is_allowed():
+ LOG.info(
+ "Restored fallback datasource from checked cache: %s",
+ ds,
+ )
+ else:
+ raise e
self.datasource = ds
# Ensure we adjust our path members datasource
# now that we have one (thus allowing ipath to be used)
--
2.39.3

View File

@ -1,42 +0,0 @@
From 8a83f1e6077cea9dc9ebc909e1332e15c8cbadac Mon Sep 17 00:00:00 2001
From: James Falcon <james.falcon@canonical.com>
Date: Tue, 19 Mar 2024 14:24:11 -0500
Subject: [PATCH 3/3] fix: Undeprecate 'network' in schema route definition
(#5072)
RH-Author: Ani Sinha <anisinha@redhat.com>
RH-MergeRequest: 129: fix: Undeprecate 'network' in schema route definition (#5072)
RH-Jira: RHEL-29710
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Commit: [1/1] c482c3e11720f01daa7b0d37035157b062b35213
It is passed through to our v1 schema from OpenStack network_data.json
Fixes GH-5051
(cherry picked from commit ff40d1af8a6de3ee27937382ec4ceea931d80a88)
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
cloudinit/config/schemas/schema-network-config-v1.json | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/cloudinit/config/schemas/schema-network-config-v1.json b/cloudinit/config/schemas/schema-network-config-v1.json
index 56dc27c9..64c492a4 100644
--- a/cloudinit/config/schemas/schema-network-config-v1.json
+++ b/cloudinit/config/schemas/schema-network-config-v1.json
@@ -445,10 +445,7 @@
},
"network": {
"type": "string",
- "description": "IPv4 network address with CIDR netmask notation or IPv6 with prefix length. Alias for ``destination`` and only read when ``destination`` key is absent.",
- "deprecated": true,
- "deprecated_version": "23.3",
- "deprecated_description": "Use ``destination`` instead."
+ "description": "IPv4 network address with CIDR netmask notation or IPv6 with prefix length. Alias for ``destination`` and only read when ``destination`` key is absent. This exists for OpenStack support. OpenStack route definitions are passed through to v1 config and OpenStack's ``network_data.json`` uses ``network`` instead of ``destination``."
},
"destination": {
"type": "string",
--
2.41.0

View File

@ -1,58 +0,0 @@
From 6e3c351b013dc2ac01035853229ffdfdafa3afa8 Mon Sep 17 00:00:00 2001
From: Brett Holman <brett.holman@canonical.com>
Date: Wed, 3 Jan 2024 09:11:40 -0700
Subject: [PATCH] fix(cloudstack): Use parsed lease file for virtual router in
cloudstack
RH-Author: Ani Sinha <anisinha@redhat.com>
RH-MergeRequest: 137: fix(cloudstack): Use parsed lease file for virtual router in cloudstack
RH-Jira: RHEL-40418
RH-Acked-by: Cathy Avery <cavery@redhat.com>
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
RH-Commit: [1/1] 77f97c04432fffff125dc1725d66b33ae0ab4af8
Fixes 5942f4023e2581a
(cherry picked from commit cb36bf38b823f811a3e938ccffc03d7d13190095)
Signed-off-by: Ani Sinha <anisinha@redhat.com>
---
cloudinit/sources/DataSourceCloudStack.py | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py
index fd2482a3..f752765d 100644
--- a/cloudinit/sources/DataSourceCloudStack.py
+++ b/cloudinit/sources/DataSourceCloudStack.py
@@ -229,18 +229,18 @@ def get_vr_address():
)
return latest_address
- # Try dhcp lease files next...
+ # Try dhcp lease files next
lease_file = dhcp.IscDhclient.get_latest_lease()
- if not lease_file:
- LOG.debug("No lease file found, using default gateway")
- return get_default_gateway()
-
- lease_file = dhcp.IscDhclient.parse_dhcp_server_from_lease_file(lease_file)
- if not latest_address:
- # No virtual router found, fallback on default gateway
- LOG.debug("No DHCP found, using default gateway")
- return get_default_gateway()
- return latest_address
+ if lease_file:
+ latest_address = dhcp.IscDhclient.parse_dhcp_server_from_lease_file(
+ lease_file
+ )
+ if latest_address:
+ return latest_address
+
+ # No virtual router found, fallback to default gateway
+ LOG.debug("No DHCP found, using default gateway")
+ return get_default_gateway()
# Used to match classes to dependencies
--
2.39.3

View File

@ -6,7 +6,7 @@
Name: cloud-init
Version: 23.4
Release: 7%{?dist}.8
Release: 5%{?dist}
Summary: Cloud instance init scripts
Group: System Environment/Base
@ -35,28 +35,6 @@ Patch19: ci-Revert-Use-grep-for-faster-parsing-of-cloud-config-i.patch
Patch20: ci-ci-Pin-pytest-8.0.0.-4816.patch
# For RHEL-21323 - [rhel-8] The schema WARNING info for network-config.json is not suitable in cloud-init-23.4
Patch21: ci-fix-Add-types-to-network-v1-schema-4841.patch
# For RHEL-21290 - Unknown lvalue 'ConditionEnvironment' in section 'Unit' for /usr/lib/systemd/system/cloud-init.target,cloud-init.service
Patch22: ci-Revert-systemd-Standardize-cloud-init-systemd-enable.patch
# For RHEL-28817 - [RHEL 8.10] cloud-init 23.4 returns 2 on recoverable errors instead of 0
Patch23: ci-Retain-exit-code-in-cloud-init-status-for-recoverabl.patch
# For RHEL-27134 - [rhel-8]cloud-init fails to configure DNS/search domains for network-config v1
Patch24: ci-fix-Correct-v2-NetworkManager-route-rendering-4637.patch
# For RHEL-27134 - [rhel-8]cloud-init fails to configure DNS/search domains for network-config v1
Patch25: ci-feat-apply-global-DNS-to-interfaces-in-network-manag.patch
# For RHEL-29710 - Suggest to backport patch ff40d1a to undeprecate 'network' in schema route definition [rhel-8.10.0.z]
Patch26: ci-fix-Undeprecate-network-in-schema-route-definition-5.patch
# For RHEL-32841 - [cloud-init][ESXi]VMware datasource resets on every boot causing it to lose network configuration [rhel-8.10.z]
Patch27: ci-fix-Fall-back-to-cached-local-ds-if-no-valid-ds-foun.patch
# For RHEL-36701 - DataSourceNoCloudNet not configurable via config files [rhel-8.10.z]
Patch28: ci-fix-Always-use-single-datasource-if-specified-5098.patch
# For RHEL-40418 - [Cloud-init] CloudstackDataSource cannot work with NetworkManager [rhel-8.10.z]
Patch29: ci-fix-cloudstack-Use-parsed-lease-file-for-virtual-rou.patch
# For RHEL-46013 - [RHEL-8] cloud-init fails to configure DNS search domains [rhel-8.10.z]
Patch30: ci-feat-sysconfig-Add-DNS-from-interface-config-to-reso.patch
# For RHEL-49742 - [Cloud-init] [RHEL-8.10] Password reset feature broken with CloudstackDataSource
Patch31: ci-fix-Clean-cache-if-no-datasource-fallback-5499.patch
# For RHEL-54155 - [RHEL 8.10] cloud-init schema validation fails.
Patch32: ci-fix-Add-subnet-ipv4-ipv6-to-network-schema-5191.patch
BuildArch: noarch
@ -272,55 +250,6 @@ fi
%config(noreplace) %{_sysconfdir}/rsyslog.d/21-cloudinit.conf
%changelog
* Tue Aug 20 2024 Jon Maloy <jmaloy@redhat.com> - 23.4-7.el8_10.8
- ci-fix-Add-subnet-ipv4-ipv6-to-network-schema-5191.patch [RHEL-54155]
- Resolves: RHEL-54155
([RHEL 8.10] cloud-init schema validation fails.)
* Thu Jul 25 2024 Miroslav Rezanina <mrezanin@redhat.com> - 23.4-7.el8_10.7
- ci-fix-Clean-cache-if-no-datasource-fallback-5499.patch [RHEL-49742]
- Resolves: RHEL-49742
([Cloud-init] [RHEL-8.10] Password reset feature broken with CloudstackDataSource)
* Tue Jul 09 2024 Jon Maloy <jmaloy@redhat.com> - 23.4-7.el8_10.6
- ci-feat-sysconfig-Add-DNS-from-interface-config-to-reso.patch [RHEL-46013]
- Resolves: RHEL-46013
([RHEL-8] cloud-init fails to configure DNS search domains [rhel-8.10.z])
* Tue Jul 09 2024 Miroslav Rezanina <mrezanin@redhat.com> - 23.4-7.el8_10.5
- ci-fix-cloudstack-Use-parsed-lease-file-for-virtual-rou.patch [RHEL-40418]
- Resolves: RHEL-40418
([Cloud-init] CloudstackDataSource cannot work with NetworkManager [rhel-8.10.z])
* Wed May 29 2024 Jon Maloy <jmaloy@redhat.com> - 23.4-7.el8.3
- ci-fix-Always-use-single-datasource-if-specified-5098.patch [RHEL-36701]
- Resolves: RHEL-36701
(DataSourceNoCloudNet not configurable via config files [rhel-8.10.z])
* Tue Apr 23 2024 Miroslav Rezanina <mrezanin@redhat.com> - 23.4-7.el8_10.2
- ci-fix-Fall-back-to-cached-local-ds-if-no-valid-ds-foun.patch [RHEL-32841]
- Resolves: RHEL-32841
([cloud-init][ESXi]VMware datasource resets on every boot causing it to lose network configuration [rhel-8.10.z])
* Fri Apr 05 2024 Jon Maloy <jmaloy@redhat.com> - 23.4-7.el8.1
- ci-fix-Correct-v2-NetworkManager-route-rendering-4637.patch [RHEL-27134]
- ci-feat-apply-global-DNS-to-interfaces-in-network-manag.patch [RHEL-27134]
- ci-fix-Undeprecate-network-in-schema-route-definition-5.patch [RHEL-29710]
- Resolves: RHEL-27134
([rhel-8]cloud-init fails to configure DNS/search domains for network-config v1)
- Resolves: RHEL-29710
(Suggest to backport patch ff40d1a to undeprecate 'network' in schema route definition [rhel-8.10.0.z])
* Thu Mar 14 2024 Miroslav Rezanina <mrezanin@redhat.com> - 23.4-7
- ci-Retain-exit-code-in-cloud-init-status-for-recoverabl.patch [RHEL-28817]
- Resolves: RHEL-28817
([RHEL 8.10] cloud-init 23.4 returns 2 on recoverable errors instead of 0)
* Mon Mar 11 2024 Miroslav Rezanina <mrezanin@redhat.com> - 23.4-6
- ci-Revert-systemd-Standardize-cloud-init-systemd-enable.patch [RHEL-21290]
- Resolves: RHEL-21290
(Unknown lvalue 'ConditionEnvironment' in section 'Unit' for /usr/lib/systemd/system/cloud-init.target,cloud-init.service)
* Mon Feb 26 2024 Miroslav Rezanina <mrezanin@redhat.com> - 23.4-5
- ci-ci-Pin-pytest-8.0.0.-4816.patch [RHEL-21323]
- ci-fix-Add-types-to-network-v1-schema-4841.patch [RHEL-21323]