157 lines
6.0 KiB
Diff
157 lines
6.0 KiB
Diff
From d45780e65158d7f9b0a00933f8998934f5ceb4cf Mon Sep 17 00:00:00 2001
|
|
From: CodeBleu <400979+CodeBleu@users.noreply.github.com>
|
|
Date: Tue, 20 Jan 2026 13:52:54 -0500
|
|
Subject: [PATCH 2/3] fix(cloudstack): Improve domain-name DHCP lease lookup
|
|
(Cloudstack) (#6554)
|
|
|
|
RH-Author: Ani Sinha <anisinha@redhat.com>
|
|
RH-MergeRequest: 178: fix(cloudstack): Improve domain-name DHCP lease lookup
|
|
RH-Jira: RHEL-159032
|
|
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
|
RH-Acked-by: xiachen <xiachen@redhat.com>
|
|
RH-Commit: [2/3] 5f3c7dac734ddcc068f086866963d05c42e0c24f
|
|
|
|
* Adding case-insensitive options for systemd-networkd leases ("DOMAINNAME", "Domain", "domain-name").
|
|
* Falling back gracefully from systemd leases to ISC dhclient leases.
|
|
* Including dhcpcd ephemeral leases as an additional fallback.
|
|
* Returning an empty string when no domain name found instead of None for non-fatal missing cases.
|
|
|
|
(cherry picked from commit d2bf883931723cfdc9f529eba5395d83059d09c6)
|
|
Signed-off-by: Ani Sinha <anisinha@redhat.com>
|
|
---
|
|
cloudinit/sources/DataSourceCloudStack.py | 50 +++++++++++-----------
|
|
tests/unittests/sources/test_cloudstack.py | 27 ++++++++++++
|
|
2 files changed, 53 insertions(+), 24 deletions(-)
|
|
|
|
diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py
|
|
index 6d19077c..98189c23 100644
|
|
--- a/cloudinit/sources/DataSourceCloudStack.py
|
|
+++ b/cloudinit/sources/DataSourceCloudStack.py
|
|
@@ -23,6 +23,7 @@ from cloudinit import sources, subp
|
|
from cloudinit import url_helper as uhelp
|
|
from cloudinit import util
|
|
from cloudinit.net import dhcp
|
|
+from cloudinit.net.dhcp import NoDHCPLeaseError
|
|
from cloudinit.sources.helpers import ec2
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
@@ -97,46 +98,47 @@ class DataSourceCloudStack(sources.DataSource):
|
|
self.cfg = {}
|
|
|
|
def _get_domainname(self):
|
|
+ """Try obtaining a "domain-name" DHCP lease parameter:
|
|
+ - From systemd-networkd lease (case-insensitive)
|
|
+ - From ISC dhclient
|
|
+ - From dhcpcd (ephemeral)
|
|
+ - Return empty string if not found (non-fatal)
|
|
"""
|
|
- Try obtaining a "domain-name" DHCP lease parameter:
|
|
- - From systemd-networkd lease
|
|
- - From dhclient lease
|
|
- """
|
|
+
|
|
LOG.debug("Try obtaining domain name from networkd leases")
|
|
- domainname = dhcp.networkd_get_option_from_leases("DOMAINNAME")
|
|
- if domainname:
|
|
- return domainname
|
|
+ for key in ["DOMAINNAME", "Domain", "domain-name"]:
|
|
+ domainname = dhcp.networkd_get_option_from_leases(key)
|
|
+ if domainname:
|
|
+ return domainname.strip()
|
|
+
|
|
LOG.debug(
|
|
- "Could not obtain FQDN from networkd leases. "
|
|
- "Falling back to ISC dhclient"
|
|
+ "Could not obtain FQDN from networkd leases. Falling back to "
|
|
+ "ISC dhclient"
|
|
)
|
|
-
|
|
- # some distros might use isc-dhclient for network setup via their
|
|
- # network manager. If this happens, the lease is more recent than the
|
|
- # ephemeral lease, so use it first.
|
|
with suppress(dhcp.NoDHCPLeaseMissingDhclientError):
|
|
domain_name = dhcp.IscDhclient().get_key_from_latest_lease(
|
|
self.distro, "domain-name"
|
|
)
|
|
if domain_name:
|
|
- return domain_name
|
|
+ return domain_name.strip()
|
|
|
|
LOG.debug(
|
|
- "Could not obtain FQDN from ISC dhclient leases. "
|
|
- "Falling back to %s",
|
|
+ "Could not obtain FQDN from ISC dhclient leases. Falling back to "
|
|
+ "%s",
|
|
self.distro.dhcp_client.client_name,
|
|
)
|
|
-
|
|
- # If no distro leases were found, check the ephemeral lease that
|
|
- # cloud-init set up.
|
|
- with suppress(FileNotFoundError):
|
|
+ try:
|
|
latest_lease = self.distro.dhcp_client.get_newest_lease(
|
|
self.distro.fallback_interface
|
|
)
|
|
- domain_name = latest_lease.get("domain-name") or None
|
|
- return domain_name
|
|
- LOG.debug("No dhcp leases found")
|
|
- return None
|
|
+ domain_name = latest_lease.get("domain-name")
|
|
+ if domain_name:
|
|
+ return domain_name.strip()
|
|
+ except (NoDHCPLeaseError, FileNotFoundError, AttributeError):
|
|
+ pass
|
|
+
|
|
+ LOG.debug("No domain name found in any DHCP lease; returning empty")
|
|
+ return ""
|
|
|
|
def get_hostname(
|
|
self,
|
|
diff --git a/tests/unittests/sources/test_cloudstack.py b/tests/unittests/sources/test_cloudstack.py
|
|
index a64d80f3..0f987066 100644
|
|
--- a/tests/unittests/sources/test_cloudstack.py
|
|
+++ b/tests/unittests/sources/test_cloudstack.py
|
|
@@ -1,5 +1,6 @@
|
|
# This file is part of cloud-init. See LICENSE file for license information.
|
|
from textwrap import dedent
|
|
+from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
@@ -327,6 +328,32 @@ class TestCloudStackHostname(CiTestCase):
|
|
result = ds.get_hostname(fqdn=True)
|
|
self.assertTupleEqual(expected, result)
|
|
|
|
+ @pytest.mark.parametrize(
|
|
+ "lease_key,expected_domain",
|
|
+ [
|
|
+ ("DOMAINNAME", "example.com"),
|
|
+ ("Domain", "example.com"),
|
|
+ ("domain-name", "example.com"),
|
|
+ ],
|
|
+ )
|
|
+ def test__get_domainname_supports_all_casing_variants(
|
|
+ self, lease_key, expected_domain
|
|
+ ):
|
|
+ """Ensure _get_domainname works with DOMAINNAME, Domain and
|
|
+ domain-name."""
|
|
+ # Mock the helper to return the domain only when the exact key is asked
|
|
+ with patch(
|
|
+ "cloudinit.net.dhcp.networkd_get_option_from_leases"
|
|
+ ) as m_get:
|
|
+ m_get.side_effect = lambda key, extra_keys=None: (
|
|
+ "example.com " if key == lease_key else None
|
|
+ )
|
|
+
|
|
+ ds = DataSourceCloudStack(
|
|
+ {}, distro=MockDistro(), paths=helpers.Paths({})
|
|
+ )
|
|
+ assert ds._get_domainname() == expected_domain
|
|
+
|
|
|
|
@pytest.mark.usefixtures("dhclient_exists")
|
|
class TestCloudStackPasswordFetching(CiTestCase):
|
|
--
|
|
2.47.3
|
|
|