mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-05 11:46:50 -06:00
* feat(ipam): Add ASDOT notation support for ASN ranges Introduces ASDOT notation for ASN Ranges to improve readability of large AS numbers. Adds `start_asdot` and `end_asdot` properties, columns, and display logic for ASN ranges in the UI. Fixes #20309 * Wrap "ASDOT" with parentheses in column header --------- Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
@@ -16,6 +16,7 @@ __all__ = (
|
|||||||
# BGP ASN bounds
|
# BGP ASN bounds
|
||||||
BGP_ASN_MIN = 1
|
BGP_ASN_MIN = 1
|
||||||
BGP_ASN_MAX = 2**32 - 1
|
BGP_ASN_MAX = 2**32 - 1
|
||||||
|
BGP_ASN_ASDOT_BASE = 2**16
|
||||||
|
|
||||||
|
|
||||||
class BaseIPField(models.Field):
|
class BaseIPField(models.Field):
|
||||||
@@ -126,3 +127,16 @@ class ASNField(models.BigIntegerField):
|
|||||||
}
|
}
|
||||||
defaults.update(**kwargs)
|
defaults.update(**kwargs)
|
||||||
return super().formfield(**defaults)
|
return super().formfield(**defaults)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_asdot(value) -> str:
|
||||||
|
"""
|
||||||
|
Return ASDOT notation for AS numbers greater than 16 bits.
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if value >= BGP_ASN_ASDOT_BASE:
|
||||||
|
hi, lo = divmod(value, BGP_ASN_ASDOT_BASE)
|
||||||
|
return f'{hi}.{lo}'
|
||||||
|
return str(value)
|
||||||
|
|||||||
@@ -55,13 +55,6 @@ class ASNRange(OrganizationalModel):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.name} ({self.range_as_string()})'
|
return f'{self.name} ({self.range_as_string()})'
|
||||||
|
|
||||||
@property
|
|
||||||
def range(self):
|
|
||||||
return range(self.start, self.end + 1)
|
|
||||||
|
|
||||||
def range_as_string(self):
|
|
||||||
return f'{self.start}-{self.end}'
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
@@ -72,7 +65,45 @@ class ASNRange(OrganizationalModel):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def range(self):
|
||||||
|
"""
|
||||||
|
Return a range of integers representing the ASN range.
|
||||||
|
"""
|
||||||
|
return range(self.start, self.end + 1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_asdot(self):
|
||||||
|
"""
|
||||||
|
Return ASDOT notation for AS numbers greater than 16 bits.
|
||||||
|
"""
|
||||||
|
return ASNField.to_asdot(self.start)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_asdot(self):
|
||||||
|
"""
|
||||||
|
Return ASDOT notation for AS numbers greater than 16 bits.
|
||||||
|
"""
|
||||||
|
return ASNField.to_asdot(self.end)
|
||||||
|
|
||||||
|
def range_as_string(self):
|
||||||
|
"""
|
||||||
|
Return a string representation of the ASN range.
|
||||||
|
"""
|
||||||
|
return f'{self.start}-{self.end}'
|
||||||
|
|
||||||
|
def range_as_string_with_asdot(self):
|
||||||
|
"""
|
||||||
|
Return a string representation of the ASN range, including ASDOT notation.
|
||||||
|
"""
|
||||||
|
if self.end >= 65536:
|
||||||
|
return f'{self.range_as_string()} ({self.start_asdot}-{self.end_asdot})'
|
||||||
|
return self.range_as_string()
|
||||||
|
|
||||||
def get_child_asns(self):
|
def get_child_asns(self):
|
||||||
|
"""
|
||||||
|
Return all child ASNs (ASNs within the range).
|
||||||
|
"""
|
||||||
return ASN.objects.filter(
|
return ASN.objects.filter(
|
||||||
asn__gte=self.start,
|
asn__gte=self.start,
|
||||||
asn__lte=self.end
|
asn__lte=self.end
|
||||||
@@ -131,20 +162,20 @@ class ASN(ContactsMixin, PrimaryModel):
|
|||||||
"""
|
"""
|
||||||
Return ASDOT notation for AS numbers greater than 16 bits.
|
Return ASDOT notation for AS numbers greater than 16 bits.
|
||||||
"""
|
"""
|
||||||
if self.asn > 65535:
|
return ASNField.to_asdot(self.asn)
|
||||||
return f'{self.asn // 65536}.{self.asn % 65536}'
|
|
||||||
return self.asn
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def asn_with_asdot(self):
|
def asn_with_asdot(self):
|
||||||
"""
|
"""
|
||||||
Return both plain and ASDOT notation, where applicable.
|
Return both plain and ASDOT notation, where applicable.
|
||||||
"""
|
"""
|
||||||
if self.asn > 65535:
|
if self.asn >= 65536:
|
||||||
return f'{self.asn} ({self.asn // 65536}.{self.asn % 65536})'
|
return f'{self.asn} ({self.asn_asdot})'
|
||||||
else:
|
return str(self.asn)
|
||||||
return self.asn
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prefixed_name(self):
|
def prefixed_name(self):
|
||||||
|
"""
|
||||||
|
Return the ASN with ASDOT notation prefixed with "AS".
|
||||||
|
"""
|
||||||
return f'AS{self.asn_with_asdot}'
|
return f'AS{self.asn_with_asdot}'
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ class ASNRangeTable(TenancyColumnsMixin, NetBoxTable):
|
|||||||
verbose_name=_('RIR'),
|
verbose_name=_('RIR'),
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
start_asdot = tables.Column(
|
||||||
|
accessor=tables.A('start_asdot'),
|
||||||
|
order_by=tables.A('start'),
|
||||||
|
verbose_name=_('Start (ASDOT)')
|
||||||
|
)
|
||||||
|
end_asdot = tables.Column(
|
||||||
|
accessor=tables.A('end_asdot'),
|
||||||
|
order_by=tables.A('end'),
|
||||||
|
verbose_name=_('End (ASDOT)')
|
||||||
|
)
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='ipam:asnrange_list'
|
url_name='ipam:asnrange_list'
|
||||||
)
|
)
|
||||||
@@ -30,8 +40,8 @@ class ASNRangeTable(TenancyColumnsMixin, NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = ASNRange
|
model = ASNRange
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'name', 'slug', 'rir', 'start', 'end', 'asn_count', 'tenant', 'tenant_group', 'description', 'tags',
|
'pk', 'name', 'slug', 'rir', 'start', 'start_asdot', 'end', 'end_asdot', 'asn_count', 'tenant',
|
||||||
'created', 'last_updated', 'actions',
|
'tenant_group', 'description', 'tags', 'created', 'last_updated', 'actions',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'rir', 'start', 'end', 'tenant', 'asn_count', 'description')
|
default_columns = ('pk', 'name', 'rir', 'start', 'end', 'tenant', 'asn_count', 'description')
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Range" %}</th>
|
<th scope="row">{% trans "Range" %}</th>
|
||||||
<td>{{ object.range_as_string }}</td>
|
<td>{{ object.range_as_string_with_asdot }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{% trans "Tenant" %}</th>
|
<th scope="row">{% trans "Tenant" %}</th>
|
||||||
|
|||||||
Reference in New Issue
Block a user