summaryrefslogtreecommitdiff
path: root/cloudinit/net
diff options
context:
space:
mode:
authorNigel Kukard <nkukard@lbsd.net>2022-12-07 23:29:52 +0000
committerGitHub <noreply@github.com>2022-12-07 16:29:52 -0700
commitf75be2ebe15b0dc78092fe47b1ef8d506607e9da (patch)
tree1e737cab6527b4749dc2e786342be66b27af5685 /cloudinit/net
parent9446bdbd4d4f3ecf8cf14fde589373d860c5a2e4 (diff)
downloadcloud-init-git-f75be2ebe15b0dc78092fe47b1ef8d506607e9da.tar.gz
networkd: Add support for multiple [Route] sections (#1868)
Networkd supports multiple [Route] sections within the same file. Currently all [Route] section tags are squashed into one and if there is a default gateway it means defining a device route is not possible as the target is set to the default gateway. This patch adds support for multiple [Route] sections allowing us to support device routes. This is done by tracking each route in the route list individually and ensuring the key-value pairs are maintained within their respective [Route] section. This both maintains backwards compatibility with previous behavior and allows the specification of routes with no destination IP, causing the destination to be added with a device target.
Diffstat (limited to 'cloudinit/net')
-rw-r--r--cloudinit/net/networkd.py51
1 files changed, 45 insertions, 6 deletions
diff --git a/cloudinit/net/networkd.py b/cloudinit/net/networkd.py
index 3591513f..4fd8a9b8 100644
--- a/cloudinit/net/networkd.py
+++ b/cloudinit/net/networkd.py
@@ -28,7 +28,7 @@ class CfgParser:
"DHCPv4": [],
"DHCPv6": [],
"Address": [],
- "Route": [],
+ "Route": {},
}
)
@@ -40,6 +40,22 @@ class CfgParser:
self.conf_dict[k] = list(dict.fromkeys(self.conf_dict[k]))
self.conf_dict[k].sort()
+ def update_route_section(self, sec, rid, key, val):
+ """
+ For each route section we use rid as a key, this allows us to isolate
+ this route from others on subsequent calls.
+ """
+ for k in self.conf_dict.keys():
+ if k == sec:
+ if rid not in self.conf_dict[k]:
+ self.conf_dict[k][rid] = []
+ self.conf_dict[k][rid].append(key + "=" + str(val))
+ # remove duplicates from list
+ self.conf_dict[k][rid] = list(
+ dict.fromkeys(self.conf_dict[k][rid])
+ )
+ self.conf_dict[k][rid].sort()
+
def get_final_conf(self):
contents = ""
for k, v in sorted(self.conf_dict.items()):
@@ -50,6 +66,12 @@ class CfgParser:
contents += "[" + k + "]\n"
contents += e + "\n"
contents += "\n"
+ elif k == "Route":
+ for n in sorted(v):
+ contents += "[" + k + "]\n"
+ for e in sorted(v[n]):
+ contents += e + "\n"
+ contents += "\n"
else:
contents += "[" + k + "]\n"
for e in sorted(v):
@@ -112,7 +134,11 @@ class Renderer(renderer.Renderer):
if "mtu" in iface and iface["mtu"]:
cfg.update_section(sec, "MTUBytes", iface["mtu"])
- def parse_routes(self, conf, cfg: CfgParser):
+ def parse_routes(self, rid, conf, cfg: CfgParser):
+ """
+ Parse a route and use rid as a key in order to isolate the route from
+ others in the route dict.
+ """
sec = "Route"
route_cfg_map = {
"gateway": "Gateway",
@@ -130,11 +156,12 @@ class Renderer(renderer.Renderer):
continue
if k == "network":
v += prefix
- cfg.update_section(sec, route_cfg_map[k], v)
+ cfg.update_route_section(sec, rid, route_cfg_map[k], v)
def parse_subnets(self, iface, cfg: CfgParser):
dhcp = "no"
sec = "Network"
+ rid = 0
for e in iface.get("subnets", []):
t = e["type"]
if t == "dhcp4" or t == "dhcp":
@@ -149,7 +176,10 @@ class Renderer(renderer.Renderer):
dhcp = "yes"
if "routes" in e and e["routes"]:
for i in e["routes"]:
- self.parse_routes(i, cfg)
+ # Use "r" as a dict key prefix for this route to isolate
+ # it from other sources of routes
+ self.parse_routes(f"r{rid}", i, cfg)
+ rid = rid + 1
if "address" in e:
subnet_cfg_map = {
"address": "Address",
@@ -163,7 +193,12 @@ class Renderer(renderer.Renderer):
v += "/" + str(e["prefix"])
cfg.update_section("Address", subnet_cfg_map[k], v)
elif k == "gateway":
- cfg.update_section("Route", subnet_cfg_map[k], v)
+ # Use "a" as a dict key prefix for this route to
+ # isolate it from other sources of routes
+ cfg.update_route_section(
+ "Route", f"a{rid}", subnet_cfg_map[k], v
+ )
+ rid = rid + 1
elif k == "dns_nameservers" or k == "dns_search":
cfg.update_section(sec, subnet_cfg_map[k], " ".join(v))
@@ -280,8 +315,12 @@ class Renderer(renderer.Renderer):
dhcp = self.parse_subnets(iface, cfg)
self.parse_dns(iface, cfg, ns)
+ rid = 0
for route in ns.iter_routes():
- self.parse_routes(route, cfg)
+ # Use "c" as a dict key prefix for this route to isolate it
+ # from other sources of routes
+ self.parse_routes(f"c{rid}", route, cfg)
+ rid = rid + 1
if ns.version == 2:
name: Optional[str] = iface["name"]