Repository: netbox-community/netbox
Pull request: Adding asdot notation to ASN views
### Fixes: #8293 
Adds custom property to asn model to compute asdot notation if required.
Updates asn view to show asdot notation if one exists in the format xxxxx (yyy.yyy)
Adds a custom column renderer to asn table to display asdot notation if one exists


Problem statement:
support for asdot notation for 4 Byte ASN
### NetBox version

v3.1.5

### Feature type

Change to existing functionality

### Proposed functionality

In the netbox url/ipam/asns/ and  netbox ur>/ipam/asns/#/ view the AS Number should be displayed in asplain and asdot notation. 
The db model does not have to be changed as we can still only store the asplain value in the db.

The actual conversion can be done in the web browser with some simple javascript like this:

```
 function conv_asplain2asdot() {
        asplain = document.AScalc.asplain.value;
        var asdot1 = (Math.floor(asplain/65536));
        var asdot2 = asplain%65536;
        document.AScalc.asdot1.value = asdot1;
        document.AScalc.asdot2.value = asdot2;
};

function conv_asdot2asplain() {
        var asdot1 = document.AScalc.asdot1.value;
        var asdot2 = document.AScalc.asdot2.value;
        asplain = ((parseInt(asdot1) * 65536) + parseInt(asdot2));
        document.AScalc.asplain.value = asplain;
};
```


### Use case

4 Byte AS number get more popular these day and the asdot notation seems to be much easier to work with. 

### Database changes

not required, conversion can be done in javascript at runtime as mentioned above.

### External dependencies

none

Hints from issue discussion:
I understand this was done in JS, but should this also be targetted for the API or do you think it is not needed as API consumers should be capable of handling it?
I think is is just more convenient for humans. As the conversion can be done really simple API users should be able to convert themself.
But you are right, it could be done on the server side as well, while keeping only the asplain value in the DB.
In the UI, we can just display both formats in the UI for AS number larger than 65535. We should also come up with a custom form field to handle input in either format. IMO though the REST API should only return the plain integer format.
How do you want to add it in? Extend the ASN base class with the conversion functions or just add it into the table creation?
What would be the best way to display this?

**With an additional row:**
<img width="713" alt="Screenshot 2022-01-11 at 22 56 11" src="https://user-images.githubusercontent.com/242483/149034600-251e5c74-8229-4ead-bec4-ea850d3849df.png">

**Appending the asdot version alongside the asplain. E.g.:**
<img width="692" alt="Screenshot 2022-01-11 at 22 56 42" src="https://user-images.githubusercontent.com/242483/149034610-c6384324-30ea-4441-bc6f-63d195612cc1.png">

Same would apply to the table view. An extra column or appending it after the existing value.


I would just put it in parentheses immediately following the first value.

Commit message: Updating page title to include asdot notation

Patch to review:
diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py
index 9c00a906866..fa27aaf1bbc 100644
--- a/netbox/ipam/models/ip.py
+++ b/netbox/ipam/models/ip.py
@@ -125,11 +125,22 @@ class Meta:
         verbose_name_plural = 'ASNs'
 
     def __str__(self):
-        return f'AS{self.asn}'
+        if self.asn > 65535:
+            return 'AS{} ({}.{})'.format(self.asn, self.asn // 65536, self.asn % 65536)
+        else:
+            return f'AS{self.asn}'
 
     def get_absolute_url(self):
         return reverse('ipam:asn', args=[self.pk])
 
+    @property
+    def asdot_notation(self):
+        # Return asdot notation for an ASN larger than 65535
+        if self.asn > 65535:
+            return '{}.{}'.format(self.asn // 65536, self.asn % 65536)
+        else:
+            return None
+
 
 @extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
 class Aggregate(GetAvailablePrefixesMixin, PrimaryModel):
diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py
index 3fddbf48e52..988c89aa2c8 100644
--- a/netbox/ipam/tables/ip.py
+++ b/netbox/ipam/tables/ip.py
@@ -106,6 +106,13 @@ class ASNTable(BaseTable):
     asn = tables.Column(
         linkify=True
     )
+
+    def render_asn(self, value, record):
+        if record.asdot_notation:
+            return f'{value} ({record.asdot_notation})'
+        else:
+            return value
+
     site_count = LinkedCountColumn(
         viewname='dcim:site_list',
         url_params={'asn_id': 'pk'},
diff --git a/netbox/templates/ipam/asn.html b/netbox/templates/ipam/asn.html
index 53afd5ebbac..85eaf8122e9 100644
--- a/netbox/templates/ipam/asn.html
+++ b/netbox/templates/ipam/asn.html
@@ -18,7 +18,7 @@ <h5 class="card-header">ASN</h5>
           <table class="table table-hover attr-table">
             <tr>
               <td>AS Number</td>
-              <td>{{ object.asn }}</td>
+              <td>{{ object.asn }} {% if object.asdot_notation %}({{ object.asdot_notation }}){% endif %}</td>
             </tr>
             <tr>
               <td>RIR</td>


Code review comments:
diff_hunk: @@ -125,11 +125,22 @@ class Meta:
         verbose_name_plural = 'ASNs'
 
     def __str__(self):
-        return f'AS{self.asn}'
+        if self.asn > 65535:
+            return 'AS{} ({}.{})'.format(self.asn, self.asn // 65536, self.asn % 65536)
+        else:
+            return f'AS{self.asn}'
 
     def get_absolute_url(self):
         return reverse('ipam:asn', args=[self.pk])
 
+    @property
+    def asdot_notation(self):
path: netbox/ipam/models/ip.py
text: @user1:
Maybe rename this to `asn_with_asdot` or something like that, and return either the ASN with ASDOT notiation or just the ASN. Then you can reference it directly from within the ASNTable and object template.

@author:
Great suggestion. That works much cleaner. Updated ASNTable & object template.

diff_hunk: @@ -125,11 +125,22 @@ class Meta:
         verbose_name_plural = 'ASNs'
 
     def __str__(self):
-        return f'AS{self.asn}'
+        if self.asn > 65535:
+            return 'AS{} ({}.{})'.format(self.asn, self.asn // 65536, self.asn % 65536)
path: netbox/ipam/models/ip.py
text: @user1:
This should probably return `self.asdot_notation`.

@user2:
I think we should also stick with fstrings if we can IMO

@author:
Updated to use fstring and to return `asn_with_asdot` per below suggestion. Resulting code is much cleaner.