1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge develop into develop-2.10

This commit is contained in:
Jeremy Stretch
2020-09-04 16:09:05 -04:00
parent ec66e1a5c0
commit 08c492f1f4
46 changed files with 464 additions and 117 deletions

View File

@@ -88,7 +88,7 @@ class PrefixViewSet(CustomFieldModelViewSet):
return super().get_serializer_class()
@swagger_auto_schema(method='get', responses={200: serializers.AvailablePrefixSerializer(many=True)})
@swagger_auto_schema(method='post', responses={201: serializers.AvailablePrefixSerializer(many=True)})
@swagger_auto_schema(method='post', responses={201: serializers.PrefixSerializer(many=False)})
@action(detail=True, url_path='available-prefixes', methods=['get', 'post'])
@advisory_lock(ADVISORY_LOCK_KEYS['available-prefixes'])
def available_prefixes(self, request, pk=None):
@@ -247,7 +247,7 @@ class PrefixViewSet(CustomFieldModelViewSet):
class IPAddressViewSet(CustomFieldModelViewSet):
queryset = IPAddress.objects.prefetch_related(
'vrf__tenant', 'tenant', 'nat_inside', 'nat_outside', 'tags',
'vrf__tenant', 'tenant', 'nat_inside', 'nat_outside', 'tags', 'assigned_object'
)
serializer_class = serializers.IPAddressSerializer
filterset_class = filters.IPAddressFilterSet

View File

@@ -41,12 +41,14 @@ class IPAddressStatusChoices(ChoiceSet):
STATUS_RESERVED = 'reserved'
STATUS_DEPRECATED = 'deprecated'
STATUS_DHCP = 'dhcp'
STATUS_SLAAC = 'slaac'
CHOICES = (
(STATUS_ACTIVE, 'Active'),
(STATUS_RESERVED, 'Reserved'),
(STATUS_DEPRECATED, 'Deprecated'),
(STATUS_DHCP, 'DHCP'),
(STATUS_SLAAC, 'SLAAC'),
)

View File

@@ -669,6 +669,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
'reserved': 'info',
'deprecated': 'danger',
'dhcp': 'success',
'slaac': 'success',
}
ROLE_CLASS_MAP = {
@@ -745,12 +746,18 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
'vminterface': f"IP address is primary for virtual machine {vm} but not assigned to an "
f"interface"
})
elif self.interface.virtual_machine != vm:
elif self.assigned_object.virtual_machine != vm:
raise ValidationError({
'vminterface': f"IP address is primary for virtual machine {vm} but assigned to "
f"{self.assigned_object.virtual_machine} ({self.assigned_object})"
})
# Validate IP status selection
if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
raise ValidationError({
'status': "Only IPv6 addresses can be assigned SLAAC status"
})
def save(self, *args, **kwargs):
# Force dns_name to lowercase

View File

@@ -67,11 +67,7 @@ IPADDRESS_LINK = """
"""
IPADDRESS_ASSIGN_LINK = """
{% if request.GET %}
<a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ request.GET.interface }}&return_url={{ request.GET.return_url }}">{{ record }}</a>
{% else %}
<a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?interface={{ record.interface.pk }}&return_url={{ request.path }}">{{ record }}</a>
{% endif %}
<a href="{% url 'ipam:ipaddress_edit' pk=record.pk %}?{% if request.GET.interface %}interface={{ request.GET.interface }}{% elif request.GET.vminterface %}vminterface={{ request.GET.vminterface }}{% endif %}&return_url={{ request.GET.return_url }}">{{ record }}</a>
"""
VRF_LINK = """
@@ -103,7 +99,7 @@ VLAN_LINK = """
"""
VLAN_PREFIXES = """
{% for prefix in record.prefixes.unrestricted %}
{% for prefix in record.prefixes.all %}
<a href="{% url 'ipam:prefix' pk=prefix.pk %}">{{ prefix }}</a>{% if not forloop.last %}<br />{% endif %}
{% empty %}
&mdash;
@@ -387,15 +383,23 @@ class IPAddressTable(BaseTable):
tenant = tables.TemplateColumn(
template_code=TENANT_LINK
)
assigned = tables.BooleanColumn(
accessor='assigned_object_id',
verbose_name='Assigned'
assigned_object = tables.Column(
linkify=True,
orderable=False,
verbose_name='Interface'
)
assigned_object_parent = tables.Column(
accessor='assigned_object__parent',
linkify=True,
orderable=False,
verbose_name='Interface Parent'
)
class Meta(BaseTable.Meta):
model = IPAddress
fields = (
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned', 'dns_name', 'description',
'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'assigned_object', 'assigned_object_parent', 'dns_name',
'description',
)
row_attrs = {
'class': lambda record: 'success' if not isinstance(record, IPAddress) else '',
@@ -411,6 +415,10 @@ class IPAddressDetailTable(IPAddressTable):
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
assigned = tables.BooleanColumn(
accessor='assigned_object_id',
verbose_name='Assigned'
)
tags = TagColumn(
url_name='ipam:ipaddress_list'
)

View File

@@ -493,7 +493,7 @@ class PrefixBulkDeleteView(BulkDeleteView):
class IPAddressListView(ObjectListView):
queryset = IPAddress.objects.prefetch_related(
'vrf__tenant', 'tenant', 'nat_inside'
'vrf__tenant', 'tenant', 'nat_inside', 'assigned_object'
)
filterset = filters.IPAddressFilterSet
filterset_form = forms.IPAddressFilterForm
@@ -582,7 +582,7 @@ class IPAddressAssignView(ObjectView):
def dispatch(self, request, *args, **kwargs):
# Redirect user if an interface has not been provided
if 'interface' not in request.GET:
if 'interface' not in request.GET and 'vminterface' not in request.GET:
return redirect('ipam:ipaddress_add')
return super().dispatch(request, *args, **kwargs)
@@ -609,7 +609,7 @@ class IPAddressAssignView(ObjectView):
return render(request, 'ipam/ipaddress_assign.html', {
'form': form,
'table': table,
'return_url': request.GET.get('return_url', ''),
'return_url': request.GET.get('return_url'),
})