fix(scripts): enhance get_or_create logic and parameter handling
- Replace `endpoint.get()` with `endpoint.filter()` in `get_or_create` to handle object retrieval more robustly and avoid potential exceptions with multiple results. - Decouple `search_params` from `create_params` to correctly handle differences between API filtering keys (e.g., `group_id`, `vrf_id`) and creation keys (e.g., `group`, `vrf`). - Refine IP address lookups to include `assigned_object_id` in search parameters, preventing ambiguous matches against IPs not assigned to the target interface.
This commit is contained in:
@@ -399,11 +399,12 @@ PREFIXES = [
|
|||||||
|
|
||||||
def get_or_create(endpoint, search_params: dict, create_params: dict) -> Any:
|
def get_or_create(endpoint, search_params: dict, create_params: dict) -> Any:
|
||||||
"""Get existing object or create new one."""
|
"""Get existing object or create new one."""
|
||||||
obj = endpoint.get(**search_params)
|
# Use filter() instead of get() to handle multiple results gracefully
|
||||||
if obj:
|
results = list(endpoint.filter(**search_params))
|
||||||
return obj, False
|
if results:
|
||||||
|
return results[0], False
|
||||||
|
|
||||||
obj = endpoint.create({**search_params, **create_params})
|
obj = endpoint.create(create_params)
|
||||||
return obj, True
|
return obj, True
|
||||||
|
|
||||||
|
|
||||||
@@ -501,6 +502,10 @@ def create_vlans(nb: pynetbox.api, site) -> dict:
|
|||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
# VLAN Group
|
# VLAN Group
|
||||||
|
# For NetBox 4.x, scope_type/scope_id are deprecated in favor of scope_object
|
||||||
|
# but for simple site scope we can just pass scope_type and scope_id or
|
||||||
|
# rely on backward compatibility if it exists. Ideally we check NetBox version
|
||||||
|
# but here we'll try to be compatible.
|
||||||
vlan_group, created = get_or_create(
|
vlan_group, created = get_or_create(
|
||||||
nb.ipam.vlan_groups,
|
nb.ipam.vlan_groups,
|
||||||
{"slug": VLAN_GROUP_NAME},
|
{"slug": VLAN_GROUP_NAME},
|
||||||
@@ -515,7 +520,11 @@ def create_vlans(nb: pynetbox.api, site) -> dict:
|
|||||||
vlan_obj, created = get_or_create(
|
vlan_obj, created = get_or_create(
|
||||||
nb.ipam.vlans,
|
nb.ipam.vlans,
|
||||||
{"vid": vlan["vid"], "group_id": vlan_group.id},
|
{"vid": vlan["vid"], "group_id": vlan_group.id},
|
||||||
{"name": vlan["name"], "description": vlan.get("description", "")},
|
{
|
||||||
|
"name": vlan["name"],
|
||||||
|
"description": vlan.get("description", ""),
|
||||||
|
"group": vlan_group.id, # Create expects 'group', filter expects 'group_id'
|
||||||
|
},
|
||||||
)
|
)
|
||||||
log_result("VLAN", "VLAN", f"{vlan['vid']} ({vlan['name']})", created)
|
log_result("VLAN", "VLAN", f"{vlan['vid']} ({vlan['name']})", created)
|
||||||
result["vlans"][vlan["vid"]] = vlan_obj
|
result["vlans"][vlan["vid"]] = vlan_obj
|
||||||
@@ -578,10 +587,14 @@ def create_prefixes(nb: pynetbox.api, vrfs: dict):
|
|||||||
if vrf_id:
|
if vrf_id:
|
||||||
vrf_id = vrf_id.id
|
vrf_id = vrf_id.id
|
||||||
|
|
||||||
|
create_params = {"prefix": prefix_def["prefix"], "description": prefix_def.get("description", "")}
|
||||||
|
if vrf_id:
|
||||||
|
create_params["vrf"] = vrf_id
|
||||||
|
|
||||||
prefix, created = get_or_create(
|
prefix, created = get_or_create(
|
||||||
nb.ipam.prefixes,
|
nb.ipam.prefixes,
|
||||||
{"prefix": prefix_def["prefix"], "vrf_id": vrf_id},
|
{"prefix": prefix_def["prefix"], "vrf_id": vrf_id},
|
||||||
{"description": prefix_def.get("description", "")},
|
create_params,
|
||||||
)
|
)
|
||||||
log_result("Prefix", "Prefix", prefix_def["prefix"], created)
|
log_result("Prefix", "Prefix", prefix_def["prefix"], created)
|
||||||
|
|
||||||
@@ -736,8 +749,12 @@ def create_ip_addresses(nb: pynetbox.api, devices: dict, interfaces: dict, vrfs:
|
|||||||
|
|
||||||
ip, created = get_or_create(
|
ip, created = get_or_create(
|
||||||
nb.ipam.ip_addresses,
|
nb.ipam.ip_addresses,
|
||||||
{"address": spine["loopback0"]},
|
|
||||||
{
|
{
|
||||||
|
"address": spine["loopback0"],
|
||||||
|
"assigned_object_id": intf.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": spine["loopback0"],
|
||||||
"assigned_object_type": "dcim.interface",
|
"assigned_object_type": "dcim.interface",
|
||||||
"assigned_object_id": intf.id,
|
"assigned_object_id": intf.id,
|
||||||
"description": f"{spine['name']} Router-ID",
|
"description": f"{spine['name']} Router-ID",
|
||||||
@@ -753,8 +770,12 @@ def create_ip_addresses(nb: pynetbox.api, devices: dict, interfaces: dict, vrfs:
|
|||||||
intf = interfaces[leaf["name"]]["Loopback0"]
|
intf = interfaces[leaf["name"]]["Loopback0"]
|
||||||
ip, created = get_or_create(
|
ip, created = get_or_create(
|
||||||
nb.ipam.ip_addresses,
|
nb.ipam.ip_addresses,
|
||||||
{"address": leaf["loopback0"]},
|
|
||||||
{
|
{
|
||||||
|
"address": leaf["loopback0"],
|
||||||
|
"assigned_object_id": intf.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": leaf["loopback0"],
|
||||||
"assigned_object_type": "dcim.interface",
|
"assigned_object_type": "dcim.interface",
|
||||||
"assigned_object_id": intf.id,
|
"assigned_object_id": intf.id,
|
||||||
"description": f"{leaf['name']} Router-ID",
|
"description": f"{leaf['name']} Router-ID",
|
||||||
@@ -766,8 +787,12 @@ def create_ip_addresses(nb: pynetbox.api, devices: dict, interfaces: dict, vrfs:
|
|||||||
intf = interfaces[leaf["name"]]["Loopback1"]
|
intf = interfaces[leaf["name"]]["Loopback1"]
|
||||||
ip, created = get_or_create(
|
ip, created = get_or_create(
|
||||||
nb.ipam.ip_addresses,
|
nb.ipam.ip_addresses,
|
||||||
{"address": leaf["loopback1"]},
|
|
||||||
{
|
{
|
||||||
|
"address": leaf["loopback1"],
|
||||||
|
"assigned_object_id": intf.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": leaf["loopback1"],
|
||||||
"assigned_object_type": "dcim.interface",
|
"assigned_object_type": "dcim.interface",
|
||||||
"assigned_object_id": intf.id,
|
"assigned_object_id": intf.id,
|
||||||
"description": f"{leaf['name']} VTEP",
|
"description": f"{leaf['name']} VTEP",
|
||||||
@@ -781,8 +806,12 @@ def create_ip_addresses(nb: pynetbox.api, devices: dict, interfaces: dict, vrfs:
|
|||||||
spine_intf = interfaces["spine1"][f"Ethernet{list(SPINE1_P2P.keys()).index(leaf_name) + 1}"]
|
spine_intf = interfaces["spine1"][f"Ethernet{list(SPINE1_P2P.keys()).index(leaf_name) + 1}"]
|
||||||
ip, created = get_or_create(
|
ip, created = get_or_create(
|
||||||
nb.ipam.ip_addresses,
|
nb.ipam.ip_addresses,
|
||||||
{"address": spine_ip},
|
|
||||||
{
|
{
|
||||||
|
"address": spine_ip,
|
||||||
|
"assigned_object_id": spine_intf.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": spine_ip,
|
||||||
"assigned_object_type": "dcim.interface",
|
"assigned_object_type": "dcim.interface",
|
||||||
"assigned_object_id": spine_intf.id,
|
"assigned_object_id": spine_intf.id,
|
||||||
"description": f"spine1 to {leaf_name}",
|
"description": f"spine1 to {leaf_name}",
|
||||||
@@ -794,8 +823,12 @@ def create_ip_addresses(nb: pynetbox.api, devices: dict, interfaces: dict, vrfs:
|
|||||||
leaf_intf = interfaces[leaf_name]["Ethernet11"]
|
leaf_intf = interfaces[leaf_name]["Ethernet11"]
|
||||||
ip, created = get_or_create(
|
ip, created = get_or_create(
|
||||||
nb.ipam.ip_addresses,
|
nb.ipam.ip_addresses,
|
||||||
{"address": leaf_ip},
|
|
||||||
{
|
{
|
||||||
|
"address": leaf_ip,
|
||||||
|
"assigned_object_id": leaf_intf.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": leaf_ip,
|
||||||
"assigned_object_type": "dcim.interface",
|
"assigned_object_type": "dcim.interface",
|
||||||
"assigned_object_id": leaf_intf.id,
|
"assigned_object_id": leaf_intf.id,
|
||||||
"description": f"{leaf_name} to spine1",
|
"description": f"{leaf_name} to spine1",
|
||||||
@@ -808,8 +841,12 @@ def create_ip_addresses(nb: pynetbox.api, devices: dict, interfaces: dict, vrfs:
|
|||||||
spine_intf = interfaces["spine2"][f"Ethernet{list(SPINE2_P2P.keys()).index(leaf_name) + 1}"]
|
spine_intf = interfaces["spine2"][f"Ethernet{list(SPINE2_P2P.keys()).index(leaf_name) + 1}"]
|
||||||
ip, created = get_or_create(
|
ip, created = get_or_create(
|
||||||
nb.ipam.ip_addresses,
|
nb.ipam.ip_addresses,
|
||||||
{"address": spine_ip},
|
|
||||||
{
|
{
|
||||||
|
"address": spine_ip,
|
||||||
|
"assigned_object_id": spine_intf.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": spine_ip,
|
||||||
"assigned_object_type": "dcim.interface",
|
"assigned_object_type": "dcim.interface",
|
||||||
"assigned_object_id": spine_intf.id,
|
"assigned_object_id": spine_intf.id,
|
||||||
"description": f"spine2 to {leaf_name}",
|
"description": f"spine2 to {leaf_name}",
|
||||||
@@ -820,8 +857,12 @@ def create_ip_addresses(nb: pynetbox.api, devices: dict, interfaces: dict, vrfs:
|
|||||||
leaf_intf = interfaces[leaf_name]["Ethernet12"]
|
leaf_intf = interfaces[leaf_name]["Ethernet12"]
|
||||||
ip, created = get_or_create(
|
ip, created = get_or_create(
|
||||||
nb.ipam.ip_addresses,
|
nb.ipam.ip_addresses,
|
||||||
{"address": leaf_ip},
|
|
||||||
{
|
{
|
||||||
|
"address": leaf_ip,
|
||||||
|
"assigned_object_id": leaf_intf.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": leaf_ip,
|
||||||
"assigned_object_type": "dcim.interface",
|
"assigned_object_type": "dcim.interface",
|
||||||
"assigned_object_id": leaf_intf.id,
|
"assigned_object_id": leaf_intf.id,
|
||||||
"description": f"{leaf_name} to spine2",
|
"description": f"{leaf_name} to spine2",
|
||||||
|
|||||||
Reference in New Issue
Block a user