1
0
Fork 0

add PasteDeploy dependency, remove pudge dependency

* upgrade migrate repository structure (fixes #32, #27)
 * switch to PasteDeploy (fixes #31)
 * update for SQLAlchemy 0.5 compatibility
 * add python-gnutls dependency (addresses #35)
This commit is contained in:
Jan Dittberner 2009-07-19 01:03:23 +02:00
parent 483c1f9038
commit 222b35b033
24 changed files with 247 additions and 177 deletions

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 by Jan Dittberner.
# Copyright (C) 2007, 2008, 2009 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
@ -25,7 +25,6 @@ import logging
import tempfile
from gnuviechadmin.exceptions import MissingFieldsError
from gnuviechadmin.backend.settings import config
from gnuviechadmin.util import gpgmail
from subprocess import Popen, PIPE
from sqlalchemy.orm import object_mapper
@ -34,9 +33,10 @@ from sqlalchemy.orm import object_mapper
class BackendEntity(object):
"""This is the abstract base class for all backend entity classes."""
def __init__(self, delegateto, verbose = False):
def __init__(self, config, delegateto, verbose = False):
self.logger = logging.getLogger("%s.%s" % (
self.__class__.__module__, self.__class__.__name__))
self.config = config
self.delegateto = delegateto
self.verbose = verbose
@ -49,7 +49,7 @@ class BackendEntity(object):
subprocess."""
self.logger.debug("sucommand called: %s (pipedata=%s)", cmdline,
str(pipedata))
suwrapper = config.get('common', 'suwrapper')
suwrapper = self.config['suwrapper']
toexec = "%s %s" % (suwrapper, cmdline)
if pipedata:
pipeproc = Popen(toexec, shell = True, stdin=PIPE)
@ -72,7 +72,7 @@ class BackendEntity(object):
"""Executes multiple commands as root and pipes the output of
the commands to the input of the next commands."""
self.logger.debug("supipe called: %s", " | ".join(cmdlines))
suwrapper = config.get('common', 'suwrapper')
suwrapper = self.config['suwrapper']
predecessor = None
for cmdline in cmdlines:
toexec = "%s %s" % (suwrapper, cmdline)
@ -89,7 +89,7 @@ class BackendEntity(object):
"""This method sends a mail with the given text and subject
and signs it usign GnuPG. If a public key of the recipient is
available the mail is encrypted."""
gpgmail.send_mail(subject, text)
gpgmail.send_mail(self.config, subject, text)
def validate(self):
"""Validates whether all mandatory fields of the entity have

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 by Jan Dittberner.
# Copyright (C) 2007, 2008, 2009 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
@ -21,38 +21,41 @@
"""This module defines the BackendEntityHandler class."""
import logging
from sqlalchemy.orm import create_session
from sqlalchemy.orm import create_session, mapper, relation
from gnuviechadmin.backend.tables import dbsetup
class BackendEntityHandler(object):
"""This class is a handler for BackendEntity instances."""
def __init__(self, entityclass, toclass, verbose = False):
def __init__(self, entityclass, toclass, config, verbose = False):
"""Initialize the handler with a specific entity class,
transfer object class and verbosity flag."""
self.logger = logging.getLogger("%s.%s" % (
self.__class__.__module__, self.__class__.__name__))
dbsetup(config)
self.entityclass = entityclass
self.toclass = toclass
self.config = config
self.verbose = verbose
def create(self, **kwargs):
"""Create a new entity of the managed type with the fields set
to the values in kwargs."""
self.logger.debug("create with params %s", str(kwargs))
delegate = self.toclass(self.config, **kwargs)
entity = self.entityclass(self.config, delegate, self.verbose)
sess = create_session()
transaction = sess.create_transaction()
delegate = self.toclass(**kwargs)
entity = self.entityclass(delegate, self.verbose)
try:
sess.save(delegate)
sess.begin()
sess.add(delegate)
sess.flush()
sess.refresh(delegate)
entity.create_hook(sess)
sess.flush()
transaction.commit()
sess.commit()
except:
transaction.rollback()
sess.rollback()
self.logger.exception("Exception in create.")
raise
@ -65,7 +68,7 @@ class BackendEntityHandler(object):
allentities = query.filter_by(**kwargs).all()
else:
allentities = query.all()
return [self.entityclass(entity, self.verbose) \
return [self.entityclass(self.config, entity, self.verbose) \
for entity in allentities]
def delete(self, pkvalue):
@ -73,19 +76,18 @@ class BackendEntityHandler(object):
specified primary key value."""
self.logger.debug("delete with primary key %s", str(pkvalue))
sess = create_session()
transaction = sess.create_transaction()
try:
sess.begin()
tobj = sess.query(self.toclass).get(pkvalue)
if tobj:
entity = self.entityclass(tobj, self.verbose)
entity = self.entityclass(self.config, tobj, self.verbose)
self.logger.info("delete %s", str(entity))
if self.verbose:
print "delete %s" % (str(entity))
entity.delete_hook(sess)
sess.delete(tobj)
sess.flush()
transaction.commit()
sess.commit()
except Exception:
transaction.rollback()
sess.rollback()
self.logger.exception("Exception in delete.")
raise

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 by Jan Dittberner.
# Copyright (C) 2007, 2008, 2009 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
@ -19,16 +19,15 @@
#
# Version: $Id$
from sqlalchemy.orm import object_mapper, mapper, relation
from tables import *
from sqlalchemy.orm import object_mapper
class BackendTo(object):
"""Backend transfer object class."""
def __init__(self, **kwargs):
def __init__(self, config, **kwargs):
for (key, value) in kwargs.items():
self.__setattr__(key, value)
self.__setattr__(key, unicode(value, 'utf8'))
def __repr__(self, **kwargs):
if 'verbose' in kwargs and kwargs['verbose']:
@ -64,12 +63,3 @@ class Domain(BackendTo):
class Record(BackendTo):
"""Transfer object class for DNS domain records."""
_shortkeys = ("recordid", "domainid", "name", "type", "content")
client_mapper = mapper(Client, client_table, {
'sysusers': relation(Sysuser, backref = 'client')})
sysuser_mapper = mapper(Sysuser, sysuser_table, {
'domains': relation(Domain, backref = 'sysuser')})
domain_mapper = mapper(Domain, domain_table, {
'records': relation(Record, cascade = 'all', backref = 'domain')})
record_mapper = mapper(Record, record_table)

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 by Jan Dittberner.
# Copyright (C) 2007, 2008, 2009 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
@ -20,8 +20,7 @@
# Version: $Id$
"""This module defines the ClientEntity class."""
from gnuviechadmin.backend.settings import config, get_template, \
get_template_string
from gnuviechadmin.backend.settings import get_template, get_template_string
from gnuviechadmin.exceptions import CannotDeleteError
from gnuviechadmin.backend.BackendTo import Client
from gnuviechadmin.backend.BackendEntity import BackendEntity
@ -31,9 +30,9 @@ from gnuviechadmin.backend.BackendEntityHandler import BackendEntityHandler
class ClientEntity(BackendEntity):
"""Entity class for clients."""
def __init__(self, delegate, verbose = False, **kwargs):
def __init__(self, config, delegate, verbose = False, **kwargs):
"""Initializes the client entity instance."""
BackendEntity.__init__(self, delegate, verbose)
BackendEntity.__init__(self, config, delegate, verbose)
for (key, value) in kwargs.items():
self.__setattr__(key, value)
if not self.delegateto.country:
@ -42,8 +41,8 @@ class ClientEntity(BackendEntity):
def _client_mail(self):
"""Mails a summary about the creation of the client."""
text = get_template(config.get('common', 'mailtemplates'),
config.get('client', 'create.mail')).substitute({
text = get_template(self.config['mailtemplates'],
self.config['client.create.mail']).substitute({
'firstname': self.delegateto.firstname,
'lastname': self.delegateto.lastname,
'email': self.delegateto.email,
@ -52,11 +51,11 @@ class ClientEntity(BackendEntity):
'city': self.delegateto.city,
'phone': self.delegateto.phone})
subject = get_template_string(
config.get('client', 'create_subject')).substitute({
self.config['client.create_subject']).substitute({
'firstname': self.delegateto.firstname,
'lastname': self.delegateto.lastname})
self.send_mail(subject, text)
def create_hook(self, session):
"""Actions to perform when a client is created."""
self._client_mail()
@ -72,11 +71,12 @@ class ClientEntity(BackendEntity):
def _get_default_country(self):
"""Gets the default country."""
return config.get('common', 'defaultcountry')
return self.config['client.defaultcountry']
class ClientHandler(BackendEntityHandler):
"""BackendEntityHandler for Client entities."""
def __init__(self, verbose = False):
BackendEntityHandler.__init__(self, ClientEntity, Client, verbose)
def __init__(self, config, verbose = False):
BackendEntityHandler.__init__(self, ClientEntity, Client, config,
verbose)

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 by Jan Dittberner.
# Copyright (C) 2007, 2008, 2009 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
@ -25,32 +25,13 @@ This module handles all central configuration of Gnuviech Admin. It
parses configuration files and provides functions for reading
templates."""
import ConfigParser
import os
from string import Template
# global settings which must not be user configurable
required_version = 3
# load user configuration
config = ConfigParser.ConfigParser()
config.readfp(open('gnuviechadmin/defaults.cfg'))
config.read(['gnuviechadmin/gva.cfg', os.path.expanduser('~/.gva.cfg')])
dbschema = None
if config.get('database', 'uri').startswith('postgres://'):
dbschema = 'gva'
def get_template_dir(dirname):
"""Returns the template directory for the given directory."""
templatepath = config.get('common', 'templatedir')
return os.path.join(templatepath, dirname)
def get_template(dirname, filename):
def get_template(templatedir, filename):
"""Returns the template data from the given template file."""
templatefile = file(os.path.join(get_template_dir(dirname),
templatefile = file(os.path.join(templatedir,
filename))
templatedata = templatefile.read()
return Template(templatedata.decode('utf_8'))

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, 2008 by Jan Dittberner.
# Copyright (C) 2007, 2008, 2009 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
@ -19,57 +19,69 @@
#
# Version: $Id$
from sqlalchemy import *
from sqlalchemy import MetaData, Table
from sqlalchemy.orm import mapper, relation
from sqlalchemy.exceptions import NoSuchTableError
import sys
import migrate.versioning.api
from settings import *
import logging
from gnuviechadmin.backend.BackendTo import Client, Sysuser, Domain, Record
try:
dbversion = migrate.versioning.api.db_version(
config.get('database', 'uri'),
config.get('database', 'repository'))
if dbversion < required_version:
print("""Database version is %d but required version is %d. Trying
automatic upgrade.""" %
(dbversion, required_version))
def dbsetup(config):
logger = logging.getLogger(__name__)
required_version = int(config['migrate.required_version'])
try:
dbversion = migrate.versioning.api.db_version(
config['sqlalchemy.uri'], config['database.repository'])
if dbversion < required_version:
logger.info("""Database version is %d but required version \
is %d. Trying automatic upgrade.""" % (dbversion, required_version))
try:
migrate.versioning.api.upgrade(
config['sqlalchemy.uri'], config['database.repository'],
required_version)
except e:
logger.error("Automatic upgrade failed.", e)
raise
elif dbversion > required_version:
logger.error("""Database version is %d which is higher than \
the required version %d. I cannot handle this situation without possible \
data loss.""" % (dbversion, required_version))
sys.exit(1)
except NoSuchTableError, nste:
logger.info("""The database is not versioned. \
Trying automatic versioning.""")
try:
migrate.versioning.api.version_control(
config['sqlalchemy.uri'], config['database.repository'])
migrate.versioning.api.upgrade(
config.get('database', 'uri'),
config.get('database', 'repository'),
config['sqlalchemy.uri'], config['database.repository'],
required_version)
except:
print "Automatic upgrade failed."
logger.error("Automatic setup failed.")
raise
elif dbversion > required_version:
print("""Database version is %d which is higher than the required
version %d. I cannot handle this situation without possible data loss.""" %
(dbversion, required_version))
sys.exit(1)
except NoSuchTableError, nste:
print """The database is not versioned. Trying automatic versioning."""
try:
migrate.versioning.api.version_control(
config.get('database', 'uri'),
config.get('database', 'repository'))
migrate.versioning.api.upgrade(
config.get('database', 'uri'),
config.get('database', 'repository', required_version))
except:
print "Automatic setup failed."
raise
meta = MetaData(config.get('database', 'uri'))
#meta.engine.echo = True
client_table = Table('client', meta, schema = dbschema, autoload = True)
sysuser_table = Table('sysuser', meta, schema = dbschema, autoload = True)
domain_table = Table('domain', meta, schema = dbschema, autoload = True)
record_table = Table('record', meta, schema = dbschema, autoload = True)
supermaster_table = Table('supermaster', meta, schema = dbschema,
autoload = True)
mailaccount_table = Table('mailaccount', meta, schema = dbschema,
autoload = True)
mailaddress_table = Table('mailaddress', meta, schema = dbschema,
autoload = True)
mailtarget_table = Table('mailtarget', meta, schema = dbschema,
autoload = True)
meta = MetaData(config['sqlalchemy.uri'])
meta.bind.engine.echo = config['sqlalchemy.echo']
dbschema = None
if 'database.schema' in config:
dbschema = config['database.schema']
(client_table, sysuser_table, domain_table, \
record_table, supermaster_table, mailaccount_table, \
mailaddress_table, mailtarget_table) = \
[Table(tabname, meta, schema = dbschema,
autoload = True) for tabname in \
('client', 'sysuser', 'domain', 'record',
'supermaster', 'mailaccount', 'mailaddress', 'mailtarget')]
client_mapper = mapper(Client, client_table, {
'sysusers': relation(Sysuser, backref = 'client')})
sysuser_mapper = mapper(Sysuser, sysuser_table, {
'domains': relation(Domain, backref = 'sysuser')})
domain_mapper = mapper(Domain, domain_table, {
'records': relation(Record, cascade = 'all',
backref = 'domain')})
record_mapper = mapper(Record, record_table)