# -*- coding: utf-8 -*- # # Copyright (C) 2007, 2008 by Jan Dittberner. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. # # Version: $Id$ """This module defines the code for handling domains.""" import datetime import os from gnuviechadmin.exceptions import ValidationFailedError from gnuviechadmin.backend.settings import config, get_template, \ get_template_dir, get_template_string from gnuviechadmin.backend.BackendTo import Record, Domain from gnuviechadmin.backend.BackendEntity import BackendEntity from gnuviechadmin.backend.BackendEntityHandler import BackendEntityHandler class DomainEntity(BackendEntity): """Entity class for DNS domains.""" # the valid domain types _valid_domain_types = ("MASTER", "SLAVE") def __init__(self, delegate, verbose = False, **kwargs): """Initializes the DomainEntity instance. `delegate` is the corresponding database object. If `verbose` is `True` verbose logging is turned on. """ BackendEntity.__init__(self, delegate, verbose) self.ns1 = None self.ns2 = None self.mxrr = None self.ipaddr = None for (key, value) in kwargs.items(): self.__setattr__(key, value) if not self.delegateto.type: self.delegateto.type = self.getdefaultdomaintype() if not self.ns1: self.ns1 = config.get('domain', 'defaultns1') if not self.ns2: self.ns2 = config.get('domain', 'defaultns2') if not self.mxrr: self.mxrr = config.get('domain', 'defaultmx') if not self.ipaddr: self.ipaddr = config.get('domain', 'defaultip') self.delegateto.type = self.delegateto.type.upper() self.validate() def getdefaultdomaintype(self): """Returns the default domain type.""" return self._valid_domain_types[0] def validate(self): """Validates the consistency if the entity instance and dependent entities.""" BackendEntity.validate(self) if not self.delegateto.type in self._valid_domain_types: raise ValidationFailedError( self, "invalid domain type %s" % (self.delegateto.type)) if self.delegateto.type == 'SLAVE' and not self.delegateto.master: raise ValidationFailedError( self, "you have to specify a master for slave domains.") if self.delegateto.type == 'MASTER': if not self.ns1 or not self.ns2: raise ValidationFailedError( self, "two nameservers must be specified.") if not self.mxrr: raise ValidationFailedError( self, "a primary mx host must be specified.") def _getnewserial(self, oldserial = None): """Gets a new zone serial number for the DNS domain entity.""" current = datetime.datetime.now() datepart = "%04d%02d%02d" % \ (current.year, current.month, current.day) retval = None if oldserial: if str(oldserial)[:len(datepart)] == datepart: retval = oldserial + 1 if not retval: retval = int("%s01" % (datepart)) return retval def _getnewsoa(self): """Gets a new SOA record for the DNS domain entity.""" return '%s %s %d %d %d %d %d' % \ (self.ns1, config.get('domain', 'defaulthostmaster'), self._getnewserial(), config.getint('domain', 'defaultrefresh'), config.getint('domain', 'defaultretry'), config.getint('domain', 'defaultexpire'), config.getint('domain', 'defaultminimumttl')) def update_serial(self, session): """Updates the serial of the domain.""" query = session.query(Record) soarecord = query.get_by(Record.c.type == 'SOA', Record.c.domainid == self.delegateto.domainid) parts = soarecord.content.split(" ") parts[2] = str(self._getnewserial(int(parts[2]))) soarecord.content = " ".join(parts) session.save(soarecord) session.flush() def _get_vhost_dir(self): """Gets the directory name for the Apache VirtualHost of the domain.""" return os.path.join(self.delegateto.sysuser.home, self.delegateto.name, config.get('domain', 'htdir')) def _get_log_dir(self): """Gets the Apache log file directory for the domain.""" return os.path.join(config.get('domain', 'logpath'), self.delegateto.name) def _get_stats_dir(self): """Gets the statistics dir for the domain.""" return os.path.join(config.get('domain', 'statspath'), self.delegateto.name) def _create_vhost_dir(self): """Creates the Apache VirtualHost directory for the domain.""" vhostdir = self._get_vhost_dir() self.logger.debug("creating virtual host dir %s" % (vhostdir)) cmd = 'mkdir -p "%s"' % (vhostdir) self.sucommand(cmd) for tpl in [tpl for tpl in os.listdir( get_template_dir(config.get('domain', 'htdocstemplate'))) \ if not tpl.startswith('.')]: self.logger.debug("processing template %s" % (tpl)) template = get_template(config.get('domain', 'htdocstemplate'), tpl) template = template.substitute({ 'domain': self.delegateto.name}) self.write_to_file(os.path.join(vhostdir, tpl), template) cmd = 'chown -R %(username)s:%(group)s "%(dir)s"' % { 'username': self.delegateto.sysuser.username, 'group': config.get('sysuser', 'defaultgroup'), 'dir': vhostdir} self.sucommand(cmd) def _create_log_dir(self): """Creates the Apache log file directory for the domain.""" cmd = 'mkdir -p "%s"' % (self._get_log_dir()) self.sucommand(cmd) def _get_auth_userfile(self): """Gets the file name of the password file for statistic logins for the domain.""" authdir = config.get('domain', 'authdir') if not os.path.isdir(authdir): cmd = 'mkdir -p "%s"' % (authdir) self.sucommand(cmd) return os.path.join(authdir, '%s.passwd' % (self.delegateto.name)) def _create_stats_dir(self): """Creates the statistics directory for the domain and sets Apache .htaccess password protection.""" statsdir = self._get_stats_dir() authfile = self._get_auth_userfile() cmd = 'htpasswd -m -c -b "%s" "%s" "%s"' % ( authfile, self.delegateto.sysuser.username, self.delegateto.sysuser.clearpass) self.sucommand(cmd) cmd = 'mkdir -p "%s"' % (statsdir) self.sucommand(cmd) template = get_template(config.get('domain', 'conftemplates'), config.get('domain', 'statshtaccesstemplate')) template = template.substitute({ 'domain': self.delegateto.name, 'userfile': authfile}) self.write_to_file(os.path.join(self._get_stats_dir(), '.htaccess'), template) def _create_stats_conf(self): """Creates the modlogan statistics configuration for the domain.""" modlogandir = os.path.join(config.get('domain', 'modlogandir'), self.delegateto.sysuser.username) cmd = 'mkdir -p "%s"' % (modlogandir) self.sucommand(cmd) template = get_template(config.get('domain', 'conftemplates'), config.get('domain', 'modlogantemplate')) template = template.substitute({ 'statsdir': self._get_stats_dir(), 'logdir': self._get_log_dir(), 'domain': self.delegateto.name, 'domainesc': self.delegateto.name.replace('.', '\.')}) self.write_to_file(os.path.join(modlogandir, self.delegateto.name + '.conf'), template) def _create_apache_conf(self): """Creates the Apache configuration file for the domain.""" template = get_template(config.get('domain', 'conftemplates'), config.get('domain', 'apachetemplate')) template = template.substitute({ 'ipaddr': self.ipaddr, 'statsdir': self._get_stats_dir(), 'logdir': self._get_log_dir(), 'domain': self.delegateto.name, 'docroot': self._get_vhost_dir()}) self.write_to_file(os.path.join(config.get('domain', 'sitesdir'), self.delegateto.name), template) def _mail_domain(self): """Mail a summary of the domain data.""" template = get_template(config.get('common', 'mailtemplates'), config.get('domain', 'create.mail')) text = template.substitute({ 'sysuser': self.delegateto.sysuser.username, 'domain': self.delegateto.name, 'docroot': self._get_vhost_dir(), 'statspass': self.delegateto.sysuser.clearpass}) template = get_template_string(config.get('domain', 'create_subject')) subject = template.substitute({ 'domain': self.delegateto.name}) self.send_mail(subject, text) def create_hook(self, session): """Hook for the creation of the domain. This method is called by `gnuviechadmin.backend.BackendEntityHandler.create()`. """ self.delegateto.records.append(Record( name = self.delegateto.name, type = 'SOA', content = self._getnewsoa(), ttl = config.getint('domain', 'defaultttl'))) self.delegateto.records.append(Record( name = self.delegateto.name, type = 'NS', content = self.ns1, ttl = config.getint('domain', 'defaultttl'))) self.delegateto.records.append(Record( name = self.delegateto.name, type = 'NS', content = self.ns2, ttl = config.getint('domain', 'defaultttl'))) self.delegateto.records.append(Record( name = self.delegateto.name, type = 'MX', content = self.mxrr, ttl = config.getint('domain', 'defaultttl'), prio = config.getint('domain', 'defaultmxprio'))) self.delegateto.records.append(Record( name = self.delegateto.name, type = 'A', content = self.ipaddr, ttl = config.getint('domain', 'defaultttl'))) self.delegateto.records.append(Record( name = "www.%s" % (self.delegateto.name), type = 'A', content = self.ipaddr, ttl = config.getint('domain', 'defaultttl'))) session.save_or_update(self.delegateto) session.flush() self._create_vhost_dir() self._create_log_dir() self._create_stats_dir() self._create_stats_conf() self._create_apache_conf() self._mail_domain() def _delete_apache_conf(self): """Deletes the Apache configuration file for the domain.""" cmd = 'a2dissite %s' % (self.delegateto.name) self.sucommand(cmd) cmd = 'rm "%s"' % (os.path.join(config.get('domain', 'sitesdir'), self.delegateto.name)) self.sucommand(cmd) def _delete_stats_conf(self): """Deletes the modlogan stastics configuration for the domain.""" cmd = 'rm "%s"' % (os.path.join(config.get('domain', 'modlogandir'), self.delegateto.sysuser.username, self.delegateto.name + '.conf')) self.sucommand(cmd) def _archive_stats_dir(self): """Archives the statistics directory for the domain.""" archive = os.path.join(self.delegateto.sysuser.home, '%(domain)s-stats.tar.gz' % { 'domain': self.delegateto.name}) cmd = 'tar czf "%(archive)s" --directory="%(statsbase)s" ' + \ '"%(statsdir)s"' % { 'archive': archive, 'statsbase': config.get('domain', 'statspath'), 'statsdir': self.delegateto.name} self.sucommand(cmd) cmd = 'rm -r "%(statsdir)s"' % { 'statsdir': self._get_stats_dir()} self.sucommand(cmd) cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % { 'username': self.delegateto.sysuser.username, 'group': config.get('sysuser', 'defaultgroup'), 'archive': archive} self.sucommand(cmd) def _archive_log_dir(self): """Archives the Apache log file directory for the domain.""" archive = os.path.join(self.delegateto.sysuser.home, '%(domain)s-logs.tar.gz' % { 'domain': self.delegateto.name}) cmd = 'tar czf "%(archive)s" --directory="%(logbase)s" ' + \ '"%(logdir)s"' % { 'archive': archive, 'logbase': config.get('domain', 'logpath'), 'logdir': self.delegateto.name} self.sucommand(cmd) cmd = 'rm -r "%(logdir)s"' % { 'logdir': self._get_log_dir()} self.sucommand(cmd) cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % { 'username': self.delegateto.sysuser.username, 'group': config.get('sysuser', 'defaultgroup'), 'archive': archive} self.sucommand(cmd) def _archive_vhost_dir(self): """Archives the Apache VirtualHost directory for the domain.""" archive = os.path.join(self.delegateto.sysuser.home, '%(domain)s-vhost.tar.gz' % { 'domain': self.delegateto.name}) cmd = 'tar czf "%(archive)s" --directory="%(vhostbase)s" ' + \ '"%(vhostdir)s"' % { 'archive': archive, 'vhostbase': self.delegateto.sysuser.home, 'vhostdir': self.delegateto.name} self.sucommand(cmd) cmd = 'rm -r "%(vhostdir)s"' % { 'vhostdir': os.path.join(self.delegateto.sysuser.home, self.delegateto.name)} self.sucommand(cmd) cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % { 'username': self.delegateto.sysuser.username, 'group': config.get('sysuser', 'defaultgroup'), 'archive': archive} self.sucommand(cmd) def delete_hook(self, session): """Deletes domain related files and directories. This method is called by `BackendEntityHandler.delete()`. """ self._delete_apache_conf() self._delete_stats_conf() self._archive_stats_dir() self._archive_log_dir() self._archive_vhost_dir() class DomainHandler(BackendEntityHandler): """BackendEntityHandler for Domain entities.""" def __init__(self, verbose = False): """Initialize the DomainHandler.""" BackendEntityHandler.__init__(self, DomainEntity, Domain, verbose)