goa/tools/laxctl.py
changeset 0 b97547f5f1fa
child 1131 544609e83317
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """provides all lax instances management commands into a single utility script
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 import sys
       
    10 import os
       
    11 import os.path as osp
       
    12 import time
       
    13 import re
       
    14 import urllib2
       
    15 from urllib import urlencode
       
    16 from Cookie import SimpleCookie
       
    17 
       
    18 from logilab.common.clcommands import Command, register_commands, main_run
       
    19 
       
    20 from cubicweb import CW_SOFTWARE_ROOT
       
    21 from cubicweb.common.uilib import remove_html_tags
       
    22 
       
    23 APPLROOT = osp.abspath(osp.join(osp.dirname(osp.abspath(__file__)), '..'))
       
    24 
       
    25 # XXX import custom?
       
    26 
       
    27 from tools import i18n
       
    28 
       
    29 def initialize_vregistry(applroot):
       
    30     # apply monkey patches first
       
    31     from cubicweb.goa import do_monkey_patch    
       
    32     do_monkey_patch()
       
    33     from cubicweb.goa.goavreg import GAERegistry
       
    34     from cubicweb.goa.goaconfig import GAEConfiguration
       
    35     #WebConfiguration.ext_resources['JAVASCRIPTS'].append('DATADIR/goa.js')
       
    36     config = GAEConfiguration('toto', applroot)
       
    37     vreg = GAERegistry(config)
       
    38     vreg.set_schema(config.load_schema())
       
    39     return vreg
       
    40         
       
    41 def alistdir(directory):
       
    42     return [osp.join(directory, f) for f in os.listdir(directory)]
       
    43 
       
    44 
       
    45 class LaxCommand(Command):
       
    46     """base command class for all lax commands
       
    47     creates vreg, schema and calls 
       
    48     """
       
    49     min_args = max_args = 0
       
    50 
       
    51     def run(self, args):
       
    52         self.vreg = initialize_vregistry(APPLROOT)
       
    53         self._run(args)
       
    54                 
       
    55 
       
    56 class I18nUpdateCommand(LaxCommand):
       
    57     """updates i18n catalogs"""
       
    58     name = 'i18nupdate'
       
    59     
       
    60     def _run(self, args):
       
    61         assert not args, 'no argument expected'
       
    62         i18ndir = i18n.get_i18n_directory(APPLROOT)
       
    63         i18n.update_cubes_catalog(self.vreg, APPLROOT,
       
    64                                       langs=i18n.getlangs(i18ndir))
       
    65 
       
    66 
       
    67 class I18nCompileCommand(LaxCommand):
       
    68     """compiles i18n catalogs"""
       
    69     name = 'i18ncompile'
       
    70     min_args = max_args = 0
       
    71     
       
    72     def _run(self, args):
       
    73         assert not args, 'no argument expected'
       
    74         i18ndir = i18n.get_i18n_directory(APPLROOT)
       
    75         langs = i18n.getlangs(i18ndir)
       
    76         print 'generating .mo files for langs', ', '.join(langs)
       
    77         cubicweb_i18ndir = osp.join(APPLROOT, 'cubes', 'shared')
       
    78         paths = self.vreg.config.cubes_path() + [cubicweb_i18ndir]
       
    79         sourcedirs = [i18ndir] + [osp.join(path, 'i18n') for path in paths]
       
    80         i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs=langs)
       
    81         
       
    82 
       
    83 class GenerateSchemaCommand(LaxCommand):
       
    84     """generates the schema's png file"""
       
    85     name = 'genschema'
       
    86 
       
    87     def _run(self, args):
       
    88         assert not args, 'no argument expected'
       
    89         from yams import schema2dot        
       
    90         schema = self.vreg.schema
       
    91         skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
       
    92         path = osp.join(APPLROOT, 'data', 'schema.png')
       
    93         schema2dot.schema2dot(schema, path, #size=size,
       
    94                               skiprels=skip_rels, skipmeta=True)
       
    95         print 'generated', path
       
    96         path = osp.join(APPLROOT, 'data', 'metaschema.png')
       
    97         schema2dot.schema2dot(schema, path, #size=size,
       
    98                               skiprels=skip_rels, skipmeta=False)
       
    99         print 'generated', path
       
   100 
       
   101 
       
   102 class PopulateDataDirCommand(LaxCommand):
       
   103     """populate application's data directory according to used cubes"""
       
   104     name = 'populatedata'
       
   105 
       
   106     def _run(self, args):
       
   107         assert not args, 'no argument expected'
       
   108         # first clean everything which is a symlink from the data directory
       
   109         datadir = osp.join(APPLROOT, 'data')
       
   110         if not osp.exists(datadir):
       
   111             print 'created data directory'
       
   112             os.mkdir(datadir)
       
   113         for filepath in alistdir(datadir):
       
   114             if osp.islink(filepath):
       
   115                 print 'removing', filepath
       
   116                 os.remove(filepath)
       
   117         cubes = list(self.vreg.config.cubes()) + ['shared']
       
   118         for templ in cubes:
       
   119             templpath = self.vreg.config.cube_dir(templ)
       
   120             templdatadir = osp.join(templpath, 'data')
       
   121             if not osp.exists(templdatadir):
       
   122                 print 'no data provided by', templ
       
   123                 continue
       
   124             for resource in os.listdir(templdatadir):
       
   125                 if resource == 'external_resources':
       
   126                     continue
       
   127                 if not osp.exists(osp.join(datadir, resource)):
       
   128                     print 'symlinked %s from %s' % (resource, templ)
       
   129                     os.symlink(osp.join(templdatadir, resource),
       
   130                                osp.join(datadir, resource))
       
   131 
       
   132 
       
   133 class NoRedirectHandler(urllib2.HTTPRedirectHandler):
       
   134     def http_error_302(self, req, fp, code, msg, headers):
       
   135         raise urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
       
   136     http_error_301 = http_error_303 = http_error_307 = http_error_302
       
   137 
       
   138 
       
   139 class GetSessionIdHandler(urllib2.HTTPRedirectHandler):
       
   140     def __init__(self, config):
       
   141         self.config = config
       
   142         
       
   143     def http_error_303(self, req, fp, code, msg, headers):
       
   144         cookie = SimpleCookie(headers['Set-Cookie'])
       
   145         sessionid = cookie['__session'].value
       
   146         print 'session id', sessionid
       
   147         setattr(self.config, 'cookie', '__session=' + sessionid)
       
   148         return 1 # on exception should be raised
       
   149 
       
   150     
       
   151 class URLCommand(LaxCommand):
       
   152     """abstract class for commands doing stuff by accessing the web application
       
   153     """
       
   154     min_args = max_args = 1
       
   155     arguments = '<site url>'
       
   156 
       
   157     options = (
       
   158         ('cookie',
       
   159          {'short': 'C', 'type' : 'string', 'metavar': 'key=value',
       
   160           'default': None,
       
   161           'help': 'session/authentication cookie.'}),
       
   162         ('user',
       
   163          {'short': 'u', 'type' : 'string', 'metavar': 'login',
       
   164           'default': None,
       
   165           'help': 'user login instead of giving raw cookie string (require lax '
       
   166           'based authentication).'}),
       
   167         ('password',
       
   168          {'short': 'p', 'type' : 'string', 'metavar': 'password',
       
   169           'default': None,
       
   170           'help': 'user password instead of giving raw cookie string (require '
       
   171           'lax based authentication).'}),
       
   172         )
       
   173     
       
   174     def _run(self, args):
       
   175         baseurl = args[0]
       
   176         if not baseurl.startswith('http'):
       
   177             baseurl = 'http://' + baseurl
       
   178         if not baseurl.endswith('/'):
       
   179             baseurl += '/'
       
   180         self.base_url = baseurl
       
   181         if not self.config.cookie and self.config.user:
       
   182             # no cookie specified but a user is. Try to open a session using
       
   183             # given authentication info
       
   184             print 'opening session for', self.config.user
       
   185             opener = urllib2.build_opener(GetSessionIdHandler(self.config))
       
   186             urllib2.install_opener(opener)
       
   187             data = urlencode(dict(__login=self.config.user,
       
   188                                   __password=self.config.password))
       
   189             self.open_url(urllib2.Request(baseurl, data))            
       
   190         opener = urllib2.build_opener(NoRedirectHandler())
       
   191         urllib2.install_opener(opener)        
       
   192         self.do_base_url(baseurl)
       
   193 
       
   194     def build_req(self, url):
       
   195         req = urllib2.Request(url)
       
   196         if self.config.cookie:
       
   197             req.headers['Cookie'] = self.config.cookie
       
   198         return req
       
   199     
       
   200     def open_url(self, req):
       
   201         try:
       
   202             return urllib2.urlopen(req)
       
   203         except urllib2.HTTPError, ex:
       
   204             if ex.code == 302:
       
   205                 self.error_302(req, ex)
       
   206             elif ex.code == 500:
       
   207                 self.error_500(req, ex)
       
   208             else:
       
   209                 raise
       
   210 
       
   211     def error_302(self, req, ex):
       
   212         print 'authentication required'
       
   213         print ('visit %s?vid=authinfo with your browser to get '
       
   214                'authentication info' % self.base_url)
       
   215         sys.exit(1)
       
   216 
       
   217     def error_500(self, req, ex):
       
   218         print 'an unexpected error occured on the server'
       
   219         print ('you may get more information by visiting '
       
   220                '%s' % req.get_full_url())
       
   221         sys.exit(1)
       
   222 
       
   223     def extract_message(self, data):
       
   224         match = re.search(r'<div class="message">(.*?)</div>', data.read(), re.M|re.S)
       
   225         if match:
       
   226             msg = remove_html_tags(match.group(1))
       
   227             print msg
       
   228             return msg
       
   229         
       
   230     def do_base_url(self, baseurl):
       
   231         raise NotImplementedError()
       
   232 
       
   233         
       
   234 class DSInitCommand(URLCommand):
       
   235     """initialize the datastore"""
       
   236     name = 'db-init'
       
   237 
       
   238     options = URLCommand.options + (
       
   239         ('sleep',
       
   240          {'short': 's', 'type' : 'int', 'metavar': 'nb seconds',
       
   241           'default': None,
       
   242           'help': 'number of seconds to wait between each request to avoid '
       
   243           'going out of quota.'}),
       
   244         )
       
   245         
       
   246     def do_base_url(self, baseurl):
       
   247         req = self.build_req(baseurl + '?vid=contentinit')
       
   248         while True:
       
   249             try:
       
   250                 data = self.open_url(req)
       
   251             except urllib2.HTTPError, ex:
       
   252                 if ex.code == 303: # redirect
       
   253                     print 'process completed'
       
   254                     break
       
   255                 raise
       
   256             msg = self.extract_message(data)
       
   257             if msg and msg.startswith('error: '):
       
   258                 print ('you may to cleanup datastore by visiting '
       
   259                        '%s?vid=contentclear (ALL ENTITIES WILL BE DELETED)'
       
   260                        % baseurl)
       
   261                 break
       
   262             if self.config.sleep:
       
   263                 time.sleep(self.config.sleep)
       
   264 
       
   265 
       
   266 class CleanSessionsCommand(URLCommand):
       
   267     """cleanup sessions on the server. This command should usually be called
       
   268     regularly by a cron job or equivalent.
       
   269     """
       
   270     name = "cleansessions"
       
   271     def do_base_url(self, baseurl):
       
   272         req = self.build_req(baseurl + '?vid=cleansessions')
       
   273         data = self.open_url(req)
       
   274         self.extract_message(data)
       
   275             
       
   276     
       
   277 register_commands([I18nUpdateCommand,
       
   278                    I18nCompileCommand,
       
   279                    GenerateSchemaCommand,
       
   280                    PopulateDataDirCommand,
       
   281                    DSInitCommand,
       
   282                    CleanSessionsCommand,
       
   283                    ])
       
   284 
       
   285 def run():
       
   286     main_run(sys.argv[1:])
       
   287     
       
   288 if __name__ == '__main__':
       
   289     run()