Initial commit for Device History

This commit is contained in:
Mark 2017-04-02 00:38:40 +02:00
parent 1c38f705a7
commit 6a9ec035d6
10 changed files with 250 additions and 3 deletions

View File

@ -484,6 +484,12 @@ class InventoryItemFilter(DeviceComponentFilterSet):
fields = ['name']
class HistoryLogFilter(DeviceComponentFilterSet):
class Meta:
model = InventoryItem
fields = ['name']
class ConsoleConnectionFilter(django_filters.FilterSet):
site = django_filters.CharFilter(
method='filter_site',

View File

@ -23,7 +23,7 @@ from .models import (
Interface, IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_ORDERING_CHOICES, InterfaceConnection, InterfaceTemplate,
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_TYPE_CHOICES,
RACK_WIDTH_CHOICES, Rack, RackGroup, RackReservation, RackRole, Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD,
SUBDEVICE_ROLE_PARENT, VIRTUAL_IFACE_TYPES
SUBDEVICE_ROLE_PARENT, VIRTUAL_IFACE_TYPES, HistoryRole, HistoryLog
)
@ -1710,3 +1710,26 @@ class InventoryItemForm(BootstrapMixin, forms.ModelForm):
class Meta:
model = InventoryItem
fields = ['name', 'manufacturer', 'part_id', 'serial']
#
# HistoryRole
#
class HistoryRoleForm(BootstrapMixin, forms.ModelForm):
slug = SlugField()
class Meta:
model = HistoryRole
fields = ['name', 'slug', 'color']
#
# HistoryLog
#
class HistoryLogForm(BootstrapMixin, forms.ModelForm):
class Meta:
model = HistoryLog
fields = ['device', 'role', 'message']

View File

@ -1421,3 +1421,42 @@ class InventoryItem(models.Model):
def __str__(self):
return self.name
#
# History
#
@python_2_unicode_compatible
class HistoryRole(models.Model):
"""
Grouping the history activity type
"""
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True)
color = ColorField()
class Meta:
ordering = ['name']
def __str__(self):
return self.name
@python_2_unicode_compatible
class HistoryLog(models.Model):
"""
A history is a free text form which gives capability to store any comment or change in time based view.
"""
time = models.DateTimeField(auto_now_add=True, editable=False)
user = models.ForeignKey(User, related_name='history_log', blank=True, null=True, on_delete=models.SET_NULL)
device = models.ForeignKey('Device', related_name='history_log', on_delete=models.CASCADE)
role = models.ForeignKey('HistoryRole', related_name='history_log', blank=True, null=True, on_delete=models.SET_NULL)
message = models.TextField(blank=True)
class Meta:
ordering = ['-time']
def __str__(self):
return self.message

View File

