Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20064ec9bd | ||
|
|
6ac1548eef | ||
|
|
d8f4b83c2a |
No files matched your search
@@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
__version__ = "1.0.1"
|
__version__ = "1.0.4"
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import socketserver
|
import socketserver
|
||||||
import os
|
import os
|
||||||
|
#import re
|
||||||
import threading
|
import threading
|
||||||
import dns.query
|
import dns.query
|
||||||
import dns.message
|
import dns.message
|
||||||
@@ -246,21 +247,26 @@ class DNSBaseRequestHandler(socketserver.BaseRequestHandler):
|
|||||||
# If the record has no TTL, use the zone default
|
# If the record has no TTL, use the zone default
|
||||||
ttl = record.ttl or nb_zone.default_ttl
|
ttl = record.ttl or nb_zone.default_ttl
|
||||||
|
|
||||||
# Apply special quoting for TXT records to stop tokanizer
|
# Apply quoting for TXT records to stop tokanizer
|
||||||
# from cutting it up:
|
# from cutting it up:
|
||||||
|
value = record.value
|
||||||
if rdtype == dns.rdatatype.TXT:
|
if rdtype == dns.rdatatype.TXT:
|
||||||
if len(record.value) <= 255:
|
if value.startswith('"') and value.endswith('"'):
|
||||||
record_value = f'"{record.value}"'
|
value = value[1:-1].replace('" "', '').replace('"', '\"')
|
||||||
|
|
||||||
|
if len(value) > 255:
|
||||||
|
# This is a bug fix for netbox. If netbox allowed for
|
||||||
|
# an unquoted value to be larger then 255 characters,
|
||||||
|
# it misunderstood everything behind a ; as a comment.
|
||||||
|
chunks = ['"{}"'.format(value[i:i+255]) for i in range(0, len(value), 255)]
|
||||||
|
value = " ".join(chunks)
|
||||||
else:
|
else:
|
||||||
chunks = ['"{}"'.format(record.value[i:i+255]) for i in range(0, len(record.value), 255)]
|
value = f'"{value}"'
|
||||||
record_value = " ".join(chunks)
|
|
||||||
else:
|
|
||||||
record_value = record.value
|
|
||||||
|
|
||||||
rdata = dns.rdata.from_text(
|
rdata = dns.rdata.from_text(
|
||||||
dns.rdataclass.IN,
|
dns.rdataclass.IN,
|
||||||
rdtype,
|
rdtype,
|
||||||
record_value, #asd
|
value,
|
||||||
relativize=False,
|
relativize=False,
|
||||||
origin=zone.origin,
|
origin=zone.origin,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import os
|
||||||
|
import dns.zone
|
||||||
|
import dns.rdatatype
|
||||||
|
import dns.rdataclass
|
||||||
|
import dns.exception
|
||||||
|
import netbox_dns.models
|
||||||
|
from netbox_plugin_bind_provisioner.utils import export_bind_zone_file
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
"--path", type=str, help="Path to the directory where zone files are to be written"
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
# Load parameters
|
||||||
|
base_path = options['path']
|
||||||
|
|
||||||
|
if not base_path:
|
||||||
|
raise CommandError("No --path parameter given")
|
||||||
|
|
||||||
|
# Ensure base_path exists
|
||||||
|
os.makedirs(base_path, exist_ok=True)
|
||||||
|
|
||||||
|
# Iterate through all zones
|
||||||
|
nb_zones = netbox_dns.models.Zone.objects.prefetch_related("view").all()
|
||||||
|
if not nb_zones.exists():
|
||||||
|
self.stdout.write(self.style.WARNING("No zones found in NetBox DNS."))
|
||||||
|
return
|
||||||
|
|
||||||
|
for zone in nb_zones:
|
||||||
|
# Determine view directory
|
||||||
|
view_name = zone.view.name
|
||||||
|
view_dir = os.path.join(base_path, view_name)
|
||||||
|
os.makedirs(view_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Zone file path
|
||||||
|
file_path = os.path.join(view_dir, zone.name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Call existing export function
|
||||||
|
export_bind_zone_file(zone, file_path=file_path)
|
||||||
|
self.stdout.write(self.style.SUCCESS(f"Exported zone '{zone.name}' to '{file_path}'"))
|
||||||
|
except Exception as e:
|
||||||
|
self.stderr.write(f"Failed to export zone '{zone.name}': {e}")
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import os
|
||||||
|
import dns.zone
|
||||||
|
import dns.rdatatype
|
||||||
|
import dns.rdataclass
|
||||||
|
import dns.exception
|
||||||
|
import netbox_dns.models
|
||||||
|
from netbox_plugin_bind_provisioner.utils import export_bind_zone_file
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
"--view", type=str, help="The name of the view the Zone to be exported is in"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--zone", type=str, help="The FQDN of the Zone to be exported"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--file", type=str, help="Path of the zone file to be written"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
#if len(options) < 2:
|
||||||
|
# print("export-zone <zone name> <file path>")
|
||||||
|
# sys.exit(1)
|
||||||
|
|
||||||
|
# Load parameters
|
||||||
|
view_name = options['view']
|
||||||
|
zone_name = options['zone']
|
||||||
|
file_path = options['file']
|
||||||
|
|
||||||
|
if not view_name:
|
||||||
|
raise CommandError("No --view parameter given")
|
||||||
|
elif not zone_name:
|
||||||
|
raise CommandError("No --zone parameter given")
|
||||||
|
elif not file_path:
|
||||||
|
raise CommandError("No --file parameter given")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Load the zone from NetBox DNS
|
||||||
|
nb_zone = netbox_dns.models.Zone.objects.get(view__name=view_name, name=zone_name)
|
||||||
|
|
||||||
|
if nb_zone:
|
||||||
|
export_bind_zone_file(nb_zone, file_path=file_path)
|
||||||
|
else:
|
||||||
|
print("Zone not found in Netbox. Aborting")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise CommandError(f"Failed to export zone: {e}")
|
||||||
|
|
||||||
|
self.stdout.write(self.style.SUCCESS(f"Zone '{zone_name}' exported to '{file_path}'"))
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import dns.zone
|
||||||
|
import dns.rdatatype
|
||||||
|
import dns.rdataclass
|
||||||
|
import dns.exception
|
||||||
|
import netbox_dns.models
|
||||||
|
|
||||||
|
def export_bind_zone_file(nb_zone: netbox_dns.models.Zone, file_path: str):
|
||||||
|
|
||||||
|
# Create dnspython zone object
|
||||||
|
dp_zone = dns.zone.Zone(origin=dns.name.from_text(nb_zone.name))
|
||||||
|
|
||||||
|
for record in nb_zone.records.all():
|
||||||
|
rname = record.name or "@"
|
||||||
|
rdclass = dns.rdataclass.IN
|
||||||
|
rdatatype = dns.rdatatype.from_text(record.type)
|
||||||
|
|
||||||
|
# Create rdata object
|
||||||
|
rdata = dns.rdata.from_text(rdclass, rdatatype, record.value)
|
||||||
|
|
||||||
|
# Create rdataset
|
||||||
|
rdataset = dns.rdataset.Rdataset(rdclass, rdatatype, record.ttl)
|
||||||
|
rdataset.add(rdata)
|
||||||
|
|
||||||
|
# Add to zone
|
||||||
|
node = dp_zone.find_node(rname, create=True)
|
||||||
|
node.rdatasets.append(rdataset)
|
||||||
|
|
||||||
|
# Write zone to file in BIND format
|
||||||
|
try:
|
||||||
|
with open(file_path, "w") as f:
|
||||||
|
dp_zone.to_file(f, sorted=True)
|
||||||
|
except IOError as e:
|
||||||
|
raise IOError(f"Failed to write zone file to {file_path}: {e}")
|
||||||
|
|
||||||
Reference in new issue
Block a user