From a1d319bd0d9d102dd9556d343bbed7fc67dc7a77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Antoine=20Beaupr=C3=A9?= <anarcat@koumbit.org>
Date: Mon, 23 Aug 2010 18:40:47 +0200
Subject: [PATCH] change the data structure storage to be more logical and
 efficient

a complete zonefile can now be described like this:

$zonefile = array('www' => array('a' => array('1.2.3.3', '1.2.3.4')),
                  '@' => array('SOA' => array('hostmaster' => 'localhost', 'email' => 'admin.localhost', 'serial' => '2010082301', 'refresh' => 21600 3600 604800 86400 )'),
                              'A' => array('1.2.3.3'),
                              'MX' => array('mail.localhost'),
                              'NS' => array('localhost', 'ns2.localhost'),
                              )
                 );

i had to change the provision storage class to merge with existing
records in some cases for that to work.
---
 dns/NOTES             | 20 ++++++++++++++++++-
 dns/bind/zone.tpl.php | 32 +++++++++++++++++++-----------
 dns/dns.config.inc    |  8 ++------
 dns/dns.drush.inc     | 46 +++++++++++++++++++------------------------
 provision.service.inc | 12 ++++++++++-
 5 files changed, 73 insertions(+), 45 deletions(-)

diff --git a/dns/NOTES b/dns/NOTES
index 7e502e59b..834ce1c5d 100644
--- a/dns/NOTES
+++ b/dns/NOTES
@@ -24,10 +24,28 @@ Bugs:
  1. since the rewrite of the parser, some fields in the SOA are
  missing (retry, refresh, etc) because we lack the context from the
  server (switching to create_config() shoudl fix that)
+ 2. there's a duplicate serial in PHP storage (patched?)
+ 3. the internal data structure is confusing and not efficient. here's
+ a proposed change:
+
+$zonefile = array('www' => array('a' => array('1.2.3.3', '1.2.3.4')),
+                  '@' => array('SOA' => array('hostmaster' => 'localhost', 'email' => 'admin.localhost', 'serial' => '2010082301', 'refresh' => 21600 3600 604800 86400 )'),
+                              'A' => array('1.2.3.3'),
+                              'MX' => array('mail.localhost'),
+                              'NS' => array('localhost', 'ns2.localhost'),
+                              )
+                 );
+  (patched, needs testing and verification)
+ 4. removing the last record in a zonefile doesn't remove the zone
+ 5. removing records doesn't work either
+ 6. we increment the serial in two places, it seems
 
 Caveats:
 
- 1. only targeting bind right now
+ 1. there's duplicate storage altogether: we could parse zonefiles and
+ use that as storage, not PHP files, but that would make switching
+ engines more difficult and would require writing parsers for all
+ engines.
 
 Todo:
 
diff --git a/dns/bind/zone.tpl.php b/dns/bind/zone.tpl.php
index 6eb4b3ffa..2b319f97b 100644
--- a/dns/bind/zone.tpl.php
+++ b/dns/bind/zone.tpl.php
@@ -4,29 +4,39 @@
 $TTL <?php print $server->dns_ttl; ?>
 
 <?php
-print("@     IN     SOA  $server->remote_host $dns_email 
-			      $serial ; serial
+print("@     IN     SOA  $server->remote_host $dns_email (
+			      " . $records['@']['SOA']['serial'] . " ; serial
 			      $server->dns_refresh; refresh
 			      $server->dns_retry ; retry
 			      $server->dns_expire ; expire
 			      $server->dns_negativettl ; minimum
           )\n");
-?>
 
+print "@\tIN\tNS\t" . $server->remote_host . " ; primary DNS\n";
 
-       IN  NS    <?php print $server->remote_host ?>. ; primary DNS 
-       IN  NS     ns2.example.com. ; secondary DNS
-       IN  MX  10 <?php print $server->remote_host ?>. ; external mail provider
-; non server domain hosts
-<?php
+foreach ($server->dns_slaves as $slave) {
+  print "@\tIN\tNS\t$slave ; slave DNS\n";
+}
 
-foreach ($records['A'] as $ip) {
-  print "   IN  A     {$ip}\n";
+foreach ($records['@'] as $type => $destinations) {
+  if ($type != 'SOA' && $type != 'NS') {
+    foreach ($destinations as $destination) {
+        print "@\tIN\t$type\t$destination\n";
+    }
+  }
 }
 
+foreach ($records as $name => $record) {
+  if ($name != '@') {
+    foreach ($record as $type => $destinations) {
+      foreach ($destinations as $destination) {
+        print "$name\tIN\t$type\t$destination\n";
+      }
+    }
+  }
+}
 foreach ($hosts as $host => $info) {
   foreach ($info['A'] as $ip) {
     print "{$info['sub']}   IN  A     {$ip}\n";
   }
 }
-?>
diff --git a/dns/dns.config.inc b/dns/dns.config.inc
index 322860e95..9a5a5561e 100644
--- a/dns/dns.config.inc
+++ b/dns/dns.config.inc
@@ -95,13 +95,9 @@ class provisionConfig_dns_zone extends provisionConfig_dns {
     $this->data['dns_email'] = str_replace('@', '.', $this->data['server']->admin_email);
 
     // increment the serial.
-    $this->store->records['serial'] = $this->data['serial'] = provisionService_dns::increment_serial($records['serial']);
+    $this->store->records['@']['SOA']['serial'] = $records['serial'] = provisionService_dns::increment_serial($records['@']['SOA']['serial']);
 
-    foreach ($records as $key => $record) {
-      if (preg_match('/^host:/', $key)) {
-        $this->data['hosts'][$record['host']] = $record;
-      }
-    }
+    $this->data['records'] = $records;
   }
 
   function write() {
diff --git a/dns/dns.drush.inc b/dns/dns.drush.inc
index fdfe2351d..75de93205 100644
--- a/dns/dns.drush.inc
+++ b/dns/dns.drush.inc
@@ -14,6 +14,7 @@ include_once(dirname(__FILE__) . '/../provision.service.inc');
 
 /**
  * Implementation of hok_drush_command().
+ */
 function dns_drush_command() {
   $items['provision-zone'] = array(
     'arguments' => array('operation' => dt('The operation to perform on a zone (verify, delete, rr-add, rr-delete)')),
@@ -39,18 +40,14 @@ function drush_dns_provision_zone($action, $zone, $name = null, $type = null, $d
     $status = d()->service('dns')->delete_zone($zone);
     break;
   case 'rr-add':
-    d()->service('dns')->config('zone', $zone)->record_set("host:$host", array(
-      'sub' => $sub,
-      'A' => $ips,
-      'host' => $host,
-    ));
-    $status = d()->service('dns')->add_record($zone, $name, $type, $destination);
+    $record = d()->service('dns')->config('zone', $zone)->record_get($name);
+    $record[$type] = array_merge($record[$type], array($destination));
+    d()->service('dns')->config('zone', $zone)->record_set($name, $record)->write();
     break;
   case 'rr-modify':
-    $status = d()->service('dns')->modify_record($zone, $name, $type, $destination);
+    d()->service('dns')->config('zone', $zone)->record_set($name, array($type => array($destination)))->write();
     break;
   case 'rr-delete':
-    $status = d()->service('dns')->delete_record($zone, $name, $type, $destination);
     break;
   }
   $status = $status && d()->service('dns')->commit($zone);
@@ -58,8 +55,6 @@ function drush_dns_provision_zone($action, $zone, $name = null, $type = null, $d
   return $status;
 }
 
- */
-
 function dns_provision_services() {
   return array('dns' => NULL);
 }
@@ -236,8 +231,7 @@ class provisionService_dns extends provisionService {
 
     $this->config('server')->record_set($zone, $zone)->write();
 
-    $soa['serial'] = $this->increment_serial($soa['serial']);
-    $this->config('zone', $zone)->record_set('soa', $soa)->write();
+    $this->config('zone', $zone)->record_set('serial', $this->increment_serial())->write();
   }
 
   /**
@@ -278,19 +272,17 @@ class provisionService_dns extends provisionService {
       $ips = array('127.0.0.1');
     }
 
-    if (is_array($ips)) {
-      if ($sub) {
-        $this->config('zone', $zone)->record_set("host:$host", array(
-          'sub' => $sub,
-          'A' => $ips,
-          'host' => $host,
-        ));
-      }
-      else {
-        $this->config('zone', $zone)->record_set("A", $ips);
-      }
+    // XXX: kill me?
+    if (!is_array($ips)) {
+      $ips = array($ips); // backward compatibility?
+    }
+
+    if (empty($sub)) {
+      $sub = '@';
     }
- 
+    drush_log("zone: $zone, sub: $sub");
+    $this->config('zone', $zone)->record_set($sub, array('A' => $ips));
+    
     $this->create_zone($zone);
     $this->create_config('host');
   }
@@ -320,9 +312,11 @@ class provisionService_dns extends provisionService {
 
     // remove the records from the zone store
     $this->config('zone', $zone)->
-      record_del("host:$host");
+      record_set($host, array('A' => null));
 
-    $this->create_zone($zone);
+    // XXX: need to guess the number of records left now and remove only if
+    // there are no more records
+    //$this->delete_zone($zone);
     $this->delete_config('host');
   }
 
diff --git a/provision.service.inc b/provision.service.inc
index 4f9d870c6..92ecd725b 100644
--- a/provision.service.inc
+++ b/provision.service.inc
@@ -131,7 +131,17 @@ class provisionService extends provisionChainedState {
           $this->_config->store->records = array_merge($this->_config->store->records, $arg1);
         }
         elseif (!is_numeric($arg1)) {
-          $this->_config->store->records[$arg1] = $arg2;
+          if (is_array($arg2)) {
+            if (!is_array($this->_config->store->loaded_records[$arg1])) {
+              $this->_config->store->loaded_records[$arg1] = array();
+            }
+            if (!is_array($this->_config->store->records[$arg1])) {
+              $this->_config->store->records[$arg1] = array();
+            }
+            $this->_config->store->records[$arg1] = array_merge($this->_config->store->loaded_records[$arg1], $this->_config->store->records[$arg1], $arg2);
+          } else {
+            $this->_config->store->records[$arg1] = $arg2;
+          }
         }
       }
     }
-- 
GitLab