فهرست منبع

Merge branch 'develop' into develop-2.4

Jeremy Stretch 7 سال پیش
والد
کامیت
57f6d22c64

+ 1 - 1
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={

+ 7 - 7
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), []),

+ 3 - 5
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()
 

+ 1 - 1
netbox/netbox/settings.py

@@ -22,7 +22,7 @@ if sys.version_info[0] < 3:
         DeprecationWarning
     )
 
-VERSION = '2.3.3-dev'
+VERSION = '2.4-dev'
 
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 

+ 2 - 1
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):

+ 1 - 1
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
 

+ 17 - 5
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, WritableNestedSerializer
 from virtualization.constants import VM_STATUS_CHOICES
@@ -116,14 +116,26 @@ class NestedVirtualMachineSerializer(WritableNestedSerializer):
 # VM interfaces
 #
 
-class InterfaceSerializer(ValidatedModelSerializer):
+# 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()
-    form_factor = serializers.IntegerField(default=IFACE_FF_VIRTUAL)
+    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',
         ]