You've already forked ansible-netbox-inventory
test: ipv6 end-to-end scenarios
vm01 gains a primary IPv6 next to its IPv4, one standalone IPv6 address joins the dataset; covers ip_version on both sources, combination with filters, the IPv6-preferring behaviour of netbox's combined primary IP, and the invalid value error path.
This commit is contained in:
@@ -59,6 +59,7 @@ def run_inventory(stack):
|
|||||||
source: str = "",
|
source: str = "",
|
||||||
filter_name: str = "",
|
filter_name: str = "",
|
||||||
filter_value: str = "",
|
filter_value: str = "",
|
||||||
|
ip_version: str = "",
|
||||||
args: tuple[str, ...] = ("--list",),
|
args: tuple[str, ...] = ("--list",),
|
||||||
token: str | None = None,
|
token: str | None = None,
|
||||||
):
|
):
|
||||||
@@ -71,6 +72,7 @@ def run_inventory(stack):
|
|||||||
"NETBOX_INVENTORY_SOURCE": source or "ip-addresses",
|
"NETBOX_INVENTORY_SOURCE": source or "ip-addresses",
|
||||||
"NETBOX_INVENTORY_FILTER": filter_name,
|
"NETBOX_INVENTORY_FILTER": filter_name,
|
||||||
"NETBOX_INVENTORY_FILTER_VALUE": filter_value,
|
"NETBOX_INVENTORY_FILTER_VALUE": filter_value,
|
||||||
|
"NETBOX_INVENTORY_IP_VERSION": ip_version or "both",
|
||||||
}
|
}
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
[SCRIPT, *args], capture_output=True, text=True, timeout=120, env=env
|
[SCRIPT, *args], capture_output=True, text=True, timeout=120, env=env
|
||||||
|
|||||||
21
e2e/seed.py
21
e2e/seed.py
@@ -192,13 +192,13 @@ def seed(url: str, admin_token: str) -> dict[str, str]:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# give vm01 a primary IP: interface -> assigned address -> primary_ip4
|
# give vm01 primary IPs: interface -> assigned addresses -> primary_ip4/6
|
||||||
eth0 = api.ensure(
|
eth0 = api.ensure(
|
||||||
"virtualization/interfaces",
|
"virtualization/interfaces",
|
||||||
{"virtual_machine_id": vm01["id"], "name": "eth0"},
|
{"virtual_machine_id": vm01["id"], "name": "eth0"},
|
||||||
{"virtual_machine": vm01["id"], "name": "eth0"},
|
{"virtual_machine": vm01["id"], "name": "eth0"},
|
||||||
)
|
)
|
||||||
vm01_ip = api.ensure(
|
vm01_ip4 = api.ensure(
|
||||||
"ipam/ip-addresses",
|
"ipam/ip-addresses",
|
||||||
{"address": "192.0.2.20/24"},
|
{"address": "192.0.2.20/24"},
|
||||||
{
|
{
|
||||||
@@ -208,12 +208,27 @@ def seed(url: str, admin_token: str) -> dict[str, str]:
|
|||||||
"assigned_object_id": eth0["id"],
|
"assigned_object_id": eth0["id"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
api.patch("virtualization/virtual-machines", vm01["id"], {"primary_ip4": vm01_ip["id"]})
|
vm01_ip6 = api.ensure(
|
||||||
|
"ipam/ip-addresses",
|
||||||
|
{"address": "2001:db8::20/64"},
|
||||||
|
{
|
||||||
|
"address": "2001:db8::20/64",
|
||||||
|
"dns_name": "vm01-v6.example.com",
|
||||||
|
"assigned_object_type": "virtualization.vminterface",
|
||||||
|
"assigned_object_id": eth0["id"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
api.patch(
|
||||||
|
"virtualization/virtual-machines",
|
||||||
|
vm01["id"],
|
||||||
|
{"primary_ip4": vm01_ip4["id"], "primary_ip6": vm01_ip6["id"]},
|
||||||
|
)
|
||||||
|
|
||||||
standalone_addresses = [
|
standalone_addresses = [
|
||||||
{"address": "192.0.2.10/24", "dns_name": "web.example.com", "tenant": customer1["id"]},
|
{"address": "192.0.2.10/24", "dns_name": "web.example.com", "tenant": customer1["id"]},
|
||||||
{"address": "192.0.2.11/24", "vrf": internal["id"]},
|
{"address": "192.0.2.11/24", "vrf": internal["id"]},
|
||||||
{"address": "192.0.2.12/24", "dns_name": "web.example.com"},
|
{"address": "192.0.2.12/24", "dns_name": "web.example.com"},
|
||||||
|
{"address": "2001:db8::10/64", "dns_name": "v6.example.com"},
|
||||||
{
|
{
|
||||||
"address": "192.0.2.13/24",
|
"address": "192.0.2.13/24",
|
||||||
"dns_name": "db.other.org",
|
"dns_name": "db.other.org",
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ End-to-end tests: every source/filter scenario against a live NetBox.
|
|||||||
|
|
||||||
The dataset these expectations rest on is created by seed.py:
|
The dataset these expectations rest on is created by seed.py:
|
||||||
tenants customer1/customer2, sites milan/turin, clusters cluster1 (milan) and
|
tenants customer1/customer2, sites milan/turin, clusters cluster1 (milan) and
|
||||||
cluster2 (turin), VRF internal, VMs vm01 (cluster1, customer1, primary IP
|
cluster2 (turin), VRF internal, VMs vm01 (cluster1, customer1, primary IPs
|
||||||
192.0.2.20 named vm01.example.com), vm02 (cluster1, customer2, no primary IP)
|
192.0.2.20 vm01.example.com and 2001:db8::20 vm01-v6.example.com), vm02
|
||||||
and vm03.example.com (cluster2), plus standalone addresses 192.0.2.10
|
(cluster1, customer2, no primary IP) and vm03.example.com (cluster2), plus
|
||||||
(web.example.com, customer1), 192.0.2.11 (unnamed, internal), 192.0.2.12
|
standalone addresses 192.0.2.10 (web.example.com, customer1), 192.0.2.11
|
||||||
(web.example.com duplicate) and 192.0.2.13 (db.other.org, customer2, internal).
|
(unnamed, internal), 192.0.2.12 (web.example.com duplicate), 2001:db8::10
|
||||||
|
(v6.example.com) and 192.0.2.13 (db.other.org, customer2, internal).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -32,6 +33,8 @@ class TestIpAddressSource:
|
|||||||
"192.0.2.12",
|
"192.0.2.12",
|
||||||
"db.other.org",
|
"db.other.org",
|
||||||
"vm01.example.com",
|
"vm01.example.com",
|
||||||
|
"v6.example.com",
|
||||||
|
"vm01-v6.example.com",
|
||||||
}
|
}
|
||||||
web = hostvars(result)["web.example.com"]
|
web = hostvars(result)["web.example.com"]
|
||||||
assert web["ansible_host"] == "192.0.2.10"
|
assert web["ansible_host"] == "192.0.2.10"
|
||||||
@@ -48,7 +51,13 @@ class TestIpAddressSource:
|
|||||||
def test_named_filter_deduplicates(self, run_inventory):
|
def test_named_filter_deduplicates(self, run_inventory):
|
||||||
result = run_inventory(source="ip-addresses", filter_name="named")
|
result = run_inventory(source="ip-addresses", filter_name="named")
|
||||||
assert result.returncode == 0, result.stderr
|
assert result.returncode == 0, result.stderr
|
||||||
assert hosts(result) == {"web.example.com", "db.other.org", "vm01.example.com"}
|
assert hosts(result) == {
|
||||||
|
"web.example.com",
|
||||||
|
"db.other.org",
|
||||||
|
"vm01.example.com",
|
||||||
|
"v6.example.com",
|
||||||
|
"vm01-v6.example.com",
|
||||||
|
}
|
||||||
|
|
||||||
def test_named_false_keeps_unnamed(self, run_inventory):
|
def test_named_false_keeps_unnamed(self, run_inventory):
|
||||||
result = run_inventory(source="ip-addresses", filter_name="named", filter_value="false")
|
result = run_inventory(source="ip-addresses", filter_name="named", filter_value="false")
|
||||||
@@ -65,7 +74,13 @@ class TestIpAddressSource:
|
|||||||
source="ip-addresses", filter_name="domain", filter_value="example.com"
|
source="ip-addresses", filter_name="domain", filter_value="example.com"
|
||||||
)
|
)
|
||||||
assert result.returncode == 0, result.stderr
|
assert result.returncode == 0, result.stderr
|
||||||
assert hosts(result) == {"web.example.com", "192.0.2.12", "vm01.example.com"}
|
assert hosts(result) == {
|
||||||
|
"web.example.com",
|
||||||
|
"192.0.2.12",
|
||||||
|
"vm01.example.com",
|
||||||
|
"v6.example.com",
|
||||||
|
"vm01-v6.example.com",
|
||||||
|
}
|
||||||
|
|
||||||
def test_domain_filter_other_domain(self, run_inventory):
|
def test_domain_filter_other_domain(self, run_inventory):
|
||||||
result = run_inventory(
|
result = run_inventory(
|
||||||
@@ -74,6 +89,33 @@ class TestIpAddressSource:
|
|||||||
assert result.returncode == 0, result.stderr
|
assert result.returncode == 0, result.stderr
|
||||||
assert hosts(result) == {"db.other.org"}
|
assert hosts(result) == {"db.other.org"}
|
||||||
|
|
||||||
|
def test_ip_version_v4(self, run_inventory):
|
||||||
|
result = run_inventory(source="ip-addresses", ip_version="v4")
|
||||||
|
assert result.returncode == 0, result.stderr
|
||||||
|
assert hosts(result) == {
|
||||||
|
"web.example.com",
|
||||||
|
"192.0.2.11",
|
||||||
|
"192.0.2.12",
|
||||||
|
"db.other.org",
|
||||||
|
"vm01.example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_ip_version_v6(self, run_inventory):
|
||||||
|
result = run_inventory(source="ip-addresses", ip_version="v6")
|
||||||
|
assert result.returncode == 0, result.stderr
|
||||||
|
assert hosts(result) == {"v6.example.com", "vm01-v6.example.com"}
|
||||||
|
assert hostvars(result)["v6.example.com"]["ansible_host"] == "2001:db8::10"
|
||||||
|
|
||||||
|
def test_ip_version_combines_with_filters(self, run_inventory):
|
||||||
|
result = run_inventory(
|
||||||
|
source="ip-addresses",
|
||||||
|
filter_name="domain",
|
||||||
|
filter_value="example.com",
|
||||||
|
ip_version="v4",
|
||||||
|
)
|
||||||
|
assert result.returncode == 0, result.stderr
|
||||||
|
assert hosts(result) == {"web.example.com", "192.0.2.12", "vm01.example.com"}
|
||||||
|
|
||||||
|
|
||||||
class TestVirtualMachineSource:
|
class TestVirtualMachineSource:
|
||||||
def test_all_virtual_machines(self, run_inventory):
|
def test_all_virtual_machines(self, run_inventory):
|
||||||
@@ -81,7 +123,8 @@ class TestVirtualMachineSource:
|
|||||||
assert result.returncode == 0, result.stderr
|
assert result.returncode == 0, result.stderr
|
||||||
assert hosts(result) == {"vm01", "vm02", "vm03.example.com"}
|
assert hosts(result) == {"vm01", "vm02", "vm03.example.com"}
|
||||||
vm01 = hostvars(result)["vm01"]
|
vm01 = hostvars(result)["vm01"]
|
||||||
assert vm01["ansible_host"] == "192.0.2.20"
|
# with both families set, NetBox's primary_ip prefers IPv6 by default
|
||||||
|
assert vm01["ansible_host"] == "2001:db8::20"
|
||||||
assert vm01["netbox_cluster"] == "cluster1"
|
assert vm01["netbox_cluster"] == "cluster1"
|
||||||
assert vm01["netbox_site"] == "milan"
|
assert vm01["netbox_site"] == "milan"
|
||||||
assert vm01["netbox_tenant"] == "customer1"
|
assert vm01["netbox_tenant"] == "customer1"
|
||||||
@@ -115,6 +158,20 @@ class TestVirtualMachineSource:
|
|||||||
assert result.returncode == 0, result.stderr
|
assert result.returncode == 0, result.stderr
|
||||||
assert hosts(result) == {"vm03.example.com"}
|
assert hosts(result) == {"vm03.example.com"}
|
||||||
|
|
||||||
|
def test_ip_version_v4_uses_primary_ip4(self, run_inventory):
|
||||||
|
result = run_inventory(source="virtual-machines", ip_version="v4")
|
||||||
|
assert result.returncode == 0, result.stderr
|
||||||
|
assert hosts(result) == {"vm01", "vm02", "vm03.example.com"}
|
||||||
|
assert hostvars(result)["vm01"]["ansible_host"] == "192.0.2.20"
|
||||||
|
|
||||||
|
def test_ip_version_v6_uses_primary_ip6(self, run_inventory):
|
||||||
|
result = run_inventory(source="virtual-machines", ip_version="v6")
|
||||||
|
assert result.returncode == 0, result.stderr
|
||||||
|
assert hostvars(result)["vm01"]["ansible_host"] == "2001:db8::20"
|
||||||
|
# VMs without a primary IP of that family keep no ansible_host
|
||||||
|
assert "ansible_host" not in hostvars(result)["vm02"]
|
||||||
|
assert "ansible_host" not in hostvars(result)["vm03.example.com"]
|
||||||
|
|
||||||
|
|
||||||
class TestErrorScenarios:
|
class TestErrorScenarios:
|
||||||
def test_unknown_tenant_name(self, run_inventory):
|
def test_unknown_tenant_name(self, run_inventory):
|
||||||
@@ -144,6 +201,11 @@ class TestErrorScenarios:
|
|||||||
assert result.returncode == 1
|
assert result.returncode == 1
|
||||||
assert "requires a value" in result.stderr
|
assert "requires a value" in result.stderr
|
||||||
|
|
||||||
|
def test_invalid_ip_version(self, run_inventory):
|
||||||
|
result = run_inventory(source="ip-addresses", ip_version="ipv4")
|
||||||
|
assert result.returncode == 1
|
||||||
|
assert "Unknown ip_version" in result.stderr
|
||||||
|
|
||||||
def test_invalid_token(self, run_inventory):
|
def test_invalid_token(self, run_inventory):
|
||||||
result = run_inventory(source="ip-addresses", token="wrong-token")
|
result = run_inventory(source="ip-addresses", token="wrong-token")
|
||||||
assert result.returncode == 1
|
assert result.returncode == 1
|
||||||
|
|||||||
Reference in New Issue
Block a user