Merge branch 'develop' into feature

This commit is contained in:
Jeremy Stretch
2021-03-31 17:11:54 -04:00
12 changed files with 86 additions and 78 deletions

View File

@@ -1054,7 +1054,7 @@ class RackReservationBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomField
nullable_fields = []
class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm):
class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
model = RackReservation
field_order = ['q', 'region_id', 'site_id', 'location_id', 'user_id', 'tenant_group_id', 'tenant_id']
q = forms.CharField(
@@ -4319,7 +4319,7 @@ class CableBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFo
})
class CableFilterForm(BootstrapMixin, forms.Form):
class CableFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Cable
q = forms.CharField(
required=False,

View File

@@ -244,6 +244,11 @@ class CableTerminationTable(BaseTable):
cable = tables.Column(
linkify=True
)
cable_color = ColorColumn(
accessor='cable.color',
orderable=False,
verbose_name='Cable Color'
)
cable_peer = tables.TemplateColumn(
accessor='_cable_peer',
template_code=CABLETERMINATION,
@@ -276,8 +281,8 @@ class ConsolePortTable(DeviceComponentTable, PathEndpointTable):
class Meta(DeviceComponentTable.Meta):
model = ConsolePort
fields = (
'pk', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
'connection', 'tags',
'pk', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_color',
'cable_peer', 'connection', 'tags',
)
default_columns = ('pk', 'device', 'name', 'label', 'type', 'speed', 'description')
@@ -296,8 +301,8 @@ class DeviceConsolePortTable(ConsolePortTable):
class Meta(DeviceComponentTable.Meta):
model = ConsolePort
fields = (
'pk', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
'connection', 'tags', 'actions'
'pk', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_color',
'cable_peer', 'connection', 'tags', 'actions'
)
default_columns = ('pk', 'name', 'label', 'type', 'speed', 'description', 'cable', 'connection', 'actions')
row_attrs = {
@@ -319,8 +324,8 @@ class ConsoleServerPortTable(DeviceComponentTable, PathEndpointTable):
class Meta(DeviceComponentTable.Meta):
model = ConsoleServerPort
fields = (
'pk', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
'connection', 'tags',
'pk', 'device', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_color',
'cable_peer', 'connection', 'tags',
)
default_columns = ('pk', 'device', 'name', 'label', 'type', 'speed', 'description')
@@ -340,8 +345,8 @@ class DeviceConsoleServerPortTable(ConsoleServerPortTable):
class Meta(DeviceComponentTable.Meta):
model = ConsoleServerPort
fields = (
'pk', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_peer',
'connection', 'tags', 'actions',
'pk', 'name', 'label', 'type', 'speed', 'description', 'mark_connected', 'cable', 'cable_color',
'cable_peer', 'connection', 'tags', 'actions',
)
default_columns = ('pk', 'name', 'label', 'type', 'speed', 'description', 'cable', 'connection', 'actions')
row_attrs = {
@@ -364,7 +369,7 @@ class PowerPortTable(DeviceComponentTable, PathEndpointTable):
model = PowerPort
fields = (
'pk', 'device', 'name', 'label', 'type', 'description', 'mark_connected', 'maximum_draw', 'allocated_draw',
'cable', 'cable_peer', 'connection', 'tags',
'cable', 'cable_color', 'cable_peer', 'connection', 'tags',
)
default_columns = ('pk', 'device', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description')
@@ -385,7 +390,7 @@ class DevicePowerPortTable(PowerPortTable):
model = PowerPort
fields = (
'pk', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'mark_connected', 'cable',
'cable_peer', 'connection', 'tags', 'actions',
'cable_color', 'cable_peer', 'connection', 'tags', 'actions',
)
default_columns = (
'pk', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', 'cable', 'connection',
@@ -414,7 +419,7 @@ class PowerOutletTable(DeviceComponentTable, PathEndpointTable):
model = PowerOutlet
fields = (
'pk', 'device', 'name', 'label', 'type', 'description', 'power_port', 'feed_leg', 'mark_connected', 'cable',
'cable_peer', 'connection', 'tags',
'cable_color', 'cable_peer', 'connection', 'tags',
)
default_columns = ('pk', 'device', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description')
@@ -434,7 +439,7 @@ class DevicePowerOutletTable(PowerOutletTable):
model = PowerOutlet
fields = (
'pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'mark_connected', 'cable',
'cable_peer', 'connection', 'tags', 'actions',
'cable_color', 'cable_peer', 'connection', 'tags', 'actions',
)
default_columns = (
'pk', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', 'cable', 'connection', 'actions',
@@ -475,7 +480,7 @@ class InterfaceTable(DeviceComponentTable, BaseInterfaceTable, PathEndpointTable
model = Interface
fields = (
'pk', 'device', 'name', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', 'mode', 'mac_address',
'description', 'mark_connected', 'cable', 'cable_peer', 'connection', 'tags', 'ip_addresses',
'description', 'mark_connected', 'cable', 'cable_color', 'cable_peer', 'connection', 'tags', 'ip_addresses',
'untagged_vlan', 'tagged_vlans',
)
default_columns = ('pk', 'device', 'name', 'label', 'enabled', 'type', 'description')
@@ -506,7 +511,7 @@ class DeviceInterfaceTable(InterfaceTable):
model = Interface
fields = (
'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mgmt_only', 'mtu', 'mode', 'mac_address',
'description', 'mark_connected', 'cable', 'cable_peer', 'connection', 'tags', 'ip_addresses',
'description', 'mark_connected', 'cable', 'cable_color', 'cable_peer', 'connection', 'tags', 'ip_addresses',
'untagged_vlan', 'tagged_vlans', 'actions',
)
default_columns = (
@@ -540,7 +545,7 @@ class FrontPortTable(DeviceComponentTable, CableTerminationTable):
model = FrontPort
fields = (
'pk', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'mark_connected',
'cable', 'cable_peer', 'tags',
'cable', 'cable_color', 'cable_peer', 'tags',
)
default_columns = ('pk', 'device', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description')
@@ -561,7 +566,7 @@ class DeviceFrontPortTable(FrontPortTable):
model = FrontPort
fields = (
'pk', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'mark_connected', 'cable',
'cable_peer', 'tags', 'actions',
'cable_color', 'cable_peer', 'tags', 'actions',
)
default_columns = (
'pk', 'name', 'label', 'type', 'rear_port', 'rear_port_position', 'description', 'cable', 'cable_peer',
@@ -587,7 +592,7 @@ class RearPortTable(DeviceComponentTable, CableTerminationTable):
model = RearPort
fields = (
'pk', 'device', 'name', 'label', 'type', 'positions', 'description', 'mark_connected', 'cable',
'cable_peer', 'tags',
'cable_color', 'cable_peer', 'tags',
)
default_columns = ('pk', 'device', 'name', 'label', 'type', 'description')
@@ -607,8 +612,8 @@ class DeviceRearPortTable(RearPortTable):
class Meta(DeviceComponentTable.Meta):
model = RearPort
fields = (
'pk', 'name', 'label', 'type', 'positions', 'description', 'mark_connected', 'cable', 'cable_peer', 'tags',
'actions',
'pk', 'name', 'label', 'type', 'positions', 'description', 'mark_connected', 'cable', 'cable_color',
'cable_peer', 'tags', 'actions',
)
default_columns = (
'pk', 'name', 'label', 'type', 'positions', 'description', 'cable', 'cable_peer', 'actions',

View File

@@ -1,10 +1,8 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from dcim.models import PowerFeed, PowerPanel
from utilities.tables import BaseTable, ChoiceFieldColumn, LinkedCountColumn, TagColumn, ToggleColumn
from .devices import CableTerminationTable
from .template_code import POWERFEED_CABLE, POWERFEED_CABLETERMINATION
__all__ = (
'PowerFeedTable',
@@ -68,7 +66,8 @@ class PowerFeedTable(CableTerminationTable):
model = PowerFeed
fields = (
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase',
'max_utilization', 'mark_connected', 'cable', 'cable_peer', 'connection', 'available_power', 'tags',
'max_utilization', 'mark_connected', 'cable', 'cable_color', 'cable_peer', 'connection', 'available_power',
'tags',
)
default_columns = (
'pk', 'name', 'power_panel', 'rack', 'status', 'type', 'supply', 'voltage', 'amperage', 'phase', 'cable',

View File

@@ -502,10 +502,11 @@ class RackView(generic.ObjectView):
queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
def get_extra_context(self, request, instance):
# Get 0U and child devices located within the rack
# Get 0U devices located within the rack
nonracked_devices = Device.objects.filter(
rack=instance,
position__isnull=True
position__isnull=True,
parent_bay__isnull=True
).prefetch_related('device_type__manufacturer')
peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)

View File

@@ -131,16 +131,17 @@
</li>
{% endif %}
{% endwith %}
{% if perms.dcim.napalm_read_device %}
{% if object.status != 'active' %}
{% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='Device must be in active status' %}
{% elif not object.platform %}
{% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='No platform assigned to this device' %}
{% elif not object.platform.napalm_driver %}
{% include 'dcim/inc/device_napalm_tabs.html' with disabled_message='No NAPALM driver assigned for this platform' %}
{% else %}
{% include 'dcim/inc/device_napalm_tabs.html' %}
{% endif %}
{% if perms.dcim.napalm_read_device and object.status == 'active' and object.primary_ip and object.platform.napalm_driver %}
{# NAPALM-enabled tabs #}
<li role="presentation"{% if active_tab == 'status' %} class="active"{% endif %}>
<a href="{% url 'dcim:device_status' pk=object.pk %}">Status</a>
</li>
<li role="presentation"{% if active_tab == 'lldp-neighbors' %} class="active"{% endif %}>
<a href="{% url 'dcim:device_lldp_neighbors' pk=object.pk %}">LLDP Neighbors</a>
</li>
<li role="presentation"{% if active_tab == 'config' %} class="active"{% endif %}>
<a href="{% url 'dcim:device_config' pk=object.pk %}">Configuration</a>
</li>
{% endif %}
{% if perms.extras.view_configcontext %}
<li role="presentation"{% if active_tab == 'config-context' %} class="active"{% endif %}>

View File

@@ -1,15 +0,0 @@
{% if not disabled_message %}
<li role="presentation"{% if active_tab == 'status' %} class="active"{% endif %}>
<a href="{% url 'dcim:device_status' pk=object.pk %}">Status</a>
</li>
<li role="presentation"{% if active_tab == 'lldp-neighbors' %} class="active"{% endif %}>
<a href="{% url 'dcim:device_lldp_neighbors' pk=object.pk %}">LLDP Neighbors</a>
</li>
<li role="presentation"{% if active_tab == 'config' %} class="active"{% endif %}>
<a href="{% url 'dcim:device_config' pk=object.pk %}">Configuration</a>
</li>
{% else %}
<li role="presentation" class="disabled"><a href="#" title="{{ disabled_message }}">Status</a></li>
<li role="presentation" class="disabled"><a href="#" title="{{ disabled_message }}">LLDP Neighbors</a></li>
<li role="presentation" class="disabled"><a href="#" title="{{ disabled_message }}">Configuration</a></li>
{% endif %}

View File

@@ -11,12 +11,8 @@
<div class="panel-heading">
<div class="pull-right noprint">
<a class="btn btn-xs btn-success copy-token" data-clipboard-target="#token_{{ token.pk }}">Copy</a>
{% if perms.users.change_token %}
<a href="{% url 'user:token_edit' pk=token.pk %}" class="btn btn-xs btn-warning">Edit</a>
{% endif %}
{% if perms.users.delete_token %}
<a href="{% url 'user:token_delete' pk=token.pk %}" class="btn btn-xs btn-danger">Delete</a>
{% endif %}
<a href="{% url 'user:token_edit' pk=token.pk %}" class="btn btn-xs btn-warning">Edit</a>
<a href="{% url 'user:token_delete' pk=token.pk %}" class="btn btn-xs btn-danger">Delete</a>
</div>
<i class="mdi mdi-key"></i>
<samp><span id="token_{{ token.pk }}">{{ token.key }}</span></samp>
@@ -55,16 +51,10 @@
{% empty %}
<p>You do not have any API tokens.</p>
{% endfor %}
{% if perms.users.add_token %}
<a href="{% url 'user:token_add' %}" class="btn btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span>
Add a token
</a>
{% else %}
<div class="alert alert-info text-center" role="alert">
You do not have permission to create new API tokens. If needed, ask an administrator to enable token creation for your account or an assigned group.
</div>
{% endif %}
<a href="{% url 'user:token_add' %}" class="btn btn-primary">
<span class="mdi mdi-plus-thick" aria-hidden="true"></span>
Add a token
</a>
</div>
</div>
{% endblock %}

View File

@@ -6,7 +6,7 @@ from django.contrib.auth import login as auth_login, logout as auth_logout, upda
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import update_last_login
from django.contrib.auth.signals import user_logged_in
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
@@ -282,13 +282,9 @@ class TokenEditView(LoginRequiredMixin, View):
def get(self, request, pk=None):
if pk is not None:
if not request.user.has_perm('users.change_token'):
return HttpResponseForbidden()
if pk:
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
else:
if not request.user.has_perm('users.add_token'):
return HttpResponseForbidden()
token = Token(user=request.user)
form = TokenForm(instance=token)
@@ -302,11 +298,11 @@ class TokenEditView(LoginRequiredMixin, View):
def post(self, request, pk=None):
if pk is not None:
if pk:
token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
form = TokenForm(request.POST, instance=token)
else:
token = Token()
token = Token(user=request.user)
form = TokenForm(request.POST)
if form.is_valid():
@@ -314,7 +310,7 @@ class TokenEditView(LoginRequiredMixin, View):
token.user = request.user
token.save()
msg = "Modified token {}".format(token) if pk else "Created token {}".format(token)
msg = f"Modified token {token}" if pk else f"Created token {token}"
messages.success(request, msg)
if '_addanother' in request.POST:

View File

@@ -800,6 +800,10 @@ class VMInterfaceFilterForm(forms.Form):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
mac_address = forms.CharField(
required=False,
label='MAC address'
)
tag = TagFilterField(model)