mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
Merge pull request #170 from peelman/add-mac-address-to-interface
Add mac address to interface
This commit is contained in:
commit
9793b406e9
@ -334,7 +334,7 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ['id', 'device', 'name', 'form_factor', 'mgmt_only', 'description', 'is_connected']
|
fields = ['id', 'device', 'name', 'form_factor', 'mac_address', 'mgmt_only', 'description', 'is_connected']
|
||||||
|
|
||||||
|
|
||||||
class InterfaceNestedSerializer(InterfaceSerializer):
|
class InterfaceNestedSerializer(InterfaceSerializer):
|
||||||
@ -348,7 +348,7 @@ class InterfaceDetailSerializer(InterfaceSerializer):
|
|||||||
connected_interface = InterfaceSerializer(source='get_connected_interface')
|
connected_interface = InterfaceSerializer(source='get_connected_interface')
|
||||||
|
|
||||||
class Meta(InterfaceSerializer.Meta):
|
class Meta(InterfaceSerializer.Meta):
|
||||||
fields = ['id', 'device', 'name', 'form_factor', 'mgmt_only', 'description', 'is_connected',
|
fields = ['id', 'device', 'name', 'form_factor', 'mac_address', 'mgmt_only', 'description', 'is_connected',
|
||||||
'connected_interface']
|
'connected_interface']
|
||||||
|
|
||||||
|
|
||||||
|
44
netbox/dcim/fields.py
Normal file
44
netbox/dcim/fields.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from netaddr import EUI, mac_unix_expanded
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
from .formfields import MACAddressFormField
|
||||||
|
|
||||||
|
|
||||||
|
class mac_unix_expanded_uppercase(mac_unix_expanded):
|
||||||
|
word_fmt = '%.2X'
|
||||||
|
|
||||||
|
|
||||||
|
class MACAddressField(models.Field):
|
||||||
|
description = "PostgreSQL MAC Address field"
|
||||||
|
|
||||||
|
def python_type(self):
|
||||||
|
return EUI
|
||||||
|
|
||||||
|
def from_db_value(self, value, expression, connection, context):
|
||||||
|
return self.to_python(value)
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if not value:
|
||||||
|
return value
|
||||||
|
try:
|
||||||
|
return EUI(value, dialect=mac_unix_expanded_uppercase)
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValidationError(e)
|
||||||
|
|
||||||
|
def db_type(self, connection):
|
||||||
|
return 'macaddr'
|
||||||
|
|
||||||
|
def get_prep_value(self, value):
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
return str(self.to_python(value))
|
||||||
|
|
||||||
|
def form_class(self):
|
||||||
|
return MACAddressFormField
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
defaults = {'form_class': self.form_class()}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(MACAddressField, self).formfield(**defaults)
|
@ -3419,6 +3419,7 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"device": 3,
|
"device": 3,
|
||||||
"name": "em0",
|
"name": "em0",
|
||||||
|
"mac_address": "00-00-00-AA-BB-CC",
|
||||||
"form_factor": 800,
|
"form_factor": 800,
|
||||||
"mgmt_only": true,
|
"mgmt_only": true,
|
||||||
"description": ""
|
"description": ""
|
||||||
@ -3772,6 +3773,7 @@
|
|||||||
"device": 4,
|
"device": 4,
|
||||||
"name": "em0",
|
"name": "em0",
|
||||||
"form_factor": 1000,
|
"form_factor": 1000,
|
||||||
|
"mac_address": "ff-ee-dd-33-22-11",
|
||||||
"mgmt_only": true,
|
"mgmt_only": true,
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
@ -5686,6 +5688,7 @@
|
|||||||
"device": 9,
|
"device": 9,
|
||||||
"name": "eth0",
|
"name": "eth0",
|
||||||
"form_factor": 1000,
|
"form_factor": 1000,
|
||||||
|
"mac_address": "44-55-66-77-88-99",
|
||||||
"mgmt_only": true,
|
"mgmt_only": true,
|
||||||
"description": ""
|
"description": ""
|
||||||
}
|
}
|
||||||
@ -5865,4 +5868,4 @@
|
|||||||
"connection_status": true
|
"connection_status": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
26
netbox/dcim/formfields.py
Normal file
26
netbox/dcim/formfields.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from netaddr import EUI, AddrFormatError
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Form fields
|
||||||
|
#
|
||||||
|
|
||||||
|
class MACAddressFormField(forms.Field):
|
||||||
|
default_error_messages = {
|
||||||
|
'invalid': "Enter a valid MAC address.",
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(value, EUI):
|
||||||
|
return value
|
||||||
|
|
||||||
|
try:
|
||||||
|
return EUI(value)
|
||||||
|
except AddrFormatError:
|
||||||
|
raise ValidationError("Please specify a valid MAC address.")
|
@ -925,7 +925,7 @@ class InterfaceForm(forms.ModelForm, BootstrapMixin):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ['device', 'name', 'form_factor', 'mgmt_only', 'description']
|
fields = ['device', 'name', 'form_factor', 'mac_address', 'mgmt_only', 'description']
|
||||||
widgets = {
|
widgets = {
|
||||||
'device': forms.HiddenInput(),
|
'device': forms.HiddenInput(),
|
||||||
}
|
}
|
||||||
@ -936,7 +936,7 @@ class InterfaceCreateForm(forms.ModelForm, BootstrapMixin):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ['name_pattern', 'form_factor', 'mgmt_only', 'description']
|
fields = ['name_pattern', 'form_factor', 'mac_address', 'mgmt_only', 'description']
|
||||||
|
|
||||||
|
|
||||||
class InterfaceBulkCreateForm(InterfaceCreateForm, BootstrapMixin):
|
class InterfaceBulkCreateForm(InterfaceCreateForm, BootstrapMixin):
|
||||||
|
26
netbox/dcim/migrations/0005_auto_20160706_1722.py
Normal file
26
netbox/dcim/migrations/0005_auto_20160706_1722.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.7 on 2016-07-06 17:22
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import dcim.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0004_auto_20160701_2049'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='interface',
|
||||||
|
name='mac_address',
|
||||||
|
field=dcim.fields.MACAddressField(blank=True, null=True, verbose_name=b'MAC Address'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='devicetype',
|
||||||
|
name='subdevice_role',
|
||||||
|
field=models.NullBooleanField(choices=[(None, b'None'), (True, b'Parent'), (False, b'Child')], default=None, help_text=b'Parent devices house child devices in device bays. Select "None" if this device type is neither a parent nor a child.', verbose_name=b'Parent/child status'),
|
||||||
|
),
|
||||||
|
]
|
@ -10,6 +10,7 @@ from extras.rpc import RPC_CLIENTS
|
|||||||
from utilities.fields import NullableCharField
|
from utilities.fields import NullableCharField
|
||||||
from utilities.models import CreatedUpdatedModel
|
from utilities.models import CreatedUpdatedModel
|
||||||
|
|
||||||
|
from .fields import MACAddressField
|
||||||
|
|
||||||
RACK_FACE_FRONT = 0
|
RACK_FACE_FRONT = 0
|
||||||
RACK_FACE_REAR = 1
|
RACK_FACE_REAR = 1
|
||||||
@ -856,6 +857,7 @@ class Interface(models.Model):
|
|||||||
device = models.ForeignKey('Device', related_name='interfaces', on_delete=models.CASCADE)
|
device = models.ForeignKey('Device', related_name='interfaces', on_delete=models.CASCADE)
|
||||||
name = models.CharField(max_length=30)
|
name = models.CharField(max_length=30)
|
||||||
form_factor = models.PositiveSmallIntegerField(choices=IFACE_FF_CHOICES, default=IFACE_FF_SFP_PLUS)
|
form_factor = models.PositiveSmallIntegerField(choices=IFACE_FF_CHOICES, default=IFACE_FF_SFP_PLUS)
|
||||||
|
mac_address = MACAddressField(null=True, blank=True, verbose_name='MAC Address')
|
||||||
mgmt_only = models.BooleanField(default=False, verbose_name='OOB Management',
|
mgmt_only = models.BooleanField(default=False, verbose_name='OOB Management',
|
||||||
help_text="This interface is used only for out-of-band management")
|
help_text="This interface is used only for out-of-band management")
|
||||||
description = models.CharField(max_length=100, blank=True)
|
description = models.CharField(max_length=100, blank=True)
|
||||||
|
@ -529,6 +529,7 @@ class InterfaceTest(APITestCase):
|
|||||||
'device',
|
'device',
|
||||||
'name',
|
'name',
|
||||||
'form_factor',
|
'form_factor',
|
||||||
|
'mac_address',
|
||||||
'mgmt_only',
|
'mgmt_only',
|
||||||
'description',
|
'description',
|
||||||
'is_connected'
|
'is_connected'
|
||||||
@ -541,6 +542,7 @@ class InterfaceTest(APITestCase):
|
|||||||
'device',
|
'device',
|
||||||
'name',
|
'name',
|
||||||
'form_factor',
|
'form_factor',
|
||||||
|
'mac_address',
|
||||||
'mgmt_only',
|
'mgmt_only',
|
||||||
'description',
|
'description',
|
||||||
'is_connected',
|
'is_connected',
|
||||||
|
@ -1252,6 +1252,7 @@ def interface_add(request, pk):
|
|||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
'name': name,
|
'name': name,
|
||||||
'form_factor': form.cleaned_data['form_factor'],
|
'form_factor': form.cleaned_data['form_factor'],
|
||||||
|
'mac_address': form.cleaned_data['mac_address'],
|
||||||
'mgmt_only': form.cleaned_data['mgmt_only'],
|
'mgmt_only': form.cleaned_data['mgmt_only'],
|
||||||
'description': form.cleaned_data['description'],
|
'description': form.cleaned_data['description'],
|
||||||
})
|
})
|
||||||
@ -1339,6 +1340,7 @@ class InterfaceBulkAddView(PermissionRequiredMixin, BulkEditView):
|
|||||||
iface_form = forms.InterfaceForm({
|
iface_form = forms.InterfaceForm({
|
||||||
'device': device.pk,
|
'device': device.pk,
|
||||||
'name': name,
|
'name': name,
|
||||||
|
'mac_address': mac_address,
|
||||||
'form_factor': form.cleaned_data['form_factor'],
|
'form_factor': form.cleaned_data['form_factor'],
|
||||||
'mgmt_only': form.cleaned_data['mgmt_only'],
|
'mgmt_only': form.cleaned_data['mgmt_only'],
|
||||||
'description': form.cleaned_data['description'],
|
'description': form.cleaned_data['description'],
|
||||||
|
@ -295,9 +295,6 @@
|
|||||||
{% if interfaces or device.device_type.is_network_device %}
|
{% if interfaces or device.device_type.is_network_device %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
{% if perms.dcim.add_interface %}
|
|
||||||
<a href="{% url 'dcim:interface_add' pk=device.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Interfaces</a>
|
|
||||||
{% endif %}
|
|
||||||
<strong>Interfaces</strong>
|
<strong>Interfaces</strong>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover panel-body">
|
<table class="table table-hover panel-body">
|
||||||
@ -309,6 +306,14 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
{% if perms.dcim.add_interface %}
|
||||||
|
<div class="panel-footer text-right">
|
||||||
|
<a href="{% url 'dcim:interface_add' pk=device.pk %}" class="btn btn-xs btn-primary">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
|
Add interface
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if cs_ports or device.device_type.is_console_server %}
|
{% if cs_ports or device.device_type.is_console_server %}
|
||||||
|
@ -6,22 +6,26 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% if not iface.is_physical %}
|
{% if not iface.is_physical %}
|
||||||
<td colspan="2">Virtual</td>
|
<td>Virtual</td>
|
||||||
{% elif iface.connection %}
|
{% elif iface.connection %}
|
||||||
{% with iface.get_connected_interface as connected_iface %}
|
{% with iface.get_connected_interface as connected_iface %}
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'dcim:device' pk=connected_iface.device.pk %}">{{ connected_iface.device }}</a>
|
<a href="{% url 'dcim:device' pk=connected_iface.device.pk %}">{{ connected_iface.device }}</a>
|
||||||
</td>
|
∷
|
||||||
<td>
|
|
||||||
<span title="{{ connected_iface.get_form_factor_display }}">{{ connected_iface }}</span>
|
<span title="{{ connected_iface.get_form_factor_display }}">{{ connected_iface }}</span>
|
||||||
</td>
|
</td>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% elif iface.circuit %}
|
{% elif iface.circuit %}
|
||||||
<td colspan="2">
|
<td>
|
||||||
<a href="{% url 'circuits:circuit' pk=iface.circuit.pk %}">{{ iface.circuit }}</a>
|
<a href="{% url 'circuits:circuit' pk=iface.circuit.pk %}">{{ iface.circuit }}</a>
|
||||||
</td>
|
</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td colspan="2">Not connected</td>
|
<td>Not connected</td>
|
||||||
|
{% endif %}
|
||||||
|
{% if iface.mac_address %}
|
||||||
|
<td><span class="small text-muted">{{ iface.mac_address }}</span></td>
|
||||||
|
{% else %}
|
||||||
|
<td><span class="small text-muted">00:00:00:00:00:00</span></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% if iface.circuit or iface.connection %}
|
{% if iface.circuit or iface.connection %}
|
||||||
|
Loading…
Reference in New Issue
Block a user