From d3b16ba443b0d1ca45299f6765cd92fc926c7ed0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 7 Apr 2017 14:50:08 -0400 Subject: [PATCH 01/12] Fixes #1057: Corrected VLAN validation during prefix import --- netbox/ipam/forms.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index fef30d78f..ec324a8b0 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -210,28 +210,33 @@ class PrefixFromCSVForm(forms.ModelForm): site = self.cleaned_data.get('site') vlan_group_name = self.cleaned_data.get('vlan_group_name') vlan_vid = self.cleaned_data.get('vlan_vid') - - # Validate VLAN vlan_group = None + vlan = None + + # Validate VLAN group if vlan_group_name: try: vlan_group = VLANGroup.objects.get(site=site, name=vlan_group_name) except VLANGroup.DoesNotExist: - self.add_error('vlan_group_name', "Invalid VLAN group ({} - {}).".format(site, vlan_group_name)) - if vlan_vid and vlan_group: + if site: + self.add_error('vlan_group_name', "Invalid VLAN group ({} - {}).".format(site, vlan_group_name)) + else: + self.add_error('vlan_group_name', "Invalid global VLAN group ({}).".format(vlan_group_name)) + + # Validate VLAN + if vlan_vid: try: - self.instance.vlan = VLAN.objects.get(group=vlan_group, vid=vlan_vid) + self.instance.vlan = VLAN.objects.get(site=site, group=vlan_group, vid=vlan_vid) except VLAN.DoesNotExist: - self.add_error('vlan_vid', "Invalid VLAN ID ({} - {}).".format(vlan_group, vlan_vid)) - elif vlan_vid and site: - try: - self.instance.vlan = VLAN.objects.get(site=site, vid=vlan_vid) - except VLAN.DoesNotExist: - self.add_error('vlan_vid', "Invalid VLAN ID ({}) for site {}.".format(vlan_vid, site)) + if site: + self.add_error('vlan_vid', "Invalid VLAN ID ({}) for site {}.".format(vlan_vid, site)) + elif vlan_group: + self.add_error('vlan_vid', "Invalid VLAN ID ({}) for group {}.".format(vlan_vid, vlan_group_name)) + elif not vlan_group_name: + self.add_error('vlan_vid', "Invalid global VLAN ID ({}).".format(vlan_vid)) except VLAN.MultipleObjectsReturned: self.add_error('vlan_vid', "Multiple VLANs found ({} - VID {})".format(site, vlan_vid)) - elif vlan_vid: - self.add_error('vlan_vid', "Must specify site and/or VLAN group when assigning a VLAN.") + self.instance.vlan = vlan def save(self, *args, **kwargs): From 105e9da8668aa11fd8762c00dff568b57507a6ab Mon Sep 17 00:00:00 2001 From: Anthony Steinhauser Date: Mon, 10 Apr 2017 16:00:22 +0200 Subject: [PATCH 02/12] XSS flaw bugfix --- netbox/utilities/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 010a60daa..491aa309e 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -12,6 +12,7 @@ from django.forms import CharField, ModelMultipleChoiceField, MultipleHiddenInpu from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect, render from django.template import TemplateSyntaxError +from django.utils.html import escape from django.utils.http import is_safe_url from django.views.generic import View @@ -194,9 +195,9 @@ class ObjectEditView(View): msg = u'Created ' if obj_created else u'Modified ' msg += self.model._meta.verbose_name if hasattr(obj, 'get_absolute_url'): - msg = u'{} {}'.format(msg, obj.get_absolute_url(), obj) + msg = u'{} {}'.format(msg, obj.get_absolute_url(), escape(obj)) else: - msg = u'{} {}'.format(msg, obj) + msg = u'{} {}'.format(msg, escape(obj)) messages.success(request, msg) if obj_created: UserAction.objects.log_create(request.user, obj, msg) @@ -266,7 +267,7 @@ class ObjectDeleteView(View): handle_protectederror(obj, request, e) return redirect(obj.get_absolute_url()) - msg = u'Deleted {} {}'.format(self.model._meta.verbose_name, obj) + msg = u'Deleted {} {}'.format(self.model._meta.verbose_name, escape(obj)) messages.success(request, msg) UserAction.objects.log_delete(request.user, obj, msg) From cf5be85dadd9d236d401ea52a6fc0d429b52856c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 10 Apr 2017 10:54:35 -0400 Subject: [PATCH 03/12] Closes #1061: Escape all messages by default (complements #1062) --- netbox/templates/_base.html | 2 +- netbox/utilities/views.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index 800739b74..148b2ba46 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -273,7 +273,7 @@ - {{ message|safe }} + {{ message }} {% endfor %} {% block content %}{% endblock %} diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 491aa309e..6f0895734 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -14,6 +14,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.template import TemplateSyntaxError from django.utils.html import escape from django.utils.http import is_safe_url +from django.utils.safestring import mark_safe from django.views.generic import View from extras.forms import CustomFieldForm @@ -198,7 +199,7 @@ class ObjectEditView(View): msg = u'{} {}'.format(msg, obj.get_absolute_url(), escape(obj)) else: msg = u'{} {}'.format(msg, escape(obj)) - messages.success(request, msg) + messages.success(request, mark_safe(msg)) if obj_created: UserAction.objects.log_create(request.user, obj, msg) else: @@ -267,7 +268,7 @@ class ObjectDeleteView(View): handle_protectederror(obj, request, e) return redirect(obj.get_absolute_url()) - msg = u'Deleted {} {}'.format(self.model._meta.verbose_name, escape(obj)) + msg = u'Deleted {} {}'.format(self.model._meta.verbose_name, obj) messages.success(request, msg) UserAction.objects.log_delete(request.user, obj, msg) From ba1a4f06fff7c0f41dcfe4cb2ddf2c797f3f2bf8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 10 Apr 2017 10:55:05 -0400 Subject: [PATCH 04/12] Replace tabs with spaces --- netbox/templates/_base.html | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index 148b2ba46..7dcb83d73 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -3,11 +3,11 @@ - NetBox - {% block title %}Home{% endblock %} - + NetBox - {% block title %}Home{% endblock %} + - + @@ -256,10 +256,10 @@ -
+
{% if settings.BANNER_TOP %} {% endif %} {% if settings.MAINTENANCE_MODE %} @@ -268,24 +268,24 @@

