Removed slaves domain settings, now generated on the fly
This commit is contained in:
parent
9ecfc8d4dd
commit
6a3e3f637c
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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('.')
|
||||
|
|
|
@ -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')
|
||||
'')
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import ftplib
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
from functools import partial
|
||||
|
||||
import paramiko
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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; };
|
||||
```
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
MySQL
|
||||
=====
|
||||
|
||||
apt-get install mysql-server
|
Loading…
Reference in a new issue