CI / test (3.10) (push) Failing after 15s
CI / test (3.11) (push) Failing after 15s
CI / test (3.12) (push) Successful in 30s
CI / test (3.10) (pull_request) Successful in 30s
CI / test (3.11) (pull_request) Successful in 28s
CI / test (3.12) (pull_request) Successful in 29s
carp_virtual_ip() and its state table moved here from napalm-device-types (4.10.1, NAPALM/napalm-device-types#39): under the agreed structure the base package carries only the shape of the reading, and what a CARP state means is protocol knowledge. Tests moved along (test_carp_states.py).
112 lines
3.3 KiB
Python
112 lines
3.3 KiB
Python
"""carp_virtual_ip: one CARP address as a virtual IP, with its holder in the reading.
|
|
|
|
CARP differs from vip-manager and keepalived in what the caller can see. The
|
|
address is configured on every member, and the interface list of a firewall need
|
|
not show it at all -- so the interfaces cannot say who holds it. CARP's own state
|
|
can: the member in MASTER holds it, a BACKUP stands by. That is CARP protocol
|
|
knowledge, so it lives with the driver that speaks CARP (netOrk #1121), not in
|
|
napalm-device-types.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from napalm_opnsense.virtual_ips import carp_virtual_ip
|
|
|
|
|
|
def _entry(**overrides):
|
|
fields = {
|
|
"address": "172.22.8.1",
|
|
"prefix_length": 24,
|
|
"vhid": "108",
|
|
"state": "MASTER",
|
|
"instance": "MGMT",
|
|
"interface": "vtnet0",
|
|
}
|
|
fields.update(overrides)
|
|
return carp_virtual_ip(**fields)
|
|
|
|
|
|
def test_a_master_holds_the_address():
|
|
assert _entry() == {
|
|
"mechanism": "carp",
|
|
"instance": "MGMT",
|
|
"address": "172.22.8.1",
|
|
"prefix_length": 24,
|
|
"interface": "vtnet0",
|
|
"group_key": "vhid:108",
|
|
"running": True,
|
|
"holds": True,
|
|
}
|
|
|
|
|
|
def test_a_backup_stands_by():
|
|
entry = _entry(state="BACKUP")
|
|
assert entry is not None
|
|
assert (entry["running"], entry["holds"]) == (True, False)
|
|
|
|
|
|
@pytest.mark.parametrize("state", ["INIT", "DISABLED"])
|
|
def test_a_member_whose_carp_is_not_up_neither_runs_nor_holds(state):
|
|
entry = _entry(state=state)
|
|
assert entry is not None
|
|
assert (entry["running"], entry["holds"]) == (False, False)
|
|
|
|
|
|
@pytest.mark.parametrize("state", [None, "", "SOMETHING-NEW"])
|
|
def test_an_unknown_state_says_nothing_about_the_holder(state):
|
|
"""None leaves the holder to the interfaces, as for the other mechanisms."""
|
|
entry = _entry(state=state)
|
|
assert entry is not None
|
|
assert (entry["running"], entry["holds"]) == (None, None)
|
|
|
|
|
|
def test_the_state_is_read_case_blind():
|
|
entry = _entry(state=" master ")
|
|
assert entry is not None
|
|
assert entry["holds"] is True
|
|
|
|
|
|
def test_the_address_is_normalised():
|
|
entry = _entry(address="FD00::0001", prefix_length=64)
|
|
assert entry is not None
|
|
assert entry["address"] == "fd00::1"
|
|
|
|
|
|
def test_an_address_written_with_its_prefix_takes_that_prefix():
|
|
entry = _entry(address="10.42.100.1/24", prefix_length=None)
|
|
assert entry is not None
|
|
assert (entry["address"], entry["prefix_length"]) == ("10.42.100.1", 24)
|
|
|
|
|
|
@pytest.mark.parametrize("address", ["", "not-an-address", "10.0.0.300"])
|
|
def test_what_is_no_address_is_no_virtual_ip(address):
|
|
assert _entry(address=address) is None
|
|
|
|
|
|
@pytest.mark.parametrize("prefix", [33, -1])
|
|
def test_an_impossible_prefix_is_dropped(prefix):
|
|
entry = _entry(prefix_length=prefix)
|
|
assert entry is not None
|
|
assert entry["prefix_length"] is None
|
|
|
|
|
|
@pytest.mark.parametrize("vhid", [None, "", "abc"])
|
|
def test_without_a_numeric_vhid_there_is_no_group(vhid):
|
|
entry = _entry(vhid=vhid)
|
|
assert entry is not None
|
|
assert entry["group_key"] is None
|
|
|
|
|
|
def test_a_vhid_given_as_a_number_groups_the_same_way():
|
|
entry = _entry(vhid=108)
|
|
assert entry is not None
|
|
assert entry["group_key"] == "vhid:108"
|
|
|
|
|
|
def test_an_empty_instance_falls_back_to_the_vhid():
|
|
entry = _entry(instance="")
|
|
assert entry is not None
|
|
assert entry["instance"] == "vhid 108"
|