Removed slaves domain settings, now generated on the fly

This commit is contained in:
Marc 2014-10-03 17:37:36 +00:00
parent 9ecfc8d4dd
commit 6a3e3f637c
14 changed files with 88 additions and 44 deletions

View file

@ -15,7 +15,7 @@ Note `*` _for sustancial progress_
4. [ ] Data model, crazy input validation, admin and REST interfaces, permissions, unit and functional tests, service management, migration scripts and documentation of:
1. [x] PHP/static Web applications
1. [x] Websites with Apache
2. [ ] *FTP/rsync/scp/shell system accounts
2. [x] FTP/rsync/scp/shell system accounts
2. [ ] *Databases and database users with MySQL
1. [ ] *Mail accounts, aliases, forwards with Postfix and Dovecot
1. [x] DNS with Bind
@ -63,4 +63,4 @@ Note `*` _for sustancial progress_
2. [ ] REST API functionality for superusers
3. [ ] Responsive user interface, based on a JS framework.
4. [ ] Full documentation
5. [ ] [http://www.ansible.com/home](Ansible) orchestration method, which synchronize the whole service config everytime instead of incremental changes.
5. [ ] [Ansible](http://www.ansible.com/home) orchestration method, which synchronizes the whole service config everytime instead of incremental changes.

View file

@ -1,9 +1,11 @@
import textwrap
from django.utils.translation import ugettext_lazy as _
from . import settings
from orchestra.apps.orchestration import ServiceController
from . import settings
class Bind9MasterDomainBackend(ServiceController):
verbose_name = _("Bind9 master domain")
@ -52,20 +54,31 @@ class Bind9MasterDomainBackend(ServiceController):
""" reload bind if needed """
self.append('[[ $UPDATED == 1 ]] && service bind9 reload')
def get_servers(self, domain, backend):
from orchestra.apps.orchestration.models import Route, BackendOperation as Operation
operation = Operation(backend=backend, action='save', instance=domain)
servers = []
for server in Route.get_servers(operation):
servers.append(server.get_ip())
return servers
def get_context(self, domain):
context = {
'name': domain.name,
'zone_path': settings.DOMAINS_ZONE_PATH % {'name': domain.name},
'subdomains': domain.subdomains.all(),
'banner': self.get_banner(),
'slaves': '; '.join(self.get_servers(domain, Bind9SlaveDomainBackend)),
}
context.update({
'conf_path': settings.DOMAINS_MASTERS_PATH,
'conf': 'zone "%(name)s" {\n'
' // %(banner)s\n'
' type master;\n'
' file "%(zone_path)s";\n'
'};\n' % context
'conf': textwrap.dedent("""
zone "%(name)s" {
// %(banner)s
type master;
file "%(zone_path)s";
allow-transfer { %(slaves)s; };
};""" % context)
})
return context
@ -91,15 +104,19 @@ class Bind9SlaveDomainBackend(Bind9MasterDomainBackend):
def get_context(self, domain):
context = {
'name': domain.name,
'masters': '; '.join(settings.DOMAINS_MASTERS),
'subdomains': domain.subdomains.all()
'banner': self.get_banner(),
'subdomains': domain.subdomains.all(),
'masters': '; '.join(self.get_servers(domain, Bind9MasterDomainBackend)),
}
context.update({
'conf_path': settings.DOMAINS_SLAVES_PATH,
'conf': 'zone "%(name)s" {\n'
' type slave;\n'
' file "%(name)s";\n'
' masters { %(masters)s; };\n'
'};\n' % context
'conf': textwrap.dedent("""
zone "%(name)s" {
// %(banner)s
type slave;
file "%(name)s";
masters { %(masters)s; };
allow-notify { %(masters)s; };
};""" % context)
})
return context

View file

@ -124,6 +124,7 @@ class Domain(models.Model):
top = self.get_top()
if top:
self.top = top
self.account_id = self.account_id or top.account_id
else:
update = True
super(Domain, self).save(*args, **kwargs)
@ -132,7 +133,7 @@ class Domain(models.Model):
for domain in domains.filter(name__endswith=self.name):
domain.top = self
domain.save(update_fields=['top'])
self.subdomains.update(account=self.account)
self.subdomains.update(account_id=self.account_id)
def get_top(self):
split = self.name.split('.')

View file

@ -4,48 +4,61 @@ from django.conf import settings
DOMAINS_DEFAULT_NAME_SERVER = getattr(settings, 'DOMAINS_DEFAULT_NAME_SERVER',
'ns.orchestra.lan')
DOMAINS_DEFAULT_HOSTMASTER = getattr(settings, 'DOMAINS_DEFAULT_HOSTMASTER',
'hostmaster@orchestra.lan')
DOMAINS_DEFAULT_TTL = getattr(settings, 'DOMAINS_DEFAULT_TTL', '1h')
DOMAINS_DEFAULT_REFRESH = getattr(settings, 'DOMAINS_DEFAULT_REFRESH', '1d')
DOMAINS_DEFAULT_RETRY = getattr(settings, 'DOMAINS_DEFAULT_RETRY', '2h')
DOMAINS_DEFAULT_EXPIRATION = getattr(settings, 'DOMAINS_DEFAULT_EXPIRATION', '4w')
DOMAINS_DEFAULT_MIN_CACHING_TIME = getattr(settings, 'DOMAINS_DEFAULT_MIN_CACHING_TIME', '1h')
DOMAINS_ZONE_PATH = getattr(settings, 'DOMAINS_ZONE_PATH', '/etc/bind/master/%(name)s')
DOMAINS_MASTERS_PATH = getattr(settings, 'DOMAINS_MASTERS_PATH', '/etc/bind/named.conf.local')
DOMAINS_SLAVES_PATH = getattr(settings, 'DOMAINS_SLAVES_PATH', '/etc/bind/named.conf.local')
DOMAINS_MASTERS = getattr(settings, 'DOMAINS_MASTERS', ['10.0.3.13'])
DOMAINS_CHECKZONE_BIN_PATH = getattr(settings, 'DOMAINS_CHECKZONE_BIN_PATH',
'/usr/sbin/named-checkzone -i local')
DOMAINS_CHECKZONE_PATH = getattr(settings, 'DOMAINS_CHECKZONE_PATH', '/dev/shm')
DOMAINS_DEFAULT_A = getattr(settings, 'DOMAINS_DEFAULT_A', '10.0.3.13')
DOMAINS_DEFAULT_MX = getattr(settings, 'DOMAINS_DEFAULT_MX', (
'10 mail.orchestra.lan.',
'10 mail2.orchestra.lan.',
))
DOMAINS_DEFAULT_NS = getattr(settings, 'DOMAINS_DEFAULT_NS', (
'ns1.orchestra.lan.',
'ns2.orchestra.lan.',
))
DOMAINS_FORBIDDEN = getattr(settings, 'DOMAINS_FORBIDDEN',
# This setting prevents users from providing random domain names, i.e. google.com
# You can generate a 5K forbidden domains list from Alexa's top 1M
# wget http://s3.amazonaws.com/alexa-static/top-1m.csv.zip -O /tmp/top-1m.csv.zip
# unzip -p /tmp/top-1m.csv.zip | head -n 5000 | sed "s/^.*,//" > forbidden_domains.list
# '%(site_root)s/forbidden_domains.list')
'')

View file

@ -26,7 +26,6 @@ class DomainTestMixin(object):
def setUp(self):
djsettings.DEBUG = True
settings.DOMAINS_MASTERS = [self.MASTER_SERVER_ADDR]
super(DomainTestMixin, self).setUp()
self.domain_name = 'orchestra%s.lan' % random_ascii(10)
self.domain_records = (

View file

@ -1,17 +1,18 @@
from django.test import TestCase
from orchestra.utils.tests import BaseTestCase
from ..models import Domain
class DomainTests(TestCase):
def setUp(self):
self.domain = Domain.objects.create(name='rostrepalid.org')
class DomainTest(BaseTestCase):
def test_top_relation(self):
account = self.create_account()
domain = Domain.objects.create(name='rostrepalid.org', account=account)
Domain.objects.create(name='www.rostrepalid.org')
Domain.objects.create(name='mail.rostrepalid.org')
def test_top_relation(self):
self.assertEqual(2, len(self.domain.subdomains.all()))
self.assertEqual(2, len(domain.subdomains.all()))
def test_render_zone(self):
print self.domain.render_zone()
account = self.create_account()
domain = Domain.objects.create(name='rostrepalid.org', account=account)
domain.render_zone()

View file

@ -88,18 +88,16 @@ class BackendLogAdmin(admin.ModelAdmin):
)
list_display_links = ('id', 'backend')
list_filter = ('state', 'backend')
date_hierarchy = 'updated_at'
inlines = [BackendOperationInline]
fields = [
'backend', 'server_link', 'state', 'mono_script', 'mono_stdout',
'mono_stderr', 'mono_traceback', 'exit_code', 'task_id', 'display_created',
'display_updated', 'execution_time'
'execution_time'
]
readonly_fields = fields
server_link = admin_link('server')
display_updated = admin_date('updated_at')
display_created = admin_date('created_at')
display_created = admin_date('created_at', short_description=_("Created"))
display_state = admin_colored('state', colors=STATE_COLORS)
mono_script = display_mono('script')
mono_stdout = display_mono('stdout')

View file

@ -52,9 +52,10 @@ def message_user(request, logs):
_('{errors} out of {total} <a href="{url}">banckends</a> has fail to execute.'),
_('{errors} out of {total} <a href="{url}">banckends</a> have fail to execute.'),
errors)
messages.error(request, mark_safe(msg.format(errors=errors, total=total, url=url)))
else:
msg = ungettext(
_('{total} <a href="{url}">banckend</a> has been executed.'),
_('{total} <a href="{url}">banckends</a> have been executed.'),
total)
messages.warning(request, mark_safe(msg.format(errors=errors, total=total, url=url)))
messages.success(request, mark_safe(msg.format(total=total, url=url)))

View file

@ -1,8 +1,11 @@
import socket
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import ugettext_lazy as _
from orchestra.core.validators import validate_ip_address, ValidationError
from orchestra.models.fields import NullableCharField
from orchestra.utils.apps import autodiscover
@ -27,6 +30,16 @@ class Server(models.Model):
if self.address:
return self.address
return self.name
def get_ip(self):
if self.address:
return self.address
try:
validate_ip_address(self.name)
except ValidationError:
return socket.gethostbyname(self.name)
else:
return self.name
class BackendLog(models.Model):

View file

@ -1,7 +1,6 @@
import ftplib
import os
import re
import socket
from functools import partial
import paramiko

View file

@ -159,7 +159,8 @@ function install_requirements () {
selenium \
xvfbwrapper \
freezegun \
coverage"
coverage \
orchestra-orm==dev"
fi
# Make sure locales are in place before installing postgres

View file

@ -29,6 +29,14 @@ def validate_ipv6_address(value):
raise ValidationError(msg)
def validate_ip_address(value):
msg = _("%s is not a valid IP address") % value
try:
ip = IP(value)
except:
raise ValidationError(msg)
def validate_name(value):
"""
A single non-empty line of free-form text with no whitespace.

View file

@ -12,13 +12,3 @@ Bind9 Master and Slave
mkdir /etc/bind/master
chown bind.bind /etc/bind/master
```
2. Allow zone transfer on master by adding the following line to `named.conf.options`
```bash
allow-transfer { slave-ip; };
```
3. Addlow notifications on the slave server by adding the following line to `named.conf.options`
```bash
allow-notify { master-ip; };
```

View file

@ -1 +1,4 @@
MySQL
=====
apt-get install mysql-server