From 2ae626178378fd32c06b341159698d375b8c560a Mon Sep 17 00:00:00 2001
From: John Eckersberg <jeckersb@redhat.com>
Date: Thu, 1 May 2008 15:37:37 -0400
Subject: [PATCH] BIND dns support

---
 cobbler/action_sync.py            |    4 +
 cobbler/dnsgen.py                 |  224 +++++++++++++++++++++++++++++++++++++
 cobbler/settings.py               |    4 +
 cobbler/webui/master.py           |    1 -
 config/settings                   |    1 +
 docs/cobbler.pod                  |   14 ++-
 setup.py                          |    4 +-
 templates/named.template          |   18 +++
 templates/zone.template           |   13 ++
 triggers/restart-services.trigger |    6 +
 website/new/docs/cobbler.html     |   14 +++
 11 files changed, 299 insertions(+), 4 deletions(-)
 create mode 100644 cobbler/dnsgen.py
 create mode 100644 templates/named.template
 create mode 100644 templates/zone.template

diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py
index 4156ea7..98faa0c 100644
--- a/cobbler/action_sync.py
+++ b/cobbler/action_sync.py
@@ -29,6 +29,7 @@ from cexceptions import *
 import templar 
 import pxegen
 import dhcpgen
+import dnsgen
 import yumgen
 
 import item_distro
@@ -84,6 +85,7 @@ class BootSync:
         self.repos    = self.config.repos()
         self.pxegen   = pxegen.PXEGen(self.config)
         self.dhcpgen  = dhcpgen.DHCPGen(self.config)
+        self.dnsgen   = dnsgen.DNSGen(self.config)
         self.yumgen   = yumgen.YumGen(self.config)
 
         # execute the core of the sync operation
@@ -98,6 +100,8 @@ class BootSync:
            self.dhcpgen.write_dhcp_file()
            self.dhcpgen.regen_ethers()
            self.dhcpgen.regen_hosts()
+        if self.settings.manage_dns:
+           self.dnsgen.write_bind_files()
         self.pxegen.make_pxe_menu()
 
         # run post-triggers
