if not ctx.skip_mon_network:
# make sure IP is configured locally, and then figure out the
# CIDR network
- for net, ips in list_networks(ctx).items():
+ for net, ifaces in list_networks(ctx).items():
+ ips: List[str] = []
+ for iface, ls in ifaces.items():
+ ips.extend(ls)
if ipaddress.ip_address(unwrap_ipv6(base_ip)) in \
[ipaddress.ip_address(ip) for ip in ips]:
mon_network = net
def list_networks(ctx):
- # type: (CephadmContext) -> Dict[str,List[str]]
+ # type: (CephadmContext) -> Dict[str,Dict[str,List[str]]]
# sadly, 18.04's iproute2 4.15.0-2ubun doesn't support the -j flag,
# so we'll need to use a regex to parse 'ip' command output.
def _parse_ipv4_route(out):
- r = {} # type: Dict[str,List[str]]
- p = re.compile(r'^(\S+) (.*)scope link (.*)src (\S+)')
+ r = {} # type: Dict[str,Dict[str,List[str]]]
+ p = re.compile(r'^(\S+) dev (\S+) (.*)scope link (.*)src (\S+)')
for line in out.splitlines():
m = p.findall(line)
if not m:
continue
net = m[0][0]
- ip = m[0][3]
+ iface = m[0][1]
+ ip = m[0][4]
if net not in r:
- r[net] = []
- r[net].append(ip)
+ r[net] = {}
+ if iface not in r[net]:
+ r[net][iface] = []
+ r[net][iface].append(ip)
return r
def _parse_ipv6_route(routes, ips):
- r = {} # type: Dict[str,List[str]]
+ r = {} # type: Dict[str,Dict[str,List[str]]]
route_p = re.compile(r'^(\S+) dev (\S+) proto (\S+) metric (\S+) .*pref (\S+)$')
ip_p = re.compile(r'^\s+inet6 (\S+)/(.*)scope (.*)$')
+ iface_p = re.compile(r'^(\d+): (\S+): (.*)$')
for line in routes.splitlines():
m = route_p.findall(line)
if not m or m[0][0].lower() == 'default':
continue
net = m[0][0]
+ if '/' not in net: # only consider networks with a mask
+ continue
+ iface = m[0][1]
if net not in r:
- r[net] = []
+ r[net] = {}
+ if iface not in r[net]:
+ r[net][iface] = []
+ iface = None
for line in ips.splitlines():
m = ip_p.findall(line)
if not m:
+ m = iface_p.findall(line)
+ if m:
+ # drop @... suffix, if present
+ iface = m[0][1].split('@')[0]
continue
ip = m[0][0]
# find the network it belongs to
net = [n for n in r.keys()
if ipaddress.ip_address(ip) in ipaddress.ip_network(n)]
if net:
- r[net[0]].append(ip)
+ assert(iface)
+ r[net[0]][iface].append(ip)
return r
195.135.221.12 via 192.168.178.1 dev enxd89ef3f34260 proto static metric 100
""",
{
- '10.4.0.1': ['10.4.0.2'],
- '172.17.0.0/16': ['172.17.0.1'],
- '192.168.39.0/24': ['192.168.39.1'],
- '192.168.122.0/24': ['192.168.122.1'],
- '192.168.178.0/24': ['192.168.178.28']
+ '10.4.0.1': {'tun0': ['10.4.0.2']},
+ '172.17.0.0/16': {'docker0': ['172.17.0.1']},
+ '192.168.39.0/24': {'virbr1': ['192.168.39.1']},
+ '192.168.122.0/24': {'virbr0': ['192.168.122.1']},
+ '192.168.178.0/24': {'enxd89ef3f34260': ['192.168.178.28']}
}
), (
"""
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
""",
{
- '10.3.64.0/24': ['10.3.64.23', '10.3.64.27'],
- '10.88.0.0/16': ['10.88.0.1'],
- '172.21.3.1': ['172.21.3.2'],
- '192.168.122.0/24': ['192.168.122.1']}
+ '10.3.64.0/24': {'eno1': ['10.3.64.23', '10.3.64.27']},
+ '10.88.0.0/16': {'cni-podman0': ['10.88.0.1']},
+ '172.21.3.1': {'tun0': ['172.21.3.2']},
+ '192.168.122.0/24': {'virbr0': ['192.168.122.1']}}
),
])
def test_parse_ipv4_route(self, test_input, expected):
(
"""
::1 dev lo proto kernel metric 256 pref medium
-fdbc:7574:21fe:9200::/64 dev wlp2s0 proto ra metric 600 pref medium
-fdd8:591e:4969:6363::/64 dev wlp2s0 proto ra metric 600 pref medium
-fde4:8dba:82e1::/64 dev eth1 proto kernel metric 256 expires 1844sec pref medium
+fe80::/64 dev eno1 proto kernel metric 100 pref medium
+fe80::/64 dev br-3d443496454c proto kernel metric 256 linkdown pref medium
fe80::/64 dev tun0 proto kernel metric 256 pref medium
-fe80::/64 dev wlp2s0 proto kernel metric 600 pref medium
-default dev tun0 proto static metric 50 pref medium
-default via fe80::2480:28ec:5097:3fe2 dev wlp2s0 proto ra metric 20600 pref medium
+fe80::/64 dev br-4355f5dbb528 proto kernel metric 256 pref medium
+fe80::/64 dev docker0 proto kernel metric 256 linkdown pref medium
+fe80::/64 dev cni-podman0 proto kernel metric 256 linkdown pref medium
+fe80::/64 dev veth88ba1e8 proto kernel metric 256 pref medium
+fe80::/64 dev vethb6e5fc7 proto kernel metric 256 pref medium
+fe80::/64 dev vethaddb245 proto kernel metric 256 pref medium
+fe80::/64 dev vethbd14d6b proto kernel metric 256 pref medium
+fe80::/64 dev veth13e8fd2 proto kernel metric 256 pref medium
+fe80::/64 dev veth1d3aa9e proto kernel metric 256 pref medium
+fe80::/64 dev vethe485ca9 proto kernel metric 256 pref medium
""",
"""
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
- inet6 ::1/128 scope host
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
+ inet6 fe80::225:90ff:fee5:26e8/64 scope link noprefixroute
+ valid_lft forever preferred_lft forever
+6: br-3d443496454c: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 state DOWN
+ inet6 fe80::42:23ff:fe9d:ee4/64 scope link
+ valid_lft forever preferred_lft forever
+7: br-4355f5dbb528: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::42:6eff:fe35:41fe/64 scope link
+ valid_lft forever preferred_lft forever
+8: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 state DOWN
+ inet6 fe80::42:faff:fee6:40a0/64 scope link
+ valid_lft forever preferred_lft forever
+11: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 100
+ inet6 fe80::98a6:733e:dafd:350/64 scope link stable-privacy
+ valid_lft forever preferred_lft forever
+28: cni-podman0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 state DOWN qlen 1000
+ inet6 fe80::3449:cbff:fe89:b87e/64 scope link
+ valid_lft forever preferred_lft forever
+31: vethaddb245@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::90f7:3eff:feed:a6bb/64 scope link
+ valid_lft forever preferred_lft forever
+33: veth88ba1e8@if32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::d:f5ff:fe73:8c82/64 scope link
+ valid_lft forever preferred_lft forever
+35: vethbd14d6b@if34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::b44f:8ff:fe6f:813d/64 scope link
+ valid_lft forever preferred_lft forever
+37: vethb6e5fc7@if36: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::4869:c6ff:feaa:8afe/64 scope link
valid_lft forever preferred_lft forever
-2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
- inet6 fdd8:591e:4969:6363:4c52:cafe:8dd4:dc4/64 scope global temporary dynamic
- valid_lft 86394sec preferred_lft 14394sec
- inet6 fdbc:7574:21fe:9200:4c52:cafe:8dd4:dc4/64 scope global temporary dynamic
- valid_lft 6745sec preferred_lft 3145sec
- inet6 fdd8:591e:4969:6363:103a:abcd:af1f:57f3/64 scope global temporary deprecated dynamic
- valid_lft 86394sec preferred_lft 0sec
- inet6 fdbc:7574:21fe:9200:103a:abcd:af1f:57f3/64 scope global temporary deprecated dynamic
- valid_lft 6745sec preferred_lft 0sec
- inet6 fdd8:591e:4969:6363:a128:1234:2bdd:1b6f/64 scope global temporary deprecated dynamic
- valid_lft 86394sec preferred_lft 0sec
- inet6 fdbc:7574:21fe:9200:a128:1234:2bdd:1b6f/64 scope global temporary deprecated dynamic
- valid_lft 6745sec preferred_lft 0sec
- inet6 fdd8:591e:4969:6363:d581:4321:380b:3905/64 scope global temporary deprecated dynamic
- valid_lft 86394sec preferred_lft 0sec
- inet6 fdbc:7574:21fe:9200:d581:4321:380b:3905/64 scope global temporary deprecated dynamic
- valid_lft 6745sec preferred_lft 0sec
- inet6 fe80::1111:2222:3333:4444/64 scope link noprefixroute
+39: veth13e8fd2@if38: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::78f4:71ff:fefe:eb40/64 scope link
+ valid_lft forever preferred_lft forever
+41: veth1d3aa9e@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::24bd:88ff:fe28:5b18/64 scope link
+ valid_lft forever preferred_lft forever
+43: vethe485ca9@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
+ inet6 fe80::6425:87ff:fe42:b9f0/64 scope link
+ valid_lft forever preferred_lft forever
+""",
+ {
+ "fe80::/64": {
+ "eno1": [
+ "fe80::225:90ff:fee5:26e8"
+ ],
+ "br-3d443496454c": [
+ "fe80::42:23ff:fe9d:ee4"
+ ],
+ "tun0": [
+ "fe80::98a6:733e:dafd:350"
+ ],
+ "br-4355f5dbb528": [
+ "fe80::42:6eff:fe35:41fe"
+ ],
+ "docker0": [
+ "fe80::42:faff:fee6:40a0"
+ ],
+ "cni-podman0": [
+ "fe80::3449:cbff:fe89:b87e"
+ ],
+ "veth88ba1e8": [
+ "fe80::d:f5ff:fe73:8c82"
+ ],
+ "vethb6e5fc7": [
+ "fe80::4869:c6ff:feaa:8afe"
+ ],
+ "vethaddb245": [
+ "fe80::90f7:3eff:feed:a6bb"
+ ],
+ "vethbd14d6b": [
+ "fe80::b44f:8ff:fe6f:813d"
+ ],
+ "veth13e8fd2": [
+ "fe80::78f4:71ff:fefe:eb40"
+ ],
+ "veth1d3aa9e": [
+ "fe80::24bd:88ff:fe28:5b18"
+ ],
+ "vethe485ca9": [
+ "fe80::6425:87ff:fe42:b9f0"
+ ]
+ }
+ }
+ ),
+ (
+"""
+::1 dev lo proto kernel metric 256 pref medium
+2001:1458:301:eb::100:1a dev ens20f0 proto kernel metric 100 pref medium
+2001:1458:301:eb::/64 dev ens20f0 proto ra metric 100 pref medium
+fd01:1458:304:5e::/64 dev ens20f0 proto ra metric 100 pref medium
+fe80::/64 dev ens20f0 proto kernel metric 100 pref medium
+default proto ra metric 100
+ nexthop via fe80::46ec:ce00:b8a0:d3c8 dev ens20f0 weight 1
+ nexthop via fe80::46ec:ce00:b8a2:33c8 dev ens20f0 weight 1 pref medium
+""",
+"""
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
+ inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
- inet6 fde4:8dba:82e1:0:ec4a:e402:e9df:b357/64 scope global temporary dynamic
- valid_lft 1074sec preferred_lft 1074sec
- inet6 fde4:8dba:82e1:0:5054:ff:fe72:61af/64 scope global dynamic mngtmpaddr
- valid_lft 1074sec preferred_lft 1074sec
-12: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 100
- inet6 fe80::cafe:cafe:cafe:cafe/64 scope link stable-privacy
+2: ens20f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
+ inet6 2001:1458:301:eb::100:1a/128 scope global dynamic noprefixroute
+ valid_lft 590879sec preferred_lft 590879sec
+ inet6 fe80::2e60:cff:fef8:da41/64 scope link noprefixroute
valid_lft forever preferred_lft forever
""",
{
- "::1": ["::1"],
- "fdbc:7574:21fe:9200::/64": ["fdbc:7574:21fe:9200:4c52:cafe:8dd4:dc4",
- "fdbc:7574:21fe:9200:103a:abcd:af1f:57f3",
- "fdbc:7574:21fe:9200:a128:1234:2bdd:1b6f",
- "fdbc:7574:21fe:9200:d581:4321:380b:3905"],
- "fdd8:591e:4969:6363::/64": ["fdd8:591e:4969:6363:4c52:cafe:8dd4:dc4",
- "fdd8:591e:4969:6363:103a:abcd:af1f:57f3",
- "fdd8:591e:4969:6363:a128:1234:2bdd:1b6f",
- "fdd8:591e:4969:6363:d581:4321:380b:3905"],
- "fde4:8dba:82e1::/64": ["fde4:8dba:82e1:0:ec4a:e402:e9df:b357",
- "fde4:8dba:82e1:0:5054:ff:fe72:61af"],
- "fe80::/64": ["fe80::1111:2222:3333:4444",
- "fe80::cafe:cafe:cafe:cafe"]
+ '2001:1458:301:eb::/64': {
+ 'ens20f0': [
+ '2001:1458:301:eb::100:1a'
+ ],
+ },
+ 'fe80::/64': {
+ 'ens20f0': ['fe80::2e60:cff:fef8:da41'],
+ },
+ 'fd01:1458:304:5e::/64': {
+ 'ens20f0': []
+ },
}
- )])
+ ),
+ ])
def test_parse_ipv6_route(self, test_routes, test_ips, expected):
assert cd._parse_ipv6_route(test_routes, test_ips) == expected
self.last_facts_update = {} # type: Dict[str, datetime.datetime]
self.osdspec_previews = {} # type: Dict[str, List[Dict[str, Any]]]
self.osdspec_last_applied = {} # type: Dict[str, Dict[str, datetime.datetime]]
- self.networks = {} # type: Dict[str, Dict[str, List[str]]]
+ self.networks = {} # type: Dict[str, Dict[str, Dict[str, List[str]]]]
self.last_device_update = {} # type: Dict[str, datetime.datetime]
self.last_device_change = {} # type: Dict[str, datetime.datetime]
self.daemon_refresh_queue = [] # type: List[str]
orchestrator.DaemonDescription.from_json(d)
for d in j.get('devices', []):
self.devices[host].append(inventory.Device.from_json(d))
- self.networks[host] = j.get('networks', {})
+ self.networks[host] = j.get('networks_and_interfaces', {})
self.osdspec_previews[host] = j.get('osdspec_previews', {})
for name, ts in j.get('osdspec_last_applied', {}).items():
self.osdspec_last_applied[host][name] = str_to_datetime(ts)
return True
return False
- def update_host_devices_networks(self, host, dls, nets):
- # type: (str, List[inventory.Device], Dict[str,List[str]]) -> None
+ def update_host_devices_networks(
+ self,
+ host: str,
+ dls: List[inventory.Device],
+ nets: Dict[str, Dict[str, List[str]]]
+ ) -> None:
if (
host not in self.devices
or host not in self.last_device_change
for d in self.devices[host]:
j['devices'].append(d.to_json())
if host in self.networks:
- j['networks'] = self.networks[host]
+ j['networks_and_interfaces'] = self.networks[host]
if host in self.daemon_config_deps:
for name, depi in self.daemon_config_deps[host].items():
j['daemon_config_deps'][name] = {