server/__init__.py
author Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
Wed, 05 Nov 2008 15:52:50 +0100
changeset 0 b97547f5f1fa
child 480 71376fda9b36
permissions -rw-r--r--
Showtime !

"""Server subcube of cubicweb : defines objects used only on the server
(repository) side

This module contains functions to initialize a new repository.

:organization: Logilab
:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"

import sys
from os.path import join, exists

from logilab.common.modutils import LazyObject

# server debugging flag
DEBUG = False

def init_repository(config, interactive=True, drop=False, vreg=None):
    """initialise a repository database by creating tables add filling them
    with the minimal set of entities (ie at least the schema, base groups and
    a initial user)
    """
    from glob import glob
    from cubicweb.schema import BASEGROUPS
    from cubicweb.dbapi import in_memory_cnx
    from cubicweb.server.repository import Repository
    from cubicweb.server.utils import manager_userpasswd
    from cubicweb.server.sqlutils import sqlexec, sqlschema, sqldropschema
    # configuration to avoid db schema loading and user'state checking
    # on connection
    read_application_schema = config.read_application_schema
    bootstrap_schema = config.bootstrap_schema
    config.read_application_schema = False
    config.creating = True
    config.bootstrap_schema = True
    config.consider_user_state = False
    config.set_language = False
    # only enable the system source at initialization time + admin which is not
    # an actual source but contains initial manager account information
    config.enabled_sources = ('system', 'admin')
    repo = Repository(config, vreg=vreg)
    assert len(repo.sources) == 1, repo.sources
    schema = repo.schema
    sourcescfg = config.sources()
    print 'creating necessary tables into the system source'
    source = sourcescfg['system']
    driver = source['db-driver']
    sqlcnx = repo.system_source.get_connection()
    sqlcursor = sqlcnx.cursor()
    def execute(sql, args=None):
        repo.system_source.doexec(sqlcursor, sql, args)
    if drop:
        dropsql = sqldropschema(schema, driver)
        try:
            sqlexec(dropsql, execute)
        except Exception, ex:
            print 'drop failed, skipped (%s)' % ex
            sqlcnx.rollback()
    # schema entities and relations tables
    # can't skip entities table even if system source doesn't support them,
    # they are used sometimes by generated sql. Keeping them empty is much
    # simpler than fixing this...
    if sqlcnx.logged_user != source['db-user']:
        schemasql = sqlschema(schema, driver, user=source['db-user'])
    else:
        schemasql = sqlschema(schema, driver)
        #skip_entities=[str(e) for e in schema.entities()
        #               if not repo.system_source.support_entity(str(e))])
    sqlexec(schemasql, execute)
    # install additional driver specific sql files
    for fpath in glob(join(config.schemas_lib_dir(), '*.sql.%s' % driver)):
        print 'install', fpath
        sqlexec(open(fpath).read(), execute, False, delimiter=';;')
    for directory in config.cubes_path():
        for fpath in glob(join(directory, 'schema', '*.sql.%s' % driver)):
            print 'install', fpath
            sqlexec(open(fpath).read(), execute, False, delimiter=';;')
    sqlcursor.close()
    sqlcnx.commit()
    sqlcnx.close()
    session = repo.internal_session()
    try:
        login = unicode(sourcescfg['admin']['login'])
        pwd = sourcescfg['admin']['password']
    except KeyError:
        if interactive:
            msg = 'enter login and password of the initial manager account'
            login, pwd = manager_userpasswd(msg=msg, confirm=True)
        else:
            login, pwd = unicode(source['db-user']), source['db-password']
    print 'inserting default user and groups'
    needisfix = []
    for group in BASEGROUPS:
        rset = session.execute('INSERT EGroup X: X name %(name)s',
                               {'name': unicode(group)})
        needisfix.append( (rset.rows[0][0], rset.description[0][0]) )
    rset = session.execute('INSERT EUser X: X login %(login)s, X upassword %(pwd)s',
                           {'login': login, 'pwd': pwd})
    needisfix.append( (rset.rows[0][0], rset.description[0][0]) )
    session.execute('SET U in_group G WHERE G name "managers"')
    session.commit()
    # reloging using the admin user
    config._cubes = None # avoid assertion error
    repo, cnx = in_memory_cnx(config, login, pwd)
    assert len(repo.sources) == 1, repo.sources
    handler = config.migration_handler(schema, interactive=False,
                                       cnx=cnx, repo=repo)
    initialize_schema(config, schema, handler)
    # admin user and groups have been added before schema entities, fix the 'is'
    # relation
    for eid, etype in needisfix:
        handler.session.unsafe_execute('SET X is E WHERE X eid %(x)s, E name %(name)s',
                                       {'x': eid, 'name': etype}, 'x')
    # insert versions
    handler.cmd_add_entity('EProperty', pkey=u'system.version.cubicweb',
                           value=unicode(config.cubicweb_version()))
    for cube in config.cubes():
        handler.cmd_add_entity('EProperty', 
                               pkey=u'system.version.%s' % cube.lower(),
                               value=unicode(config.cube_version(cube)))
    # yoo !
    cnx.commit()
    config.enabled_sources = None
    for uri, source_config in config.sources().items():
        if uri in ('admin', 'system'):
            # not an actual source or init_creating already called
            continue
        source = repo.get_source(uri, source_config)
        source.init_creating()
    cnx.commit()
    cnx.close()
    session.close()
    # restore initial configuration
    config.creating = False
    config.read_application_schema = read_application_schema
    config.bootstrap_schema = bootstrap_schema
    config.consider_user_state = True
    config.set_language = True
    print 'application %s initialized' % config.appid


def initialize_schema(config, schema, mhandler, event='create'):
    from cubicweb.server.schemaserial import serialize_schema
    paths = [p for p in config.cubes_path() + [config.apphome]
             if exists(join(p, 'migration'))]
    # execute cubicweb's pre<event> script
    mhandler.exec_event_script('pre%s' % event)
    # execute cubes pre<event> script if any
    for path in reversed(paths):
        mhandler.exec_event_script('pre%s' % event, path)
    # enter application'schema into the database
    serialize_schema(mhandler.rqlcursor, schema)
    # execute cubicweb's post<event> script
    mhandler.exec_event_script('post%s' % event)
    # execute cubes'post<event> script if any
    for path in reversed(paths):
        mhandler.exec_event_script('post%s' % event, path)

def set_debug(debugmode):
    global DEBUG
    DEBUG = debugmode

def debugged(func):
    """decorator to activate debug mode"""
    def wrapped(*args, **kwargs):
        global DEBUG
        DEBUG = True
        try:
            return func(*args, **kwargs)
        finally:
            DEBUG = False
    return wrapped

# sqlite'stored procedures have to be registered at connexion opening time
SQL_CONNECT_HOOKS = {}

# add to this set relations which should have their add security checking done
# *BEFORE* adding the actual relation (done after by default)
BEFORE_ADD_RELATIONS = set(('owned_by',))

# add to this set relations which should have their add security checking done
# *at COMMIT TIME* (done after by default)
ON_COMMIT_ADD_RELATIONS = set(())

# available sources registry
SOURCE_TYPES = {'native': LazyObject('cubicweb.server.sources.native', 'NativeSQLSource'),
                # XXX private sources installed by an external cube
                'pyrorql': LazyObject('cubicweb.server.sources.pyrorql', 'PyroRQLSource'),
                'ldapuser': LazyObject('cubicweb.server.sources.ldapuser', 'LDAPUserSource'),
                }