Skip to content

Commit

Permalink
Merge pull request #74 from peteeckel/feature/ipam-coupling
Browse files Browse the repository at this point in the history
IPAM Coupling by @jean1
  • Loading branch information
peteeckel authored Oct 17, 2023
2 parents 5f25951 + bf385a9 commit f22557b
Show file tree
Hide file tree
Showing 18 changed files with 890 additions and 56 deletions.
Binary file added docs/images/IPAMCouplingCustomFields.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/IPAMCouplingRecordDetailView.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/IPAMCouplingRelatedAddressRecord.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/IPAMCouplingRelatedPointerRecord.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 66 additions & 1 deletion docs/using_netbox_dns.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,69 @@ The NetBox detail view for tenants shows a table of NetBox DNS objects assigned

![NetBox Tenant Detail](images/NetBoxTenantDetail.png)

The colums of the table on the left side are clickable and link to filtered lists showing the related views, nameservers, zones and records.
The colums of the table on the left side are clickable and link to filtered lists showing the related views, nameservers, zones and records.

## IPAM Coupling

Starting with NetBox DNS 0.20.0, a new experimental feature providing coupling between NetBox DNS and NetBox IPAM data is available. This feature can be used to link IP addresses in IPAM to NetBox DNS address records. The old IPAM integration feature was dropped in favour of the new and improved functionality.

Thanks to Jean Benoît for this contribution!

### Enabling IPAM Coupling

The new experimental feature needs to be enabled in the NetBox configuration file by setting its flag:

```
PLUGINS_CONFIG = {
'netbox_dns': {
...
'feature_ipam_coupling': True,
...
},
}
```

In addition, two custom fields on `ipam.IPAddress` objects are required for the feature to work. These custom fields can be created using the Django management command `setup_coupling`:

```
/opt/netbox/netbox/manage.py setup_coupling
```

In order to remove the custom fields and all related data, the same command can be used with the option `--remove`.

After these steps, a restart of NetBox is required.

### Using IPAM Coupling

With the new custom fields it is possible to automatically generate a DNS address record for an IP address. To do this, define a name for the record in the 'Name' custom field and select a zone in the 'Zone' custom in the DNS group.

![Custom Fields for IPAM Coupling](images/IPAMCouplingCustomFields.png)

When the IP address is saved, NetBox DNS now automatically creates a managed address record for it in the selected zone, using the name from the 'Name' custom field. The 'DNS Name' field for the IP address is set to the FQDN of the resulting address record.

The IP address is now linked to the address record in the following ways:

* When one of the custom fields for the IP address is updated, the DNS record is updated as well. This includes changing the name as well as moving it to a different DNS zone
* When the IP address is deleted, the managed DNS record is deleted as well
* When the DNS zone is renamed, the 'DNS Name' for the IP address is updated to reflect the zone's new name
* When the DNS zone is deleted, the address record is deleted and the connection from the IP address object is cleared

### Additional Information for IP Addresses and DNS Records

When a link between an IP address and a DNS address record is present, there are some additional panes in the IPAM IP address and NetBox DNS record view, as well as in the detail views for NetBox DNS managed records.

#### IP Address Information

If a DNS address record is linked to an IP address, the detail view for the IP address contains an additional pane showing that address record.

![Related DNS Address Record](images/IPAMCouplingRelatedAddressRecord.png)

If NetBox DNS also created a PTR record for the linked DNS address record, the detail view for the IP address contains an a second additional pane showing that pointer record.

![Related DNS Address Record](images/IPAMCouplingRelatedPointerRecord.png)

#### DNS Record Information

The detail views for the address and pointer records created for coupled IP addresses include a link to that IP address, which can be used to navigate to the address.

![Record Detail View for Coupled IP Address](images/IPAMCouplingRecordDetailView.png)
33 changes: 32 additions & 1 deletion netbox_dns/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from extras.plugins import PluginConfig
import logging

logger = logging.getLogger("netbox.config")

__version__ = "0.19.4"

