dns.drush.inc 9.32 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
<?php
// $Id: dns.drush.inc,v 1.4 2009/03/20 16:13:24 adrian Exp $
/**
 * @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.
 */

12 13
include_once(dirname(__FILE__) . '/../provision.service.inc');

14 15 16 17 18 19 20 21 22 23 24 25
/**
 * 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)')),
    'description' => dt('Manipulate a zonefile'),
    'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
  );
  return $items;
}

26
function drush_dns_provision_zone($action, $zone, $name = null, $type = null, $destination = null) {
27
  switch ($action) {
28 29 30 31 32 33
  case 'create-host':
    $status = d()->service('dns')->create_host($zone);
    break;
  case 'delete-host':
    $status = d()->service('dns')->delete_host($zone);
    break;
34 35 36 37 38 39 40 41
  case 'verify':
  case 'create':
    $status = d()->service('dns')->create_zone($zone);
    break;
  case 'delete':
    $status = d()->service('dns')->delete_zone($zone);
    break;
  case 'rr-add':
42
    $status = d()->service('dns')->add_record($zone, $name, $type, $destination);
43 44
    break;
  case 'rr-modify':
45
    $status = d()->service('dns')->modify_record($zone, $name, $type, $destination);
46
    break;
47
  case 'rr-delete':
48
    $status = d()->service('dns')->delete_record($zone, $name, $type, $destination);
49 50
    break;
  }
51 52 53
  $status = $status && d()->service('dns')->commit($zone);
  
  return $status;
54 55 56 57 58 59 60
}


function dns_provision_services() {
  return array('dns' => NULL);
}

61 62


63
class provisionService_dns extends provisionService {
64
  public $service = 'dns';
Adrian Rossouw's avatar
Adrian Rossouw committed
65 66
  public $slave = null;

67 68 69 70 71 72 73 74 75 76 77 78
}

class provisionService_dns_basic extends provisionService_dns {


  function parse_configs() {
    return null;
  }

  function init() {
    parent::init();

Adrian Rossouw's avatar
Adrian Rossouw committed
79 80 81
    // Path for storing data store config files.
    $this->server->dns_data_path = $this->server->aegir_root . '/dns.d';

82 83 84 85 86 87 88 89 90
    if (!is_null($this->application_name)) {
      $app_dir = "{$this->server->config_path}/{$this->application_name}";
      $this->server->dns_zoned_path = "{$app_dir}/zone.d";
      $this->server->dns_hostd_path = "{$app_dir}/host.d";
    }
  }

  function verify() {
    if ($this->context->type === 'server') {
Adrian Rossouw's avatar
Adrian Rossouw committed
91 92
      provision_file()->create_dir($this->server->dns_data_path, dt("DNS data store"), 0700);

93
      if (!is_null($this->application_name)) {
94
        provision_file()->create_dir($this->server->dns_zoned_path, dt("DNS zone configuration"), 0755);
95 96 97 98
        $this->sync($this->server->dns_zoned_path, array(
          'exclude' => $this->server->dns_zoned_path . '/*',  // Make sure remote directory is created
        )); 

99
        provision_file()->create_dir($this->server->dns_hostd_path , dt("DNS host configuration"), 0755);
100 101 102 103
        $this->sync($this->server->dns_hostd_path, array(
          'exclude' => $this->server->dns_hostd_path . '/*',  // Make sure remote directory is created
        ));

Adrian Rossouw's avatar
Adrian Rossouw committed
104 105
        # TODO: create a slave zone path too.

106 107 108
        $this->create_config('server');
      } 
    }
109

110 111
  }

112 113 114
  function config_data($config = null, $class = null) {
    $data = parent::config_data($config, $class);
    if (!is_null($this->application_name)) {
Adrian Rossouw's avatar
Adrian Rossouw committed
115
      $data['dns_data_path_path'] = $this->server->dns_zoned_path;
116 117 118 119 120 121 122 123 124 125 126 127 128 129
      $data['dns_zoned_path'] = $this->server->dns_zoned_path;
      $data['dns_hostd_path'] = $this->server->dns_hostd_path;
    }

    if ($config == 'host') {
      $data['site_ip_addresses'] = drush_get_option('site_ip_addresses', array(), 'site');
    }

    return $data;
  }
}

class provisionService_dns_complex extends provisionService_dns_basic {

130
  function init() {
131
    parent::init();
132 133 134 135 136
    $this->server->setProperty('dns_ttl', 86400); # 24h
    $this->server->setProperty('dns_refresh', 21600); # 6h
    $this->server->setProperty('dns_retry', 3600); # 1h
    $this->server->setProperty('dns_expire', 604800); # 7d
    $this->server->setProperty('dns_negativettl', 86400); # 24h
137 138 139
  }

  function verify() {
140
    switch ($this->context->type) {
141 142 143 144 145
      case 'site' :
        $this->create_host();
        break;
    }

Adrian Rossouw's avatar
Adrian Rossouw committed
146 147
    // This will restart the server if needed.
    parent::verify();
148 149
  }

150 151 152 153 154 155 156 157 158 159 160
  /**
   * Determine if a zone is defined on the nameserver.
   *
   * To be implemented by engines.
   *
   * @return if the zone exists
   */
  function zone_exists($zone) {
    return FALSE;
  }

161 162 163 164 165 166 167
  /**
   * This returns the number of records in the zone.
   */
  function count_records($zone, $include = null, $exclude = null) {
    return FALSE;
  }

