Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Render bridges correctly for v2 on sysconfig with set-name #5674

Merged
merged 2 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cloudinit/net/eni.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import os
import re
from contextlib import suppress
from typing import Optional

from cloudinit import subp, util
Expand Down Expand Up @@ -421,6 +422,11 @@ def _render_route(self, route, indent=""):
return content

def _render_iface(self, iface, render_hwaddress=False):
iface = copy.deepcopy(iface)

# Remove irrelevant keys
with suppress(KeyError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like using suppress, but in this case it doesn't appear to add value. Wouldn't something like this suffice?

if iface.get("config_id"):
    iface.pop("config_id")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't remove value either than I think easier to ask forgiveness than permission is still slightly more idiomatic.

iface.pop("config_id")
sections = []
subnets = iface.get("subnets", {})
accept_ra = iface.pop("accept-ra", None)
Expand Down
29 changes: 10 additions & 19 deletions cloudinit/net/network_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ def handle_physical(self, command):
wakeonlan = util.is_true(wakeonlan)
iface.update(
{
"config_id": command.get("config_id"),
"name": command.get("name"),
"type": command.get("type"),
"mac_address": command.get("mac_address"),
Expand All @@ -424,7 +425,8 @@ def handle_physical(self, command):
"wakeonlan": wakeonlan,
}
)
self._network_state["interfaces"].update({command.get("name"): iface})
iface_key = command.get("config_id", command.get("name"))
self._network_state["interfaces"].update({iface_key: iface})
self.dump_network_state()

@ensure_command_keys(["name", "vlan_id", "vlan_link"])
Expand Down Expand Up @@ -712,6 +714,7 @@ def handle_ethernets(self, command):

for eth, cfg in command.items():
phy_cmd = {
"config_id": eth,
"type": "physical",
}
match = cfg.get("match", {})
Expand Down Expand Up @@ -800,26 +803,14 @@ def handle_wifis(self, command):
def _v2_common(self, cfg) -> None:
LOG.debug("v2_common: handling config:\n%s", cfg)
for iface, dev_cfg in cfg.items():
if "set-name" in dev_cfg:
set_name_iface = dev_cfg.get("set-name")
if set_name_iface:
iface = set_name_iface
if "nameservers" in dev_cfg:
search = dev_cfg.get("nameservers").get("search", [])
dns = dev_cfg.get("nameservers").get("addresses", [])
search = dev_cfg.get("nameservers").get("search")
dns = dev_cfg.get("nameservers").get("addresses")
name_cmd = {"type": "nameserver"}
if len(search) > 0:
name_cmd.update({"search": search})
if len(dns) > 0:
name_cmd.update({"address": dns})

mac_address: Optional[str] = dev_cfg.get("match", {}).get(
"macaddress"
)
if mac_address:
real_if_name = find_interface_name_from_mac(mac_address)
if real_if_name:
iface = real_if_name
if search:
name_cmd["search"] = search
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could similarly exploit the falsyness of search and dns (no need to check that len() > 0) and also remove the default values in .get("search", []) and .get("addresses", []), if you want.

if dns:
name_cmd["address"] = dns

self._handle_individual_nameserver(name_cmd, iface)

Expand Down
24 changes: 16 additions & 8 deletions cloudinit/net/sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import logging
import os
import re
from typing import Mapping, Optional
from typing import Dict, Optional

from cloudinit import subp, util
from cloudinit.distros.parsers import networkmanager_conf, resolv_conf
Expand Down Expand Up @@ -705,7 +705,7 @@ def _render_physical_interfaces(
):
physical_filter = renderer.filter_by_physical
for iface in network_state.iter_interfaces(physical_filter):
iface_name = iface["name"]
iface_name = iface.get("config_id") or iface["name"]
iface_subnets = iface.get("subnets", [])
iface_cfg = iface_contents[iface_name]
route_cfg = iface_cfg.routes
Expand Down Expand Up @@ -910,7 +910,9 @@ def _render_networkmanager_conf(network_state, templates=None):
return out

@classmethod
def _render_bridge_interfaces(cls, network_state, iface_contents, flavor):
def _render_bridge_interfaces(
cls, network_state: NetworkState, iface_contents, flavor
):
bridge_key_map = {
old_k: new_k
for old_k, new_k in cls.cfg_key_maps[flavor].items()
Expand Down Expand Up @@ -991,23 +993,29 @@ def _render_ib_interfaces(cls, network_state, iface_contents, flavor):

@classmethod
def _render_sysconfig(
cls, base_sysconf_dir, network_state, flavor, templates=None
cls,
base_sysconf_dir,
network_state: NetworkState,
flavor,
templates=None,
):
"""Given state, return /etc/sysconfig files + contents"""
if not templates:
templates = cls.templates
iface_contents: Mapping[str, NetInterface] = {}
iface_contents: Dict[str, NetInterface] = {}
for iface in network_state.iter_interfaces():
if iface["type"] == "loopback":
continue
iface_name = iface["name"]
iface_cfg = NetInterface(iface_name, base_sysconf_dir, templates)
config_id: str = iface.get("config_id") or iface["name"]
iface_cfg = NetInterface(
iface["name"], base_sysconf_dir, templates
)
if flavor == "suse":
iface_cfg.drop("DEVICE")
# If type detection fails it is considered a bug in SUSE
iface_cfg.drop("TYPE")
cls._render_iface_shared(iface, iface_cfg, flavor)
iface_contents[iface_name] = iface_cfg
iface_contents[config_id] = iface_cfg
cls._render_physical_interfaces(network_state, iface_contents, flavor)
cls._render_bond_interfaces(network_state, iface_contents, flavor)
cls._render_vlan_interfaces(network_state, iface_contents, flavor)
Expand Down
85 changes: 85 additions & 0 deletions tests/unittests/net/network_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4912,4 +4912,89 @@
"""
),
},
"v2-bridges-set-name": {
"yaml": textwrap.dedent(
"""\
version: 2
ethernets:
baremetalport:
match:
macaddress: 52:54:00:bd:8f:cb
set-name: baremetal0
provisioningport:
match:
macaddress: 52:54:00:25:ae:12
set-name: provisioning0
bridges:
baremetal:
addresses:
- fc00:1:1::2/64
interfaces:
- baremetalport
provisioning:
addresses:
- fc00:1:2::2/64
interfaces:
- provisioningport
"""
),
"expected_sysconfig_rhel": {
"ifcfg-baremetal": textwrap.dedent(
"""\
# Created by cloud-init automatically, do not edit.
#
BOOTPROTO=none
DEVICE=baremetal
IPV6ADDR=fc00:1:1::2/64
IPV6INIT=yes
IPV6_AUTOCONF=no
IPV6_FORCE_ACCEPT_RA=no
ONBOOT=yes
TYPE=Bridge
USERCTL=no
"""
),
"ifcfg-baremetal0": textwrap.dedent(
"""\
# Created by cloud-init automatically, do not edit.
#
BOOTPROTO=none
BRIDGE=baremetal
DEVICE=baremetal0
HWADDR=52:54:00:bd:8f:cb
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
"""
),
"ifcfg-provisioning": textwrap.dedent(
"""\
# Created by cloud-init automatically, do not edit.
#
BOOTPROTO=none
DEVICE=provisioning
IPV6ADDR=fc00:1:2::2/64
IPV6INIT=yes
IPV6_AUTOCONF=no
IPV6_FORCE_ACCEPT_RA=no
ONBOOT=yes
TYPE=Bridge
USERCTL=no
"""
),
"ifcfg-provisioning0": textwrap.dedent(
"""\
# Created by cloud-init automatically, do not edit.
#
BOOTPROTO=none
BRIDGE=provisioning
DEVICE=provisioning0
HWADDR=52:54:00:25:ae:12
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
"""
),
},
},
}
1 change: 1 addition & 0 deletions tests/unittests/test_net.py
Original file line number Diff line number Diff line change
Expand Up @@ -2098,6 +2098,7 @@ def test_config_with_explicit_loopback(self):
("dhcpv6_stateful", "yaml"),
("wakeonlan_disabled", "yaml_v2"),
("wakeonlan_enabled", "yaml_v2"),
("v2-bridges-set-name", "yaml"),
pytest.param(
"v1-dns",
"yaml",
Expand Down