mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Introduced ability to edit/delete modules
This commit is contained in:
@@ -146,7 +146,7 @@ class InterfaceAdmin(admin.TabularInline):
|
||||
|
||||
class ModuleAdmin(admin.TabularInline):
|
||||
model = Module
|
||||
readonly_fields = ['parent']
|
||||
readonly_fields = ['parent', 'discovered']
|
||||
|
||||
|
||||
@admin.register(Device)
|
||||
|
@@ -12,7 +12,7 @@ from utilities.forms import (
|
||||
from .models import (
|
||||
CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED, ConsolePort, ConsolePortTemplate,
|
||||
ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType, Interface, IFACE_FF_VIRTUAL,
|
||||
InterfaceConnection, InterfaceTemplate, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort,
|
||||
InterfaceConnection, InterfaceTemplate, Manufacturer, Module, Platform, PowerOutlet, PowerOutletTemplate, PowerPort,
|
||||
PowerPortTemplate, Rack, RackGroup, Site, STATUS_CHOICES
|
||||
)
|
||||
|
||||
@@ -1107,3 +1107,14 @@ class IPAddressForm(forms.ModelForm, BootstrapMixin):
|
||||
# If this device does not have any IP addresses assigned, default to setting the first IP as its primary
|
||||
if not IPAddress.objects.filter(interface__device=device).count():
|
||||
self.fields['set_as_primary'].initial = True
|
||||
|
||||
|
||||
#
|
||||
# Interfaces
|
||||
#
|
||||
|
||||
class ModuleForm(forms.ModelForm, BootstrapMixin):
|
||||
|
||||
class Meta:
|
||||
model = Module
|
||||
fields = ['name', 'part_id', 'serial']
|
||||
|
20
netbox/dcim/migrations/0007_module_discovered.py
Normal file
20
netbox/dcim/migrations/0007_module_discovered.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-06-15 16:31
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0006_remove_device_ro_snmp'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='module',
|
||||
name='discovered',
|
||||
field=models.BooleanField(default=False, verbose_name=b'Discovered'),
|
||||
),
|
||||
]
|
@@ -692,7 +692,6 @@ class InterfaceConnection(models.Model):
|
||||
verbose_name='Status')
|
||||
|
||||
def clean(self):
|
||||
|
||||
if self.interface_a == self.interface_b:
|
||||
raise ValidationError("Cannot connect an interface to itself")
|
||||
|
||||
@@ -706,6 +705,7 @@ class Module(models.Model):
|
||||
name = models.CharField(max_length=50, verbose_name='Name')
|
||||
part_id = models.CharField(max_length=50, verbose_name='Part ID', blank=True)
|
||||
serial = models.CharField(max_length=50, verbose_name='Serial number', blank=True)
|
||||
discovered = models.BooleanField(default=False, verbose_name='Discovered')
|
||||
|
||||
class Meta:
|
||||
ordering = ['device__id', 'parent__id', 'name']
|
||||
|
@@ -141,4 +141,8 @@ urlpatterns = [
|
||||
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.interface_edit, name='interface_edit'),
|
||||
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.interface_delete, name='interface_delete'),
|
||||
|
||||
# Modules
|
||||
url(r'^modules/(?P<pk>\d+)/edit/$', views.module_edit, name='module_edit'),
|
||||
url(r'^modules/(?P<pk>\d+)/delete/$', views.module_delete, name='module_delete'),
|
||||
|
||||
]
|
||||
|
@@ -1497,3 +1497,51 @@ def ipaddress_assign(request, pk):
|
||||
'form': form,
|
||||
'cancel_url': reverse('dcim:device', kwargs={'pk': device.pk}),
|
||||
})
|
||||
|
||||
|
||||
#
|
||||
# Modules
|
||||
#
|
||||
|
||||
@permission_required('dcim.change_module')
|
||||
def module_edit(request, pk):
|
||||
|
||||
module = get_object_or_404(Module, pk=pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = forms.ModuleForm(request.POST, instance=module)
|
||||
if form.is_valid():
|
||||
module = form.save()
|
||||
messages.success(request, "Modified {} module {}".format(module.device.name, module.name))
|
||||
return redirect('dcim:device_inventory', pk=module.device.pk)
|
||||
|
||||
else:
|
||||
form = forms.ModuleForm(instance=module)
|
||||
|
||||
return render(request, 'dcim/module_edit.html', {
|
||||
'module': module,
|
||||
'form': form,
|
||||
'cancel_url': reverse('dcim:device_inventory', kwargs={'pk': module.device.pk}),
|
||||
})
|
||||
|
||||
|
||||
@permission_required('dcim.delete_module')
|
||||
def module_delete(request, pk):
|
||||
|
||||
module = get_object_or_404(Module, pk=pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
form = ConfirmationForm(request.POST)
|
||||
if form.is_valid():
|
||||
module.delete()
|
||||
messages.success(request, "Module {} has been deleted from {}".format(module, module.device))
|
||||
return redirect('dcim:device_inventory', pk=module.device.pk)
|
||||
|
||||
else:
|
||||
form = ConfirmationForm()
|
||||
|
||||
return render(request, 'dcim/module_delete.html', {
|
||||
'module': module,
|
||||
'form': form,
|
||||
'cancel_url': reverse('dcim:device_inventory', kwargs={'pk': module.device.pk}),
|
||||
})
|
||||
|
@@ -28,7 +28,7 @@ class Command(BaseCommand):
|
||||
def create_modules(modules, parent=None):
|
||||
for module in modules:
|
||||
m = Module(device=device, parent=parent, name=module['name'], part_id=module['part_id'],
|
||||
serial=module['serial'])
|
||||
serial=module['serial'], discovered=True)
|
||||
m.save()
|
||||
create_modules(module.get('modules', []), parent=m)
|
||||
|
||||
@@ -119,7 +119,7 @@ class Command(BaseCommand):
|
||||
if device.serial != inventory['chassis']['serial']:
|
||||
device.serial = inventory['chassis']['serial']
|
||||
device.save()
|
||||
Module.objects.filter(device=device).delete()
|
||||
Module.objects.filter(device=device, discovered=True).delete()
|
||||
create_modules(inventory.get('modules', []))
|
||||
|
||||
self.stdout.write("Finished!")
|
||||
|
@@ -42,38 +42,76 @@
|
||||
<div class="panel-heading">
|
||||
<strong>Hardware</strong>
|
||||
</div>
|
||||
<table class="table table-hover panel-body" id="hardware">
|
||||
<table class="table table-hover table-condensed panel-body" id="hardware">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Module</th>
|
||||
<th>Part Number</th>
|
||||
<th>Serial Number</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for m in modules %}
|
||||
<tr>
|
||||
<td>{% if not m.discovered %}<i class="fa fa-asterisk" title="Manually created"></i>{% endif %}</td>
|
||||
<td>{{ m.name }}</td>
|
||||
<td>{{ m.part_id }}</td>
|
||||
<td>{{ m.serial }}</td>
|
||||
<td class="text-right">
|
||||
{% if perms.dcim.change_module %}
|
||||
<a href="{% url 'dcim:module_edit' pk=m.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_module %}
|
||||
<a href="{% url 'dcim:module_delete' pk=m.pk %}" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% for m2 in m.submodules.all %}
|
||||
<tr>
|
||||
<td>{% if not m.discovered %}<i class="fa fa-asterisk" title="Manually created"></i>{% endif %}</td>
|
||||
<td style="padding-left: 20px">{{ m2.name }}</td>
|
||||
<td>{{ m2.part_id }}</td>
|
||||
<td>{{ m2.serial }}</td>
|
||||
<td class="text-right">
|
||||
{% if perms.dcim.change_module %}
|
||||
<a href="{% url 'dcim:module_edit' pk=m.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_module %}
|
||||
<a href="{% url 'dcim:module_delete' pk=m.pk %}" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% for m3 in m2.submodules.all %}
|
||||
<tr>
|
||||
<td>{% if not m.discovered %}<i class="fa fa-asterisk" title="Manually created"></i>{% endif %}</td>
|
||||
<td style="padding-left: 40px">{{ m3.name }}</td>
|
||||
<td>{{ m3.part_id }}</td>
|
||||
<td>{{ m3.serial }}</td>
|
||||
<td class="text-right">
|
||||
{% if perms.dcim.change_module %}
|
||||
<a href="{% url 'dcim:module_edit' pk=m.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_module %}
|
||||
<a href="{% url 'dcim:module_delete' pk=m.pk %}" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% for m4 in m3.submodules.all %}
|
||||
<tr>
|
||||
<td>{% if not m.discovered %}<i class="fa fa-asterisk" title="Manually created"></i>{% endif %}</td>
|
||||
<td style="padding-left: 60px">{{ m4.name }}</td>
|
||||
<td>{{ m4.part_id }}</td>
|
||||
<td>{{ m4.serial }}</td>
|
||||
<td class="text-right">
|
||||
{% if perms.dcim.change_module %}
|
||||
<a href="{% url 'dcim:module_edit' pk=m.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
{% if perms.dcim.delete_module %}
|
||||
<a href="{% url 'dcim:module_delete' pk=m.pk %}" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
8
netbox/templates/dcim/module_delete.html
Normal file
8
netbox/templates/dcim/module_delete.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends 'utilities/confirmation_form.html' %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Delete module {{ module }}?{% endblock %}
|
||||
|
||||
{% block message %}
|
||||
<p>Are you sure you want to delete this module from <strong>{{ module.device }}</strong>?</p>
|
||||
{% endblock %}
|
47
netbox/templates/dcim/module_edit.html
Normal file
47
netbox/templates/dcim/module_edit.html
Normal file
@@ -0,0 +1,47 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Editing {{ module.device }} {{ module }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="." method="post" class="form form-horizontal">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
{% if form.non_field_errors %}
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading"><strong>Errors</strong></div>
|
||||
<div class="panel-body">
|
||||
{{ form.non_field_errors }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Editing {{ module.device }} {{ module }}</strong>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label required">Device</label>
|
||||
<div class="col-md-9">
|
||||
<p class="form-control-static">{{ module.device }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% render_form form %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
{% if module.pk %}
|
||||
<button type="submit" name="_update" class="btn btn-primary">Save</button>
|
||||
{% else %}
|
||||
<button type="submit" name="_create" class="btn btn-primary">Create</button>
|
||||
<button type="submit" name="_addanother" class="btn btn-primary">Create and Add More</button>
|
||||
{% endif %}
|
||||
<a href="{{ cancel_url }}" class="btn btn-default">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user