Merge pull request #170 from peelman/add-mac-address-to-interface

Add mac address to interface
This commit is contained in:
Jeremy Stretch 2016-07-06 13:38:46 -04:00 committed by GitHub
commit 9793b406e9
11 changed files with 127 additions and 13 deletions

View File

@ -334,7 +334,7 @@ class InterfaceSerializer(serializers.ModelSerializer):
class Meta:
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):
@ -348,7 +348,7 @@ class InterfaceDetailSerializer(InterfaceSerializer):
connected_interface = InterfaceSerializer(source='get_connected_interface')
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']

44
netbox/dcim/fields.py Normal file
View 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)

View File

@ -3419,6 +3419,7 @@
"fields": {
"device": 3,
"name": "em0",
"mac_address": "00-00-00-AA-BB-CC",
"form_factor": 800,
"mgmt_only": true,
"description": ""
@ -3772,6 +3773,7 @@
"device": 4,
"name": "em0",
"form_factor": 1000,
"mac_address": "ff-ee-dd-33-22-11",
"mgmt_only": true,
"description": ""
}
@ -5686,6 +5688,7 @@
"device": 9,
"name": "eth0",
"form_factor": 1000,
"mac_address": "44-55-66-77-88-99",
"mgmt_only": true,
"description": ""
}
@ -5865,4 +5868,4 @@
"connection_status": true
}
}
]
]

26
netbox/dcim/formfields.py Normal file
View 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.")

View File

@ -925,7 +925,7 @@ class InterfaceForm(forms.ModelForm, BootstrapMixin):
class Meta:
model = Interface
fields = ['device', 'name', 'form_factor', 'mgmt_only', 'description']
fields = ['device', 'name', 'form_factor', 'mac_address', 'mgmt_only', 'description']
widgets = {
'device': forms.HiddenInput(),
}
@ -936,7 +936,7 @@ class InterfaceCreateForm(forms.ModelForm, BootstrapMixin):
class Meta:
model = Interface
fields = ['name_pattern', 'form_factor', 'mgmt_only', 'description']
fields = ['name_pattern', 'form_factor', 'mac_address', 'mgmt_only', 'description']
class InterfaceBulkCreateForm(InterfaceCreateForm, BootstrapMixin):

View 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'),
),
]

View File

@ -10,6 +10,7 @@ from extras.rpc import RPC_CLIENTS
from utilities.fields import NullableCharField
from utilities.models import CreatedUpdatedModel
from .fields import MACAddressField
RACK_FACE_FRONT = 0
RACK_FACE_REAR = 1
@ -856,6 +857,7 @@ class Interface(models.Model):
device = models.ForeignKey('Device', related_name='interfaces', on_delete=models.CASCADE)
name = models.CharField(max_length=30)
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',
help_text="This interface is used only for out-of-band management")
description = models.CharField(max_length=100, blank=True)

View File

@ -529,6 +529,7 @@ class InterfaceTest(APITestCase):
'device',
'name',
'form_factor',
'mac_address',
'mgmt_only',
'description',
'is_connected'
@ -541,6 +542,7 @@ class InterfaceTest(APITestCase):
'device',
'name',
'form_factor',
'mac_address',
'mgmt_only',
'description',
'is_connected',

View File

@ -1252,6 +1252,7 @@ def interface_add(request, pk):
'device': device.pk,
'name': name,
'form_factor': form.cleaned_data['form_factor'],
'mac_address': form.cleaned_data['mac_address'],
'mgmt_only': form.cleaned_data['mgmt_only'],
'description': form.cleaned_data['description'],
})
@ -1339,6 +1340,7 @@ class InterfaceBulkAddView(PermissionRequiredMixin, BulkEditView):
iface_form = forms.InterfaceForm({
'device': device.pk,
'name': name,
'mac_address': mac_address,
'form_factor': form.cleaned_data['form_factor'],
'mgmt_only': form.cleaned_data['mgmt_only'],
'description': form.cleaned_data['description'],

View File

@ -295,9 +295,6 @@
{% if interfaces or device.device_type.is_network_device %}
<div class="panel panel-default">
<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>
</div>
<table class="table table-hover panel-body">
@ -309,6 +306,14 @@
</tr>
{% endfor %}
</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>
{% endif %}
{% if cs_ports or device.device_type.is_console_server %}

View File

@ -6,22 +6,26 @@
{% endif %}
</td>
{% if not iface.is_physical %}
<td colspan="2">Virtual</td>
<td>Virtual</td>
{% elif iface.connection %}
{% with iface.get_connected_interface as connected_iface %}
<td>
<a href="{% url 'dcim:device' pk=connected_iface.device.pk %}">{{ connected_iface.device }}</a>
</td>
<td>
&Colon;
<span title="{{ connected_iface.get_form_factor_display }}">{{ connected_iface }}</span>
</td>
{% endwith %}
{% elif iface.circuit %}
<td colspan="2">
<td>
<a href="{% url 'circuits:circuit' pk=iface.circuit.pk %}">{{ iface.circuit }}</a>
</td>
{% 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 %}
<td class="text-right">
{% if iface.circuit or iface.connection %}