Compare commits

...

41 Commits

Author SHA1 Message Date
Jeremy Stretch
425670f52a Merge pull request #3745 from netbox-community/develop
Release v2.6.8
2019-12-10 10:47:48 -05:00
Jeremy Stretch
6a2651991a Release v2.6.8 2019-12-10 10:42:48 -05:00
Jeremy Stretch
f25e2a1922 Fixes #3644: Fix exception when connecting a cable to a RearPort with no corresponding FrontPort 2019-12-09 15:42:04 -05:00
Jeremy Stretch
95edec5448 #3722: Tweak ordering of permitted characters to avoid creating a regex range 2019-12-09 10:02:56 -05:00
Jeremy Stretch
f2076c9572 Fixed git commit hook 2019-12-06 12:54:13 -05:00
Jeremy Stretch
d71e6698f4 Extend git commit hook to check for missing schema migrations 2019-12-06 12:42:59 -05:00
Jeremy Stretch
9b26225fdd #3720: Update migration to add powerfeeds to termination_type limit list (does not impact database) 2019-12-06 12:29:31 -05:00
Jeremy Stretch
f8c5ca942a #3722: Update migration with new validator (does not impact database) 2019-12-06 12:19:29 -05:00
Jeremy Stretch
16353ce63c Correct stalebot config file name 2019-12-05 21:43:22 -05:00
Jeremy Stretch
7a4b202064 Fixes #3725: Enforce client validation for minimum service port number 2019-12-05 21:22:34 -05:00
Jeremy Stretch
a97ebc6d4c Fixes #3722: Allow the underscore character in IPAddress DNS names 2019-12-05 21:14:29 -05:00
Jeremy Stretch
9e765b1704 Fixes #3730: Add link to docs page for custom links 2019-12-05 21:07:43 -05:00
John Anderson
1048d3909b fixes #3724 - allow filtering interfaces by more than one device name 2019-12-04 02:00:08 -05:00
Jeremy Stretch
b2caaa6733 Fixes #3312: Fix validation error when editing power cables in bulk 2019-11-27 09:19:34 -05:00
Jeremy Stretch
15722c1871 Fixes #3720: Correctly indicate power feed terminations on cable list 2019-11-26 16:56:11 -05:00
Jeremy Stretch
1d18948307 Fixes #3709: Prevent exception when importing an invalid cable definition 2019-11-26 16:46:51 -05:00
Jeremy Stretch
6a451e0c0e Merge pull request #3701 from hSaria/3697-custom-scripts-example-typo
Fixes #3697: Correct field_order attribute name
2019-11-22 08:45:21 -05:00
hSaria
b46e52eccc Fixed #3697: Correct field_order attribute name 2019-11-20 17:03:12 +00:00
Jeremy Stretch
de986ec392 Merge pull request #3687 from kobayashi/3679
fix url expressions
2019-11-14 22:03:03 -05:00
Jeremy Stretch
4b415caad2 Changelog for #3663 2019-11-14 22:01:37 -05:00
Jeremy Stretch
bfede60f3d Rename CreatedUpdatedFilter to CreatedUpdatedFilterSet 2019-11-14 22:00:12 -05:00
Jeremy Stretch
03b8759597 'base_name' deprecated in DRF v3.9.0 2019-11-14 21:58:37 -05:00
Jeremy Stretch
bdd623f82c Merge pull request #3680 from struppinet/develop
Closes #3663: API filter by created, last_updated
2019-11-14 21:57:26 -05:00
kobayashi
3c8083ed7f fix url expressions 2019-11-13 00:48:47 -05:00
kobayashi
5904f1f8ee Changelog for #3329 2019-11-12 09:47:31 -05:00
kobayashi
cc848a3f01 Merge pull request #3684 from bbock/docs-remove-p3p
Docs: Remove obsolete P3P policy
2019-11-12 09:38:46 -05:00
Bernhard Bock
1aaa101fb5 Docs: Remove obsolete P3P policy
P3P is obsolete (https://www.w3.org/TR/P3P11/), therefore the HTTP header
should be removed from the recommended config in the installation docs.
2019-11-08 15:13:32 +01:00
struppi
0319450643 Closes #3663: rename filter class 2019-11-07 22:41:09 +01:00
struppi
099774d667 Closes #3663: PEP8 fixes 2019-11-07 22:38:51 +01:00
Jeremy Stretch
b1761f7856 #3139: Add a message indicating why the user is redirected 2019-11-06 10:01:42 -05:00
Jeremy Stretch
1bc38f66ae Changelog entries for #3139 and #3457 2019-11-06 09:58:30 -05:00
Jeremy Stretch
b4433a8471 Merge pull request #3667 from steffann/3139-disable-user-password-change-if-come-in-with-ldap-auth
Hide password change page when user is logged in using LDAP
2019-11-06 09:57:25 -05:00
Jeremy Stretch
053f49c76a Merge pull request #3673 from ananace/cable-color-display
3457 Display cable colors in device interface list
2019-11-06 09:53:59 -05:00
Jeremy Stretch
fbde6187ea Fixes #3669: Include weight field in prefix/VLAN role form 2019-11-06 09:39:21 -05:00
Jeremy Stretch
5eb5c4bac5 Fixes #3674: Include comments on PowerFeed view 2019-11-06 09:26:49 -05:00
Jeremy Stretch
fb4283ed53 Move alternative installations to the GitHub wiki 2019-11-06 09:05:12 -05:00
Alexander Olofsson
bbd65988f9 3457 Display cable colors in device interface list 2019-11-06 10:15:55 +01:00
struppi
a11fa44170 Closes #3663: fix inheritance error 2019-11-04 21:00:44 +01:00
struppi
99a542e4e4 Closes #3663: API filter by created, last_updated 2019-11-04 20:51:56 +01:00
Sander Steffann
7f779e3942 Hide password change page when user is logged in using LDAP 2019-11-03 16:05:53 +03:00
Jeremy Stretch
a2a83a4a4c Post-release version bump 2019-11-01 15:54:17 -04:00
30 changed files with 158 additions and 55 deletions

View File

@@ -36,13 +36,6 @@ Please see [the documentation](http://netbox.readthedocs.io/en/stable/) for
instructions on installing NetBox. To upgrade NetBox, please download the [latest release](https://github.com/netbox-community/netbox/releases)
and run `upgrade.sh`.
## Alternative Installations
* [Docker container](https://github.com/netbox-community/netbox-docker) (via [@cimnine](https://github.com/cimnine))
* [Vagrant deployment](https://github.com/ryanmerolle/netbox-vagrant) (via [@ryanmerolle](https://github.com/ryanmerolle))
* [Ansible deployment](https://github.com/lae/ansible-role-netbox) (via [@lae](https://github.com/lae))
* [Kubernetes deployment](https://github.com/CENGN/netbox-kubernetes) (via [@CENGN](https://github.com/CENGN))
# Providing Feedback
Feature requests and bug reports must be submitted as GiHub issues. (Please be

View File

@@ -182,7 +182,7 @@ class NewBranchScript(Script):
class Meta:
name = "New Branch"
description = "Provision a new branch site"
fields = ['site_name', 'switch_count', 'switch_model']
field_order = ['site_name', 'switch_count', 'switch_model']
site_name = StringVar(
description="Name of the new site"

View File

@@ -32,7 +32,6 @@ server {
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
}
}
```

View File

@@ -1,3 +1,25 @@
# v2.6.8 (2019-12-10)
## Enhancements
* [#3139](https://github.com/netbox-community/netbox/issues/3139) - Disable password change form for LDAP-authenticated users
* [#3457](https://github.com/netbox-community/netbox/issues/3457) - Display cable colors on device view
* [#3329](https://github.com/netbox-community/netbox/issues/3329) - Remove obsolete P3P policy header
* [#3663](https://github.com/netbox-community/netbox/issues/3663) - Add query filters for `created` and `last_updated` fields
* [#3722](https://github.com/netbox-community/netbox/issues/3722) - Allow the underscore character in IPAddress DNS names
## Bug Fixes
* [#3312](https://github.com/netbox-community/netbox/issues/3312) - Fix validation error when editing power cables in bulk
* [#3644](https://github.com/netbox-community/netbox/issues/3644) - Fix exception when connecting a cable to a RearPort with no corresponding FrontPort
* [#3669](https://github.com/netbox-community/netbox/issues/3669) - Include `weight` field in prefix/VLAN role form
* [#3674](https://github.com/netbox-community/netbox/issues/3674) - Include comments on PowerFeed view
* [#3679](https://github.com/netbox-community/netbox/issues/3679) - Fix link for assigned ipaddress in interface page
* [#3709](https://github.com/netbox-community/netbox/issues/3709) - Prevent exception when importing an invalid cable definition
* [#3720](https://github.com/netbox-community/netbox/issues/3720) - Correctly indicate power feed terminations on cable list
* [#3724](https://github.com/netbox-community/netbox/issues/3724) - Fix API filtering of interfaces by more than one device name
* [#3725](https://github.com/netbox-community/netbox/issues/3725) - Enforce client validation for minimum service port number
# v2.6.7 (2019-11-01)
## Enhancements

View File

@@ -31,6 +31,7 @@ pages:
- Change Logging: 'additional-features/change-logging.md'
- Context Data: 'additional-features/context-data.md'
- Custom Fields: 'additional-features/custom-fields.md'
- Custom Links: 'additional-features/custom-links.md'
- Custom Scripts: 'additional-features/custom-scripts.md'
- Export Templates: 'additional-features/export-templates.md'
- Graphs: 'additional-features/graphs.md'

View File

@@ -2,14 +2,14 @@ import django_filters
from django.db.models import Q
from dcim.models import Region, Site
from extras.filters import CustomFieldFilterSet
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter
from .constants import *
from .models import Circuit, CircuitTermination, CircuitType, Provider
class ProviderFilter(CustomFieldFilterSet):
class ProviderFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -54,7 +54,7 @@ class CircuitTypeFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class CircuitFilter(CustomFieldFilterSet, TenancyFilterSet):
class CircuitFilter(CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'

View File

@@ -384,7 +384,8 @@ CONNECTION_STATUS_CHOICES = [
# Cable endpoint types
CABLE_TERMINATION_TYPES = [
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination',
'consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport',
'circuittermination', 'powerfeed',
]
# Cable types

View File

@@ -2,13 +2,13 @@ import django_filters
from django.contrib.auth.models import User
from django.db.models import Q
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilter
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilter, CreatedUpdatedFilterSet
from tenancy.filtersets import TenancyFilterSet
from tenancy.models import Tenant
from utilities.constants import COLOR_CHOICES
from utilities.filters import (
MultiValueMACAddressFilter, MultiValueNumberFilter, NameSlugSearchFilterSet, NumericInFilter, TagFilter,
TreeNodeMultipleChoiceFilter,
MultiValueCharFilter, MultiValueMACAddressFilter, MultiValueNumberFilter, NameSlugSearchFilterSet, NumericInFilter,
TagFilter, TreeNodeMultipleChoiceFilter,
)
from virtualization.models import Cluster
from .constants import *
@@ -38,7 +38,7 @@ class RegionFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class SiteFilter(TenancyFilterSet, CustomFieldFilterSet):
class SiteFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -116,7 +116,7 @@ class RackRoleFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug', 'color']
class RackFilter(TenancyFilterSet, CustomFieldFilterSet):
class RackFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -251,7 +251,7 @@ class ManufacturerFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class DeviceTypeFilter(CustomFieldFilterSet):
class DeviceTypeFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -423,7 +423,7 @@ class PlatformFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug', 'napalm_driver']
class DeviceFilter(LocalConfigContextFilter, TenancyFilterSet, CustomFieldFilterSet):
class DeviceFilter(LocalConfigContextFilter, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -696,7 +696,7 @@ class InterfaceFilter(django_filters.FilterSet):
method='search',
label='Search',
)
device = django_filters.CharFilter(
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
label='Device',
@@ -749,8 +749,10 @@ class InterfaceFilter(django_filters.FilterSet):
def filter_device(self, queryset, name, value):
try:
device = Device.objects.get(**{name: value})
vc_interface_ids = device.vc_interfaces.values_list('id', flat=True)
devices = Device.objects.filter(**{'{}__in'.format(name): value})
vc_interface_ids = []
for device in devices:
vc_interface_ids.extend(device.vc_interfaces.values_list('id', flat=True))
return queryset.filter(pk__in=vc_interface_ids)
except Device.DoesNotExist:
return queryset.none()
@@ -1096,7 +1098,7 @@ class PowerPanelFilter(django_filters.FilterSet):
return queryset.filter(qs_filter)
class PowerFeedFilter(CustomFieldFilterSet):
class PowerFeedFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'

View File

@@ -174,8 +174,8 @@ class Migration(migrations.Migration):
('length', models.PositiveSmallIntegerField(blank=True, null=True)),
('length_unit', models.PositiveSmallIntegerField(blank=True, null=True)),
('_abs_length', models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True)),
('termination_a_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
('termination_b_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
('termination_a_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination', 'powerfeed']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
('termination_b_type', models.ForeignKey(limit_choices_to={'model__in': ['consoleport', 'consoleserverport', 'interface', 'poweroutlet', 'powerport', 'frontport', 'rearport', 'circuittermination', 'powerfeed']}, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='contenttypes.ContentType')),
],
),
migrations.AlterUniqueTogether(

View File

@@ -98,6 +98,8 @@ class CableTermination(models.Model):
object_id_field='termination_b_id'
)
is_path_endpoint = True
class Meta:
abstract = True
@@ -2444,6 +2446,8 @@ class FrontPort(CableTermination, ComponentModel):
validators=[MinValueValidator(1), MaxValueValidator(64)]
)
is_path_endpoint = False
objects = NaturalOrderingManager()
tags = TaggableManager(through=TaggedItem)
@@ -2506,6 +2510,8 @@ class RearPort(CableTermination, ComponentModel):
validators=[MinValueValidator(1), MaxValueValidator(64)]
)
is_path_endpoint = False
objects = NaturalOrderingManager()
tags = TaggableManager(through=TaggedItem)
@@ -2838,6 +2844,8 @@ class Cable(ChangeLoggedModel):
def clean(self):
# Validate that termination A exists
if not hasattr(self, 'termination_a_type'):
raise ValidationError('Termination A type has not been specified')
try:
self.termination_a_type.model_class().objects.get(pk=self.termination_a_id)
except ObjectDoesNotExist:
@@ -2846,6 +2854,8 @@ class Cable(ChangeLoggedModel):
})
# Validate that termination B exists
if not hasattr(self, 'termination_b_type'):
raise ValidationError('Termination B type has not been specified')
try:
self.termination_b_type.model_class().objects.get(pk=self.termination_b_id)
except ObjectDoesNotExist:

View File

@@ -45,7 +45,7 @@ def update_connected_endpoints(instance, **kwargs):
# Check if this Cable has formed a complete path. If so, update both endpoints.
endpoint_a, endpoint_b, path_status = instance.get_path_endpoints()
if endpoint_a is not None and endpoint_b is not None:
if getattr(endpoint_a, 'is_path_endpoint', False) and getattr(endpoint_b, 'is_path_endpoint', False):
endpoint_a.connected_endpoint = endpoint_b
endpoint_a.connection_status = path_status
endpoint_a.save()

View File

@@ -181,8 +181,10 @@ VIRTUALCHASSIS_ACTIONS = """
CABLE_TERMINATION_PARENT = """
{% if value.device %}
<a href="{{ value.device.get_absolute_url }}">{{ value.device }}</a>
{% else %}
{% elif value.circuit %}
<a href="{{ value.circuit.get_absolute_url }}">{{ value.circuit }}</a>
{% elif value.power_panel %}
<a href="{{ value.power_panel.get_absolute_url }}">{{ value.power_panel }}</a>
{% endif %}
"""
@@ -718,7 +720,7 @@ class CableTable(BaseTable):
orderable=False,
verbose_name='Termination A'
)
termination_a = tables.Column(
termination_a = tables.LinkColumn(
accessor=Accessor('termination_a'),
orderable=False,
verbose_name=''
@@ -729,7 +731,7 @@ class CableTable(BaseTable):
orderable=False,
verbose_name='Termination B'
)
termination_b = tables.Column(
termination_b = tables.LinkColumn(
accessor=Accessor('termination_b'),
orderable=False,
verbose_name=''

View File

@@ -18,7 +18,7 @@ router.APIRootView = ExtrasRootView
router.register(r'_choices', views.ExtrasFieldChoicesViewSet, basename='field-choice')
# Custom field choices
router.register(r'_custom_field_choices', views.CustomFieldChoicesViewSet, base_name='custom-field-choice')
router.register(r'_custom_field_choices', views.CustomFieldChoicesViewSet, basename='custom-field-choice')
# Graphs
router.register(r'graphs', views.GraphViewSet)

View File

@@ -241,3 +241,24 @@ class ObjectChangeFilter(django_filters.FilterSet):
Q(user_name__icontains=value) |
Q(object_repr__icontains=value)
)
class CreatedUpdatedFilterSet(django_filters.FilterSet):
created = django_filters.DateFilter()
created__gte = django_filters.DateFilter(
field_name='created',
lookup_expr='gte'
)
created__lte = django_filters.DateFilter(
field_name='created',
lookup_expr='lte'
)
last_updated = django_filters.DateTimeFilter()
last_updated__gte = django_filters.DateTimeFilter(
field_name='last_updated',
lookup_expr='gte'
)
last_updated__lte = django_filters.DateTimeFilter(
field_name='last_updated',
lookup_expr='lte'
)

View File

@@ -5,7 +5,7 @@ from django.db.models import Q
from netaddr.core import AddrFormatError
from dcim.models import Site, Device, Interface
from extras.filters import CustomFieldFilterSet
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
from virtualization.models import VirtualMachine
@@ -13,7 +13,7 @@ from .constants import *
from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
class VRFFilter(TenancyFilterSet, CustomFieldFilterSet):
class VRFFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -49,7 +49,7 @@ class RIRFilter(NameSlugSearchFilterSet):
fields = ['name', 'slug', 'is_private']
class AggregateFilter(CustomFieldFilterSet):
class AggregateFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -110,7 +110,7 @@ class RoleFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class PrefixFilter(TenancyFilterSet, CustomFieldFilterSet):
class PrefixFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -247,7 +247,7 @@ class PrefixFilter(TenancyFilterSet, CustomFieldFilterSet):
return queryset.filter(prefix__net_mask_length=value)
class IPAddressFilter(TenancyFilterSet, CustomFieldFilterSet):
class IPAddressFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -384,7 +384,7 @@ class VLANGroupFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class VLANFilter(TenancyFilterSet, CustomFieldFilterSet):
class VLANFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -444,7 +444,7 @@ class VLANFilter(TenancyFilterSet, CustomFieldFilterSet):
return queryset.filter(qs_filter)
class ServiceFilter(django_filters.FilterSet):
class ServiceFilter(CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',

View File

@@ -240,7 +240,7 @@ class RoleForm(BootstrapMixin, forms.ModelForm):
class Meta:
model = Role
fields = [
'name', 'slug',
'name', 'slug', 'weight',
]
@@ -1250,6 +1250,10 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
#
class ServiceForm(BootstrapMixin, CustomFieldForm):
port = forms.IntegerField(
min_value=1,
max_value=65535
)
tags = TagField(
required=False
)

View File

@@ -14,6 +14,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='ipaddress',
name='dns_name',
field=models.CharField(blank=True, max_length=255, validators=[django.core.validators.RegexValidator(code='invalid', message='Only alphanumeric characters, hyphens, and periods are allowed in DNS names', regex='^[0-9A-Za-z.-]+$')]),
field=models.CharField(blank=True, max_length=255, validators=[django.core.validators.RegexValidator(code='invalid', message='Only alphanumeric characters, hyphens, periods, and underscores are allowed in DNS names', regex='^[0-9A-Za-z._-]+$')]),
),
]

View File

@@ -85,7 +85,7 @@ IPADDRESS_LINK = """
"""
IPADDRESS_ASSIGN_LINK = """
<a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ request.GET.interface }}&return_url={{ request.GET.return_url }}">{{ record }}</a>
<a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ record.interface.pk }}&return_url={{ request.path }}">{{ record }}</a>
"""
IPADDRESS_PARENT = """
@@ -292,7 +292,7 @@ class RoleTable(BaseTable):
class Meta(BaseTable.Meta):
model = Role
fields = ('pk', 'name', 'prefix_count', 'vlan_count', 'slug', 'actions')
fields = ('pk', 'name', 'prefix_count', 'vlan_count', 'slug', 'weight', 'actions')
#

View File

@@ -2,7 +2,7 @@ from django.core.validators import RegexValidator
DNSValidator = RegexValidator(
regex='^[0-9A-Za-z.-]+$',
message='Only alphanumeric characters, hyphens, and periods are allowed in DNS names',
regex='^[0-9A-Za-z._-]+$',
message='Only alphanumeric characters, hyphens, periods, and underscores are allowed in DNS names',
code='invalid'
)

View File

@@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured
# Environment setup
#
VERSION = '2.6.7'
VERSION = '2.6.8'
# Hostname
HOSTNAME = platform.node()

View File

@@ -457,6 +457,14 @@ table.report th a {
width: 80px;
border: 1px solid grey;
}
.inline-color-block {
display: inline-block;
width: 1.5em;
height: 1.5em;
border: 1px solid grey;
border-radius: .25em;
vertical-align: middle;
}
.text-nowrap {
white-space: nowrap;
}

View File

@@ -2,7 +2,7 @@ import django_filters
from django.db.models import Q
from dcim.models import Device
from extras.filters import CustomFieldFilterSet
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
from .models import Secret, SecretRole
@@ -14,7 +14,7 @@ class SecretRoleFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class SecretFilter(CustomFieldFilterSet):
class SecretFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'

View File

@@ -48,6 +48,9 @@
<td class="text-nowrap">
{% if iface.cable %}
<a href="{{ iface.cable.get_absolute_url }}">{{ iface.cable }}</a>
{% if iface.cable.color %}
<span class="inline-color-block" style="background-color: #{{ iface.cable.color }}">&nbsp;</span>
{% endif %}
<a href="{% url 'dcim:interface_trace' pk=iface.pk %}" class="btn btn-primary btn-xs" title="Trace">
<i class="fa fa-share-alt" aria-hidden="true"></i>
</a>

View File

@@ -121,6 +121,18 @@
</tr>
</table>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<strong>Comments</strong>
</div>
<div class="panel-body rendered-markdown">
{% if powerfeed.comments %}
{{ powerfeed.comments|gfm }}
{% else %}
<span class="text-muted">None</span>
{% endif %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">

View File

@@ -12,9 +12,11 @@
<li{% ifequal active_tab "profile" %} class="active"{% endifequal %}>
<a href="{% url 'user:profile' %}">Profile</a>
</li>
<li{% ifequal active_tab "change_password" %} class="active"{% endifequal %}>
<a href="{% url 'user:change_password' %}">Change Password</a>
</li>
{% if not request.user.ldap_username %}
<li{% ifequal active_tab "change_password" %} class="active"{% endifequal %}>
<a href="{% url 'user:change_password' %}">Change Password</a>
</li>
{% endif %}
<li{% ifequal active_tab "api_tokens" %} class="active"{% endifequal %}>
<a href="{% url 'user:token_list' %}">API Tokens</a>
</li>

View File

@@ -1,7 +1,7 @@
import django_filters
from django.db.models import Q
from extras.filters import CustomFieldFilterSet
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
from .models import Tenant, TenantGroup
@@ -13,7 +13,7 @@ class TenantGroupFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class TenantFilter(CustomFieldFilterSet):
class TenantFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'

View File

@@ -95,6 +95,11 @@ class ChangePasswordView(LoginRequiredMixin, View):
template_name = 'users/change_password.html'
def get(self, request):
# LDAP users cannot change their password here
if getattr(request.user, 'ldap_username'):
messages.warning(request, "LDAP-authenticated user credentials cannot be changed within NetBox.")
return redirect('user:profile')
form = PasswordChangeForm(user=request.user)
return render(request, self.template_name, {

View File

@@ -4,7 +4,7 @@ from netaddr import EUI
from netaddr.core import AddrFormatError
from dcim.models import DeviceRole, Interface, Platform, Region, Site
from extras.filters import CustomFieldFilterSet
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import (
MultiValueMACAddressFilter, NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter,
@@ -27,7 +27,7 @@ class ClusterGroupFilter(NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class ClusterFilter(CustomFieldFilterSet):
class ClusterFilter(CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'
@@ -81,7 +81,7 @@ class ClusterFilter(CustomFieldFilterSet):
)
class VirtualMachineFilter(TenancyFilterSet, CustomFieldFilterSet):
class VirtualMachineFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
id__in = NumericInFilter(
field_name='id',
lookup_expr='in'

View File

@@ -9,6 +9,24 @@
exec 1>&2
EXIT=0
RED='\033[0;31m'
NOCOLOR='\033[0m'
echo "Validating PEP8 compliance..."
pycodestyle --ignore=W504,E501 netbox/
if [ $? != 0 ]; then
EXIT=1
fi
echo "Checking for missing migrations..."
python netbox/manage.py makemigrations --dry-run --check
if [ $? != 0 ]; then
EXIT=1
fi
if [ $EXIT != 0 ]; then
printf "${RED}COMMIT FAILED${NOCOLOR}\n"
fi
exit $EXIT