goa/appobjects/dbmgmt.py
changeset 6366 1806148d6ce8
parent 6333 e3994fcc21c3
parent 6365 a15cc5e16178
child 6367 d4c485ec1ca1
equal deleted inserted replaced
6333:e3994fcc21c3 6366:1806148d6ce8
     1 # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """special management views to manage repository content (initialization and
       
    19 restoration).
       
    20 
       
    21 """
       
    22 __docformat__ = "restructuredtext en"
       
    23 
       
    24 from os.path import exists, join, abspath
       
    25 from pickle import loads, dumps
       
    26 
       
    27 from logilab.common.decorators import cached
       
    28 from logilab.mtconverter import xml_escape
       
    29 
       
    30 from cubicweb.selectors import none_rset, match_user_groups
       
    31 from cubicweb.view import StartupView
       
    32 from cubicweb.web import Redirect
       
    33 from cubicweb.goa.dbinit import fix_entities, init_persistent_schema, insert_versions
       
    34 
       
    35 from google.appengine.api.datastore import Entity, Key, Get, Put, Delete
       
    36 from google.appengine.api.datastore_types import Blob
       
    37 from google.appengine.api.datastore_errors import EntityNotFoundError
       
    38 
       
    39 
       
    40 def _get_status(name, create=True):
       
    41     key = Key.from_path('EApplicationStatus', name)
       
    42     try:
       
    43         status = Get(key)
       
    44     except EntityNotFoundError:
       
    45         if create:
       
    46             status = Entity('EApplicationStatus', name=name)
       
    47         else:
       
    48             status = None
       
    49     return status
       
    50 
       
    51 
       
    52 class AuthInfo(StartupView):
       
    53     """special management view to get cookie values to give to laxctl commands
       
    54     which are doing datastore administration requests
       
    55     """
       
    56     id = 'authinfo'
       
    57     __select__ = none_rset() & match_user_groups('managers')
       
    58 
       
    59     def call(self):
       
    60         cookie = self.req.get_cookie()
       
    61         values = []
       
    62         if self.config['use-google-auth']:
       
    63             for param in ('ACSID', 'dev_appserver_login'):
       
    64                 morsel = cookie.get(param)
       
    65                 if morsel:
       
    66                     values.append('%s=%s' % (param, morsel.value))
       
    67                     break
       
    68         values.append('__session=%s' % cookie['__session'].value)
       
    69         self.w(u"<p>pass this flag to the client: --cookie='%s'</p>"
       
    70                % xml_escape('; '.join(values)))
       
    71 
       
    72 
       
    73 
       
    74 class ContentInit(StartupView):
       
    75     """special management view to initialize content of a repository,
       
    76     step by step to avoid depassing quotas
       
    77     """
       
    78     id = 'contentinit'
       
    79     __select__ = none_rset() & match_user_groups('managers')
       
    80 
       
    81     def server_session(self):
       
    82         ssession = self.config.repo_session(self.req.cnx.sessionid)
       
    83         ssession.set_pool()
       
    84         return ssession
       
    85 
       
    86     def end_core_step(self, msg, status, stepid):
       
    87         status['cpath'] = ''
       
    88         status['stepid'] = stepid
       
    89         Put(status)
       
    90         self.msg(msg)
       
    91 
       
    92     def call(self):
       
    93         status = _get_status('creation')
       
    94         if status.get('finished'):
       
    95             self.redirect('process already completed')
       
    96         config = self.config
       
    97         # execute cubicweb's post<event> script
       
    98         #mhandler.exec_event_script('post%s' % event)
       
    99         # execute cubes'post<event> script if any
       
   100         paths = [p for p in config.cubes_path() + [config.apphome]
       
   101                  if exists(join(p, 'migration'))]
       
   102         paths = [abspath(p) for p in (reversed(paths))]
       
   103         cpath = status.get('cpath')
       
   104         if cpath is None and status.get('stepid') is None:
       
   105             init_persistent_schema(self.server_session(), self.schema)
       
   106             self.end_core_step(u'inserted schema entities', status, 0)
       
   107             return
       
   108         if cpath == '' and status.get('stepid') == 0:
       
   109             fix_entities(self.schema)
       
   110             self.end_core_step(u'fixed bootstrap groups and users', status, 1)
       
   111             return
       
   112         if cpath == '' and status.get('stepid') == 1:
       
   113             insert_versions(self.server_session(), self.config)
       
   114             self.end_core_step(u'inserted software versions', status, None)
       
   115             return
       
   116         for i, path in enumerate(paths):
       
   117             if not cpath or cpath == path:
       
   118                 self.info('running %s', path)
       
   119                 stepid = status.get('stepid')
       
   120                 context = status.get('context')
       
   121                 if context is not None:
       
   122                     context = loads(context)
       
   123                 else:
       
   124                     context = {}
       
   125                 stepid = self._migrhandler.exec_event_script(
       
   126                     'postcreate', path, 'stepable_postcreate', stepid, context)
       
   127                 if stepid is None: # finished for this script
       
   128                     # reset script state
       
   129                     context = stepid = None
       
   130                     # next time, go to the next script
       
   131                     self.msg(u'finished postcreate for %s' % path)
       
   132                     try:
       
   133                         path = paths[i+1]
       
   134                         self.continue_link()
       
   135                     except IndexError:
       
   136                         status['finished'] = True
       
   137                         path = None
       
   138                         self.redirect('process completed')
       
   139                 else:
       
   140                     if context.get('stepidx'):
       
   141                         self.msg(u'created %s entities for step %s of %s' % (
       
   142                             context['stepidx'], stepid, path))
       
   143                     else:
       
   144                         self.msg(u'finished postcreate step %s for %s' % (
       
   145                             stepid, path))
       
   146                     context = Blob(dumps(context))
       
   147                     self.continue_link()
       
   148                 status['context'] = context
       
   149                 status['stepid'] = stepid
       
   150                 status['cpath'] = path
       
   151                 break
       
   152         else:
       
   153             if not cpath:
       
   154                 # nothing to be done
       
   155                 status['finished'] = True
       
   156                 self.redirect('process completed')
       
   157             else:
       
   158                 # Note the error: is expected by the laxctl command line tool,
       
   159                 # deal with this if internationalization is introduced
       
   160                 self.msg(u'error: strange creation state, can\'t find %s'
       
   161                          % cpath)
       
   162                 self.w(u'<div>click <a href="%s?vid=contentclear">here</a> to '
       
   163                        '<b>delete all datastore content</b> so process can be '
       
   164                        'reinitialized</div>' % xml_escape(self.req.base_url()))
       
   165         Put(status)
       
   166 
       
   167     @property
       
   168     @cached
       
   169     def _migrhandler(self):
       
   170         return self.config.migration_handler(self.schema, interactive=False,
       
   171                                              cnx=self.req.cnx,
       
   172                                              repo=self.config.repository())
       
   173 
       
   174     def msg(self, msg):
       
   175         self.w(u'<div class="message">%s</div>' % xml_escape(msg))
       
   176     def redirect(self, msg):
       
   177         raise Redirect(self.req.build_url('', msg))
       
   178     def continue_link(self):
       
   179         self.w(u'<a href="%s">continue</a><br/>' % xml_escape(self.req.url()))
       
   180 
       
   181 
       
   182 class ContentClear(StartupView):
       
   183     id = 'contentclear'
       
   184     __select__ = none_rset() & match_user_groups('managers')
       
   185     skip_etypes = ('CWGroup', 'CWUser')
       
   186 
       
   187     def call(self):
       
   188         # XXX should use unsafe execute with all hooks deactivated
       
   189         # XXX step by catching datastore errors?
       
   190         for eschema in self.schema.entities():
       
   191             if eschema.final or eschema in self.skip_etypes:
       
   192                 continue
       
   193             self.req.execute('DELETE %s X' % eschema)
       
   194             self.w(u'deleted all %s entities<br/>' % eschema)
       
   195         status = _get_status('creation', create=False)
       
   196         if status:
       
   197             Delete(status)
       
   198         self.w(u'done<br/>')
       
   199         self.w(u'click <a href="%s?vid=contentinit">here</a> to start the data '
       
   200                'initialization process<br/>' % self.req.base_url())