@ -6,7 +6,7 @@ from utilities.tables import BaseTable, SearchTable, ToggleColumn
from .models import (
ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType,
Interface, InterfaceTemplate, Manufacturer, Platform, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack,
RackGroup, Region, Site,
RackGroup, Region, Site, HistoryRole,
)
@ -70,6 +70,12 @@ DEVICEROLE_ACTIONS = """
{% endif %}
"""
HISTORYLOGROLE_ACTIONS = """
{% if perms.dcim.change_historylogrole %}
<a href="{% url 'dcim:historylogrole_edit' pk=record.pk %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
"""
MANUFACTURER_ACTIONS = """
{% if perms.dcim.change_manufacturer %}
<a href="{% url 'dcim:manufacturer_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
@ -498,3 +504,19 @@ class InterfaceConnectionTable(BaseTable):
class Meta(BaseTable.Meta):
model = Interface
fields = ('device_a', 'interface_a', 'device_b', 'interface_b')
#
# HistoryLog roles
#
class HistoryLogRoleTable(BaseTable):
pk = ToggleColumn()
name = tables.Column(verbose_name='Name')
color = tables.TemplateColumn(COLOR_LABEL, verbose_name='Color')
slug = tables.Column(verbose_name='Slug')
actions = tables.TemplateColumn(template_code=HISTORYLOGROLE_ACTIONS, attrs={'td': {'class': 'text-right'}},
verbose_name='')
class Meta(BaseTable.Meta):
model = HistoryRole
fields = ('pk', 'name', 'color', 'slug', 'actions')

View File

@ -113,6 +113,7 @@ urlpatterns = [
url(r'^devices/(?P<pk>\d+)/edit/$', views.DeviceEditView.as_view(), name='device_edit'),
url(r'^devices/(?P<pk>\d+)/delete/$', views.DeviceDeleteView.as_view(), name='device_delete'),
url(r'^devices/(?P<pk>\d+)/inventory/$', views.device_inventory, name='device_inventory'),
url(r'^devices/(?P<pk>\d+)/history-log/$', views.device_historylog, name='device_historylog'),
url(r'^devices/(?P<pk>\d+)/lldp-neighbors/$', views.device_lldp_neighbors, name='device_lldp_neighbors'),
url(r'^devices/(?P<pk>\d+)/ip-addresses/assign/$', views.ipaddress_assign, name='ipaddress_assign'),
url(r'^devices/(?P<pk>\d+)/add-secret/$', secret_add, name='device_addsecret'),
@ -178,6 +179,18 @@ urlpatterns = [
url(r'^inventory-items/(?P<pk>\d+)/edit/$', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
url(r'^inventory-items/(?P<pk>\d+)/delete/$', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
# HistoryLog
url(r'^devices/(?P<device>\d+)/history-log/add/$', views.HistoryLogEditView.as_view(),
name='historylog_add'),
url(r'^history-log/(?P<pk>\d+)/edit/$', views.HistoryLogEditView.as_view(), name='historylog_edit'),
url(r'^history-log/(?P<pk>\d+)/delete/$', views.HistoryLogDeleteView.as_view(), name='historylog_delete'),
# HistoryLogRoles
url(r'^history-log-roles/$', views.HistoryLogRoleListView.as_view(), name='historylogrole_list'),
url(r'^history-log-roles/add/$', views.HistoryLogRoleEditView.as_view(), name='historylogrole_add'),
url(r'^history-log-roles/delete/$', views.HistoryLogRoleBulkDeleteView.as_view(), name='historylogrole_bulk_delete'),
url(r'^history-log-roles/(?P<pk>\d+)/edit/$', views.HistoryLogRoleEditView.as_view(), name='historylogrole_edit'),
# Console/power/interface connections
url(r'^console-connections/$', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'),
url(r'^console-connections/import/$', views.ConsoleConnectionsBulkImportView.as_view(), name='console_connections_import'),

View File

@ -26,7 +26,7 @@ from .models import (
CONNECTION_STATUS_CONNECTED, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device,
DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, Interface, InterfaceConnection, InterfaceTemplate,
Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
RackReservation, RackRole, Region, Site,
RackReservation, RackRole, Region, Site, HistoryLog, HistoryRole,
)
@ -808,6 +808,17 @@ def device_inventory(request, pk):
})
def device_historylog(request, pk):
device = get_object_or_404(Device, pk=pk)
historylog_items = HistoryLog.objects.filter(device=device)
return render(request, 'dcim/device_historylog.html', {
'device': device,
'historylog_items': historylog_items,
})
def device_lldp_neighbors(request, pk):
device = get_object_or_404(Device, pk=pk)
@ -1611,3 +1622,49 @@ class InventoryItemEditView(PermissionRequiredMixin, ComponentEditView):
class InventoryItemDeleteView(PermissionRequiredMixin, ComponentDeleteView):
permission_required = 'dcim.delete_inventoryitem'
model = InventoryItem
#
# HistoryLog
#
class HistoryLogEditView(PermissionRequiredMixin, ComponentEditView):
permission_required = 'dcim.change_historylog'
model = HistoryLog
form_class = forms.HistoryLogForm
def alter_obj(self, obj, request, url_args, url_kwargs):
if 'device' in url_kwargs:
obj.device = get_object_or_404(Device, pk=url_kwargs['device'])
obj.user = request.user
return obj
class HistoryLogDeleteView(PermissionRequiredMixin, ComponentDeleteView):
permission_required = 'dcim.delete_historylog'
model = HistoryLog
#
# HistoryLogRole
#
class HistoryLogRoleListView(ObjectListView):
queryset = HistoryRole.objects.annotate(historylog_count=Count('history_log'))
table = tables.HistoryLogRoleTable
template_name = 'dcim/historylogrole_list.html'
class HistoryLogRoleEditView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'dcim.change_historylogrole'
model = HistoryRole
form_class = forms.HistoryRoleForm
def get_return_url(self, obj):
return reverse('dcim:historylogrole_list')
class HistoryLogRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_historylogrole'
cls = HistoryRole
default_return_url = 'dcim:historylogrole_list'

View File

@ -0,0 +1,46 @@
{% extends '_base.html' %}
{% block title %}{{ device }} - Inventory{% endblock %}
{% block content %}
{% include 'dcim/inc/device_header.html' with active_tab='historylog' %}
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<strong>History Log</strong>
</div>
<table class="table table-hover table-condensed panel-body" id="hardware">
<thead>
<tr>
<th>Time</th>
<th>User</th>
<th>Role</th>
<th>Message</th>
<th></th>
</tr>
</thead>
<tbody>
{% for item in historylog_items %}
{% with template_name='dcim/inc/historylog.html' indent=0 %}
{% include template_name %}
{% endwith %}
{% endfor %}
</tbody>
</table>
</div>
{% if perms.dcim.add_historylog %}
<a href="{% url 'dcim:historylog_add' device=device.pk %}" class="btn btn-success">
<span class="fa fa-plus" aria-hidden="true"></span>
Add History Log
</a>
{% endif %}
{% if perms.dcim.add_historylogrole %}
<a href="{% url 'dcim:historylogrole_list' %}" class="btn btn-success">
<span class="fa fa-plus" aria-hidden="true"></span>
History Log Roles
</a>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends '_base.html' %}
{% load helpers %}
{% block title %}History Role{% endblock %}
{% block content %}
<div class="pull-right">
{% if perms.dcim.add_historylogrole %}
<a href="{% url 'dcim:historylogrole_add' %}" class="btn btn-primary">
<span class="fa fa-plus" aria-hidden="true"></span>
Add a historylog role
</a>
{% endif %}
</div>
<h1>History Roles</h1>
<div class="row">
<div class="col-md-12">
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:historylogrole_bulk_delete' %}
</div>
</div>
{% endblock %}

View File

@ -48,4 +48,5 @@
{% if device.status %}
<li role="presentation"{% if active_tab == 'lldp-neighbors' %} class="active"{% endif %}><a href="{% url 'dcim:device_lldp_neighbors' pk=device.pk %}">LLDP Neighbors</a></li>
{% endif %}
<li role="presentation"{% if active_tab == 'historylog' %} class="active"{% endif %}><a href="{% url 'dcim:device_historylog' pk=device.pk %}">History Log</a></li>
</ul>

View File

@ -0,0 +1,19 @@
<tr>
<td style="padding-left: {{ indent|add:5 }}px">{{ item.time }}</td>
<td>{{ item.user }}</td>
<td>{{ item.role }}</td>
<td>{{ item.message }}</td>
<td class="text-right">
{% if perms.dcim.change_historylog_item %}
<a href="{% url 'dcim:historylog_edit' pk=item.pk %}?return_url={% url 'dcim:device_historylog' pk=device.pk %}" class="btn btn-xs btn-warning"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a>
{% endif %}
{% if perms.dcim.delete_historylog_item %}
<a href="{% url 'dcim:historylog_delete' pk=item.pk %}?return_url={% url 'dcim:device_historylog' pk=device.pk %}" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
{% endif %}
</td>
</tr>
{% for item in item.child_items.all %}
{% with template_name='dcim/inc/historylog.html' indent=indent|add:20 %}
{% include template_name %}
{% endwith %}
{% endfor %}