Update sunet-l4lb-namespace
Make it able to delete addresses that are no longer in the netns config. Also make it read one netns-base.json for hardware config which is managed by puppet but also make it look for netns-sunet-cdn-agent.json which is not created by puppet. This file will be generated by sunet-cdn-agent and will include the configuration for dummy0.
This commit is contained in:
parent
f638e4c6f4
commit
db2b4ca409
3 changed files with 86 additions and 44 deletions
|
@ -13,6 +13,7 @@ pylint sunet-l4lb-namespace
|
|||
mypy --strict sunet-l4lb-namespace
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
import json
|
||||
import shlex
|
||||
import subprocess
|
||||
|
@ -21,6 +22,7 @@ import sys
|
|||
|
||||
def run_command(cmd: str) -> subprocess.CompletedProcess[str]:
|
||||
"""Execute subprocess command"""
|
||||
print(f"{cmd}")
|
||||
args = shlex.split(cmd)
|
||||
try:
|
||||
proc = subprocess.run(args, capture_output=True, check=True, encoding="utf-8")
|
||||
|
@ -35,12 +37,15 @@ def run_command(cmd: str) -> subprocess.CompletedProcess[str]:
|
|||
return proc
|
||||
|
||||
|
||||
def configure_interfaces(
|
||||
def configure_interfaces( # pylint: disable=too-many-locals,too-many-branches
|
||||
namespace: str, if_data: dict[str, dict[str, list[str]]]
|
||||
) -> None:
|
||||
"""Configure interfaces"""
|
||||
proc = run_command("ip netns exec l4lb ip -j addr show")
|
||||
proc = run_command(f"ip netns exec {namespace} ip -j addr show")
|
||||
namespace_ifs = json.loads(proc.stdout)
|
||||
|
||||
ipv4_key = "ipv4"
|
||||
ipv6_key = "ipv6"
|
||||
for if_name, data in if_data.items():
|
||||
if_exists = next(
|
||||
(True for interface in namespace_ifs if interface["ifname"] == if_name),
|
||||
|
@ -54,38 +59,73 @@ def configure_interfaces(
|
|||
else:
|
||||
run_command(f"ip link set {if_name} netns {namespace}")
|
||||
|
||||
run_command(f"ip netns exec {namespace} ip link set {if_name} up")
|
||||
|
||||
proc = run_command(f"ip netns exec {namespace} ip -j addr show dev {if_name}")
|
||||
if_conf = json.loads(proc.stdout)
|
||||
for ipv4_cidr in data["ipv4"]:
|
||||
ip4, prefix = ipv4_cidr.split("/")
|
||||
v4_addr_exists = next(
|
||||
(
|
||||
True
|
||||
for addr in if_conf[0]["addr_info"]
|
||||
if addr["local"] == ip4 and addr["prefixlen"] == int(prefix)
|
||||
),
|
||||
False,
|
||||
)
|
||||
if not v4_addr_exists:
|
||||
run_command(
|
||||
f"ip netns exec {namespace} ip addr add {ipv4_cidr} dev {if_name}"
|
||||
|
||||
# Add missing addresses from config
|
||||
if ipv4_key in data:
|
||||
for configured_ipv4_cidr in data[ipv4_key]:
|
||||
ip4, prefix = configured_ipv4_cidr.split("/")
|
||||
v4_addr_exists = next(
|
||||
(
|
||||
True
|
||||
for addr in if_conf[0]["addr_info"]
|
||||
if addr["local"] == ip4 and addr["prefixlen"] == int(prefix)
|
||||
),
|
||||
False,
|
||||
)
|
||||
for ipv6_cidr in data["ipv6"]:
|
||||
ip6, prefix = ipv6_cidr.split("/")
|
||||
v6_addr_exists = next(
|
||||
(
|
||||
True
|
||||
for addr in if_conf[0]["addr_info"]
|
||||
if addr["local"] == ip6 and addr["prefixlen"] == int(prefix)
|
||||
),
|
||||
False,
|
||||
)
|
||||
if not v6_addr_exists:
|
||||
run_command(
|
||||
f"ip netns exec {namespace} ip addr add {ipv6_cidr} dev {if_name}"
|
||||
if not v4_addr_exists:
|
||||
run_command(
|
||||
f"ip netns exec {namespace} ip addr add {configured_ipv4_cidr} dev {if_name}" # pylint: disable=line-too-long
|
||||
)
|
||||
if ipv6_key in data:
|
||||
for ipv6_cidr in data[ipv6_key]:
|
||||
ip6, prefix = ipv6_cidr.split("/")
|
||||
v6_addr_exists = next(
|
||||
(
|
||||
True
|
||||
for addr in if_conf[0]["addr_info"]
|
||||
if addr["local"] == ip6 and addr["prefixlen"] == int(prefix)
|
||||
),
|
||||
False,
|
||||
)
|
||||
if not v6_addr_exists:
|
||||
run_command(
|
||||
f"ip netns exec {namespace} ip addr add {ipv6_cidr} dev {if_name}"
|
||||
)
|
||||
|
||||
# Remove no longer configured addresseses
|
||||
for addr_info in if_conf[0]["addr_info"]:
|
||||
# Ignore addresses like fe80
|
||||
if addr_info["scope"] != "global":
|
||||
continue
|
||||
|
||||
cidr = "/".join((addr_info["local"], str(addr_info["prefixlen"])))
|
||||
|
||||
# We need strict=False because otherwise ip_network() gets angry if
|
||||
# there are host bits set in the address (which of course there is
|
||||
# because we are parsing actual interface configs, not pure
|
||||
# "networks")
|
||||
cidr_net = ipaddress.ip_network(cidr, strict=False)
|
||||
|
||||
needs_removal = False
|
||||
if cidr_net.version == 4:
|
||||
if ipv4_key not in data or cidr not in data[ipv4_key]:
|
||||
needs_removal = True
|
||||
elif cidr_net.version == 6:
|
||||
if ipv6_key not in data or cidr not in data[ipv6_key]:
|
||||
needs_removal = True
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Expected IPv4 or IPv6, got something else: {cidr_net.version}"
|
||||
)
|
||||
|
||||
run_command(f"ip netns exec {namespace} ip link set {if_name} up")
|
||||
if needs_removal:
|
||||
run_command(
|
||||
f"ip netns exec {namespace} ip addr del {cidr} dev {if_name}"
|
||||
)
|
||||
|
||||
|
||||
def setup_namespaces(netns_data: dict[str, dict[str, dict[str, list[str]]]]) -> None:
|
||||
|
@ -99,8 +139,8 @@ def setup_namespaces(netns_data: dict[str, dict[str, dict[str, list[str]]]]) ->
|
|||
if not netns_exists:
|
||||
run_command(f"ip netns add {namespace}")
|
||||
|
||||
# Make localhost available
|
||||
run_command(f"ip netns exec {namespace} ip link set lo up")
|
||||
# Make localhost available
|
||||
run_command(f"ip netns exec {namespace} ip link set lo up")
|
||||
|
||||
configure_interfaces(namespace, if_data)
|
||||
|
||||
|
@ -129,10 +169,20 @@ def main() -> None:
|
|||
# }
|
||||
# }
|
||||
# }
|
||||
with open("/opt/sunet-cdn/l4lb/conf/netns.json", encoding="utf-8") as f:
|
||||
netns_data = json.load(f)
|
||||
|
||||
setup_namespaces(netns_data)
|
||||
input_files = [
|
||||
"/opt/sunet-cdn/l4lb/conf/netns-base.json",
|
||||
"/opt/sunet-cdn/l4lb/conf/netns-sunet-cdn-agent.json",
|
||||
]
|
||||
|
||||
for input_file in input_files:
|
||||
try:
|
||||
with open(input_file, encoding="utf-8") as f:
|
||||
netns_data = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"skipping nonexistant file '{input_file}'")
|
||||
continue
|
||||
setup_namespaces(netns_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -54,12 +54,12 @@ class cdn::l4lb(
|
|||
mode => '0640',
|
||||
}
|
||||
|
||||
file { '/opt/sunet-cdn/l4lb/conf/netns.json':
|
||||
file { '/opt/sunet-cdn/l4lb/conf/netns-base.json':
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
content => template('cdn/l4lb/netns.json.erb'),
|
||||
content => template('cdn/l4lb/netns-base.json.erb'),
|
||||
}
|
||||
|
||||
file { '/usr/local/bin/sunet-l4lb-namespace':
|
||||
|
|
|
@ -15,14 +15,6 @@
|
|||
"ipv6": [
|
||||
"2001:6b0:2006:75::1/127"
|
||||
]
|
||||
},
|
||||
"dummy0": {
|
||||
"ipv4": [
|
||||
"188.240.152.1/32"
|
||||
],
|
||||
"ipv6": [
|
||||
"2001:6b0:2100::1/128"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue