Commit b1d14d4e authored by spiderman's avatar spiderman Committed by spiderman

intermediate commit which re-introduces provision_dns into the 0.2 stream of development

parent 0a827286
<?php
// $Id$
/**
* @file
* Provision hooks for the delete command
**/
/**
* Implementation of hook_provision_delete()
*
* delete the RR for this site at least, but possibly also the zone itself?
* need to lookup the zid for the base zone of this site, and then call provision_dns_rr with the zid and site 'name'
* (ie: pull off the tld and second-level domain for the 'base' zone, and treat the rest as the 'name')
*/
function provision_dns_provision_delete(&$data, $url = NULL) {
if ($url) {
# provision_dns_delete($url);
}
}
<?php
// $Id$
/**
* @file
* Provision hooks for the install command
**/
/**
* Implementation of hook_provision_pre_install
**/
function provision_dns_provision_pre_install(&$data, $url = NULL) {
# data should now contain zone info directly, rather than having to pull them from
if ($url) {
# $url_parts = _provision_dns_split_url($url);
# if (!isset($url_parts['zone'])) {
provision_set_error(PROVISION_FRAMEWORK_ERROR);
return;
# }
# the zone object should be passed in..
# $zone = provision_dns_create_zone($url_parts['zone'], $data); # initialize zone for this domain (if it doesn't exist)
# provision_dns_create_record($url_parts['host'], $zone, $data); # creates the RR for the (sub)domain
# provision_dns_commit($data['dns_id']); # makes the changes live (ie: restart bind)
}
}
/**
* Implementation of hook_provision_pre_install_rollback
**/
function provision_dns_provision_pre_install_rollback(&$data, $url = NULL) {
if ($url) {
# provision_dns_delete($url);
}
}
/**
* Implementation of hook_provision_post_install
**/
function provision_dns_provision_post_install(&$data, $url = NULL) {
# commit and restart bind
}
<?php
// $Id$
/**
* @file BIND dns api engine
*
* A collection of functions used by the DNS provision hooks to accomplish their tasks.
* Uses the 'soa' and 'rr' objects defined by the provision_dns.module to manage BIND zonefiles
*/
/**
* Implementation of hook_dns_zone from provision_dns.api.inc
*
* @param $op
* The operation to be executed on the zone. Valid operations are 'add', 'update', and 'delete'.
* @param $zone
* The zone object to be operated on (as returned by provision_dns_status).
**/
function provision_bind_provision_dns_zone($op, $zone) {
switch ($op) {
case 'add':
provision_bind_create_zone_config($zone);
provision_bind_update_zone($zone);
break;
case 'update':
provision_bind_update_zone($zone, TRUE);
break;
case 'delete':
provision_bind_delete_zone($zone); # And all it's records with it!
break;
// do others later
}
_provision_bind_restart();
}
/**
* Implementation of hook_dns_rr from provision_dns.api.inc
*
* @param $op
* The operation to be executed on the record. Valid operations are 'add', 'update', and 'delete'.
* @param $zone
* The zone object on which to operate (as returned by provision_dns_status)
* @param $record
* The record object on which to operate (as returned by provision_dns_status)
**/
function provision_bind_provision_dns_rr($op, $zone, $record) {
switch ($op) {
case 'add':
_provision_bind_add_record($zone, $record);
break;
case 'update':
_provision_bind_update_record($zone, $record);
break;
case 'delete':
_provision_bind_delete_record($zone, $record);
break;
}
}
/**
* Create a new entry for the zone provided in the local named.conf file, pointing to the zonefile.
*
* @param $zone
* The zone object to create an entry for (as returned by provision_dns_status).
**/
function provision_bind_create_zone_config($zone) {
// add the zone to our local named.conf file
$path = variable_get('provision_bind_zonedata', PROVISION_CONFIG_PATH .'/named');
$conf = PROVISION_CONFIG_PATH . '/' . variable_get('provision_bind_conf', 'named.conf.drupal');
$zone_str = sprintf('zone "%s" { type master; file "%s/%s"; allow-query { any; }; };%s', $zone->origin, $path, $zone->origin, "\n");
if ($fd = fopen($conf, "a")) {
$fd = fwrite($fd, $zone_str);
}
if (!$fd) {
provision_log("Error", t("error adding the zone to the bind configuration in @path", array("@path" => $conf)));
provision_set_error(PROVISION_PERM_ERROR);
}
@fclose($fd);
}
/**
* Create/Update a zonefile with SOA and NS records, plus all other records provision_dns knows about
*
* @param $zone
* The zone object to create or update (as returned by provision_dns_status).
* @param $exists
* If the $exists argument is true, then the function will check for an old_origin field in the zone object,
* indicating the name of the zone has changed. If this is the case, the old zonefile will be removed and
* the name altered in the named.conf
*
* This should use a template, a la:
* $template = variable_get('provision_dns_zonefile_template', _provision_dns_default_template());
* But currently, just writes hardcoded string for all of this stuff directly into the zonefile.
**/
function provision_bind_update_zone($zone, $exists = FALSE) {
// write a zonefile in the appropriate place
$path = variable_get('provision_bind_zonedata', '');
# First setup the SOA record
$zone_str = sprintf("\$TTL %s\n", $zone->ttl);
$zone_str .= sprintf("@\t IN\t SOA\t %s. %s. (\n", $zone->origin, str_replace('@', '.', $zone->mbox));
$zone_str .= sprintf("\t\t\t %s ; Serial\n", $zone->serial);
$zone_str .= sprintf("\t\t\t %s ; Refresh\n", $zone->refresh);
$zone_str .= sprintf("\t\t\t %s ; Retry\n", $zone->retry);
$zone_str .= sprintf("\t\t\t %s ; Expire\n", $zone->expire);
$zone_str .= sprintf("\t\t\t %s)\t\t; Negative Cache TTL\n\n", $zone->ttl);
# Next do the NS records
$zone_str .= sprintf("@\t IN\t NS\t\t %s.\n", $zone->ns1);
if ($zone->ns2) {
$zone_str .= sprintf("@\t IN\t NS\t\t %s.\n", $zone->ns2);
}
$records = provision_dns_getrecords($zone->zid);
foreach ($records as $rid => $rec) {
$zone_str .= _provision_bind_record_string($rec);
}
$path = variable_get('provision_bind_zonedata', PROVISION_CONFIG_PATH .'/named');
$conf = PROVISION_CONFIG_PATH . '/' . variable_get('provision_bind_conf', 'named.conf.drupal');
if ($exists && isset($zone->old_origin)) { # The name of the zone has changed
#provision_log("notice", "Updating zone ".$zone->old_origin." to ".$zone->origin);
provision_path("unlink", $path . '/' . $zone->old_origin); # Remove the old zonefile
_provision_bind_editfile($conf, '^zone..'.$zone->old_origin.'.*$'); # Remove the old zone from named.conf
provision_bind_create_zone_config($zone); # And re-create the zone in named.conf
}
#provision_log("notice", "Writing zonefile: ".$zone_str);
if ($fd = fopen($path . '/'. $zone->origin, "w")) {
fwrite($fd, $zone_str); # Write the zonefile
fclose($fd);
} else {
provision_set_error(PROVISION_PERM_ERROR);
}
}
/**
* Remove the zonefile and named.conf entry for a given zone
*
* @param $zone
* The zone object (as returned by provision_dns_status) to remove.
**/
function provision_bind_delete_zone($zone) {
# Remove the zone from the named.conf file..
$path = variable_get('provision_bind_zonedata', PROVISION_CONFIG_PATH .'/named');
$conf = PROVISION_CONFIG_PATH . '/' . variable_get('provision_bind_conf', 'named.conf.drupal');
$zone_re = sprintf('^zone..%s.*$', $zone->origin);
_provision_bind_editfile($conf, $zone_re);
# and remove the zonefile itself
$zonefile = $path . '/' . $zone->origin;
provision_path("unlink", $zonefile);
}
/**
* hook_dns_commit implementation
**/
function provision_bind_dns_commit() {
return _provision_bind_restart();
}
/**
* Add an RR entry in the given zone's zonefile for the record provided.
*
* @param $zone
* The zone object (as returned by provision_dns_status) in which to add the RR
* @param $record
* The RR object (as returned by provision_dns_status) to add an entry for.
**/
function _provision_bind_add_record($zone, $record) {
$file = variable_get('provision_bind_zonedata', PROVISION_CONFIG_PATH .'/named') . '/' . $zone->origin;
$line = _provision_bind_record_string($record);
if ($fh = fopen($file, 'a')) {
fwrite($fh, $line);
fclose($fh);
} else {
provision_set_error(PROVISION_PERM_ERROR);
}
}
/**
* Update an RR entry in the given zone's zonefile for the record provided.
*
* @param $zone
* The zone object (as returned by provision_dns_status) in which the RR record resides.
* @param $record
* The record object (as returned by provision_dns_status) to update.
**/
function _provision_bind_update_record($zone, $record) {
$path = variable_get('provision_bind_zonedata', PROVISION_CONFIG_PATH .'/named');
$file = $path . '/' . $zone->origin;
if (!isset($record->old_name)) { $record->old_name = $record->name; }
$line = _provision_bind_record_string($record);
_provision_bind_editfile($file, '^'.$record->old_name.'\t.*$', $line);
}
/**
* Remove an RR entry from the given zone.
*
* @param $zone
* The zone object (as returned by provision_dns_status) in which the RR record resides.
* @param $record
* The record object (as returned by provision_dns_status) to delete.
**/
function _provision_bind_delete_record($zone, $record) {
$path = variable_get('provision_bind_zonedata', PROVISION_CONFIG_PATH .'/named');
$file = $path . '/' . $zone->origin;
$line = _provision_bind_record_string($record);
_provision_bind_editfile($file, '^'.$line.'$');
}
/**
* Helper function to restart BIND
**/
function _provision_bind_restart() {
// restart bind
$cmd = "sudo ". variable_get('provision_bind_rndc', '/usr/sbin/rndc') . " reload > /dev/null";
system($cmd, $status);
return $status;
}
/**
* Helper function to gather BIND's status (currently unused)
**/
function _provision_bind_status() {
$cmd = "sudo ". variable_get('provision_bind_rndc', '/usr/sbin/rndc') . " status > /dev/null";
system($cmd, $status);
return $status;
}
/**
* Helper function to check a given zone's zonefile (currently unused)
**/
function _provision_bind_namedcheckzone($zone) {
$path = variable_get('provision_bind_zonedata', '');
$cmd = variable_get('provision_bind_namedcheckzone', '/usr/sbin/named-checkzone') ." ". $zone->origin ." ". $path . $zone->origin ." > /dev/null";
system($cmd, $status);
return $status;
}
/**
* Helper function to check the local named.conf
**/
function _provision_bind_namedcheckconf($zone) {
$conf = variable_get('provision_bind_zonedata', '') . variable_get('provision_bind_conf', '');
$cmd = variable_get('provision_bind_namedcheckconf', '/usr/sbin/named-checkconf') ." ". $conf ." > /dev/null";
system($cmd, $status);
return $status;
}
/**
* Reads in the given file, locates the 'old' line (regexp), and replaces it with the 'new' one, using preg_replace();
* If $new is empty, simply removes the 'old' line completely.
*
* @param $file
* The file to edit.
* @param $old
* A regexp (without delimiters) which locates the line(s) to be edited
* @param $new
* A replacement string for the $old pattern. If this is empty, the $old pattern will be removed completely.
**/
function _provision_bind_editfile($file, $old, $new = '') {
# Read in the file, checking each line and doing a preg_replace on matching lines
if ($fd = fopen($file, 'r')) {
while ($line = fgets($fd)) {
if (preg_match("/$old/", $line)) {
if ($new) {
$lines[] = preg_replace("/$old/", $new, trim($line));
}
} else {
$lines[] = $line;
}
}
fclose($fd);
} else {
provision_set_error(PROVISION_PERM_ERROR);
}
# Write out the changed lines to the original file again.
if (!file_put_contents($file, $lines)) {
provision_set_error(PROVISION_PERM_ERROR);
}
}
/**
* Helper function to generate a RR line from a record object
*
* @param $record
* The record object as returned by provision_dns_status
*
* @return
* A string representing the given record as a line for a zonefile
**/
function _provision_bind_record_string($record) {
if ($record->type == "MX") { $pri = $record->aux; } else { $pri = ""; }
if (( $record->type == "NS" ||
$record->type == "PTR" ||
$record->type == "CNAME" ||
$record->type == "MX" ||
$record->type == "SRV") && ($record-data != "@")
) {
$destination = $record->data .".";
} elseif ($record->type == "TXT") {
$destination = "\"". $record->data ."\"";
} else {
$destination = $record->data;
}
return $record->name ."\t IN\t ". $record->type ."\t". $pri ."\t". $destination ."\n";
}
<?php
// $Id$
/**
* @file
* DNS provisioning module.
*
* The goal of this module is to manage DNS zonefiles and Resource Records
* (RRs), for sites that are about to be created. It uses the provision API to
* tie into the right places in the site creation work flow.
*/
# BIND engine is the only one (for now)
include_once('provision_dns.bind.inc');
/**
* Helper function for creating a new zone via the provision_dns_zone API function.
*
* @param $domain
* The domain name/origin for the zone to be created.
* @param $data
* An array of data to populate the new zone. Most importantly, the 'dns_id'
* field must contain the nid of the DNS server which on which to privision this zone.
*
* @return
* The zone object, whether newly created, or already existing.
**/
function provision_dns_create_zone($domain, $data) {
$writable = provision_path("writable", PROVISION_NAMED_PATH, TRUE, NULL,
t("DNS configuration path @path is not writable"),
PROVISION_PERM_ERROR | PROVISION_FRAMEWORK_ERROR);
# if ($zone = provision_dns_status('zone', array('domain' => $domain))) {
# provision_log("Notice", t("Loading zone file for url @url", array('@url' => $domain)));
# return $zone;
# } else {
provision_log("Notice", t("Generating zone file for url @url on dns server @id", array('@url' => $domain, '@id' => $data['dns_id'])));
# Setup args array from defaults
$dns = node_load($data['dns_id']);
$zone_fields['dns_id'] = $data['dns_id'];
$zone_fields['origin'] = $domain;
$zone_fields['serial'] = date('Ymd') . '01';
$ns_recs = explode("\n", $dns->ns);
#ob_start(); print_r($ns_recs); $out = ob_get_contents(); ob_end_clean();
provision_log("notice", "provision_dns_create_zone ns_recs: ".$ns_recs[0].", ".$ns_recs[1]);
$zone_fields['ns1'] = $ns_recs[0];
$zone_fields['ns2'] = $ns_recs[1];
$zone_fields['mbox'] = $dns->mbox;
provision_dns_zone('add', $zone_fields);
# $zone = provision_dns_status('zone', array('domain' => $domain));
# foreach ($ns_recs as $ns) {
# provision_dns_create_record('@', $zone, array('type' => 'NS', 'dns_ip' => trim($ns)));
# }
# return provision_dns_status('zone', array('domain' => $domain));
# }
}
/**
* Helper function for creating a new A record via the provision_dns_rr API function.
*
* @param $host
* The name part of the record to be created.
* @param $zone
* The zone object in which to create the record.
* @param $data
* An array of data used to populate the record. Most importantly, the 'dns_ip' field should
* contain the IP address which the host record should point to.
*
* @return
* The record object, or FALSE if the record could not be created.
**/
function provision_dns_create_record($host, $zone, $data) {
drush_log(dt("Creating %type record for url @url", array('%type' => $data['type'], '@url' => $host)));
$rec['name'] = $host;
$rec['type'] = $data['type']?$data['type']:'A';
$rec['data'] = $data['dns_ip'];
return provision_dns_rr('add', $zone->zid, $rec);
}
/**
* Helper function for removing a given URL's record as well as zonefile (if it's the last
* record in the zone) via the provision_dns_zone and provision_dns_rr API functions.
*
* @param $url
* The URL to remove. The zone and RR will be extracted from this and removed via the API.
**/
function provision_dns_delete($url) {
# do i need to resurrect this?
$url_parts = _provision_dns_split_url($url);
# These two objects should now be passed through from the hosting layer..j
# if ($zone = provision_dns_status('zone', array('domain' => $url_parts['domain']))) {
# if ($rec = provision_dns_status('rr', array('zid' => $zone->zid, 'name' => $url_parts['host'], 'type' => 'A'))) {
# delete the A record for this site..
provision_dns_rr('delete', $zone, array('rid' => $rec->rid));
# and (as long as it's the only host on this domain) delete the zone as well
# if (provision_dns_getrecords($zone->zid, TRUE) <= 1) {
provision_dns_zone('delete', array('zid' => $zone->zid));
# }
# }
# }
}
/**
* Manage zones
*
* @param $op
* The operation to be done on the zone. Valid $ops are 'add', 'delete' and 'update'.
* @param $args
* An array which identifies the zone (using the 'origin' or 'zid' keys) plus data for
* use in the operation itself. Valid data keys for $args are:
* 'dns_id', 'zid', 'origin', 'ns1', 'ns2', 'mbox', 'serial', 'refresh', 'retry', 'expire', 'minimum', 'ttl'
* The special 'dns_id' key causes the function to load a dns_server node with this ID for use in populating
* defaults for the other fields.
*
* @return
* For the 'add' and 'update' $ops, returns the zone object.
**/
function provision_dns_zone($op, $args) {
$fields = array('origin', 'ns1', 'ns2', 'mbox', 'serial', 'refresh', 'retry', 'expire', 'minimum', 'ttl', 'active', 'xfer');
if (isset($args['dns_id'])) { $dns = node_load($args['dns_id']); }
// do whatever DB setup is necessary, then call hook_dns_zone($op, $args) to let the engine do the work..
switch ($op) {
case 'add':
# Again, the zone object needs to be passed to the API
# if ($zone = provision_dns_status('zone', array('domain' => $args['origin']))) {
# // domain already exists, just return the zid
# return $zone->zid;
# } else {
if (!isset($args['origin'])) {
// error, need at least this arg
return FALSE;
}
// build query, and INSERT and set the $zone object
foreach ($fields as $field) {
if (isset($args[$field])) {
$f[] = $field;
$v[] = $args[$field];
} else {
# Use default from dns_server node
if (isset($dns)) {
$f[] = $field;
$v[] = $dns->$field;
}
}
}
# TODO: move this up into hosting, and pass the zone object down
# $sql = sprintf("INSERT INTO {provision_dns_soa} (%s) VALUES ('%s')",
# implode(",", $f),
# implode("','", $v));
# db_query($sql);
# $zone = provision_dns_status('zone', array('domain' => $args['origin']));
}
# This should call the engine..
# module_invoke_all("provision_dns_zone", $op, $zone);
if ($dns) { # Add a default A record for the origin
provision_dns_rr('add', $zone, array('name' => '@', 'type' => 'A', 'data' => $dns->default_ip));
}
break;
case 'update':
# Here again, this object should come from hosting..
# if (!isset($args['zid'])) {
# $zone = provision_dns_status('zone', array('domain' => $args['origin']));
# } else {
# $zone = provision_dns_status('zone', array('zid' => $args['zid']));
# }
# if ($zone || provision_dns_status('zone', $args)) {
// build update query
$keys = array();
if (!isset($args['serial']) || $zone->serial == $args['serial']) {
$args['serial'] = _provision_dns_increment_serial($zone->serial);
}
foreach ($fields as $field) {
if (isset($args[$field]) && $args[$field] != $zone->$field) {
if ($field == 'origin') {
$zone->old_origin = $zone->origin; # Save this so engines can modify name
}
$keys[] = $field . "= '%s'";
$values[] = $args[$field];
$zone->$field = $args[$field];
}
}
$values[] = $zone->zid;
if (!empty($keys)) {
# TODO: move this up into hosting, and pass the zone object down
# $sql = "UPDATE {provision_dns_soa} SET ". implode(',', $keys) ." WHERE zid=%d";
# db_query($sql, $values);
}
# } else {
# // zone doesn't exist, return error?
# return;
# }
# Call the engine..
# module_invoke_all("provision_dns_zone", $op, $zone);
return $zone;
break;
case 'delete':
# $zone = provision_dns_status('zone', $args);
# invoke engine
# module_invoke_all('provision_dns_zone', 'delete', $zone); # This will delete all the zone's records too
# TODO:
# db_query('DELETE FROM {provision_dns_rr} WHERE zid = %d', $zone->zid);
# db_query('DELETE FROM {provision_dns_soa} WHERE zid = %d', $zone->zid);
break;
}
}
/**
* Add, delete, or update Resource Records (see http://www.dns.net/dnsrd/rr.html and http://www.bind9.net/dns-parameters)
*
* @param $op
* What type of operation to execute on the zone: add, delete, or update.
* @param $zone
* The zone object in which the RR resides.
* @param $args
* An array identifying the RR to operate on plus data for use in the operation itself.
* Valid keys for this array are:
* 'name', 'type', 'data', 'aux', 'ttl'
*
* @return
* For the 'add' and 'update' $ops, return the record object.
**/
function provision_dns_rr($op, $zone, $args) {
$fields = array('name', 'type', 'data', 'aux', 'ttl');
# if (is_numeric($zone)) {
# $zone = provision_dns_status('zone', array('zid' => $zone));
# }
if (!is_object($zone)) {
return FALSE; # Error, the zone doesn't seem to exist!
}
// do whatever DB setup is necessary, then call hook_dns_rr($op, $args)
switch ($op) {
case 'add':
# if ($rr = provision_dns_status('rr', array_merge(array('zid' => $zone->zid), $args))) {
# // RR already exists
# return $rr;
# }
if (!isset($args['name']) || !isset($args['type']) || !isset($args['data'])) { # Not enough info
return FALSE;
}
# TODO:
# $sql = sprintf("INSERT INTO {provision_dns_rr} (zid, name, type, data, aux, ttl) VALUES (%d, '%s', '%s', '%s', %d, %d)",
# $zone->zid, $args['name'], $args['type'], $args['data'], $args['aux'], $args['ttl']);
# db_query($sql);
# $record = provision_dns_status('rr', array('zid' => $zone->zid, 'name' => $args['name'], 'type' => $args['type']));
# Call the engine..
# module_invoke_all('provision_dns_rr', $op, $zone, $record);
provision_dns_zone('update', array('zid' => $zone->zid, 'serial' => _provision_dns_increment_serial($zone->serial)));
return $record;
break;
case 'update':