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
|
mypy --strict sunet-l4lb-namespace
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -21,6 +22,7 @@ import sys
|
||||||
|
|
||||||
def run_command(cmd: str) -> subprocess.CompletedProcess[str]:
|
def run_command(cmd: str) -> subprocess.CompletedProcess[str]:
|
||||||
"""Execute subprocess command"""
|
"""Execute subprocess command"""
|
||||||
|
print(f"{cmd}")
|
||||||
args = shlex.split(cmd)
|
args = shlex.split(cmd)
|
||||||
try:
|
try:
|
||||||
proc = subprocess.run(args, capture_output=True, check=True, encoding="utf-8")
|
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
|
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]]]
|
namespace: str, if_data: dict[str, dict[str, list[str]]]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Configure interfaces"""
|
"""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)
|
namespace_ifs = json.loads(proc.stdout)
|
||||||
|
|
||||||
|
ipv4_key = "ipv4"
|
||||||
|
ipv6_key = "ipv6"
|
||||||
for if_name, data in if_data.items():
|
for if_name, data in if_data.items():
|
||||||
if_exists = next(
|
if_exists = next(
|
||||||
(True for interface in namespace_ifs if interface["ifname"] == if_name),
|
(True for interface in namespace_ifs if interface["ifname"] == if_name),
|
||||||
|
@ -54,38 +59,73 @@ def configure_interfaces(
|
||||||
else:
|
else:
|
||||||
run_command(f"ip link set {if_name} netns {namespace}")
|
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}")
|
proc = run_command(f"ip netns exec {namespace} ip -j addr show dev {if_name}")
|
||||||
if_conf = json.loads(proc.stdout)
|
if_conf = json.loads(proc.stdout)
|
||||||
for ipv4_cidr in data["ipv4"]:
|
|
||||||
ip4, prefix = ipv4_cidr.split("/")
|
# Add missing addresses from config
|
||||||
v4_addr_exists = next(
|
if ipv4_key in data:
|
||||||
(
|
for configured_ipv4_cidr in data[ipv4_key]:
|
||||||
True
|
ip4, prefix = configured_ipv4_cidr.split("/")
|
||||||
for addr in if_conf[0]["addr_info"]
|
v4_addr_exists = next(
|
||||||
if addr["local"] == ip4 and addr["prefixlen"] == int(prefix)
|
(
|
||||||
),
|
True
|
||||||
False,
|
for addr in if_conf[0]["addr_info"]
|
||||||
)
|
if addr["local"] == ip4 and addr["prefixlen"] == int(prefix)
|
||||||
if not v4_addr_exists:
|
),
|
||||||
run_command(
|
False,
|
||||||
f"ip netns exec {namespace} ip addr add {ipv4_cidr} dev {if_name}"
|
|
||||||
)
|
)
|
||||||
for ipv6_cidr in data["ipv6"]:
|
if not v4_addr_exists:
|
||||||
ip6, prefix = ipv6_cidr.split("/")
|
run_command(
|
||||||
v6_addr_exists = next(
|
f"ip netns exec {namespace} ip addr add {configured_ipv4_cidr} dev {if_name}" # pylint: disable=line-too-long
|
||||||
(
|
)
|
||||||
True
|
if ipv6_key in data:
|
||||||
for addr in if_conf[0]["addr_info"]
|
for ipv6_cidr in data[ipv6_key]:
|
||||||
if addr["local"] == ip6 and addr["prefixlen"] == int(prefix)
|
ip6, prefix = ipv6_cidr.split("/")
|
||||||
),
|
v6_addr_exists = next(
|
||||||
False,
|
(
|
||||||
)
|
True
|
||||||
if not v6_addr_exists:
|
for addr in if_conf[0]["addr_info"]
|
||||||
run_command(
|
if addr["local"] == ip6 and addr["prefixlen"] == int(prefix)
|
||||||
f"ip netns exec {namespace} ip addr add {ipv6_cidr} dev {if_name}"
|
),
|
||||||
|
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:
|
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:
|
if not netns_exists:
|
||||||
run_command(f"ip netns add {namespace}")
|
run_command(f"ip netns add {namespace}")
|
||||||
|
|
||||||
# Make localhost available
|
# Make localhost available
|
||||||
run_command(f"ip netns exec {namespace} ip link set lo up")
|
run_command(f"ip netns exec {namespace} ip link set lo up")
|
||||||
|
|
||||||
configure_interfaces(namespace, if_data)
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -54,12 +54,12 @@ class cdn::l4lb(
|
||||||
mode => '0640',
|
mode => '0640',
|
||||||
}
|
}
|
||||||
|
|
||||||
file { '/opt/sunet-cdn/l4lb/conf/netns.json':
|
file { '/opt/sunet-cdn/l4lb/conf/netns-base.json':
|
||||||
ensure => file,
|
ensure => file,
|
||||||
owner => 'root',
|
owner => 'root',
|
||||||
group => 'root',
|
group => 'root',
|
||||||
mode => '0644',
|
mode => '0644',
|
||||||
content => template('cdn/l4lb/netns.json.erb'),
|
content => template('cdn/l4lb/netns-base.json.erb'),
|
||||||
}
|
}
|
||||||
|
|
||||||
file { '/usr/local/bin/sunet-l4lb-namespace':
|
file { '/usr/local/bin/sunet-l4lb-namespace':
|
||||||
|
|
|
@ -15,14 +15,6 @@
|
||||||
"ipv6": [
|
"ipv6": [
|
||||||
"2001:6b0:2006:75::1/127"
|
"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