Expand All @@ -11,6 +14,7 @@ class DNSConfig(PluginConfig):
version = __version__
author = "Peter Eckel"
author_email = "[email protected]"
middleware = ["netbox_dns.middleware.IpamCouplingMiddleware"]
required_settings = []
default_settings = {
"zone_default_ttl": 86400,
Expand All @@ -20,7 +24,7 @@ class DNSConfig(PluginConfig):
"zone_soa_retry": 7200,
"zone_soa_expire": 2592000,
"zone_soa_minimum": 3600,
"feature_ipam_integration": False,
"feature_ipam_coupling": False,
"tolerate_underscores_in_hostnames": False,
"tolerate_leading_underscore_types": [
"TXT",
Expand All @@ -32,5 +36,32 @@ class DNSConfig(PluginConfig):
}
base_url = "netbox-dns"

def ready(self):
#
# Check if required custom field exist for IPAM coupling
#
if self.default_settings["feature_ipam_coupling"]:
from extras.models import CustomField
from ipam.models import IPAddress
from django.contrib.contenttypes.models import ContentType

objtype = ContentType.objects.get_for_model(IPAddress)
required_cf = ("ipaddress_dns_record_name", "ipaddress_dns_zone_id")

if CustomField.objects.filter(
name__in=required_cf, content_types=objtype
).count() < len(required_cf):
logger.warning(
"'feature_ipam_coupling' is enabled, but the required custom"
" fields for IPAM-DNS coupling are missing. Please run the"
" Django management command 'setup_coupling' to create the"
" custom fields."
)

super().ready()


#
# Initialize plugin config
#
config = DNSConfig
9 changes: 9 additions & 0 deletions netbox_dns/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rest_framework import serializers

from netbox.api.serializers import NetBoxModelSerializer
from ipam.api.nested_serializers import NestedIPAddressSerializer
from tenancy.api.nested_serializers import NestedTenantSerializer

from netbox_dns.api.nested_serializers import (
Expand Down Expand Up @@ -168,6 +169,13 @@ class RecordSerializer(NetBoxModelSerializer):
required=False,
read_only=True,
)
ipam_ip_address = NestedIPAddressSerializer(
many=False,
read_only=True,
required=False,
allow_null=True,
help_text="IPAddress linked to the record",
)
tenant = NestedTenantSerializer(required=False, allow_null=True)

class Meta:
Expand All @@ -193,4 +201,5 @@ class Meta:
"active",
"custom_fields",
"tenant",
"ipam_ip_address",
)
75 changes: 75 additions & 0 deletions netbox_dns/management/commands/setup_coupling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from django.core.management.base import BaseCommand, CommandError

from django.contrib.contenttypes.models import ContentType
from extras.models import CustomField
from extras.choices import CustomFieldTypeChoices
from ipam.models import IPAddress
from netbox_dns.models import Record, Zone


class Command(BaseCommand):
help = "Setup IPAddress custom fields needed for IPAM-DNS coupling"

def add_arguments(self, parser):
parser.add_argument(
"--remove", action="store_true", help="Remove custom fields"
)

def handle(self, *model_names, **options):
ipaddress_object_type = ContentType.objects.get_for_model(IPAddress)
zone_object_type = ContentType.objects.get_for_model(Zone)
record_object_type = ContentType.objects.get_for_model(Record)
customfields = ("ipaddress_dns_record_name", "ipaddress_dns_zone_id")

if options["remove"]:
for cf in customfields:
try:
CustomField.objects.get(
name=cf, content_types=ipaddress_object_type
).delete()
except:
self.stderr.write(f"Custom field '{cf}' does not exist!")
else:
self.stdout.write(f"Custom field '{cf}' removed")

else:
msg = ""
for cf in customfields:
try:
CustomField.objects.get(
name=cf, content_types=ipaddress_object_type
)
except:
pass
else:
msg += f"custom fields '{cf}' already exists, "
if msg != "":
raise CommandError(
"\n".join(
(
"Can't setup IPAM-DNS coupling:",
msg,
"Remove them with NetBox command:",
"python manage.py setup_coupling --remove",
)
)
)

cf_name = CustomField.objects.create(
name="ipaddress_dns_record_name",
label="Name",
type=CustomFieldTypeChoices.TYPE_TEXT,
required=False,
group_name="DNS",
)
cf_name.content_types.set([ipaddress_object_type])
cf_zone = CustomField.objects.create(
name="ipaddress_dns_zone_id",
label="Zone",
type=CustomFieldTypeChoices.TYPE_OBJECT,
object_type=zone_object_type,
required=False,
group_name="DNS",
)
cf_zone.content_types.set([ipaddress_object_type])
self.stdout.write(f"Custom fields for IPAM-DNS coupling added")
Loading

0 comments on commit f22557b

Please sign in to comment.