diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd560861..947093812 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,6 +135,7 @@ functionality provided by the front end UI. ## Enhancements +* [#166](https://github.com/digitalocean/netbox/issues/166) - Add `dns_name` field to IPAddress * [#323](https://github.com/digitalocean/netbox/issues/323) - Enforce view permissions by object type * [#1792](https://github.com/digitalocean/netbox/issues/1792) - Add CustomFieldChoices API endpoint * [#1863](https://github.com/digitalocean/netbox/issues/1863) - Add child object counts to API representation of organizational objects diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 02e36c63c..fc0c390cf 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -210,8 +210,8 @@ class IPAddressSerializer(TaggitSerializer, CustomFieldModelSerializer): class Meta: model = IPAddress fields = [ - 'id', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'interface', 'description', 'nat_inside', - 'nat_outside', 'tags', 'custom_fields', 'created', 'last_updated', + 'id', 'family', 'address', 'vrf', 'tenant', 'status', 'role', 'interface', 'nat_inside', + 'nat_outside', 'dns_name', 'description', 'tags', 'custom_fields', 'created', 'last_updated', ] read_only_fields = ['family'] diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py index f7125ceb0..2df680240 100644 --- a/netbox/ipam/filters.py +++ b/netbox/ipam/filters.py @@ -331,12 +331,13 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet): class Meta: model = IPAddress - fields = ['family'] + fields = ['family', 'dns_name'] def search(self, queryset, name, value): if not value.strip(): return queryset qs_filter = ( + Q(dns_name__icontains=value) | Q(description__icontains=value) | Q(address__istartswith=value) ) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index f96f1a6a2..2ff5f467f 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -659,8 +659,8 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm) class Meta: model = IPAddress fields = [ - 'address', 'vrf', 'status', 'role', 'description', 'interface', 'primary_for_parent', 'nat_site', - 'nat_rack', 'nat_inside', 'tenant_group', 'tenant', 'tags', + 'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'interface', 'primary_for_parent', + 'nat_site', 'nat_rack', 'nat_inside', 'tenant_group', 'tenant', 'tags', ] widgets = { 'status': StaticSelect2(), @@ -746,7 +746,7 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldForm): class Meta: model = IPAddress fields = [ - 'address', 'vrf', 'status', 'role', 'description', 'tenant_group', 'tenant', + 'address', 'vrf', 'status', 'role', 'dns_name', 'description', 'tenant_group', 'tenant', ] widgets = { 'status': StaticSelect2(), @@ -919,13 +919,18 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd required=False, widget=StaticSelect2() ) + dns_name = forms.CharField( + max_length=255, + required=False + ) description = forms.CharField( - max_length=100, required=False + max_length=100, + required=False ) class Meta: nullable_fields = [ - 'vrf', 'role', 'tenant', 'description', + 'vrf', 'role', 'tenant', 'dns_name', 'description', ] diff --git a/netbox/ipam/migrations/0027_ipaddress_add_dns_name.py b/netbox/ipam/migrations/0027_ipaddress_add_dns_name.py new file mode 100644 index 000000000..b4a0e3a32 --- /dev/null +++ b/netbox/ipam/migrations/0027_ipaddress_add_dns_name.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-04-22 21:43 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0026_prefix_ordering_vrf_nulls_first'), + ] + + operations = [ + 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\\.-]+$')]), + ), + ] diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index 35287dc52..77ec7409b 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -16,6 +16,7 @@ from utilities.utils import serialize_object from .constants import * from .fields import IPNetworkField, IPAddressField from .querysets import PrefixQuerySet +from .validators import DNSValidator class VRF(ChangeLoggedModel, CustomFieldModel): @@ -573,6 +574,13 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel): verbose_name='NAT (Inside)', help_text='The IP for which this address is the "outside" IP' ) + dns_name = models.CharField( + max_length=255, + blank=True, + validators=[DNSValidator], + verbose_name='DNS Name', + help_text='Hostname or FQDN' + ) description = models.CharField( max_length=100, blank=True @@ -588,7 +596,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel): csv_headers = [ 'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface_name', 'is_primary', - 'description', + 'dns_name', 'description', ] class Meta: @@ -671,6 +679,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel): self.virtual_machine.name if self.virtual_machine else None, self.interface.name if self.interface else None, is_primary, + self.dns_name, self.description, ) diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index fb48dda24..591b54c0c 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -339,7 +339,9 @@ class IPAddressTable(BaseTable): class Meta(BaseTable.Meta): model = IPAddress - fields = ('pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'description') + fields = ( + 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'parent', 'interface', 'dns_name', 'description', + ) row_attrs = { 'class': lambda record: 'success' if not isinstance(record, IPAddress) else '', } @@ -352,7 +354,8 @@ class IPAddressDetailTable(IPAddressTable): class Meta(IPAddressTable.Meta): fields = ( - 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'description', + 'pk', 'address', 'vrf', 'status', 'role', 'tenant', 'nat_inside', 'parent', 'interface', 'dns_name', + 'description', ) diff --git a/netbox/ipam/validators.py b/netbox/ipam/validators.py new file mode 100644 index 000000000..710f05348 --- /dev/null +++ b/netbox/ipam/validators.py @@ -0,0 +1,8 @@ +from django.core.validators import RegexValidator + + +DNSValidator = RegexValidator( + regex='^[a-z]+$', + message='Only alphanumeric characters, hyphens, and periods are allowed in DNS names', + code='invalid' +) diff --git a/netbox/templates/ipam/ipaddress.html b/netbox/templates/ipam/ipaddress.html index 577a5e09c..cb04e14d5 100644 --- a/netbox/templates/ipam/ipaddress.html +++ b/netbox/templates/ipam/ipaddress.html @@ -109,6 +109,10 @@ {% endif %} + + DNS Name + {{ ipaddress.dns_name|placeholder }} + Description {{ ipaddress.description|placeholder }} diff --git a/netbox/templates/ipam/ipaddress_edit.html b/netbox/templates/ipam/ipaddress_edit.html index 771999bb4..c24c94c87 100644 --- a/netbox/templates/ipam/ipaddress_edit.html +++ b/netbox/templates/ipam/ipaddress_edit.html @@ -17,6 +17,7 @@ {% render_field form.status %} {% render_field form.role %} {% render_field form.vrf %} + {% render_field form.dns_name %} {% render_field form.description %}