From e89343e10051a6dc84f099efe93eb2543e2bed0b Mon Sep 17 00:00:00 2001 From: hellerve Date: Sun, 2 Jun 2019 14:26:00 +0200 Subject: [PATCH 01/15] dcim: filter group by site in rack filter (fixes #3229) --- netbox/dcim/forms.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index f10418d57..e8bcf71b0 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -601,12 +601,18 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): widget=APISelectMultiple( api_url="/api/dcim/sites/", value_field="slug", + filter_for={ + 'group_id': 'site' + } ) ) - group_id = FilterChoiceField( - queryset=RackGroup.objects.select_related('site'), + + group_id = ChainedModelChoiceField( label='Rack group', - null_label='-- None --', + queryset=RackGroup.objects.select_related('site'), + chains=( + ('site', 'site'), + ), widget=APISelectMultiple( api_url="/api/dcim/rack-groups/", null_option=True, From 86d5b48007a514e0c7968c9711fed5ede0a78f6c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 20 Jun 2019 17:01:21 -0400 Subject: [PATCH 02/15] Post-release version bump --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 8c9d288cc..0157e2560 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured # Environment setup # -VERSION = '2.6.0' +VERSION = '2.6.1-dev' # Hostname HOSTNAME = platform.node() From 94ca3abefc682c74be26b51404ee9061bea61dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ma=CC=88der?= Date: Fri, 21 Jun 2019 22:07:37 +0200 Subject: [PATCH 03/15] Fixes Cacheops with a password protected redis As per the [`README.rst`][1] of `django-cacheops`, if a password is added to the connection string, it must be in the form `redis://:password@host:port/db`. Notice the colon, which was missing from the implementation in [`settings.py`][2]. [1]: https://github.com/Suor/django-cacheops/blob/8ad970d55a3782fbaa027332dcc4ae1ee8c41437/README.rst [2]: https://github.com/digitalocean/netbox/blob/86d5b48007a514e0c7968c9711fed5ede0a78f6c/netbox/netbox/settings.py#L349 --- netbox/netbox/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 0157e2560..d6fd90c93 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -346,7 +346,7 @@ else: REDIS_CACHE_CON_STRING = 'redis://' if REDIS_PASSWORD: - REDIS_CACHE_CON_STRING = '{}{}@'.format(REDIS_CACHE_CON_STRING, REDIS_PASSWORD) + REDIS_CACHE_CON_STRING = '{}:{}@'.format(REDIS_CACHE_CON_STRING, REDIS_PASSWORD) REDIS_CACHE_CON_STRING = '{}{}:{}/{}'.format(REDIS_CACHE_CON_STRING, REDIS_HOST, REDIS_PORT, REDIS_CACHE_DATABASE) From c067549f212a4a7fa4cc54e7ef49a84e1d851627 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Jun 2019 16:24:12 -0400 Subject: [PATCH 04/15] Fixes #3275: Fix error when adding power outlets to a device type --- CHANGELOG.md | 8 ++++++++ netbox/dcim/forms.py | 25 +++++++++++++++---------- netbox/dcim/tables.py | 2 +- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26b57caec..c5a4ec680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +v2.6.1 (FUTURE) + +## Bug Fixes + +* [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type + +--- + v2.6.0 (2019-06-20) ## New Features diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 1ae7b76f4..060d7ba50 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -951,10 +951,6 @@ class PowerPortTemplateCreateForm(ComponentForm): class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): - power_port = forms.ModelChoiceField( - queryset=PowerPortTemplate.objects.all(), - required=False - ) class Meta: model = PowerOutletTemplate @@ -965,6 +961,21 @@ class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): 'device_type': forms.HiddenInput(), } + +class PowerOutletTemplateCreateForm(ComponentForm): + name_pattern = ExpandableNameField( + label='Name' + ) + power_port = forms.ModelChoiceField( + queryset=PowerPortTemplate.objects.all(), + required=False + ) + feed_leg = forms.ChoiceField( + choices=add_blank_choice(POWERFEED_LEG_CHOICES), + required=False, + widget=StaticSelect2() + ) + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -975,12 +986,6 @@ class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): ) -class PowerOutletTemplateCreateForm(ComponentForm): - name_pattern = ExpandableNameField( - label='Name' - ) - - class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm): class Meta: diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index aafb35a0f..3958e1326 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -433,7 +433,7 @@ class PowerOutletTemplateTable(BaseTable): class Meta(BaseTable.Meta): model = PowerOutletTemplate - fields = ('pk', 'name') + fields = ('pk', 'name', 'power_port', 'feed_leg') empty_text = "None" From 5a6c928a7c83bca4b9f8b8d3303a3a2b74c12214 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 21 Jun 2019 17:34:06 -0400 Subject: [PATCH 05/15] Fixes #3279: Reset the PostgreSQL sequence for Tag and TaggedItem IDs --- CHANGELOG.md | 1 + netbox/extras/migrations/0023_fix_tag_sequences.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 netbox/extras/migrations/0023_fix_tag_sequences.py diff --git a/CHANGELOG.md b/CHANGELOG.md index c5a4ec680..55d10190b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ v2.6.1 (FUTURE) ## Bug Fixes * [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type +* [#3279](https://github.com/digitalocean/netbox/issues/3279) - Reset the PostgreSQL sequence for Tag and TaggedItem IDs --- diff --git a/netbox/extras/migrations/0023_fix_tag_sequences.py b/netbox/extras/migrations/0023_fix_tag_sequences.py new file mode 100644 index 000000000..faef5aa96 --- /dev/null +++ b/netbox/extras/migrations/0023_fix_tag_sequences.py @@ -0,0 +1,14 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0022_custom_links'), + ] + + operations = [ + # Update the last_value for tag Tag and TaggedItem ID sequences + migrations.RunSQL("SELECT setval('extras_tag_id_seq', (SELECT id FROM extras_tag ORDER BY id DESC LIMIT 1) + 1)"), + migrations.RunSQL("SELECT setval('extras_taggeditem_id_seq', (SELECT id FROM extras_taggeditem ORDER BY id DESC LIMIT 1) + 1)"), + ] From 70ef6a69ee0ee74926b524cb57083c3bc7427064 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 10:05:21 -0400 Subject: [PATCH 06/15] Fixes #3290: Fix server error when viewing cascaded PDUs --- CHANGELOG.md | 1 + netbox/templates/dcim/device.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55d10190b..31cc18af1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ v2.6.1 (FUTURE) * [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type * [#3279](https://github.com/digitalocean/netbox/issues/3279) - Reset the PostgreSQL sequence for Tag and TaggedItem IDs +* [#3290](https://github.com/digitalocean/netbox/issues/3290) - Fix server error when viewing cascaded PDUs --- diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 5f0bbdfc0..414b61bb6 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -359,7 +359,7 @@ {{ pp }} {{ utilization.outlet_count }} {{ utilization.allocated }}VA - {% if powerfeed %} + {% if powerfeed.available_power %} {{ powerfeed.available_power }}VA {% utilization_graph utilization.allocated|percentage:powerfeed.available_power %} {% else %} From 653770ede90f9eaeabbb2f2b769b2b7bd0cba75a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 11:00:18 -0400 Subject: [PATCH 07/15] Fixes #3292: Ignore empty URL query parameters --- CHANGELOG.md | 1 + netbox/utilities/filters.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31cc18af1..fdca3196b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ v2.6.1 (FUTURE) * [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type * [#3279](https://github.com/digitalocean/netbox/issues/3279) - Reset the PostgreSQL sequence for Tag and TaggedItem IDs * [#3290](https://github.com/digitalocean/netbox/issues/3290) - Fix server error when viewing cascaded PDUs +* [#3292](https://github.com/digitalocean/netbox/issues/3292) - Ignore empty URL query parameters --- diff --git a/netbox/utilities/filters.py b/netbox/utilities/filters.py index 614c09902..8ccdf2583 100644 --- a/netbox/utilities/filters.py +++ b/netbox/utilities/filters.py @@ -9,7 +9,7 @@ from extras.models import Tag def multivalue_field_factory(field_class): """ Given a form field class, return a subclass capable of accepting multiple values. This allows us to OR on multiple - filter values while maintaining the field's built-in vlaidation. Example: GET /api/dcim/devices/?name=foo&name=bar + filter values while maintaining the field's built-in validation. Example: GET /api/dcim/devices/?name=foo&name=bar """ class NewField(field_class): widget = forms.SelectMultiple @@ -17,7 +17,10 @@ def multivalue_field_factory(field_class): def to_python(self, value): if not value: return [] - return [super(field_class, self).to_python(v) for v in value] + return [ + # Only append non-empty values (this avoids e.g. trying to cast '' as an integer) + super(field_class, self).to_python(v) for v in value if v + ] return type('MultiValue{}'.format(field_class.__name__), (NewField,), dict()) From 251ba08e0954bc509161b5181150fbf384d836a9 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 11:10:35 -0400 Subject: [PATCH 08/15] Fixes #3283: Fix rack group assignment on PowerFeed CSV import --- CHANGELOG.md | 1 + netbox/dcim/forms.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdca3196b..ee48d4882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ v2.6.1 (FUTURE) * [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type * [#3279](https://github.com/digitalocean/netbox/issues/3279) - Reset the PostgreSQL sequence for Tag and TaggedItem IDs +* [#3283](https://github.com/digitalocean/netbox/issues/3283) - Fix rack group assignment on PowerFeed CSV import * [#3290](https://github.com/digitalocean/netbox/issues/3290) - Fix server error when viewing cascaded PDUs * [#3292](https://github.com/digitalocean/netbox/issues/3292) - Ignore empty URL query parameters diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 060d7ba50..657351cd0 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -3585,7 +3585,7 @@ class PowerFeedCSVForm(forms.ModelForm): # Validate rack if rack_name: try: - self.instance.rack = Rack.objects.get(site=site, rack_group=rack_group, name=rack_name) + self.instance.rack = Rack.objects.get(site=site, group__name=rack_group, name=rack_name) except Rack.DoesNotExist: raise forms.ValidationError( "Rack {} not found in site {}, group {}".format(rack_name, site, rack_group) From 5de242fe5357cb4791eaef6b090f602712a20a61 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 12:20:09 -0400 Subject: [PATCH 09/15] Closes #3281: Hide custom links which render as empty text --- CHANGELOG.md | 4 +++ netbox/extras/admin.py | 3 +- netbox/extras/templatetags/custom_links.py | 41 +++++++++++++--------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee48d4882..0c9b70a91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ v2.6.1 (FUTURE) +## Enhancements + +* [#3281](https://github.com/digitalocean/netbox/issues/3281) - Hide custom links which render as empty text + ## Bug Fixes * [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type diff --git a/netbox/extras/admin.py b/netbox/extras/admin.py index a29d0df09..d93b04037 100644 --- a/netbox/extras/admin.py +++ b/netbox/extras/admin.py @@ -87,7 +87,8 @@ class CustomLinkForm(forms.ModelForm): model = CustomLink exclude = [] help_texts = { - 'text': 'Jinja2 template code for the link text. Reference the object as {{ obj }}.', + 'text': 'Jinja2 template code for the link text. Reference the object as {{ obj }}. Links ' + 'which render as empty text will not be displayed.', 'url': 'Jinja2 template code for the link URL. Reference the object as {{ obj }}.', } diff --git a/netbox/extras/templatetags/custom_links.py b/netbox/extras/templatetags/custom_links.py index 193c465a5..ce6cc482a 100644 --- a/netbox/extras/templatetags/custom_links.py +++ b/netbox/extras/templatetags/custom_links.py @@ -15,7 +15,8 @@ GROUP_BUTTON = '
\n' \ '\n' \ - '
' GROUP_LINK = '
  • {}
  • \n' @@ -35,32 +36,40 @@ def custom_links(obj): template_code = '' group_names = OrderedDict() - # Organize custom links by group for cl in custom_links: + + # Organize custom links by group if cl.group_name and cl.group_name in group_names: group_names[cl.group_name].append(cl) elif cl.group_name: group_names[cl.group_name] = [cl] - # Add non-grouped links - for cl in custom_links: - if not cl.group_name: - link_target = ' target="_blank"' if cl.new_window else '' - template_code += LINK_BUTTON.format( - cl.url, link_target, cl.button_class, cl.text - ) + # Add non-grouped links + else: + text_rendered = Environment().from_string(source=cl.text).render(**context) + if text_rendered: + link_target = ' target="_blank"' if cl.new_window else '' + template_code += LINK_BUTTON.format( + cl.url, link_target, cl.button_class, text_rendered + ) # Add grouped links to template for group, links in group_names.items(): - template_code += GROUP_BUTTON.format( - links[0].button_class, group - ) + + links_rendered = [] + for cl in links: - link_target = ' target="_blank"' if cl.new_window else '' - template_code += GROUP_LINK.format( - cl.url, link_target, cl.text + text_rendered = Environment().from_string(source=cl.text).render(**context) + if text_rendered: + link_target = ' target="_blank"' if cl.new_window else '' + links_rendered.append( + GROUP_LINK.format(cl.url, link_target, cl.text) + ) + + if links_rendered: + template_code += GROUP_BUTTON.format( + links[0].button_class, group, ''.join(links_rendered) ) - template_code += '\n\n' # Render template rendered = Environment().from_string(source=template_code).render(**context) From 9f50ced6fcf9a5cf4462325a6f950706e4c05030 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 14:22:03 -0400 Subject: [PATCH 10/15] Changelog for #3229 --- CHANGELOG.md | 1 + netbox/dcim/forms.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c9b70a91..3adee7f7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ v2.6.1 (FUTURE) ## Bug Fixes +* [#3229](https://github.com/digitalocean/netbox/issues/3229) - Limit rack group selection by parent site on racks list * [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type * [#3279](https://github.com/digitalocean/netbox/issues/3279) - Reset the PostgreSQL sequence for Tag and TaggedItem IDs * [#3283](https://github.com/digitalocean/netbox/issues/3283) - Fix rack group assignment on PowerFeed CSV import diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index d0f0fb0b2..94bb8e6a8 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -606,7 +606,6 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): } ) ) - group_id = ChainedModelChoiceField( label='Rack group', queryset=RackGroup.objects.select_related('site'), From cf770bf40c5e299db9778e92f2f3686fa981e9bd Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 14:27:34 -0400 Subject: [PATCH 11/15] Closes #3277: Add cable trace buttons for console and power ports --- CHANGELOG.md | 1 + netbox/templates/dcim/inc/consoleport.html | 3 +++ netbox/templates/dcim/inc/powerport.html | 3 +++ 3 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3adee7f7a..7841bafb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ v2.6.1 (FUTURE) ## Enhancements +* [#3277](https://github.com/digitalocean/netbox/issues/3277) - Add cable trace buttons for console and power ports * [#3281](https://github.com/digitalocean/netbox/issues/3281) - Hide custom links which render as empty text ## Bug Fixes diff --git a/netbox/templates/dcim/inc/consoleport.html b/netbox/templates/dcim/inc/consoleport.html index e75e09076..03c28c22a 100644 --- a/netbox/templates/dcim/inc/consoleport.html +++ b/netbox/templates/dcim/inc/consoleport.html @@ -15,6 +15,9 @@ {% if cp.cable %} {{ cp.cable }} + + + {% else %} — {% endif %} diff --git a/netbox/templates/dcim/inc/powerport.html b/netbox/templates/dcim/inc/powerport.html index e8cd77857..99e9e8991 100644 --- a/netbox/templates/dcim/inc/powerport.html +++ b/netbox/templates/dcim/inc/powerport.html @@ -23,6 +23,9 @@ {% if pp.cable %} {{ pp.cable }} + + + {% else %} — {% endif %} From 3fdb655a92c6f77e4a895208166d94364b05546b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 15:42:15 -0400 Subject: [PATCH 12/15] Fixes #3269: Raise validation error when specifying non-existent cable terminationss --- CHANGELOG.md | 1 + netbox/dcim/models.py | 100 ++++++++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7841bafb7..c51306372 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ v2.6.1 (FUTURE) ## Bug Fixes * [#3229](https://github.com/digitalocean/netbox/issues/3229) - Limit rack group selection by parent site on racks list +* [#3269](https://github.com/digitalocean/netbox/issues/3269) - Raise validation error when specifying non-existent cable terminations * [#3275](https://github.com/digitalocean/netbox/issues/3275) - Fix error when adding power outlets to a device type * [#3279](https://github.com/digitalocean/netbox/issues/3279) - Reset the PostgreSQL sequence for Tag and TaggedItem IDs * [#3283](https://github.com/digitalocean/netbox/issues/3283) - Fix rack group assignment on PowerFeed CSV import diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index e1d98a2d4..12270fc3e 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -2747,55 +2747,69 @@ class Cable(ChangeLoggedModel): def clean(self): - if self.termination_a and self.termination_b: + # Validate that termination A exists + try: + self.termination_a_type.model_class().objects.get(pk=self.termination_a_id) + except ObjectDoesNotExist: + raise ValidationError({ + 'termination_a': 'Invalid ID for type {}'.format(self.termination_a_type) + }) - type_a = self.termination_a_type.model - type_b = self.termination_b_type.model + # Validate that termination B exists + try: + self.termination_b_type.model_class().objects.get(pk=self.termination_b_id) + except ObjectDoesNotExist: + raise ValidationError({ + 'termination_b': 'Invalid ID for type {}'.format(self.termination_b_type) + }) - # Check that termination types are compatible - if type_b not in COMPATIBLE_TERMINATION_TYPES.get(type_a): - raise ValidationError("Incompatible termination types: {} and {}".format( - self.termination_a_type, self.termination_b_type - )) + type_a = self.termination_a_type.model + type_b = self.termination_b_type.model - # A termination point cannot be connected to itself - if self.termination_a == self.termination_b: - raise ValidationError("Cannot connect {} to itself".format(self.termination_a_type)) + # Check that termination types are compatible + if type_b not in COMPATIBLE_TERMINATION_TYPES.get(type_a): + raise ValidationError("Incompatible termination types: {} and {}".format( + self.termination_a_type, self.termination_b_type + )) - # A front port cannot be connected to its corresponding rear port - if ( - type_a in ['frontport', 'rearport'] and - type_b in ['frontport', 'rearport'] and - ( - getattr(self.termination_a, 'rear_port', None) == self.termination_b or - getattr(self.termination_b, 'rear_port', None) == self.termination_a - ) - ): - raise ValidationError("A front port cannot be connected to it corresponding rear port") + # A termination point cannot be connected to itself + if self.termination_a == self.termination_b: + raise ValidationError("Cannot connect {} to itself".format(self.termination_a_type)) - # Check for an existing Cable connected to either termination object - if self.termination_a.cable not in (None, self): - raise ValidationError("{} already has a cable attached (#{})".format( - self.termination_a, self.termination_a.cable_id - )) - if self.termination_b.cable not in (None, self): - raise ValidationError("{} already has a cable attached (#{})".format( - self.termination_b, self.termination_b.cable_id - )) + # A front port cannot be connected to its corresponding rear port + if ( + type_a in ['frontport', 'rearport'] and + type_b in ['frontport', 'rearport'] and + ( + getattr(self.termination_a, 'rear_port', None) == self.termination_b or + getattr(self.termination_b, 'rear_port', None) == self.termination_a + ) + ): + raise ValidationError("A front port cannot be connected to it corresponding rear port") - # Virtual interfaces cannot be connected - endpoint_a, endpoint_b, _ = self.get_path_endpoints() - if ( - ( - isinstance(endpoint_a, Interface) and - endpoint_a.type == IFACE_TYPE_VIRTUAL - ) or - ( - isinstance(endpoint_b, Interface) and - endpoint_b.type == IFACE_TYPE_VIRTUAL - ) - ): - raise ValidationError("Cannot connect to a virtual interface") + # Check for an existing Cable connected to either termination object + if self.termination_a.cable not in (None, self): + raise ValidationError("{} already has a cable attached (#{})".format( + self.termination_a, self.termination_a.cable_id + )) + if self.termination_b.cable not in (None, self): + raise ValidationError("{} already has a cable attached (#{})".format( + self.termination_b, self.termination_b.cable_id + )) + + # Virtual interfaces cannot be connected + endpoint_a, endpoint_b, _ = self.get_path_endpoints() + if ( + ( + isinstance(endpoint_a, Interface) and + endpoint_a.type == IFACE_TYPE_VIRTUAL + ) or + ( + isinstance(endpoint_b, Interface) and + endpoint_b.type == IFACE_TYPE_VIRTUAL + ) + ): + raise ValidationError("Cannot connect to a virtual interface") # Validate length and length_unit if self.length is not None and self.length_unit is None: From 7effb7e8d4981c8ff0ba7dce0a32778a7b8267ff Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 15:48:49 -0400 Subject: [PATCH 13/15] Fix for #3229 --- netbox/dcim/forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 94bb8e6a8..357a8def3 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -612,6 +612,7 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): chains=( ('site', 'site'), ), + required=False, widget=APISelectMultiple( api_url="/api/dcim/rack-groups/", null_option=True, From 954ba91c861027f7b3b6a814d657d6bd39822e4c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 24 Jun 2019 16:31:21 -0400 Subject: [PATCH 14/15] Closes #3154: Add virtual_chassis_member device filter --- CHANGELOG.md | 1 + netbox/dcim/filters.py | 7 +++++++ netbox/dcim/forms.py | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c51306372..a043e5722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ v2.6.1 (FUTURE) ## Enhancements +* [#3154](https://github.com/digitalocean/netbox/issues/3154) - Add `virtual_chassis_member` device filter * [#3277](https://github.com/digitalocean/netbox/issues/3277) - Add cable trace buttons for console and power ports * [#3281](https://github.com/digitalocean/netbox/issues/3281) - Hide custom links which render as empty text diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index f1c02e713..6312fd0d5 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -527,6 +527,10 @@ class DeviceFilter(TenancyFilterSet, CustomFieldFilterSet): queryset=VirtualChassis.objects.all(), label='Virtual chassis (ID)', ) + virtual_chassis_member = django_filters.BooleanFilter( + method='_virtual_chassis_member', + label='Is a virtual chassis member' + ) console_ports = django_filters.BooleanFilter( method='_console_ports', label='Has console ports', @@ -590,6 +594,9 @@ class DeviceFilter(TenancyFilterSet, CustomFieldFilterSet): Q(primary_ip6__isnull=False) ) + def _virtual_chassis_member(self, queryset, name, value): + return queryset.exclude(virtual_chassis__isnull=value) + def _console_ports(self, queryset, name, value): return queryset.exclude(consoleports__isnull=value) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 357a8def3..06e1b35b1 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1750,6 +1750,13 @@ class DeviceFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm) choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + virtual_chassis_member = forms.NullBooleanField( + required=False, + label='Virtual chassis member', + widget=StaticSelect2( + choices=BOOLEAN_WITH_BLANK_CHOICES + ) + ) console_ports = forms.NullBooleanField( required=False, label='Has console ports', From d219c3ea88a8c8d5a54644250f28e7d464571a50 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 25 Jun 2019 09:39:30 -0400 Subject: [PATCH 15/15] Release v2.6.1 --- CHANGELOG.md | 2 +- netbox/netbox/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a043e5722..41c818be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -v2.6.1 (FUTURE) +v2.6.1 (2019-06-25) ## Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index d6fd90c93..2b6023f2a 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -12,7 +12,7 @@ from django.core.exceptions import ImproperlyConfigured # Environment setup # -VERSION = '2.6.1-dev' +VERSION = '2.6.1' # Hostname HOSTNAME = platform.node()