Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions cosmo/autodesc.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,20 @@ def toDict(self) -> CosmoOutputType | Never:
)
| ( # directly attached line has priority
{"line": attached_tobago_line.getName()}
if attached_tobago_line and self.suppressTenant()
# two different cases: either we have to suppress tenant
# OR tenant is not defined due to no linked service definition
if (attached_tobago_line and self.suppressTenant())
or (attached_tobago_line and not attached_tobago_line.hasTenant())
else {}
)
| (
{
"line": attached_tobago_line.getName(),
"tenant": attached_tobago_line.getTenantName(),
}
if attached_tobago_line and not self.suppressTenant()
if attached_tobago_line
and attached_tobago_line.hasTenant()
and not self.suppressTenant()
else {}
)
| (
Expand Down
32 changes: 19 additions & 13 deletions cosmo/netbox_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,29 +892,35 @@ class CosmoTobagoLine(AbstractNetboxType):
def getBasePath(self):
return "/plugins/tobago/lines/"

def _getCurrentLineMetadata(self):
return self["version"]
def _getCurrentLineMetadata(self) -> dict:
return self.get("version", dict()) # should always be present

def _getCurrentLine(self):
return self._getCurrentLineMetadata()["line"]
def _getCurrentLine(self) -> dict:
return self._getCurrentLineMetadata().get(
"line", dict()
) # should always be present

def _getCurrentTenant(self):
return self._getCurrentLineMetadata()["tenant"]
def _getCurrentTenant(self) -> dict | None:
return self._getCurrentLineMetadata().get("tenant")

def hasTenant(self) -> bool:
return bool(self._getCurrentTenant()) # False on None or empty

def getLineID(self) -> str:
return str(self._getCurrentLine()["id"])
return str(self._getCurrentLine().get("id"))

def getLineNameLong(self) -> str:
return str(self._getCurrentLine()["name_long"])
return str(self._getCurrentLine().get("name_long"))

def getName(self) -> str:
return self.getLineNameLong()

def getTenantName(self):
return self._getCurrentTenant()["name"]

def getLineStatus(self):
return self._getCurrentLineMetadata()["status"]
def getTenantName(self) -> str | None:
tenant = self._getCurrentTenant()
if bool(tenant) and isinstance(tenant, dict): # type safety
return tenant.get("name")
else:
return None

def getRelPath(self) -> str:
return urljoin(self.getBasePath(), self.getLineID())
88 changes: 88 additions & 0 deletions cosmo/tests/test_case_auto_descriptions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,94 @@ device_list:
name: TEST-VLAN-3
vid: 3
vrf: null
- custom_fields:
inner_tag: null
outer_tag: null
description: null
__typename: InterfaceType
enabled: true
id: '30754'
ip_addresses: [ ]
connected_endpoints: null
attached_tobago_line:
__typename: CosmoTobagoLine
component_type: CABLE
element:
description: ''
display: cable 000128935
id: 39646
label: cable 000128935
url: https://netbox.example.com/api/dcim/cables/39646/
element_id: 93828
element_type: dcim.cable
id: 5111
index: 1
termination_a:
_occupied: true
cable:
description: ''
display: cable 000128935
id: 39646
label: cable 000128935
url: https://netbox.example.com/api/dcim/cables/39646/
description: ''
device:
description: ''
display: TEST0001
id: 1747
name: TEST0001
url: https://netbox.example.com/api/dcim/devices/1747/
display: et-0/0/1.4
id: 30750
name: et-0/0/1.4
url: https://netbox.example.com/api/dcim/interfaces/30754/
termination_a_id: 92388
termination_a_type: dcim.interface
termination_b:
_occupied: true
id: 198209
url: https://netbox.example.com/api/dcim/interfaces/192879/
display: xe-0/1/4
device:
id: 39827
url: https://netbox.example.com/api/dcim/device/39827/
display: TEST0002
name: TEST0002
description: null
name: xe-0/1/4
description: "customer A"
termination_b_id: 454
termination_b_type: dcim.interface
version:
created: '2025-09-03T11:34:19.643456+01:00'
custom_fields: { }
id: 2617
last_updated: '2025-09-03T11:34:40.819703+01:00'
line:
display: cl390287
id: 9834
name: '390287'
name_long: cl390287
url: https://netbox.example.com/api/plugins/tobago/lines/9834/
service: null
status: current
lag: null
mac_address: 94:BF:41:41:41:F4
mode: ACCESS
mtu: null
name: et-0/0/1.4
tagged_vlans: [ ]
tags:
- __typename: TagType
name: edge:customer
slug: edge_customer
type: virtual
untagged_vlan:
__typename: VLANType
id: '8237463'
name: TEST-VLAN-4
vid: 4
vrf: null
- custom_fields:
inner_tag: null
outer_tag: null
Expand Down
15 changes: 15 additions & 0 deletions cosmo/tests/test_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def test_router_interface_auto_description():
assert {
"connected_endpoints": [{"name": "combo1", "device": "mikrotik01"}]
} == json.loads(sd["interfaces"]["et-0/0/0"]["description"])

# auto type description now overrides existing descriptions
# assert "do not overwrite me!" == sd["interfaces"]["et-0/0/1"]["description"]
assert {
Expand All @@ -238,12 +239,26 @@ def test_router_interface_auto_description():
"connected_endpoints": [{"name": "combo2", "device": "mikrotik01"}],
"type": "peering",
} == json.loads(sd["interfaces"]["et-0/0/1"]["units"][2]["description"])

# sub interface with line with service, connected endpoint and link peers
# should display line info, tenant info, connected_endpoint and type
assert {
"line": "cl390287",
"tenant": "Contoso Ltd.",
"connected_endpoints": [{"name": "combo2", "device": "mikrotik01"}],
"type": "customer",
} == json.loads(sd["interfaces"]["et-0/0/1"]["units"][3]["description"])

# sub interface with line without service should display line number but not tenant
# and/or service information
assert {
"connected_endpoints": [{"name": "combo2", "device": "mikrotik01"}],
"line": "cl390287",
"type": "customer",
} == json.loads(sd["interfaces"]["et-0/0/1"]["units"][4]["description"])

# interface with only link_peers and no connected_endpoints should display
# link_peer information
assert {
"link_peers": [{"name": "port_45", "device": "Panel48673"}],
"type": "access",
Expand Down