Commit 656a16a9 authored by anarcat's avatar anarcat Committed by anarcat

Merge commit 'origin/master' into production

Conflicts:

	http/http.drush.inc
	provision.environment.inc
parents 330721d0 6313007b
...@@ -4,6 +4,6 @@ api = 2 ...@@ -4,6 +4,6 @@ api = 2
projects[] = "drupal" projects[] = "drupal"
projects[hostmaster][type] = "profile" projects[hostmaster][type] = "profile"
projects[hostmaster][download][type] = "get" projects[hostmaster][download][type] = "git"
projects[hostmaster][download][url] = "http://files.aegirproject.org/hostmaster-0.4-alpha14.tgz" projects[hostmaster][download][url] = "git://git.aegirproject.org/hostmaster.git"
projects[hostmaster][download][directory_name] = "hostmaster" projects[hostmaster][download][branch] = "master"
<?php print "<?php \n"; ?>
<?php foreach ($records as $key => $record) {
print "\n\${$this->key}['{$key}'] = ". var_export($record, TRUE) .';';
}
?>
...@@ -32,29 +32,36 @@ function db_drush_help($section) { ...@@ -32,29 +32,36 @@ function db_drush_help($section) {
class provisionService_db extends provisionService { class provisionService_db extends provisionService {
protected $service = 'db'; protected $service = 'db';
/**
* Register the db handler for sites, based on the db_server option.
*/
static function subscribe_site($context) {
$context->setProperty('db_server', '@server_master');
$context->is_oid('db_server');
$context->service_subscribe('db', $context->db_server->name);
}
static function option_documentation() { static function option_documentation() {
return array( return array(
'--master_db' => 'server with db: Master database connection info, {type}://{user}:{password}@{host}', '--master_db' => 'server with db: Master database connection info, {type}://{user}:{password}@{host}',
); );
} }
function init() { function init_server() {
parent::init(); parent::init_server();
$this->server->setProperty('master_db'); $this->server->setProperty('master_db');
$this->creds = array_map('urldecode', parse_url($this->server->master_db)); $this->creds = array_map('urldecode', parse_url($this->server->master_db));
return TRUE; return TRUE;
} }
function verify() { function verify_server_cmd() {
if ($this->context->type == 'server') { $this->connect();
$this->connect(); if ($this->can_create_database()) {
if ($this->can_create_database()) { drush_log(dt('Provision can create new databases.'), 'message');
drush_log(dt('Provision can create new databases.'), 'message'); }
} else {
else { drush_set_error('PROVISION_CREATE_DB_FAILED');
drush_set_error('PROVISION_CREATE_DB_FAILED');
}
} }
} }
...@@ -258,8 +265,8 @@ class provisionService_db_pdo extends provisionService_db { ...@@ -258,8 +265,8 @@ class provisionService_db_pdo extends provisionService_db {
protected $creds; protected $creds;
private $dsn; private $dsn;
function init() { function init_server() {
parent::init(); parent::init_server();
$this->dsn = sprintf("%s:host=%s", $this->PDO_type, $this->creds['host']); $this->dsn = sprintf("%s:host=%s", $this->PDO_type, $this->creds['host']);
if ($this->has_port) { if ($this->has_port) {
......
Here is a list of pointers on how to setup DNS in aegir. It's still very crude
and needs a lot of love. It may also not be the right place for this as it
mixes todos, test procedures and install procedures.
Master server configuration
===========================
First, install bind, allow aegir to sudo rndc reload. Make sure the bind user
can read hostmaster files (adduser bind hostmaster).
Then add the DNS service in the frontend. This should trigger a server
verification and configure the DNS service in the backend. Creating a site
should then create a zone and a record and reload bind.
If you're having trouble with this, you can try to test the backend.
Testing the backend
-------------------
drush provision-save @server_master --dns_service_type=bind
drush @server_master provision-zone create foobar.com
drush @server_master provision-zone rr-add foobar.com www A 1.2.3.4
You should end up with a zonefile like:
; Bind zonefile
; File managed by Aegir
; Changes here may be lost by user configurations, tread carefully
$TTL
@ IN SOA foobar.com. hostmaster.foobar.com. ( 2010061700 21600 3600 604800 86400 )
www IN A 1.2.3.4
With this command:
provision-zone create-host www.foobar.com
should another similar record to the same zonefile.
Slave server configuration
==========================
A slave server requires the following steps:
1. install bind, sudo, rsync on the server
2. create an aegir user on the server the usual way (including SSH key exchange)
3. create the server in the frontend with the bind_slave service
4. configure the master server to use those slaves:
drush provision-save @server_master --slave_servers=@server_slaveservername
Then running the tests describe in the master configuration should create a
config file in /var/aegir/config/bind_slave.conf that you need to include in
your bind configuration. The config file should look something like this:
zone "foobar.com" { type slave; file "/var/hostmaster/config/server_ns4koumbitnet/bind_slave/zone.d/foobar.com.zone"; masters { 1.2.3.4; }; allow-query { any; }; };
Caveats
=======
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. such a parser was originally written for bind but was dropped
in one of the numerous rewrites of the DNS code.
2. changing the master/slave relationship doesn't change the zonefiles
unless every zonefile is verified again.
Todo and bugs
=============
Those should be reported in the regular trackers from now on.
name = Provision: BIND
description = Provides provisioning requirements for Bind servers
package = Provision
dependencies[] = provision
core = 6.x
<?php
/**
* Implementation of the DNS service through BIND9
*
* A lot of this is inspired by the Apache implementation of the HTTP service.
*/
class provisionService_dns_bind extends provisionService_dns {
protected $application_name = 'bind';
protected $has_restart_cmd = TRUE;
private $zone_cache = array();
static function bind_default_restart_cmd() {
return "rndc reload";
}
function default_restart_cmd() {
return provisionService_dns_bind::bind_default_restart_cmd();
}
function init_server() {
parent::init_server();
$this->configs['server'][] = 'provisionConfig_bind_server';
$this->configs['zone'][] = 'provisionConfig_bind_zone';
}
function parse_configs() {
$status = $this->restart();
return $status && parent::parse_configs();
}
}
class provisionConfig_bind_zone extends provisionConfig_dns_zone {
/**
* this renders the slave servers names (as their alias is stored)
*/
function process() {
parent::process();
$slaves = array();
if (!is_array($this->server->slave_servers)) {
$this->server->slave_servers = array($this->server->slave_servers);
}
foreach ($this->server->slave_servers as $slave) {
$slaves[] = d($slave)->remote_host;
}
$this->data['server']->slave_servers_names = $slaves;
}
}
class provisionConfig_bind_server extends provisionConfig_dns_server {
/**
* pre-render the slave servers IP addresses
*
* This is done so we can configure the allow-transfer ACL.
*/
function process() {
parent::process();
$slaves = array();
if (!is_array($this->server->slave_servers)) {
$this->server->slave_servers = array($this->server->slave_servers);
}
foreach ($this->server->slave_servers as $slave) {
$slaves = array_merge($slaves, d($slave)->ip_addresses);
}
$this->data['server']->slave_servers_ips = $slaves;
}}
<?php
$slave_acl = "";
if (is_array($server->slave_servers_ips)) {
$slaves = implode(";", $server->slave_servers_ips);
if (!empty($slaves)) {
$slave_acl = "allow-transfer { $slaves; };\n";
}
}
foreach ($records as $key => $name) {
printf('zone "%s" { type master; file "%s/%s.zone"; allow-query { any; }; %s };' . "\n", $name, $dns_zoned_path, $name, $slave_acl);
}
?>
; Bind zonefile
; File managed by Aegir
; Changes here may be lost by user configurations, tread carefully
$TTL <?php print $server->dns_ttl; ?>
<?php
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");
if (!empty($server->dns_default_mx)) {
if ($server->dns_default_mx[strlen($server->dns_default_mx)-1] != '.') {
$server->dns_default_mx .= '.';
}
print "@\tIN\tMX\t10\t" . $server->dns_default_mx . "\n";
}
print "@\tIN\tNS\t" . $server->remote_host;
if ($server->remote_host[strlen($server->remote_host)-1] != '.') {
print '.';
}
print " ; primary DNS\n";
if (is_array($server->slave_servers_names)) {
foreach ($server->slave_servers_names as $slave) {
if ($slave[strlen($slave)-1] != '.') {
$slave .= '.';
}
print "@\tIN\tNS\t" . $slave . " ; slave DNS\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";
}
}
name = Provision: BIND slaves
description = Provides provisioning requirements for Bind slave servers
package = Provision
dependencies[] = provision
core = 6.x
<?php
/**
* Implementation of a slave DNS service through BIND9
*
* A lot of this is inspired by the BIND implementation of the DNS service and
* the cluster HTTP service.
*/
class provisionService_dns_bind_slave extends provisionService_dns {
protected $application_name = 'bind_slave';
protected $has_restart_cmd = TRUE;
function default_restart_cmd() {
return provisionService_dns_bind::bind_default_restart_cmd();
}
function init_server() {
parent::init_server();
$this->configs['server'][] = 'provisionConfig_bind_slave';
}
function parse_configs() {
$this->restart();
}
/**
* Create the zonefile record on the slave server
*
* This differs from the parent implementation because it *requires* you to
* pass the master server, as it is necessary for the slave to know what its
* slave is (and it can vary according to the zone).
*
* Contrarily to the parent class implementation, this *only* creates the
* bind config (managed through the provisionConfig_bind_slave class), and no
* zonefile, because the zonefile should be managed by bind itself through
* regular zone transfers.
*
* Note that this function shouldn't be called directly through the API, but
* only from the master server's create_zone() function.
*
* @todo this needs to be pushed up in a provisionService_dns_slave common
* class
*
* @arg $master mixed a string or array of strings of IPs pointing to the
* master server for this zone
*
* @arg $zone string the zonefile name to create
*
* @see provisionService_dns::create_zone()
*/
function create_zone($master, $zone = null) {
if (is_null($zone) && ($this->context->type == 'site')) {
$host = $this->context->uri;
$zone = $this->context->dns_zone;
$sub = $this->context->dns_zone_subdomain;
}
if (empty($zone)) {
return drush_set_error('DRUSH_DNS_NO_ZONE', "Could not determine the zone to create");
}
drush_log(dt("recording zone in slave configuration"));
$this->config('server')->record_set($zone, $master)->write();
}
}
class provisionConfig_bind_slave extends provisionConfig_dns_server {
public $template = 'slave.tpl.php';
}
<?php
foreach ($records as $zone => $master) {
if (is_array($master)) {
$master = implode(";", $master);
}
printf('zone "%s" { type slave; file "%s/%s.zone"; masters { %s; }; allow-query { any; }; };' . "\n", $zone, $dns_zoned_path, $zone, $master);
}
?>
<?php
// $Id: delete.provision.inc,v 1.3 2009/05/07 22:04:30 adrian Exp $
/**
* @file
* Provision hooks for the delete command
**/
function drush_dns_post_provision_delete() {
if (d()->type == 'site') {
d()->service('dns')->delete_host();
d()->service('dns')->parse_configs();
}
}
<?php
/**
* Base config class for all dns config files.
*/
class provisionConfig_dns extends provisionConfig {
public $mode = 0777;
function write() {
parent::write();
$this->data['server']->sync($this->filename());
}
function unlink() {
parent::unlink();
$this->data['server']->sync($this->filename());
}
}
// The data store for the server configuration
// contains a list of zones we manage.
class provisionConfig_dns_server_store extends provisionConfig_data_store {
function filename() {
return $this->data['server']->dns_data_path . '/zones.master.inc';
}
}
/**
* Base config class for the server level config.
*/
class provisionConfig_dns_server extends provisionConfig_dns {
public $template = 'server.tpl.php';
public $description = 'Server-wide DNS configuration';
public $data_store_class = 'provisionConfig_dns_server_store';
function filename() {
if (isset($this->data['application_name'])) {
$file = $this->data['application_name'] . '.conf';
return $this->data['server']->config_path . '/' . $file;
}
else {
return FALSE;
}
}
function write() {
// lock the store until we are done generating our config.
$this->store->lock();
parent::write();
$this->store->write();
$this->store->close();
if (isset($this->data['application_name'])) {
$file = $this->data['application_name'] . '.conf';
// We link the app_name.conf file on the remote server to the right version.
$cmd = sprintf('ln -sf %s %s',
escapeshellarg($this->data['server']->config_path . '/' . $file),
escapeshellarg($this->data['server']->aegir_root . '/config/' . $file)
);
if ($this->data['server']->shell_exec($cmd)) {
drush_log(dt("Created symlink for %file on %server", array(
'%file' => $file,
'%server' => $this->data['server']->remote_host,
)));
};
}
}
}
class provisionConfig_dns_zone_store extends provisionConfig_data_store {
function filename() {
return "{$this->data['server']->dns_data_path}/{$this->data['name']}.zone.inc";
}
}
/**
* Representation of a DNS zonefile
*
* This is the internal representation of a zonefile. It can be
* extended by other subclasses to implement various engines, but it
* has its own internal storage (through
* provisionConfig_dns_zone_store).
*
* It assumes a certain structure in the records of the store.
*
* @example
*
* <code>
* $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 ... )'),
* 'A' => array('1.2.3.3'),
* 'MX' => array('mail.localhost'),
* 'NS' => array('localhost', 'ns2.localhost'),
* )
* );
* </code>
*
* The zonefile's serial number is incremented automaticall when the
* file is written (in process()). Note how the structure of the SOA
* record is different from the others. First, it is a key-value
* map. Second, it represents only one DNS record (whereas the other
* entries represent as many entries as there are entries in the
* array.
*
* To edit those records, some care need to be taken. Look at the
* implementation of rr-add, rr-delete and rr-modify for examples of
* how it should properly be done, in drush_dns_provision_zone().
*
* @see drush_dns_provision_zone()
* @see increment_serial()
* @see provisionConfig_dns_zone_store
*/
class provisionConfig_dns_zone extends provisionConfig_dns {
public $template = 'zone.tpl.php';
public $description = 'Zone-wide DNS configuration';
public $data_store_class = 'provisionConfig_dns_zone_store';
function filename() {
return "{$this->data['server']->dns_zoned_path}/{$this->data['name']}.zone";
}
function process() {
parent::process();
$records = $this->store->merged_records();
$this->data['dns_email'] = str_replace('@', '.', $this->data['server']->admin_email);
// increment the serial.
$this->store->records['@']['SOA']['serial'] = $records['serial'] = provisionService_dns::increment_serial($records['@']['SOA']['serial']);
$this->data['records'] = $records;
}
function write() {
// lock the store until we are done generating our config.
$this->store->lock();
if ($this->is_empty()) {
$this->unlink();
} else {
parent::write();
$this->store->write();
}
$this->store->close();
}
/**
* this destroys this zonefile, without any checks
*
* It actually removes the zonefile, the internal storage and the
* record in the server config.
*/
function unlink() {
// remove the zonefile
if (parent::unlink()) {
// remove the master record
// XXX: need to do this for slaves too
$this->server->service('dns')->config('server')->record_del($zone)->write();
// remove the zonefile storage
$this->store->unlink();
}
$this->store->unlock();
}
/**
* test to see if the
*/
function is_empty() {
$records = $this->store->merged_records();
// if there is any record that is not SOA or NS, this is
// considered empty
if (empty($records)) {
return TRUE;
}
foreach ($records as $name => $record) {
if ($name != '@') {
return FALSE;
} else {
foreach ($record as $type => $destination) {
if ($type != 'SOA' && $type != 'NS' && !is_null($destination)) {
return FALSE;
}
}
}
}
return TRUE;
}
}
class provisionConfig_dns_host extends provisionConfig_dns {
public $template = 'host.tpl.php';
public $description = 'Host-wide DNS configuration';
function filename() {
return "{$this->data['server']->dns_hostd_path}/{$this->uri}.hosts";
}
}
This diff is collapsed.
name = Provision: DNS
description = Provides provisioning requirements for DNS servers
package = Provision
dependencies[] = provision
core = 6.x
<?php
class provisionService_dns_dnsmasq extends provisionService_dns {
protected $application_name = 'dnsmasq';
protected $has_restart_cmd = TRUE;
function default_restart_cmd() {
return 'sudo /etc/init.d/dnsmasq restart';
}
function init_server() {
parent::init_server();
$this->configs['server'][] = 'provisionConfig_dnsmasq_server';
$this->configs['zone'][] = 'provisionConfig_dnsmasq_zone';
$this->configs['host'][] = 'provisionConfig_dnsmasq_host';
}
function parse_configs() {
$this->restart();
}
}
class provisionConfig_dnsmasq_server extends provisionConfig_dns_server {
}
class provisionConfig_dnsmasq_zone extends provisionConfig_dns_zone {
}
class provisionConfig_dnsmasq_host extends provisionConfig_dns_host {
}
<?php
foreach ($site_ip_addresses as $server => $ip) {
print "{$ip}\t {$this->uri}\n";
}
?>
<?php
foreach ($records as $key => $name) {
printf("conf-file=%s/%s.zone\n", $dns_zoned_path, $name);
}
?>
<?php
foreach ($hosts as $host => $info) {
print "addn-hosts={$dns_hostd_path}/{$host}.hosts\n";
}
?>
<?php
// $Id: install.provision.inc,v 1.7 2009/05/07 22:04:30 adrian Exp $
/**
* @file
* Provision hooks for the install command
**/
function drush_dns_post_provision_install() {
// we create this post-install because we need the ip's to be assigned already.
d()->service('dns')->create_host();
d()->service('dns')->parse_configs();
}
<?php
// $Id: verify.provision.inc,v 1.5 2009/03/26 01:40:39 anarcat Exp $
/**
* @file
* Provision hooks for the verify command
**/
function drush_dns_post_provision_verify() {
if (d()->type == 'site') {
d()->service('dns')->create_host();
d()->service('dns')->parse_configs();
}
}
...@@ -250,7 +250,7 @@ as the aegir user created above. This file is available in Provision or can be ...@@ -250,7 +250,7 @@ as the aegir user created above. This file is available in Provision or can be
downloaded through the web with this command:: downloaded through the web with this command::
Shell commands:: Shell commands::
wget -O install.sh.txt 'http://git.aegirproject.org/?p=provision.git;a=blob_plain;f=install.sh.txt;hb=provision-0.4-alpha14' wget -O install.sh.txt 'http://git.aegirproject.org/?p=provision.git;a=blob_plain;f=install.sh.txt;hb=HEAD'
By default, the install script will install the "correct" version of Aegir By default, the install script will install the "correct" version of Aegir
(ie. if it was downloaded through git, it will install the version from the (ie. if it was downloaded through git, it will install the version from the
...@@ -265,10 +265,11 @@ Note you must run the above command as root or prefix with sudo. ...@@ -265,10 +265,11 @@ Note you must run the above command as root or prefix with sudo.
Be sure to change 'aegir.example.com' to match the URI of your site. Be sure to change 'aegir.example.com' to match the URI of your site.