diff options
author | Nigel Kukard <nkukard@lbsd.net> | 2022-12-07 23:29:52 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-07 16:29:52 -0700 |
commit | f75be2ebe15b0dc78092fe47b1ef8d506607e9da (patch) | |
tree | 1e737cab6527b4749dc2e786342be66b27af5685 /cloudinit/net | |
parent | 9446bdbd4d4f3ecf8cf14fde589373d860c5a2e4 (diff) | |
download | cloud-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.py | 51 |
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"] |