return result
-_IPV4_LOOPBACK_SPACE = ipaddress.ip_network('127.0.0.0/8')
-_IPV6_LOOPBACK_HOST = ipaddress.ip_network('::1/128')
-
+def _list_ipv4_networks(
+ ctx: CephadmContext,
+ allow_lo_routes: bool = False,
+ allow_bgp_routes: bool = False,
+) -> Dict[str, Dict[str, Set[str]]]:
+ """IPv4 networks from ``ip route ls`` (and optional ``proto bgp`` supplement).
-def _is_ipv4_loopback(net_s: str) -> bool:
- """True if *net_s* is an IPv4 prefix wholly within host loopback 127.0.0.0/8."""
- try:
- n = ipaddress.ip_network(net_s, strict=False)
- except ValueError:
- return False
- return (
- n.version == 4
- and n.network_address in _IPV4_LOOPBACK_SPACE
- and n.broadcast_address in _IPV4_LOOPBACK_SPACE
+ When *allow_lo_routes* is false, routes on ``lo`` are ignored by the parsers.
+ When true, ``_parse_ipv4_lo_route`` supplements the base table.
+ """
+ execstr: Optional[str] = find_executable('ip')
+ if not execstr:
+ raise FileNotFoundError("unable to find 'ip' command")
+ out, _, _ = call_throws(
+ ctx,
+ [execstr, 'route', 'ls'],
+ verbosity=CallVerbosity.QUIET_UNLESS_ERROR,
)
-
-
-def _is_ipv6_loopback(net_s: str) -> bool:
- """True if *net_s* is exactly the host loopback prefix ::1/128."""
- try:
- n = ipaddress.ip_network(net_s, strict=False)
- except ValueError:
- return False
- if n.version != 6:
- return False
- return n == _IPV6_LOOPBACK_HOST
-
-
-def _merge_ipv4_network_dicts(
- dst: Dict[str, Dict[str, Set[str]]], add: Dict[str, Dict[str, Set[str]]]
-) -> None:
- """Merge *add* into *dst* (nested sets)."""
- for net, ifaces in add.items():
- if net not in dst:
- dst[net] = {}
- for iface, ips in ifaces.items():
- if iface not in dst[net]:
- dst[net][iface] = set()
- dst[net][iface].update(ips)
+ res = _parse_ipv4_route(out, allow_lo_routes)
+ if allow_lo_routes:
+ _merge_ipv4_network_dicts(res, _parse_ipv4_lo_route(out))
+ if allow_bgp_routes:
+ bgp_out, _, _ = call_throws(
+ ctx,
+ [execstr, '-j', 'route', 'ls', 'proto', 'bgp'],
+ verbosity=CallVerbosity.QUIET_UNLESS_ERROR,
+ )
+ _merge_ipv4_network_dicts(
+ res, _parse_ipv4_bgp_route(bgp_out, allow_lo_routes)
+ )
+ return res
def _parse_ipv4_route(
is_v4 = version == 4
host_plen = 32 if is_v4 else 128
+ is_loopback_net = _is_ipv4_loopback if is_v4 else _is_ipv6_loopback
def put(net: str, iface: str, ip: str) -> None:
- if _is_ipv4_loopback(net) if is_v4 else _is_ipv6_loopback(net):
+ if is_loopback_net(net):
return
if net not in r:
r[net] = {}
if not isinstance(route, dict):
continue
dst = route.get('dst')
- if not dst:
+ if not dst or dst == 'default':
continue
net = _route_dst_to_network(dst, version)
return _parse_bgp_routes_json(out, 6, allow_lo_routes)
-def _list_ipv4_networks(
- ctx: CephadmContext,
- allow_lo_routes: bool = False,
- allow_bgp_routes: bool = False,
-) -> Dict[str, Dict[str, Set[str]]]:
- """IPv4 networks from ``ip route ls`` (and optional ``proto bgp`` supplement).
-
- When *allow_lo_routes* is false, routes on ``lo`` are ignored by the parsers.
- When true, ``_parse_ipv4_lo_route`` supplements the base table.
- """
- execstr: Optional[str] = find_executable('ip')
- if not execstr:
- raise FileNotFoundError("unable to find 'ip' command")
- out, _, _ = call_throws(
- ctx,
- [execstr, 'route', 'ls'],
- verbosity=CallVerbosity.QUIET_UNLESS_ERROR,
- )
- res = _parse_ipv4_route(out, allow_lo_routes)
- if allow_lo_routes:
- _merge_ipv4_network_dicts(res, _parse_ipv4_lo_route(out))
- if allow_bgp_routes:
- bgp_out, _, _ = call_throws(
- ctx,
- [execstr, '-j', 'route', 'ls', 'proto', 'bgp'],
- verbosity=CallVerbosity.QUIET_UNLESS_ERROR,
- )
- _merge_ipv4_network_dicts(
- res, _parse_ipv4_bgp_route(bgp_out, allow_lo_routes)
- )
- return res
-
-
def _list_ipv6_networks(
ctx: CephadmContext,
allow_lo_routes: bool = False,
r[net[0]][iface].add(ip)
return r
+
+
+_IPV4_LOOPBACK_SPACE = ipaddress.ip_network('127.0.0.0/8')
+_IPV6_LOOPBACK_HOST = ipaddress.ip_network('::1/128')
+
+
+def _is_ipv4_loopback(net_s: str) -> bool:
+ """True if *net_s* is an IPv4 prefix wholly within host loopback 127.0.0.0/8."""
+ try:
+ n = ipaddress.ip_network(net_s, strict=False)
+ except ValueError:
+ return False
+ return (
+ n.version == 4
+ and n.network_address in _IPV4_LOOPBACK_SPACE
+ and n.broadcast_address in _IPV4_LOOPBACK_SPACE
+ )
+
+
+def _is_ipv6_loopback(net_s: str) -> bool:
+ """True if *net_s* is exactly the host loopback prefix ::1/128."""
+ try:
+ n = ipaddress.ip_network(net_s, strict=False)
+ except ValueError:
+ return False
+ if n.version != 6:
+ return False
+ return n == _IPV6_LOOPBACK_HOST
+
+
+def _merge_ipv4_network_dicts(
+ dst: Dict[str, Dict[str, Set[str]]], add: Dict[str, Dict[str, Set[str]]]
+) -> None:
+ """Merge *add* into *dst* (nested sets)."""
+ for net, ifaces in add.items():
+ if net not in dst:
+ dst[net] = {}
+ for iface, ips in ifaces.items():
+ if iface not in dst[net]:
+ dst[net][iface] = set()
+ dst[net][iface].update(ips)
class TestCommandListNetworks:
- # fmt: off
- # Keep table-style parametrization unchanged for reviewable diffs (see PR discussion).
@pytest.mark.parametrize("test_input, expected", [
(
dedent("""
}
),
])
- # fmt: on
def test_parse_ipv4_route(self, test_input, expected):
assert _parse_ipv4_route(test_input) == expected
- # fmt: off
@pytest.mark.parametrize("test_routes, test_ips, expected", [
(
dedent("""
::1 dev lo proto kernel metric 256 pref medium
fe80::/64 dev ceph-brx proto kernel metric 256 pref medium
fe80::/64 dev brx.0 proto kernel metric 256 pref medium
- default via fe80::327c:5e00:6487:71e0 dev enp3s0f1 proto ra metric 1024 expires 1790sec hoplimit 64 pref medium
- """),
+ default via fe80::327c:5e00:6487:71e0 dev enp3s0f1 proto ra metric 1024 expires 1790sec hoplimit 64 pref medium """),
dedent("""
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
inet6 ::1/128 scope host
}
),
])
- # fmt: on
def test_parse_ipv6_route(self, test_routes, test_ips, expected):
assert _parse_ipv6_route(test_routes, test_ips) == expected
@mock.patch('cephadmlib.host_facts.call_throws')
@mock.patch('cephadmlib.host_facts.find_executable')
- def test_command_list_networks(
- self, _find_exe, _call_throws, cephadm_fs, capsys
- ):
- _call_throws.side_effect = [
- (
- '10.4.0.1 dev tun0 proto kernel scope link src 10.4.0.2 metric 50\n',
- '',
- '',
- ),
- ('', '', ''),
- ('', '', ''),
- ]
+ def test_command_list_networks(self, _find_exe, _call_throws, cephadm_fs, capsys):
+ _call_throws.return_value = ('10.4.0.1 dev tun0 proto kernel scope link src 10.4.0.2 metric 50\n', '', '')
_find_exe.return_value = 'ip'
with with_cephadm_ctx([]) as ctx:
_cephadm.command_list_networks(ctx)