168 169 170 171 172
  /**
   * Guess in which zone we should create the record
   *
   * This function will examine the existing zones to find to which
   * this host belongs to.
173 174 175 176
   *
   * @param $host the name of the record to add (e.g. www.example.com)
   *
   * @returns array the record and zone name to add the record to (e.g. www and example.com)
177 178
   */
  function guess_zone($host) {
179
    $tld = $host;
180 181
    $parts = explode(".", $host);
    $subdomain = array();
182
    $found = FALSE;
183
    while (!$found && (count($parts) > 2)) {
184
      $tld = join(".", $parts);
185 186 187
      if ($this->zone_exists($tld)) {
        $found = TRUE;
      } else {
188 189
        $scrap = array_shift($parts);
        $subdomain[] = $scrap;
190 191
        drush_log("zone $tld not found, ditching $scrap, count: " . count($parts));
        $found = FALSE;
192
      }
193
    }
194 195
    // this is necessary if we hit the limit of two subdomains
    $tld = join(".", $parts);
196
    $subdomain = join(".", $subdomain);
197 198 199
    if (!$subdomain) {
      $subdomain = '@';
    }
200 201
    drush_log("guess_zone guessed parts $tld, $subdomain");
    return array($subdomain, $tld);
202 203
  }

204 205 206 207
  /**
   * Helper function to increment a zone's serial number.
   *
   * @param $serial
208 209
   *    A serial in YYYYMMDDnn format. If null, a new serial based on
   *    the date will be generated.
210 211 212 213
   *
   * @return
   *    The serial, incremented based on current date and index
   */
214
  function increment_serial($serial = null) {
215 216
    $date = substr($serial, 0, 8); # Get the YYYYMMDD part
    $today = date('Ymd');
217 218
    if (is_null($serial) || $date != $today) {
      return $today . '00';
219
    } else {
220
      $index = substr($serial, 8, 2); # Get the index part
221 222 223 224 225 226
      if ($index >= 99) {
        drush_set_error("serial number overflow");
      } else {
        $index++;
      }
      return $date . sprintf('%02d', $index);
227 228 229
    }
  }

230 231 232
  /**
   * This creates a zone, which mostly consists of adding the SOA record.
   */
233
  function create_zone($fqdn = null) {
234
    $this->edit_record($fqdn, '@', 'SOA');
235 236
  }

237
  /**
238
   * This completely drops a zone, without any checks.
239 240 241 242 243
   */
  function delete_zone($fqdn) {
    return FALSE;
  }

244
  /**
245 246 247 248 249
   * This adds a (potentially) duplicate record (RR) in a zonefile
   *
   * This doesn't check if the record already exists, it just adds it
   * to the end of the file.
   */
250
  function add_record($zonename, $name, $type, $destination) {
251 252 253 254 255 256
    return FALSE;
  }

  /**
   * This creates or replaces a record (RR) in a zonefile.
   */
257
  function edit_record($zonename, $name, $type, $destination) {
258 259 260 261 262 263 264
    return FALSE;
  }

  /**
   * This removes a record matching the name and destination.
   *
   * If destination or type is null, they are disregarded in the pattern.
265
   */
266
  function delete_record($zonename, $name, $type, $destination = null) {
267 268 269
    return FALSE;
  }

270 271 272 273
  /** 
   * Create a host in DNS.
   *
   * This can do a lot of things, create a zonefile, add a record to a
274 275 276 277 278
   * zonefile, it's going to make its best guess doing the Right
   * Thing.
   *
   * @arg $host string the hostname to create. If null, we look in the
   * current context (should be a site) for a URI.
279
   */
280 281 282 283
  function create_host($host = null) {
    if (is_null($host)) {
      $host = $this->context->uri;
    }
284
    $parts = $this->guess_zone($host);
285 286 287
    $zone = $parts[1];
    $sub = $parts[0];
    if (!$this->zone_exists($zone)) {
anarcat's avatar
anarcat committed
288
      drush_log("in create_host, zone $zone not found");
289 290 291
      $this->create_zone($zone);
    }
    $ips = d($site)->service('http')->server->ip_addresses;
292 293 294 295 296 297
    if (!$ips && count($ips) < 1) {
      drush_log(dt("no IP found for server, trying loopback"));
      $ips = '127.0.0.1';
    }
    if (is_array($ips)) {
      drush_log("a bunch of ips defined, deleting existing ones and readding: " . join(",", $ips));
298 299 300 301
      $this->delete_record($zone, $sub, 'A');
      foreach ($ips as $ip) {
        $this->add_record($zone, $sub, 'A', $ip);
      }
302 303
    } else {
      $this->edit_record($zone, $sub, 'A', $ips);
304
    }
305 306 307 308 309 310
  }

  /**
   * Delete a host from DNS
   *
   * Similar to create host, this will seek and destroy that host throughout zonefiles.
311 312 313
   *
   * @arg $host string the hostname to create. If null, we look in the
   * current context (should be a site) for a URI.
314
   */
315 316 317 318
  function delete_host($host = null) {
    if (is_null($host)) {
      $host = $this->context->uri;
    }
319
    $parts = $this->guess_zone($host);
320 321 322
    $this->delete_record($parts[1], $parts[0], 'A');
    if ($this->count_records($parts[1], null, array('NS', 'SOA'))) {
      $this->delete_zone($parts[1]);
323
    }
324 325 326 327 328 329 330 331 332 333 334 335 336
  }

  /**
   * Commit changes to the DNS server
   *
   * This may involve restarting the server.
   *
   * @arg $host only reload a single zone. if null, reload all zones
   */
  function commit($zone = null) {
    return FALSE;
  }
}
337 338

include_once('dns.config.inc');