goa/appobjects/dbmgmt.py
author Laure Bourgois <Laure.Bourgois@logilab.fr>
Wed, 28 Jan 2009 16:52:12 +0100
changeset 502 7882941d3530
parent 0 b97547f5f1fa
child 635 305da8d6aa2d
permissions -rw-r--r--
owl module has two views : OWLView (TBOX) and OWLABOXView (more or less ABOX. indeed, it layout ABOX information only for an specific entity and not all entities)

"""special management views to manage repository content (initialization and
restoration).

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

from os.path import exists, join, abspath
from pickle import loads, dumps

from logilab.common.decorators import cached
from logilab.mtconverter import html_escape

from cubicweb.common.view import StartupView
from cubicweb.web import Redirect
from cubicweb.goa.dbinit import fix_entities, init_persistent_schema, insert_versions

from google.appengine.api.datastore import Entity, Key, Get, Put, Delete
from google.appengine.api.datastore_types import Blob
from google.appengine.api.datastore_errors import EntityNotFoundError


def _get_status(name, create=True):
    key = Key.from_path('EApplicationStatus', name)
    try:
        status = Get(key)
    except EntityNotFoundError:
        if create:
            status = Entity('EApplicationStatus', name=name)
        else:
            status = None
    return status


class AuthInfo(StartupView):
    """special management view to get cookie values to give to laxctl commands
    which are doing datastore administration requests
    """
    id = 'authinfo'
    require_groups = ('managers',)

    def call(self):
        cookie = self.req.get_cookie()
        values = []
        if self.config['use-google-auth']:
            for param in ('ACSID', 'dev_appserver_login'):
                morsel = cookie.get(param)
                if morsel:
                    values.append('%s=%s' % (param, morsel.value))
                    break
        values.append('__session=%s' % cookie['__session'].value)
        self.w(u"<p>pass this flag to the client: --cookie='%s'</p>"
               % html_escape('; '.join(values)))
        
        

class ContentInit(StartupView):
    """special management view to initialize content of a repository,
    step by step to avoid depassing quotas
    """
    id = 'contentinit'
    require_groups = ('managers',)

    def server_session(self):
        ssession = self.config.repo_session(self.req.cnx.sessionid)
        ssession.set_pool()
        return ssession

    def end_core_step(self, msg, status, stepid):
        status['cpath'] = ''
        status['stepid'] = stepid
        Put(status)
        self.msg(msg)
        
    def call(self):
        status = _get_status('creation')
        if status.get('finished'):
            self.redirect('process already completed')
        config = self.config
        # execute cubicweb's post<event> script
        #mhandler.exec_event_script('post%s' % event)
        # execute cubes'post<event> script if any
        paths = [p for p in config.cubes_path() + [config.apphome]
                 if exists(join(p, 'migration'))]
        paths = [abspath(p) for p in (reversed(paths))]
        cpath = status.get('cpath')
        if cpath is None and status.get('stepid') is None:
            init_persistent_schema(self.server_session(), self.schema)
            self.end_core_step(u'inserted schema entities', status, 0)
            return
        if cpath == '' and status.get('stepid') == 0:
            fix_entities(self.schema)
            self.end_core_step(u'fixed bootstrap groups and users', status, 1)
            return
        if cpath == '' and status.get('stepid') == 1:
            insert_versions(self.server_session(), self.config)
            self.end_core_step(u'inserted software versions', status, None)
            return
        for i, path in enumerate(paths):
            if not cpath or cpath == path:
                self.info('running %s', path)
                stepid = status.get('stepid')
                context = status.get('context')
                if context is not None:
                    context = loads(context)
                else:
                    context = {}
                stepid = self._migrhandler.exec_event_script(
                    'postcreate', path, 'stepable_postcreate', stepid, context)
                if stepid is None: # finished for this script
                    # reset script state
                    context = stepid = None
                    # next time, go to the next script
                    self.msg(u'finished postcreate for %s' % path)
                    try:
                        path = paths[i+1]
                        self.continue_link()
                    except IndexError:
                        status['finished'] = True
                        path = None
                        self.redirect('process completed')
                else:
                    if context.get('stepidx'):
                        self.msg(u'created %s entities for step %s of %s' % (
                            context['stepidx'], stepid, path))
                    else:
                        self.msg(u'finished postcreate step %s for %s' % (
                            stepid, path))
                    context = Blob(dumps(context))
                    self.continue_link()
                status['context'] = context
                status['stepid'] = stepid
                status['cpath'] = path
                break
        else:
            if not cpath:
                # nothing to be done
                status['finished'] = True
                self.redirect('process completed')
            else:
                # Note the error: is expected by the laxctl command line tool,
                # deal with this if internationalization is introduced
                self.msg(u'error: strange creation state, can\'t find %s'
                         % cpath)
                self.w(u'<div>click <a href="%s?vid=contentclear">here</a> to '
                       '<b>delete all datastore content</b> so process can be '
                       'reinitialized</div>' % html_escape(self.req.base_url()))
        Put(status)
        
    @property
    @cached
    def _migrhandler(self):
        return self.config.migration_handler(self.schema, interactive=False,
                                             cnx=self.req.cnx,
                                             repo=self.config.repository())

    def msg(self, msg):
        self.w(u'<div class="message">%s</div>' % html_escape(msg))
    def redirect(self, msg):
        raise Redirect(self.req.build_url('', msg))
    def continue_link(self):
        self.w(u'<a href="%s">continue</a><br/>' % html_escape(self.req.url()))

        
class ContentClear(StartupView):
    id = 'contentclear'
    require_groups = ('managers',)
    skip_etypes = ('EGroup', 'EUser')
    
    def call(self):
        # XXX should use unsafe_execute with all hooks deactivated
        # XXX step by catching datastore errors?
        for eschema in self.schema.entities():
            if eschema.is_final() or eschema in self.skip_etypes:
                continue
            self.req.execute('DELETE %s X' % eschema)
            self.w(u'deleted all %s entities<br/>' % eschema)
        status = _get_status('creation', create=False)
        if status:
            Delete(status)
        self.w(u'done<br/>')
        self.w(u'click <a href="%s?vid=contentinit">here</a> to start the data '
               'initialization process<br/>' % self.req.base_url())