diff --git a/cobbler/dnsgen.py b/cobbler/dnsgen.py
new file mode 100644
index 0000000..43ccc54
--- /dev/null
+++ b/cobbler/dnsgen.py
@@ -0,0 +1,224 @@
+"""
+Builds out BIND info
+This is some of the code behind 'cobbler sync'.
+
+Copyright 2006-2008, Red Hat, Inc
+Michael DeHaan <mdehaan@redhat.com>
+John Eckersberg <jeckersb@redhat.com>
+
+This software may be freely redistributed under the terms of the GNU
+general public license.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+import os
+import os.path
+import shutil
+import time
+import sub_process
+import sys
+import glob
+import traceback
+import errno
+
+import utils
+from cexceptions import *
+import templar 
+
+import item_distro
+import item_profile
+import item_repo
+import item_system
+
+from utils import _
+
+class DNSGen:
+    """
+    Handles conversion of internal state to BIND zones
+    """
+
+    def __init__(self,config,verbose=False):
+        """
+        Constructor
+        """
+        self.verbose     = verbose
+        self.config      = config
+        self.api         = config.api
+        self.distros     = config.distros()
+        self.profiles    = config.profiles()
+        self.systems     = config.systems()
+        self.settings    = config.settings()
+        self.repos       = config.repos()
+        self.templar     = templar.Templar(config)
+
+    def __forward_zones(self):
+        """
+        Returns ALL forward zones for all systems
+        """
+        zones = {}
+        for sys in self.systems:
+            for (name, interface) in sys.interfaces.iteritems():
+                host = interface["hostname"]
+                ip   = interface["ip_address"]
+                if not host or not ip:
+                    # gotsta have some hostname and ip or else!
+                    continue
+                tokens = host.split('.')
+                zone = '.'.join(tokens[1:])
+                if zones.has_key(zone):
+                    zones[zone].append((host.split('.')[0], ip))
+                else:
+                    zones[zone] = [(host.split('.')[0], ip)]
+        return zones
+
+    def __reverse_zones(self):
+        """
+        Returns ALL reverse zones for all systems
+        """
+        zones = {}
+        for sys in self.systems:
+            for (name, interface) in sys.interfaces.iteritems():
+                host = interface["hostname"]
+                ip   = interface["ip_address"]
+                if not host or not ip:
+                    # gotsta have some hostname and ip or else!
+                    continue
+                tokens = ip.split('.')
+                zone = '.'.join(tokens[0:3])
+                if zones.has_key(zone):
+                    zones[zone].append((tokens[3], host + '.'))
+                else:
+                    zones[zone] = [(tokens[3], host + '.')]
+        return zones
+
+    def __config_forward_zones(self):
+        """
+        Returns only the forward zones which have systems and are defined
+        in the option manage_forward_zones
+
+        Alternatively if manage_forward_zones is empty, return all systems
+        """
+        all = self.__forward_zones()
+        want = self.settings.manage_forward_zones
+        if want == []: return all
+        ret = {}
+        for zone in all.keys():
+            if zone in want:
+                ret[zone] = all[zone]
+        return ret
+
+    def __config_reverse_zones(self):
+        """
+        Returns only the reverse zones which have systems and are defined
+        in the option manage_reverse_zones
+
+        Alternatively if manage_reverse_zones is empty, return all systems
+        """
+        all = self.__reverse_zones()
+        want = self.settings.manage_reverse_zones
+        if want == []: return all
+        ret = {}
+        for zone in all.keys():
+            if zone in want:
+                ret[zone] = all[zone]
+        return ret
+
+    def __write_named_conf(self):
+        """
+        Write out the named.conf main config file from the template.
+        """
+        settings_file = self.settings.named_conf
+        template_file = "/etc/cobbler/named.template"
+        forward_zones = self.settings.manage_forward_zones
+        reverse_zones = self.settings.manage_reverse_zones
+
+        metadata = {'zone_include': ''}
+        for zone in self.__config_forward_zones().keys():
+                txt =  """
+zone "%(zone)s." {
+    type master;
+    file "%(zone)s";
+};
+""" % {'zone': zone}
+                metadata['zone_include'] = metadata['zone_include'] + txt
+
+        for zone in self.__config_reverse_zones().keys():
+                tokens = zone.split('.')
+                tokens.reverse()
+                arpa = '.'.join(tokens) + '.in-addr.arpa'
+                txt = """
+zone "%(arpa)s." {
+    type master;
+    file "%(zone)s";
+};
+""" % {'arpa': arpa, 'zone': zone}
+                metadata['zone_include'] = metadata['zone_include'] + txt
+
+        try:
+            f2 = open(template_file,"r")
+        except:
+            raise CX(_("error reading template from file: %s") % template_file)
+        template_data = ""
+        template_data = f2.read()
+        f2.close()
+
+        self.templar.render(template_data, metadata, settings_file, None)
+
+    def __write_zone_files(self):
+        """
+        Write out the forward and reverse zone files for all the zones
+        defined in manage_forward_zones and manage_reverse_zones
+        """
+        template_file = "/etc/cobbler/zone.template"
+        cobbler_server = self.settings.server
+        serial = int(time.time())
+        forward = self.__config_forward_zones()
+        reverse = self.__config_reverse_zones()
+
+        try:
+            f2 = open(template_file,"r")
+        except:
+            raise CX(_("error reading template from file: %s") % template_file)
+        template_data = ""
+        template_data = f2.read()
+        f2.close()
+
+        for (zone, hosts) in forward.iteritems():
+            metadata = {
+                'cobbler_server': cobbler_server,
+                'serial': serial,
+                'host_record': ''
+            }
+
+            for host in hosts:
+                txt = '%s\tIN\tA\t%s\n' % host
+                metadata['host_record'] = metadata['host_record'] + txt
+
+            self.templar.render(template_data, metadata, '/var/named/' + zone, None)
+
+        for (zone, hosts) in reverse.iteritems():
+            metadata = {
+                'cobbler_server': cobbler_server,
+                'serial': serial,
+                'host_record': ''
+            }
+
+            for host in hosts:
+                txt = '%s\tIN\tPTR\t%s\n' % host
+                metadata['host_record'] = metadata['host_record'] + txt
+
+            self.templar.render(template_data, metadata, '/var/named/' + zone, None)
+
+
+    def write_bind_files(self):
+        """
+        BIND files are written when manage_dns is set in
+        /var/lib/cobbler/settings.
+        """
+
+        self.__write_named_conf()
+        self.__write_zone_files()
+
diff --git a/cobbler/settings.py b/cobbler/settings.py
index 40ed571..4dffe1b 100644
--- a/cobbler/settings.py
+++ b/cobbler/settings.py
@@ -57,6 +57,10 @@ DEFAULTS = {
     },
     "manage_dhcp"                 : 0,
     "manage_dhcp_mode"            : "isc",
+    "manage_dns"                  : 0,
+    "manage_forward_zones"        : [],
+    "manage_reverse_zones"        : [],
+    "named_conf"                  : "/etc/named.conf",
     "next_server"                 : "127.0.0.1",
     "pxe_just_once"               : 0,
     "register_new_installs"       : 0,
diff --git a/cobbler/webui/master.py b/cobbler/webui/master.py
index 22391eb..7e54a2d 100644
--- a/cobbler/webui/master.py
+++ b/cobbler/webui/master.py
@@ -35,7 +35,6 @@ __CHEETAH_version__ = '2.0.1'
 __CHEETAH_versionTuple__ = (2, 0, 1, 'final', 0)
 __CHEETAH_genTime__ = 1209057202.737108
 __CHEETAH_genTimestamp__ = 'Thu Apr 24 13:13:22 2008'
-__CHEETAH_src__ = 'webui_templates/master.tmpl'
 __CHEETAH_srcLastModified__ = 'Thu Apr 24 12:59:37 2008'
 __CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
 
diff --git a/config/settings b/config/settings
index 10e06e2..3ca6482 100644
--- a/config/settings
+++ b/config/settings
@@ -31,6 +31,7 @@ ldap_search_passwd: ''
 ldap_search_prefix: 'uid='
 manage_dhcp: 0
 manage_dhcp_mode: isc
+manage_dns: 0
 next_server: '127.0.0.1'
 pxe_just_once: 0
 register_new_installs: 0
diff --git a/docs/cobbler.pod b/docs/cobbler.pod
index d7a4550..21ec78e 100644
--- a/docs/cobbler.pod
+++ b/docs/cobbler.pod
@@ -403,8 +403,8 @@ Cobbler sync is used to repair or rebuild the contents /tftpboot or /var/www/cob
 
 Sync should be run whenever files in /var/lib/cobbler are manually edited (which is not recommended except for the settings file) or when making changes to kickstart files.  In practice, this should not happen often, though running sync too many times does not cause any adverse effects.  
 
-If using cobbler to manage a DHCP server (see the advanced section of this manpage), sync does need to be
-run after systems are added to regenerate and reload the DHCP configuration.
+If using cobbler to manage a DHCP and/or DNS server (see the advanced section of this manpage), sync does need to be
+run after systems are added to regenerate and reload the DHCP/DNS configuration.
 
 =head1 EXAMPLES
 
@@ -565,6 +565,16 @@ Itanium systems names also need to be assigned to a distro that was created with
 
 The dhcpd.conf file will be updated each time "cobbler sync" is run, and not until then, so it is important to remember to use "cobbler sync" when using this feature.
 
+=head2 BIND CONFIGURATION MANAGEMENT
+
+Cobbler can optionally manage DNS configuration using BIND.
+
+This feature is off by default and must be turned on by setting 'manage_dns' to 1 in /var/lib/cobbler/settings.   You may restrict the scope of zones managed with the options 'manage_forward_zones' and 'manage_reverse_zones'.  
+
+Cobbler will use /etc/cobbler/bind.template and /etc/cobbler/zone.template as a starting point for the named.conf and individual zone files, respectively.  These files must be user edited for the user's particular networking environment.  Read the file and understand how BIND works before proceeding.
+
+The named.conf file as well as all zone files will be updated each time ``cobbler sync'' is run, and not until then, so it is important to remember to use ``cobbler sync'' when using this feature.
+
 =head2 SERVICE DISCOVERY (AVAHI)
 
 If the avahi-tools package is installed, cobblerd will broadcast it's presence on the network, allowing it to be discovered by koan with the koan --server=DISCOVER parameter.
diff --git a/setup.py b/setup.py
index 91eabed..442da5b 100644
--- a/setup.py
+++ b/setup.py
@@ -94,13 +94,15 @@ if __name__ == "__main__":
                                 (etcpath,  ['kickstarts/sample_end.ks']),
                                 (etcpath,  ['kickstarts/default.ks']),
  
-                                # templates for DHCP and syslinux configs
+                                # templates for DHCP, DNS, and syslinux configs
 				(etcpath,  ['templates/dhcp.template']),
 				(etcpath,  ['templates/dnsmasq.template']),
+                                (etcpath,  ['templates/named.template']),
 				(etcpath,  ['templates/pxedefault.template']),
 				(etcpath,  ['templates/pxesystem.template']),
 				(etcpath,  ['templates/pxesystem_ia64.template']),
 				(etcpath,  ['templates/pxeprofile.template']),
+                                (etcpath,  ['templates/zone.template']),
 
                                 # kickstart dir
                                 (vl_kick,  []),
diff --git a/templates/named.template b/templates/named.template
new file mode 100644
index 0000000..f77eadc
--- /dev/null
+++ b/templates/named.template
@@ -0,0 +1,18 @@
+options {
+          listen-on port 53 { 127.0.0.1; };
+          directory       "/var/named";
+          dump-file       "/var/named/data/cache_dump.db";
+          statistics-file "/var/named/data/named_stats.txt";
+          memstatistics-file "/var/named/data/named_mem_stats.txt";
+          allow-query     { localhost; };
+          recursion yes;
+};
+
+logging {
+        channel default_debug {
+                file "data/named.run";
+                severity dynamic;
+        };
+};
+
+$zone_include
diff --git a/templates/zone.template b/templates/zone.template
new file mode 100644
index 0000000..5426f78
--- /dev/null
+++ b/templates/zone.template
@@ -0,0 +1,13 @@
+\$TTL 300
+@                       IN      SOA     $cobbler_server. nobody.example.com. (
+                                        $serial   ; Serial
+                                        600         ; Refresh
+                                        1800         ; Retry
+                                        604800       ; Expire
+                                        300          ; TTL 
+                                        )
+
+                        IN      NS      $cobbler_server.
+
+
+$host_record
diff --git a/triggers/restart-services.trigger b/triggers/restart-services.trigger
index b65b825..948d2b0 100644
--- a/triggers/restart-services.trigger
+++ b/triggers/restart-services.trigger
@@ -8,6 +8,7 @@ bootapi = capi.BootAPI()
 settings = bootapi.settings()
 manage_dhcp = str(settings.manage_dhcp).lower()
 manage_dhcp_mode = str(settings.manage_dhcp_mode).lower()
+manage_dns = str(settings.manage_dns).lower()
 
 rc = 0
 if manage_dhcp != "0":
@@ -19,4 +20,9 @@ if manage_dhcp != "0":
         print "- error: unknown DHCP engine: %s" % manage_dhcp_mode
         rc = 411
 
+if rc != 0: sys.exit(rc)
+
+if manage_dns != "0":
+    rc = os.system("/sbin/service named restart")
+
 sys.exit(rc)
diff --git a/website/new/docs/cobbler.html b/website/new/docs/cobbler.html
index 36ad3e0..06a8b14 100644
--- a/website/new/docs/cobbler.html
+++ b/website/new/docs/cobbler.html
@@ -661,6 +661,20 @@ the setting to 'isc'.</p>
 <p>The dhcpd.conf file will be updated each time ``cobbler sync'' is run, and not until then, so it is important to remember to use ``cobbler sync'' when using this feature.</p>
 <p>
 </p>
+<h2><a name="bind_configuration_management">BIND CONFIGURATION MANAGEMENT</a></h2>
+<p>Cobbler can optionally manage DNS configuration using BIND.</p>
+<p>This feature is off by default and must be turned on by setting 'manage_dns' to 1 in
+/var/lib/cobbler/settings.   You may restrict the scope of zones
+managed with the options 'manage_forward_zones' and
+'manage_reverse_zones'.  
+<p>Cobbler will use /etc/cobbler/bind.template and
+  /etc/cobbler/zone.template as a starting point for the named.conf
+  and individual zone files, respectively.  These files must be user 
+  edited for the user's particular networking environment.  Read the 
+  file and understand how BIND works before proceeding.</p>
+<p>The named.conf file as well as all zone files will be updated each time ``cobbler sync'' is run, and not until then, so it is important to remember to use ``cobbler sync'' when using this feature.</p>
+<p>
+</p>
 <h2><a name="service_discovery__avahi_">SERVICE DISCOVERY (AVAHI)</a></h2>
 <p>If the avahi-tools package is installed, cobblerd will broadcast it's presence on the network, allowing it to be discovered by koan with the koan --server=DISCOVER parameter.</p>
 <p>
-- 
1.5.4.1

