# py312
# requirements:
# pynetbox==7.9.0
from typing import List, Literal, Optional, TypedDict
import pynetbox
class netbox(TypedDict):
url: str
token: str
DynSelect_site = str
DynSelect_role = str
def api(auth: netbox):
# pynetbox adds /api itself; the resource URL may already end with it.
return pynetbox.api(
auth["url"].rstrip("/").removesuffix("/api"), token=auth["token"]
)
def keep(**fields):
# The run form sends "" / [] / {} for untouched fields: drop them.
return {k: v for k, v in fields.items() if v not in (None, "", [], {})}
def options(endpoint, key="id", **filters):
return [
{"value": str(getattr(r, key)), "label": r.display}
for r in endpoint.filter(brief=1, limit=1000, offset=0, **filters)
]
# Dropdown of the instance's sites, by slug: the first 1000 (NetBox MAX_PAGE_SIZE); set the value by expression beyond that.
def site(auth: netbox):
return options(api(auth).dcim.sites, "slug")
# Dropdown of the instance's device roles, by slug.
def role(auth: netbox):
return options(api(auth).dcim.device_roles, "slug")
DynMultiselect_device_ids = List[str]
# Candidate devices for the chosen site, role and status: the first 1000; use all_matching beyond that.
def device_ids(
auth: netbox,
site: DynSelect_site,
role: DynSelect_role,
status: Optional[List[str]] = None,
):
if not site or not role:
return []
return options(api(auth).dcim.devices, **keep(site=site, role=role, status=status))
def main(
auth: netbox,
site: DynSelect_site,
role: DynSelect_role,
status: Optional[
List[
Literal[
"offline",
"active",
"planned",
"staged",
"failed",
"inventory",
"decommissioning",
]
]
] = None,
device_ids: Optional[DynMultiselect_device_ids] = None,
all_matching: bool = False,
):
"""
Select Devices
Pick devices of a site and role (optionally by status) from a live list, or take all of them, and return their raw NetBox JSON, including platform.name and custom_fields.
"""
if not all_matching and not device_ids:
return []
# pynetbox sends a None filter as "null", so drop the empty ones.
filters = keep(
site=site, role=role, status=status, id=None if all_matching else device_ids
)
# Page by offset: pynetbox's full iteration requests NetBox's next links
# as is, and behind a proxy their host or scheme can be internal.
endpoint, devices = api(auth).dcim.devices, []
while True:
records = endpoint.filter(**filters, limit=1000, offset=len(devices))
batch = [dict(r) for r in records]
devices += batch
if not batch or len(devices) >= len(records):
return devices
Submitted by hugo989 3 hours ago