NetBox is currently in maintenance mode. Functionality may be limited.

{% endif %} - {% for message in messages %} - - {% endfor %} - {% block content %}{% endblock %} + {% for message in messages %} + + {% endfor %} + {% block content %}{% endblock %}
- {% if settings.BANNER_BOTTOM %} - -
-
+
+
+

{{ settings.HOSTNAME }} (v{{ settings.VERSION }})

@@ -302,8 +302,8 @@

-
-
+
+ From 53129125dd30f2b54df64a1390584b099fb961f5 Mon Sep 17 00:00:00 2001 From: bellwood Date: Wed, 12 Apr 2017 09:42:48 -0400 Subject: [PATCH 05/12] Python3 fixes for CentOS/RHEL 1) python3 should be python34 2) python34-pip does does exist, you must install python34-setuptools and then: easy_install-3.4 pip --- docs/installation/netbox.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/installation/netbox.md b/docs/installation/netbox.md index e47e92133..0e116a29f 100644 --- a/docs/installation/netbox.md +++ b/docs/installation/netbox.md @@ -20,7 +20,8 @@ Python 3: ```no-highlight # yum install -y epel-release -# yum install -y gcc python3 python3-devel python3-pip libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel +# yum install -y gcc python34 python34-devel python34-setuptools libxml2-devel libxslt-devel libffi-devel graphviz openssl-devel +# easy_install-3.4 pip ``` Python 2: From 7cbea49c2dd5ecc6dfdcea1cc55f52c1b92fe025 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 12 Apr 2017 15:51:14 -0400 Subject: [PATCH 06/12] Fixes #1072: Order LAG interfaces naturally on bulk interface edit form --- netbox/dcim/forms.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 32b8850f6..8967936ad 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1422,9 +1422,16 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): super(InterfaceBulkEditForm, self).__init__(*args, **kwargs) # Limit LAG choices to interfaces which belong to the parent device. + device = None if self.initial.get('device'): - self.fields['lag'].queryset = Interface.objects.filter( - device=self.initial['device'], form_factor=IFACE_FF_LAG + try: + device = Device.objects.get(pk=self.initial.get('device')) + except Device.DoesNotExist: + pass + if device is not None: + interface_ordering = device.device_type.interface_ordering + self.fields['lag'].queryset = Interface.objects.order_naturally(method=interface_ordering).filter( + device=device, form_factor=IFACE_FF_LAG ) else: self.fields['lag'].choices = [] From b42dab3eef9aacea9f2aa2b59bed54fad185834a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 12 Apr 2017 16:06:36 -0400 Subject: [PATCH 07/12] Differentiate between LAG and virtual interfaces in device interface list --- netbox/templates/dcim/inc/interface.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html index 7d7d66c2e..a74fee5ac 100644 --- a/netbox/templates/dcim/inc/interface.html +++ b/netbox/templates/dcim/inc/interface.html @@ -19,7 +19,9 @@ {{ iface.mac_address|default:'' }} - {% if iface.is_virtual %} + {% if iface.is_lag %} + LAG interface + {% elif iface.is_virtual %} Virtual interface {% elif iface.connection %} {% with iface.connected_interface as connected_iface %} From d5c3f9e7801ecfcf6c8cb46b8da25665e0542dbe Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 12 Apr 2017 22:02:23 -0400 Subject: [PATCH 08/12] #878: Show assigned IP addresses in device interfaces list --- netbox/dcim/forms.py | 2 +- netbox/dcim/views.py | 13 ++---- netbox/project-static/css/base.css | 10 ++++ netbox/templates/dcim/device.html | 54 ++++++++-------------- netbox/templates/dcim/inc/interface.html | 58 ++++++++++++++++++++---- netbox/templates/dcim/inc/ipaddress.html | 21 --------- 6 files changed, 84 insertions(+), 74 deletions(-) delete mode 100644 netbox/templates/dcim/inc/ipaddress.html diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 8967936ad..80326b137 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1713,7 +1713,7 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm): self.fields['interface'].required = True # If this device has only one interface, select it by default. - if len(interfaces) == 1: + if 'interface' not in self.initial and len(interfaces) == 1: self.fields['interface'].initial = interfaces[0] # If this device does not have any IP addresses assigned, default to setting the first IP as its primary. diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 8962d0219..edaa56d22 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -13,7 +13,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.utils.http import urlencode from django.views.generic import View -from ipam.models import Prefix, IPAddress, Service, VLAN +from ipam.models import Prefix, Service, VLAN from circuits.models import Circuit from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE from utilities.forms import ConfirmationForm @@ -700,19 +700,15 @@ def device(request, pk): interfaces = Interface.objects.order_naturally(device.device_type.interface_ordering)\ .filter(device=device, mgmt_only=False)\ .select_related('connected_as_a__interface_b__device', 'connected_as_b__interface_a__device', - 'circuit_termination__circuit') + 'circuit_termination__circuit').prefetch_related('ip_addresses') mgmt_interfaces = Interface.objects.order_naturally(device.device_type.interface_ordering)\ .filter(device=device, mgmt_only=True)\ .select_related('connected_as_a__interface_b__device', 'connected_as_b__interface_a__device', - 'circuit_termination__circuit') + 'circuit_termination__circuit').prefetch_related('ip_addresses') device_bays = natsorted( DeviceBay.objects.filter(device=device).select_related('installed_device__device_type__manufacturer'), key=attrgetter('name') ) - - # Gather relevant device objects - ip_addresses = IPAddress.objects.filter(interface__device=device).select_related('interface', 'vrf')\ - .order_by('address') services = Service.objects.filter(device=device) secrets = device.secrets.all() @@ -743,7 +739,6 @@ def device(request, pk): 'interfaces': interfaces, 'mgmt_interfaces': mgmt_interfaces, 'device_bays': device_bays, - 'ip_addresses': ip_addresses, 'services': services, 'secrets': secrets, 'related_devices': related_devices, @@ -1599,7 +1594,7 @@ def ipaddress_assign(request, pk): return redirect('dcim:device', pk=device.pk) else: - form = forms.IPAddressForm(device) + form = forms.IPAddressForm(device, initial=request.GET) return render(request, 'dcim/ipaddress_assign.html', { 'device': device, diff --git a/netbox/project-static/css/base.css b/netbox/project-static/css/base.css index 11ea04b72..0b740159a 100644 --- a/netbox/project-static/css/base.css +++ b/netbox/project-static/css/base.css @@ -313,6 +313,16 @@ li.occupied + li.available { border-top: 1px solid #474747; } +/* Devices */ +table.component-list tr.ipaddress td { + background-color: #eeffff; + padding-bottom: 4px; + padding-top: 4px; +} +table.component-list tr.ipaddress:hover td { + background-color: #e6f7f7; +} + /* Misc */ .banner-bottom { margin-bottom: 50px; diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index d38f60cb3..2aa1666c8 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -194,35 +194,6 @@ {% endif %} {% endif %} -
-
- IP Addresses -
- {% if ip_addresses %} - - {% for ip in ip_addresses %} - {% include 'dcim/inc/ipaddress.html' %} - {% endfor %} -
- {% elif interfaces or mgmt_interfaces %} -
- None assigned -
- {% else %} -
- Create an interface to assign an IP. -
- {% endif %} - {% if perms.ipam.add_ipaddress %} - {% if interfaces or mgmt_interfaces %} - - {% endif %} - {% endif %} -
Services @@ -250,7 +221,7 @@
Critical Connections
- +
{% for iface in mgmt_interfaces %} {% include 'dcim/inc/interface.html' with icon='wrench' %} {% empty %} @@ -375,7 +346,7 @@ {% endif %} -
+
{% for devicebay in device_bays %} {% include 'dcim/inc/devicebay.html' with selectable=True %} {% empty %} @@ -416,6 +387,9 @@
Interfaces
+ {% if perms.dcim.change_interface and interfaces|length > 1 %}
-
+
{% for iface in interfaces %} {% include 'dcim/inc/interface.html' with selectable=True %} {% empty %} @@ -485,7 +459,7 @@ {% endif %} -
+
{% for csp in cs_ports %} {% include 'dcim/inc/consoleserverport.html' with selectable=True %} {% empty %} @@ -537,7 +511,7 @@ {% endif %} -
+
{% for po in power_outlets %} {% include 'dcim/inc/poweroutlet.html' with selectable=True %} {% empty %} @@ -628,6 +602,18 @@ $(".powerport-toggle").click(function() { $(".interface-toggle").click(function() { return toggleConnection($(this), "dcim/interface-connections/"); }); +// Toggle the display of IP addresses under interfaces +$('button.toggle-ips').click(function() { + var selected = $(this).attr('selected'); + if (selected) { + $('table.component-list tr.ipaddress').hide(); + } else { + $('table.component-list tr.ipaddress').show(); + } + $(this).attr('selected', !selected); + $(this).children('span').toggleClass('glyphicon-check glyphicon-unchecked'); + return false; +}); diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html index a74fee5ac..7fe068304 100644 --- a/netbox/templates/dcim/inc/interface.html +++ b/netbox/templates/dcim/inc/interface.html @@ -1,4 +1,4 @@ - + {% if selectable and perms.dcim.change_interface or perms.dcim.delete_interface %} - {% if iface.is_lag %} {% elif iface.is_virtual %} @@ -53,7 +50,7 @@ Not connected {% endif %} - +{% for ip in iface.ip_addresses.all %} + + {% if selectable and perms.dcim.change_interface or perms.dcim.delete_interface %} + + {% endif %} + + + + + +{% endfor %} diff --git a/netbox/templates/dcim/inc/ipaddress.html b/netbox/templates/dcim/inc/ipaddress.html deleted file mode 100644 index 72920986e..000000000 --- a/netbox/templates/dcim/inc/ipaddress.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - From f70f0f8d62736b3b8781920bbf0aba758ae9ea14 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 13 Apr 2017 13:11:23 -0400 Subject: [PATCH 09/12] Improved handling of return_url for object edit/delete views; removed manual definitions of initial data fields --- netbox/circuits/views.py | 6 +- netbox/dcim/forms.py | 2 +- netbox/dcim/views.py | 21 +++--- netbox/ipam/views.py | 10 ++- netbox/secrets/views.py | 2 +- .../utilities/confirmation_form.html | 6 +- netbox/tenancy/views.py | 3 +- netbox/utilities/forms.py | 13 ++-- netbox/utilities/views.py | 65 ++++++++++--------- 9 files changed, 65 insertions(+), 63 deletions(-) diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 1ffda899b..110994e81 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -95,7 +95,7 @@ class CircuitTypeEditView(PermissionRequiredMixin, ObjectEditView): model = CircuitType form_class = forms.CircuitTypeForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('circuits:circuittype_list') @@ -142,7 +142,6 @@ class CircuitEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'circuits.change_circuit' model = Circuit form_class = forms.CircuitForm - fields_initial = ['provider'] template_name = 'circuits/circuit_edit.html' default_return_url = 'circuits:circuit_list' @@ -230,7 +229,6 @@ class CircuitTerminationEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'circuits.change_circuittermination' model = CircuitTermination form_class = forms.CircuitTerminationForm - fields_initial = ['term_side'] template_name = 'circuits/circuittermination_edit.html' def alter_obj(self, obj, request, url_args, url_kwargs): @@ -238,7 +236,7 @@ class CircuitTerminationEditView(PermissionRequiredMixin, ObjectEditView): obj.circuit = get_object_or_404(Circuit, pk=url_kwargs['circuit']) return obj - def get_return_url(self, obj): + def get_return_url(self, request, obj): return obj.circuit.get_absolute_url() diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 80326b137..7a4d093c4 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1708,7 +1708,7 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm): self.fields['vrf'].empty_label = 'Global' - interfaces = device.interfaces.all() + interfaces = device.interfaces.order_naturally(method=device.device_type.interface_ordering) self.fields['interface'].queryset = interfaces self.fields['interface'].required = True diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index edaa56d22..6bf5ececd 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -124,13 +124,13 @@ class ComponentCreateView(View): class ComponentEditView(ObjectEditView): - def get_return_url(self, obj): + def get_return_url(self, request, obj): return obj.device.get_absolute_url() class ComponentDeleteView(ObjectDeleteView): - def get_return_url(self, obj): + def get_return_url(self, request, obj): return obj.device.get_absolute_url() @@ -149,7 +149,7 @@ class RegionEditView(PermissionRequiredMixin, ObjectEditView): model = Region form_class = forms.RegionForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('dcim:region_list') @@ -242,7 +242,7 @@ class RackGroupEditView(PermissionRequiredMixin, ObjectEditView): model = RackGroup form_class = forms.RackGroupForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('dcim:rackgroup_list') @@ -268,7 +268,7 @@ class RackRoleEditView(PermissionRequiredMixin, ObjectEditView): model = RackRole form_class = forms.RackRoleForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('dcim:rackrole_list') @@ -379,7 +379,7 @@ class RackReservationEditView(PermissionRequiredMixin, ObjectEditView): obj.user = request.user return obj - def get_return_url(self, obj): + def get_return_url(self, request, obj): return obj.rack.get_absolute_url() @@ -387,7 +387,7 @@ class RackReservationDeleteView(PermissionRequiredMixin, ObjectDeleteView): permission_required = 'dcim.delete_rackreservation' model = RackReservation - def get_return_url(self, obj): + def get_return_url(self, request, obj): return obj.rack.get_absolute_url() @@ -412,7 +412,7 @@ class ManufacturerEditView(PermissionRequiredMixin, ObjectEditView): model = Manufacturer form_class = forms.ManufacturerForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('dcim:manufacturer_list') @@ -632,7 +632,7 @@ class DeviceRoleEditView(PermissionRequiredMixin, ObjectEditView): model = DeviceRole form_class = forms.DeviceRoleForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('dcim:devicerole_list') @@ -657,7 +657,7 @@ class PlatformEditView(PermissionRequiredMixin, ObjectEditView): model = Platform form_class = forms.PlatformForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('dcim:platform_list') @@ -750,7 +750,6 @@ class DeviceEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'dcim.change_device' model = Device form_class = forms.DeviceForm - fields_initial = ['site', 'rack', 'position', 'face', 'device_bay'] template_name = 'dcim/device_edit.html' default_return_url = 'dcim:device_list' diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 864909878..2d2e1f044 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -244,7 +244,7 @@ class RIREditView(PermissionRequiredMixin, ObjectEditView): model = RIR form_class = forms.RIRForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('ipam:rir_list') @@ -370,7 +370,7 @@ class RoleEditView(PermissionRequiredMixin, ObjectEditView): model = Role form_class = forms.RoleForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('ipam:role_list') @@ -464,7 +464,6 @@ class PrefixEditView(PermissionRequiredMixin, ObjectEditView): model = Prefix form_class = forms.PrefixForm template_name = 'ipam/prefix_edit.html' - fields_initial = ['vrf', 'tenant', 'site', 'prefix', 'vlan'] default_return_url = 'ipam:prefix_list' @@ -645,7 +644,6 @@ class IPAddressEditView(PermissionRequiredMixin, ObjectEditView): permission_required = 'ipam.change_ipaddress' model = IPAddress form_class = forms.IPAddressForm - fields_initial = ['address', 'vrf'] template_name = 'ipam/ipaddress_edit.html' default_return_url = 'ipam:ipaddress_list' @@ -718,7 +716,7 @@ class VLANGroupEditView(PermissionRequiredMixin, ObjectEditView): model = VLANGroup form_class = forms.VLANGroupForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('ipam:vlangroup_list') @@ -807,7 +805,7 @@ class ServiceEditView(PermissionRequiredMixin, ObjectEditView): obj.device = get_object_or_404(Device, pk=url_kwargs['device']) return obj - def get_return_url(self, obj): + def get_return_url(self, request, obj): return obj.device.get_absolute_url() diff --git a/netbox/secrets/views.py b/netbox/secrets/views.py index d67cd18a0..1d27c538a 100644 --- a/netbox/secrets/views.py +++ b/netbox/secrets/views.py @@ -30,7 +30,7 @@ class SecretRoleEditView(PermissionRequiredMixin, ObjectEditView): model = SecretRole form_class = forms.SecretRoleForm - def get_return_url(self, obj): + def get_return_url(self, request, obj): return reverse('secrets:secretrole_list') diff --git a/netbox/templates/utilities/confirmation_form.html b/netbox/templates/utilities/confirmation_form.html index 8c97056da..16383d6f7 100644 --- a/netbox/templates/utilities/confirmation_form.html +++ b/netbox/templates/utilities/confirmation_form.html @@ -6,13 +6,13 @@
{% csrf_token %} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %}
{% block title %}{% endblock %}
{% block message %}

Are you sure?

{% endblock %} - {% for field in form.hidden_fields %} - {{ field }} - {% endfor %}
diff --git a/netbox/templates/ipam/ipaddress_edit.html b/netbox/templates/ipam/ipaddress_edit.html index 7226a75fc..82523eba0 100644 --- a/netbox/templates/ipam/ipaddress_edit.html +++ b/netbox/templates/ipam/ipaddress_edit.html @@ -16,39 +16,20 @@ {% render_field form.vrf %} {% render_field form.tenant %} {% render_field form.status %} - {% if obj.pk %} -
- -
-

- {% if obj.interface %} - {{ obj.interface.device }} - Remove - {% else %} - None - {% if obj.pk %} - Assign - {% endif %} - {% endif %} -

-
-
-
- -
-

- {% if obj.interface %} - {{ obj.interface }} - {% else %} - None - {% endif %} -

-
-
- {% endif %} {% render_field form.description %} +
+
+ Interface Assignment +
+
+ {% render_field form.interface_site %} + {% render_field form.interface_rack %} + {% render_field form.interface_device %} + {% render_field form.interface %} +
+
NAT IP (Inside)
diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index c80bd4aae..0f867e16b 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -177,7 +177,9 @@ class ObjectEditView(GetReturnURLMixin, View): obj = self.get_object(kwargs) obj = self.alter_obj(obj, request, args, kwargs) - form = self.form_class(instance=obj, initial=request.GET) + # Parse initial data manually to avoid setting field values as lists + initial_data = {k: request.GET[k] for k in request.GET} + form = self.form_class(instance=obj, initial=initial_data) return render(request, self.template_name, { 'obj': obj, From 610b4125066449c5da0498ad08b4b6d1cf8cbe76 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 13 Apr 2017 15:09:08 -0400 Subject: [PATCH 11/12] #878: Layout tweaks --- netbox/templates/dcim/device.html | 6 +++--- netbox/templates/dcim/inc/consoleport.html | 5 ++--- netbox/templates/dcim/inc/consoleserverport.html | 4 ++-- netbox/templates/dcim/inc/devicebay.html | 4 ++-- netbox/templates/dcim/inc/poweroutlet.html | 4 ++-- netbox/templates/dcim/inc/powerport.html | 5 ++--- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 2aa1666c8..8466f2bc0 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -402,7 +402,7 @@ {% endif %}
-
@@ -16,9 +16,6 @@
{{ iface.member_interfaces.all|join:", "|default:"No members" }} {% endif %}
- {{ iface.mac_address|default:'' }} - LAG interface + {% if show_graphs %} {% if iface.circuit_termination or iface.connection %} {% endif %} {% endif %} + {% if perms.ipam.add_ipaddress %} + + + + {% endif %} {% if perms.dcim.change_interface %} {% if not iface.is_virtual %} {% if iface.connection %} @@ -73,19 +75,19 @@ {% endif %} - - + + {% elif iface.circuit_termination and perms.circuits.change_circuittermination %} - + {% else %} - + {% endif %} {% endif %} @@ -106,3 +108,41 @@ {% endif %}
+ {{ ip }} + {% if ip.description %} + + {% endif %} + {% if device.primary_ip4 == ip or device.primary_ip6 == ip %} + Primary + {% endif %} + + {% if ip.vrf %} + {{ ip.vrf }} + {% else %} + Global + {% endif %} + + {{ ip.get_status_display }} + + {% if perms.ipam.edit_ipaddress %} + + + + {% endif %} + {% if perms.ipam.delete_ipaddress %} + + + + {% endif %} +
- {{ ip }} - - {{ ip.vrf|default:"Global" }} - {{ ip.interface }} - {% if device.primary_ip4 == ip or device.primary_ip6 == ip %} - Primary - {% endif %} - - {% if perms.ipam.delete_ipaddress %} - - - - {% endif %} -
{% if ipaddress.interface %} {{ ipaddress.interface.device }} ({{ ipaddress.interface }}) - {% if perms.dcim.change_device and perms.ipam.change_ipaddress %} - Remove - {% endif %} {% else %} None - {% if perms.dcim.change_device and perms.ipam.change_ipaddress %} - Assign - {% endif %} {% endif %}
+
{% for iface in interfaces %} {% include 'dcim/inc/interface.html' with selectable=True %} {% empty %} @@ -606,9 +606,9 @@ $(".interface-toggle").click(function() { $('button.toggle-ips').click(function() { var selected = $(this).attr('selected'); if (selected) { - $('table.component-list tr.ipaddress').hide(); + $('#interfaces_table tr.ipaddress').hide(); } else { - $('table.component-list tr.ipaddress').show(); + $('#interfaces_table tr.ipaddress').show(); } $(this).attr('selected', !selected); $(this).children('span').toggleClass('glyphicon-check glyphicon-unchecked'); diff --git a/netbox/templates/dcim/inc/consoleport.html b/netbox/templates/dcim/inc/consoleport.html index e6c816780..5352c949c 100644 --- a/netbox/templates/dcim/inc/consoleport.html +++ b/netbox/templates/dcim/inc/consoleport.html @@ -1,4 +1,4 @@ - + {% if selectable and perms.dcim.change_consoleport or perms.dcim.delete_consoleport %} - {% if cp.cs_port %} {% endif %} - {% if selectable and perms.dcim.change_consoleserverport or perms.dcim.delete_consoleserverport %} {% endif %} - + {% if selectable and perms.dcim.change_devicebay or perms.dcim.delete_devicebay %} {% endif %} - {% if selectable and perms.dcim.change_poweroutlet or perms.dcim.delete_poweroutlet %} {% endif %} - {% if selectable and perms.dcim.change_powerport or perms.dcim.delete_powerport %} - {% if pp.power_outlet %} {% endif %} -
@@ -7,7 +7,6 @@ {{ cp.name }} {{ cp.cs_port.device }} @@ -20,7 +19,7 @@ Not connected + {% if perms.dcim.change_consoleport %} {% if cp.cs_port %} {% if cp.connection_status %} diff --git a/netbox/templates/dcim/inc/consoleserverport.html b/netbox/templates/dcim/inc/consoleserverport.html index d3c923e43..d317cf5a4 100644 --- a/netbox/templates/dcim/inc/consoleserverport.html +++ b/netbox/templates/dcim/inc/consoleserverport.html @@ -1,4 +1,4 @@ - +
@@ -19,7 +19,7 @@ Not connected + {% if perms.dcim.change_consoleserverport %} {% if csp.connected_console %} {% if csp.connected_console.connection_status %} diff --git a/netbox/templates/dcim/inc/devicebay.html b/netbox/templates/dcim/inc/devicebay.html index eacb27440..f69299bfb 100644 --- a/netbox/templates/dcim/inc/devicebay.html +++ b/netbox/templates/dcim/inc/devicebay.html @@ -1,4 +1,4 @@ -
@@ -19,7 +19,7 @@ Vacant + {% if perms.dcim.change_devicebay %} {% if devicebay.installed_device %} diff --git a/netbox/templates/dcim/inc/poweroutlet.html b/netbox/templates/dcim/inc/poweroutlet.html index 241ebc15c..652ac8e47 100644 --- a/netbox/templates/dcim/inc/poweroutlet.html +++ b/netbox/templates/dcim/inc/poweroutlet.html @@ -1,4 +1,4 @@ - +
@@ -19,7 +19,7 @@ Not connected + {% if perms.dcim.change_poweroutlet %} {% if po.connected_port %} {% if po.connected_port.connection_status %} diff --git a/netbox/templates/dcim/inc/powerport.html b/netbox/templates/dcim/inc/powerport.html index aacb96839..785186670 100644 --- a/netbox/templates/dcim/inc/powerport.html +++ b/netbox/templates/dcim/inc/powerport.html @@ -1,4 +1,4 @@ - +
@@ -7,7 +7,6 @@ {{ pp.name }} {{ pp.power_outlet.device }} @@ -20,7 +19,7 @@ Not connected + {% if perms.dcim.change_powerport %} {% if pp.power_outlet %} {% if pp.connection_status %} From f9a33bfc14db45a713f95b732995c76659f97824 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 13 Apr 2017 15:34:35 -0400 Subject: [PATCH 12/12] Fixes #1074: Require ncclient 0.5.3 (Python 3 fix) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b31ded015..9859ec187 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ djangorestframework>=3.5.0 graphviz>=0.4.10 Markdown>=2.6.7 natsort>=5.0.0 -ncclient==0.5.2 +ncclient==0.5.3 netaddr==0.7.18 paramiko>=2.0.0 psycopg2>=2.6.1