From 2c8bea1b5974c99777f17f6d7aec2041cd689cdb Mon Sep 17 00:00:00 2001 From: frankfarmer Date: Mon, 9 Apr 2018 17:42:54 -0700 Subject: [PATCH 1/8] avoid illegal casts on large integers A similar fix was applied in e5e32d82d00e454ba5edf25316828c1cdcd7673e --- netbox/dcim/querysets.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/netbox/dcim/querysets.py b/netbox/dcim/querysets.py index 3e977ddc6..32275ce01 100644 --- a/netbox/dcim/querysets.py +++ b/netbox/dcim/querysets.py @@ -43,13 +43,13 @@ class InterfaceQuerySet(QuerySet): }[method] TYPE_RE = r"SUBSTRING({} FROM '^([^0-9]+)')" - ID_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)([0-9]+)$') AS integer)" - SLOT_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?([0-9]+)\/') AS integer)" - SUBSLOT_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:[0-9]+\/)([0-9]+)') AS integer), 0)" - POSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:[0-9]+\/){{2}}([0-9]+)') AS integer), 0)" - SUBPOSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:[0-9]+\/){{3}}([0-9]+)') AS integer), 0)" - CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM ':([0-9]+)(\.[0-9]+)?$') AS integer), 0)" - VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '\.([0-9]+)$') AS integer), 0)" + ID_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)(\d{{1,9}})$') AS integer)" + SLOT_RE = r"CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(\d{{1,9}})\/') AS integer)" + SUBSLOT_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}\/)(\d{{1,9}})') AS integer), 0)" + POSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}\/){{2}}(\d{{1,9}})') AS integer), 0)" + SUBPOSITION_RE = r"COALESCE(CAST(SUBSTRING({} FROM '^(?:[^0-9]+)?(?:\d{{1,9}}\/){{3}}(\d{{1,9}})') AS integer), 0)" + CHANNEL_RE = r"COALESCE(CAST(SUBSTRING({} FROM ':(\d{{1,9}})(\.\d{{1,9}})?$') AS integer), 0)" + VC_RE = r"COALESCE(CAST(SUBSTRING({} FROM '\.(\d{{1,9}})$') AS integer), 0)" fields = { '_type': RawSQL(TYPE_RE.format(sql_col), []), From 81c027e7cffd73238ca108ff3bcf2ae4344a25c4 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 12 Apr 2018 12:45:25 -0400 Subject: [PATCH 2/8] Fixes #2023: Manufacturer should not be a required field when importing platforms --- netbox/dcim/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index b360108bf..05dc0ea6f 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -706,7 +706,7 @@ class PlatformCSVForm(forms.ModelForm): slug = SlugField() manufacturer = forms.ModelChoiceField( queryset=Manufacturer.objects.all(), - required=True, + required=False, to_field_name='name', help_text='Manufacturer name', error_messages={ From ef84889a573ea06308ade4af02cdcf5aa7c1798a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 12 Apr 2018 12:54:21 -0400 Subject: [PATCH 3/8] Fixes #2022: Show 0 for zero-value fields on CSV export --- netbox/utilities/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/utilities/utils.py b/netbox/utilities/utils.py index c08bfef8c..9e96a66fd 100644 --- a/netbox/utilities/utils.py +++ b/netbox/utilities/utils.py @@ -14,7 +14,7 @@ def csv_format(data): for value in data: # Represent None or False with empty string - if value in [None, False]: + if value is None or value is False: csv.append('') continue From bcb1d9af0be7ae8bd1236b9b7a0e3c98f41e8f4a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 12 Apr 2018 13:03:20 -0400 Subject: [PATCH 4/8] Fixes #2012: Fixed deselection of an IP address as the primary IP for its parent device/VM --- netbox/ipam/forms.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 5b2c6e672..3353d981f 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -508,7 +508,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm) ipaddress = super(IPAddressForm, self).save(*args, **kwargs) - # Assign this IPAddress as the primary for the associated Device. + # Assign/clear this IPAddress as the primary for the associated Device/VirtualMachine. if self.cleaned_data['primary_for_parent']: parent = self.cleaned_data['interface'].parent if ipaddress.address.version == 4: @@ -516,14 +516,12 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm) else: parent.primary_ip6 = ipaddress parent.save() - - # Clear assignment as primary for device if set. elif self.cleaned_data['interface']: parent = self.cleaned_data['interface'].parent - if ipaddress.address.version == 4 and parent.primary_ip4 == self: + if ipaddress.address.version == 4 and parent.primary_ip4 == ipaddress: parent.primary_ip4 = None parent.save() - elif ipaddress.address.version == 6 and parent.primary_ip6 == self: + elif ipaddress.address.version == 6 and parent.primary_ip6 == ipaddress: parent.primary_ip6 = None parent.save() From b44aa9d32ea654de242215238d8c9131f4594d2d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 18 Apr 2018 12:37:20 -0400 Subject: [PATCH 5/8] Fixes #2014: Allow assignment of VLANs to VM interfaces via the API --- netbox/virtualization/api/serializers.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index 7e2ec1690..c03cdc166 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -3,10 +3,10 @@ from __future__ import unicode_literals from rest_framework import serializers from dcim.api.serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer -from dcim.constants import IFACE_FF_VIRTUAL +from dcim.constants import IFACE_FF_VIRTUAL, IFACE_MODE_CHOICES from dcim.models import Interface from extras.api.customfields import CustomFieldModelSerializer -from ipam.models import IPAddress +from ipam.models import IPAddress, VLAN from tenancy.api.serializers import NestedTenantSerializer from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer from virtualization.constants import VM_STATUS_CHOICES @@ -133,13 +133,26 @@ class WritableVirtualMachineSerializer(CustomFieldModelSerializer): # VM interfaces # +# Cannot import ipam.api.serializers.NestedVLANSerializer due to circular dependency +class InterfaceVLANSerializer(serializers.ModelSerializer): + url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail') + + class Meta: + model = VLAN + fields = ['id', 'url', 'vid', 'name', 'display_name'] + + class InterfaceSerializer(serializers.ModelSerializer): virtual_machine = NestedVirtualMachineSerializer() + mode = ChoiceFieldSerializer(choices=IFACE_MODE_CHOICES) + untagged_vlan = InterfaceVLANSerializer() + tagged_vlans = InterfaceVLANSerializer(many=True) class Meta: model = Interface fields = [ - 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'description', + 'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'untagged_vlan', 'tagged_vlans', + 'description', ] @@ -157,5 +170,6 @@ class WritableInterfaceSerializer(ValidatedModelSerializer): class Meta: model = Interface fields = [ - 'id', 'name', 'virtual_machine', 'form_factor', 'enabled', 'mac_address', 'mtu', 'description', + 'id', 'name', 'virtual_machine', 'form_factor', 'enabled', 'mac_address', 'mtu', 'mode', 'untagged_vlan', + 'tagged_vlans', 'description', ] From 9153c71cbf1a9f7273bc7a29e2505687de201a59 Mon Sep 17 00:00:00 2001 From: "Nicholas St. Germain" Date: Wed, 18 Apr 2018 14:02:40 -0500 Subject: [PATCH 6/8] stop force value split w ArrayFieldSelectMultiple --- netbox/utilities/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 15fb69f7f..69b102f5c 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -205,7 +205,8 @@ class ArrayFieldSelectMultiple(SelectWithDisabled, forms.SelectMultiple): def optgroups(self, name, value, attrs=None): # Split the delimited string of values into a list - value = value[0].split(self.delimiter) + if value: + value = value[0].split(self.delimiter) return super(ArrayFieldSelectMultiple, self).optgroups(name, value, attrs) def value_from_datadict(self, data, files, name): From a7389de109bd2e30ec4b995d646eea1a1c64f849 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Apr 2018 11:07:19 -0400 Subject: [PATCH 7/8] Release v2.3.3 --- 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 e40106a21..7a8dd1a98 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -22,7 +22,7 @@ if sys.version_info[0] < 3: DeprecationWarning ) -VERSION = '2.3.3-dev' +VERSION = '2.3.3' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From e5454d6714a6f3b42166e774e7ed2dd244f63d5a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 19 Apr 2018 11:17:17 -0400 Subject: [PATCH 8/8] 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 7a8dd1a98..b74cbe7f4 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -22,7 +22,7 @@ if sys.version_info[0] < 3: DeprecationWarning ) -VERSION = '2.3.3' +VERSION = '2.3.4-dev' BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))