From 872674bb177537d85bc95543e4619cf74ec08874 Mon Sep 17 00:00:00 2001 From: Calvin Mwadime Date: Wed, 17 Apr 2024 12:52:01 +0300 Subject: [PATCH] fix(azure): Reconfigure secondary NICs by removing route to DNS via them. As from Bionic, systemd has associated DNS server with secondary NICs, this causes delay in the DNS resolution from Jammy and forwards. We see it evidently in the cloud-init logs where resolving the url takes way more seconds. --- cloudinit/sources/DataSourceAzure.py | 40 ++++++++++++++-- .../datasources/test_azure.py | 42 ++++++++++++++++- tests/unittests/sources/test_azure.py | 46 +++++++++++++++++-- 3 files changed, 119 insertions(+), 9 deletions(-) diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py index 091a3c22caa0..c129ff7ce60d 100644 --- a/cloudinit/sources/DataSourceAzure.py +++ b/cloudinit/sources/DataSourceAzure.py @@ -1488,12 +1488,18 @@ def _generate_network_config(self): and self.ds_cfg.get("apply_network_config") ): try: - return generate_network_config_from_instance_network_metadata( - self._metadata_imds["network"], - apply_network_config_for_secondary_ips=self.ds_cfg.get( - "apply_network_config_for_secondary_ips" - ), + netcfg = ( + generate_network_config_from_instance_network_metadata( + self._metadata_imds["network"], + apply_network_config_for_secondary_ips=self.ds_cfg.get( + "apply_network_config_for_secondary_ips" + ), + ) + ) + net_cfg = add_dhcp_config_for_primary_and_secondary_nics_to_network_config( + netcfg ) + return net_cfg except Exception as e: LOG.error( "Failed generating network config " @@ -1945,6 +1951,30 @@ def load_azure_ds_dir(source_dir): return (md, ud, cfg, {"ovf-env.xml": contents}) +def add_dhcp_config_for_primary_and_secondary_nics_to_network_config( + netconfig: dict, +) -> dict: + """ + Configure the ephemeral and hotplug details. + parameters: + netconfig: dict, Network config details with other ethernet devices. + + returns the updated network configuration + """ + netconfig["ethernets"]["primary"] = { + "dhcp4": True, + "match": {"driver": "hv_netvsc", "name": "eth0"}, + } + netconfig["ethernets"]["secondary"] = { + "dhcp4": True, + "dhcp4-overrides": {"use-dns": False}, + "optional": True, + "match": {"driver": "hv_netvsc", "name": "!eth0"}, + } + + return netconfig + + @azure_ds_telemetry_reporter def generate_network_config_from_instance_network_metadata( network_metadata: dict, diff --git a/tests/integration_tests/datasources/test_azure.py b/tests/integration_tests/datasources/test_azure.py index 8e663ac21d6f..271892384b63 100644 --- a/tests/integration_tests/datasources/test_azure.py +++ b/tests/integration_tests/datasources/test_azure.py @@ -1,11 +1,15 @@ +import datetime + import pytest +import yaml +from pycloudlib.azure.util import AzureCreateParams, AzureParams from pycloudlib.cloud import ImageType from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.conftest import get_validated_source from tests.integration_tests.instances import IntegrationInstance from tests.integration_tests.integration_settings import PLATFORM -from tests.integration_tests.releases import CURRENT_RELEASE +from tests.integration_tests.releases import BIONIC, CURRENT_RELEASE def _check_for_eject_errors( @@ -45,3 +49,39 @@ def test_azure_eject(session_cloud: IntegrationCloud): session_cloud.cloud_instance.delete_image(snapshot_id) else: _check_for_eject_errors(instance) + + +@pytest.mark.skipif(PLATFORM != "azure", reason="Test is Azure specific") +@pytest.mark.skipif( + CURRENT_RELEASE < BIONIC, reason="Easier to test on Bionic+" +) +def test_azure_multi_nic_setup( + setup_image, session_cloud: IntegrationCloud +) -> None: + """Integration test for https://warthogs.atlassian.net/browse/CPC-3999. + + Azure should have the primary NIC only route to DNS. + Ensure other NICs do not have route to DNS. + """ + us = datetime.datetime.now().strftime("%f") + rg_params = AzureParams(f"ci-test-multi-nic-setup-{us}", None) + nic_one = AzureCreateParams(f"ci-nic1-test-{us}", rg_params.name, None) + nic_two = AzureCreateParams(f"ci-nic2-test-{us}", rg_params.name, None) + with session_cloud.launch( + launch_kwargs={ + "resource_group_params": rg_params, + "network_interfaces_params": [nic_one, nic_two], + } + ) as snapshot_instance: + _check_for_eject_errors(snapshot_instance) + if CURRENT_RELEASE.series == "bionic": + ret = snapshot_instance.execute("systemd-resolve --status") + assert ret.ok + assert ret.stdout.count("Current Scopes: DNS") == 1 + else: + ret = snapshot_instance.execute("resolvectl dns") + assert ret.ok + routes = yaml.safe_load(ret.stdout) + routes_devices = list(routes.keys()) + eth1_dev = [dev for dev in routes_devices if "(eth1)" in dev][0] + assert routes[eth1_dev] is None diff --git a/tests/unittests/sources/test_azure.py b/tests/unittests/sources/test_azure.py index eb9f6a40bd81..1e2417345cce 100644 --- a/tests/unittests/sources/test_azure.py +++ b/tests/unittests/sources/test_azure.py @@ -967,7 +967,17 @@ def test_single_ipv4_nic_configuration( "dhcp6": False, "match": {"macaddress": "00:0d:3a:04:75:98"}, "set-name": "eth0", - } + }, + "primary": { + "dhcp4": True, + "match": {"driver": "hv_netvsc", "name": "eth0"}, + }, + "secondary": { + "dhcp4": True, + "dhcp4-overrides": {"use-dns": False}, + "optional": True, + "match": {"driver": "hv_netvsc", "name": "!eth0"}, + }, }, "version": 2, } @@ -1548,7 +1558,17 @@ def test_network_config_set_from_imds(self): "dhcp6": False, "dhcp4": True, "dhcp4-overrides": {"route-metric": 100}, - } + }, + "primary": { + "dhcp4": True, + "match": {"driver": "hv_netvsc", "name": "eth0"}, + }, + "secondary": { + "dhcp4": True, + "dhcp4-overrides": {"use-dns": False}, + "optional": True, + "match": {"driver": "hv_netvsc", "name": "!eth0"}, + }, }, "version": 2, } @@ -1586,6 +1606,16 @@ def test_network_config_set_from_imds_route_metric_for_secondary_nic(self): "dhcp4": True, "dhcp4-overrides": {"route-metric": 300}, }, + "primary": { + "dhcp4": True, + "match": {"driver": "hv_netvsc", "name": "eth0"}, + }, + "secondary": { + "dhcp4": True, + "dhcp4-overrides": {"use-dns": False}, + "optional": True, + "match": {"driver": "hv_netvsc", "name": "!eth0"}, + }, }, "version": 2, } @@ -1617,7 +1647,17 @@ def test_network_config_set_from_imds_for_secondary_nic_no_ip(self): "dhcp6": False, "dhcp4": True, "dhcp4-overrides": {"route-metric": 100}, - } + }, + "primary": { + "dhcp4": True, + "match": {"driver": "hv_netvsc", "name": "eth0"}, + }, + "secondary": { + "dhcp4": True, + "dhcp4-overrides": {"use-dns": False}, + "optional": True, + "match": {"driver": "hv_netvsc", "name": "!eth0"}, + }, }, "version": 2, }