Skip to content

Instantly share code, notes, and snippets.

@candlerb
Last active April 2, 2024 00:07
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save candlerb/5380a7cdd03b60fbd02a664feb266d44 to your computer and use it in GitHub Desktop.
Save candlerb/5380a7cdd03b60fbd02a664feb266d44 to your computer and use it in GitHub Desktop.
Netbox report to check for missing Primary IP Addresses
from extras.reports import Report
from dcim.models import Device
from virtualization.models import VirtualMachine
from ipam.constants import *
from ipam.models import IPAddress, Prefix
LOOPBACK_ROLES = [
IPADDRESS_ROLE_LOOPBACK,
IPADDRESS_ROLE_ANYCAST,
IPADDRESS_ROLE_VIP,
]
class CheckPrimaryAddress(Report):
description = "Check that every device and VM has a primary IP address assigned"
def test_device_primary_ips(self):
for device in Device.objects.prefetch_related('interfaces__ip_addresses').all():
fail = False
all_addrs = {4: [], 6: []}
for interface in device.interfaces.all():
if not interface.mgmt_only:
for addr in interface.ip_addresses.exclude(status=IPADDRESS_STATUS_DEPRECATED).all():
all_addrs[addr.family].append(addr)
# There may be dumb devices with no interfaces / IP addresses, that's OK
if not device.primary_ip4 and all_addrs[4]:
self.log_failure(device, "Device has no primary IPv4 address (could be %s)" %
" ".join([str(a) for a in all_addrs[4]]))
fail = True
if not device.primary_ip6 and all_addrs[6]:
self.log_failure(device, "Device has no primary IPv6 address (could be %s)" %
" ".join([str(a) for a in all_addrs[6]]))
fail = True
if not fail:
self.log_success(device)
def test_vm_primary_ips(self):
for vm in VirtualMachine.objects.prefetch_related('interfaces__ip_addresses').all():
fail = False
all_addrs = {4: [], 6: []}
for interface in vm.interfaces.all():
if not interface.mgmt_only:
for addr in interface.ip_addresses.exclude(status=IPADDRESS_STATUS_DEPRECATED).all():
all_addrs[addr.family].append(addr)
# A VM is useless without an IP address
if not all_addrs[4] and not all_addrs[6]:
self.log_failure(vm, "Virtual machine has no IP addresses")
continue
if not vm.primary_ip4 and all_addrs[4]:
self.log_failure(vm, "Virtual machine has no primary IPv4 address (could be %s)" %
" ".join([str(a) for a in all_addrs[4]]))
fail = True
if not vm.primary_ip6 and all_addrs[6]:
self.log_failure(vm, "Virtual machine has no primary IPv6 address (could be %s)" %
" ".join([str(a) for a in all_addrs[6]]))
fail = True
if not fail:
self.log_success(vm)
class CheckPrefixLength(Report):
description = "Check each IP address has the prefix length of the enclosing subnet"
def test_prefix_lengths(self):
prefixes = list(Prefix.objects.all())
prefixes.sort(key=lambda k: k.prefix) # overlapping subnets sort in order from largest to smallest
for ipaddr in IPAddress.objects.all():
a = ipaddr.address
if ipaddr.family != a.version:
self.log_failure(ipaddr, "family (%d) inconsistent with address.version (%d)" %
(ipaddr.family, a.version))
continue
# We allow loopback-like things to be single address *or* have the parent prefix length
if ipaddr.role in LOOPBACK_ROLES and (
(a.version == 4 and a.prefixlen == 32) or
(a.version == 6 and a.prefixlen == 128)):
self.log_success(ipaddr)
continue
parents = [p for p in prefixes if
(p.vrf and p.vrf.id) == (ipaddr.vrf and ipaddr.vrf.id) and
p.prefix.version == a.version and a.ip in p.prefix]
if not parents:
self.log_info(ipaddr, "No parent prefix")
continue
parent = parents[-1]
if a.prefixlen != parent.prefix.prefixlen:
self.log_failure(ipaddr, "prefixlen (%d) inconsistent with parent prefix (%s)" %
(a.prefixlen, str(parent.prefix)))
continue
# if the parent prefix also contains child prefixes, that probably means that
# an intermediate parent prefix is missing
pchildren = [p for p in prefixes if
(p.vrf and p.vrf.id) == (parent.vrf and parent.vrf.id) and
p.prefix.version == parent.prefix.version and
p.prefix != parent.prefix and
p.prefix in parent.prefix]
if pchildren:
self.log_warning(ipaddr, "parent prefix (%s) contains %d other child prefix(es)" %
(str(parent.prefix), len(pchildren)))
continue
self.log_success(ipaddr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment