Skip to content

Commit 112f867

Browse files
committed
change default naming for bgp cpe sessions
+ also keep old naming as a fallback when no tobago lines are attached + old naming still available using naming:cpe tag + test suite updated with all cases for new/old naming scheme switch
1 parent c23952b commit 112f867

File tree

5 files changed

+112
-23
lines changed

5 files changed

+112
-23
lines changed

cosmo/netbox_types.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
InterfaceSerializationError,
1616
head,
1717
)
18-
from typing import Self, Iterator, TypeVar, NoReturn, Never, Type
18+
from typing import Self, Iterator, TypeVar, NoReturn, Never, Type, Union
1919

2020
from .tobago_types import TobagoAbstractTerminationType
2121

@@ -540,9 +540,12 @@ def isLoopbackChild(self):
540540
def getConnectedEndpoints(self) -> list[DeviceType]:
541541
return self.get("connected_endpoints", [])
542542

543-
def hasAnAttachedTobagoLine(self):
543+
def hasAnAttachedTobagoLine(self) -> bool:
544544
return self.get("attached_tobago_line") is not None
545545

546+
def getAttachedTobagoLine(self) -> Union["CosmoTobagoLine", None]:
547+
return self.get("attached_tobago_line")
548+
546549

547550
class VLANType(AbstractNetboxType):
548551
def __repr__(self):

cosmo/routerbgpcpevisitor.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from functools import singledispatchmethod
22
from ipaddress import IPv4Interface, IPv6Interface, ip_interface
3+
from typing import TypeGuard
34

45
from cosmo.common import head, CosmoOutputType
56
from cosmo.cperoutervisitor import CpeRouterExporterVisitor, CpeRouterIPVisitor
@@ -89,6 +90,29 @@ def processUnnumberedBGP(base_group_name, linked_interface, policy_v4, policy_v6
8990
}
9091
}
9192

93+
@staticmethod
94+
def getGroupName(
95+
linked_interface: InterfaceType, parent_interface: InterfaceType
96+
) -> str:
97+
# technically a type guard, as we're narrowing on TagType. described
98+
# as such to satisfy the type checker.
99+
def tagFilter(t: TagType) -> TypeGuard[TagType]:
100+
return t.getTagName() == "naming" and t.getTagValue() == "cpe"
101+
102+
attached_tobago_line = parent_interface.getAttachedTobagoLine()
103+
# if legacy naming tag is present, or no tobago line is attached, we keep the old name as a fallback
104+
if not attached_tobago_line or any(
105+
filter(
106+
tagFilter,
107+
linked_interface.getTags(),
108+
)
109+
):
110+
return "CPE_" + linked_interface.getName().replace(".", "-").replace(
111+
"/", "-"
112+
)
113+
else: # use new naming scheme with tobago line name
114+
return "CUST_" + attached_tobago_line.getLineNameLong()
115+
92116
def processBgpCpeTag(self, o: TagType):
93117
linked_interface = o.getParent(InterfaceType)
94118
if not linked_interface.hasParentInterface():
@@ -112,9 +136,7 @@ def processBgpCpeTag(self, o: TagType):
112136
)
113137
return
114138

115-
group_name = "CPE_" + linked_interface.getName().replace(".", "-").replace(
116-
"/", "-"
117-
)
139+
group_name = self.getGroupName(linked_interface, parent_interface)
118140
vrf_name = "default"
119141
# make the type checker happy, since it cannot reliably infer
120142
# type from default values of policy_v4 and policy_v6

cosmo/routervisitor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,8 @@ def _(self, o: TagType):
850850
return self.processAccessTag(o)
851851
case "unnumbered":
852852
return self.processBgpUnnumberedTag(o)
853+
case "naming":
854+
pass # ignore, as it is treated in bgp cpe visitor
853855
case "bgp":
854856
if o.getTagValue() == "cpe":
855857
return self.bgpcpe_exporter.accept(o)

cosmo/tests/test_case_bgpcpe.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,67 @@ device_list:
3030
untagged_vlan: null
3131
vrf: null
3232
parent: null
33+
attached_tobago_line:
34+
__typename: CosmoTobagoLine
35+
component_type: CABLE
36+
element:
37+
description: ''
38+
display: cable 000128934
39+
id: 39645
40+
label: cable 000128934
41+
url: https://netbox.example.com/api/dcim/cables/39645/
42+
element_id: 93827
43+
element_type: dcim.cable
44+
id: 5110
45+
index: 1
46+
termination_a:
47+
_occupied: true
48+
cable:
49+
description: ''
50+
display: cable 000128934
51+
id: 39645
52+
label: cable 000128934
53+
url: https://netbox.example.com/api/dcim/cables/39645/
54+
description: ''
55+
device:
56+
description: ''
57+
display: TEST0001
58+
id: 17799
59+
name: TEST0001
60+
url: https://netbox.example.com/api/dcim/devices/1747/
61+
display: ifp-0/1/2
62+
id: 191940
63+
name: ifp-0/1/2
64+
url: https://netbox.example.com/api/dcim/interfaces/191940/
65+
termination_a_id: 92387
66+
termination_a_type: dcim.interface
67+
termination_b:
68+
_occupied: true
69+
id: 198208
70+
url: https://netbox.example.com/api/dcim/interfaces/192878/
71+
display: DC10 duplex front 10b
72+
device:
73+
id: 39948
74+
url: https://netbox.example.com/api/dcim/device/39948/
75+
display: Panel C
76+
name: Panel C
77+
description: null
78+
name: DC10 duplex front 10b
79+
description: ""
80+
termination_b_id: 454
81+
termination_b_type: dcim.frontport
82+
version:
83+
created: '2025-09-03T11:34:19.643456+01:00'
84+
custom_fields: { }
85+
id: 2617
86+
last_updated: '2025-09-03T11:34:40.819703+01:00'
87+
line:
88+
display: cl390287
89+
id: 9834
90+
name: '390287'
91+
name_long: cl390287
92+
url: https://netbox.example.com/api/plugins/tobago/lines/9834/
93+
status: current
3394
connected_endpoints:
3495
- device:
3596
name: ''
@@ -114,6 +175,9 @@ device_list:
114175
- name: bgp:cpe
115176
slug: bgpcpe
116177
__typename: TagType
178+
- name: naming:cpe
179+
slug: namingcpe
180+
__typename: TagType
117181
type: VIRTUAL
118182
untagged_vlan: null
119183
vrf:

cosmo/tests/test_serializer.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -475,28 +475,28 @@ def test_router_case_local_bgpcpe():
475475

476476
groups_default = d["routing_instances"]["default"]["protocols"]["bgp"]["groups"]
477477
assert len(groups_default) == 1
478-
assert "CPE_ifp-0-1-2-3" in groups_default
479478
assert (
480-
groups_default["CPE_ifp-0-1-2-3"]["neighbors"][0]["interface"] == "ifp-0/1/2.3"
481-
)
479+
"CUST_cl390287" in groups_default
480+
) # parent interface has tobago line attached
481+
assert groups_default["CUST_cl390287"]["neighbors"][0]["interface"] == "ifp-0/1/2.3"
482482
assert (
483-
groups_default["CPE_ifp-0-1-2-3"]["family"]["ipv4_unicast"]["policy"]["export"]
483+
groups_default["CUST_cl390287"]["family"]["ipv4_unicast"]["policy"]["export"]
484484
== "DEFAULT_V4"
485485
)
486486
assert (
487-
groups_default["CPE_ifp-0-1-2-3"]["family"]["ipv6_unicast"]["policy"]["export"]
487+
groups_default["CUST_cl390287"]["family"]["ipv6_unicast"]["policy"]["export"]
488488
== "DEFAULT_V6"
489489
)
490-
assert groups_default["CPE_ifp-0-1-2-3"]["family"]["ipv4_unicast"]["policy"][
490+
assert groups_default["CUST_cl390287"]["family"]["ipv4_unicast"]["policy"][
491491
"import_list"
492492
] == ["10.1.0.0/28"]
493-
assert groups_default["CPE_ifp-0-1-2-3"]["family"]["ipv6_unicast"]["policy"][
493+
assert groups_default["CUST_cl390287"]["family"]["ipv6_unicast"]["policy"][
494494
"import_list"
495495
] == ["2a0e:b941:2:42::/64", "2a0e:b941:2::/122"]
496496

497497
groups_L3VPN = d["routing_instances"]["L3VPN"]["protocols"]["bgp"]["groups"]
498498

499-
assert "CPE_ifp-0-1-2-4" in groups_L3VPN
499+
assert "CPE_ifp-0-1-2-4" in groups_L3VPN # sub interface using legacy naming tag
500500
assert groups_L3VPN["CPE_ifp-0-1-2-4"]["neighbors"][0]["interface"] == "ifp-0/1/2.4"
501501
assert (
502502
not "export"
@@ -513,25 +513,23 @@ def test_router_case_local_bgpcpe():
513513
"import_list"
514514
] == ["2a0e:b941:2:42::/64", "2a0e:b941:2::/122"]
515515

516-
assert "CPE_ifp-0-1-2-5_V4" in groups_L3VPN
517-
assert "CPE_ifp-0-1-2-5_V6" in groups_L3VPN
518-
assert groups_L3VPN["CPE_ifp-0-1-2-5_V4"]["neighbors"][0]["peer"] == "10.128.6.12"
519-
assert (
520-
groups_L3VPN["CPE_ifp-0-1-2-5_V6"]["neighbors"][0]["peer"] == "2a0e:b941:2::21"
521-
)
516+
assert "CUST_cl390287_V4" in groups_L3VPN
517+
assert "CUST_cl390287_V6" in groups_L3VPN
518+
assert groups_L3VPN["CUST_cl390287_V4"]["neighbors"][0]["peer"] == "10.128.6.12"
519+
assert groups_L3VPN["CUST_cl390287_V6"]["neighbors"][0]["peer"] == "2a0e:b941:2::21"
522520
assert (
523521
not "export"
524-
in groups_L3VPN["CPE_ifp-0-1-2-5_V4"]["family"]["ipv4_unicast"]["policy"]
522+
in groups_L3VPN["CUST_cl390287_V4"]["family"]["ipv4_unicast"]["policy"]
525523
)
526524
assert (
527525
not "export"
528-
in groups_L3VPN["CPE_ifp-0-1-2-5_V6"]["family"]["ipv6_unicast"]["policy"]
526+
in groups_L3VPN["CUST_cl390287_V6"]["family"]["ipv6_unicast"]["policy"]
529527
)
530-
assert groups_L3VPN["CPE_ifp-0-1-2-5_V4"]["family"]["ipv4_unicast"]["policy"][
528+
assert groups_L3VPN["CUST_cl390287_V4"]["family"]["ipv4_unicast"]["policy"][
531529
"import_list"
532530
] == ["10.1.0.0/28"]
533531
# should not be allowed to announce our transfer nets, so '2a0e:b941:2::/122' should not be there
534-
assert groups_L3VPN["CPE_ifp-0-1-2-5_V6"]["family"]["ipv6_unicast"]["policy"][
532+
assert groups_L3VPN["CUST_cl390287_V6"]["family"]["ipv6_unicast"]["policy"][
535533
"import_list"
536534
] == ["2a0e:b941:2:42::/64"]
537535

0 commit comments

Comments
 (0)