sobjects/services.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
     1 # copyright 2003-2014 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 """Define server side service provided by cubicweb"""
       
    19 
       
    20 import threading
       
    21 
       
    22 from six import text_type
       
    23 
       
    24 from yams.schema import role_name
       
    25 
       
    26 from cubicweb import ValidationError
       
    27 from cubicweb.server import Service
       
    28 from cubicweb.predicates import match_user_groups, match_kwargs
       
    29 
       
    30 class StatsService(Service):
       
    31     """Return a dictionary containing some statistics about the repository
       
    32     resources usage.
       
    33     """
       
    34 
       
    35     __regid__  = 'repo_stats'
       
    36     __select__ = match_user_groups('managers', 'users')
       
    37 
       
    38     def call(self):
       
    39         repo = self._cw.repo # Service are repo side only.
       
    40         results = {}
       
    41         querier = repo.querier
       
    42         source = repo.system_source
       
    43         for size, maxsize, hits, misses, title in (
       
    44             (len(querier._rql_cache), repo.config['rql-cache-size'],
       
    45             querier.cache_hit, querier.cache_miss, 'rqlt_st'),
       
    46             (len(source._cache), repo.config['rql-cache-size'],
       
    47             source.cache_hit, source.cache_miss, 'sql'),
       
    48             ):
       
    49             results['%s_cache_size' % title] = {'size': size, 'maxsize': maxsize}
       
    50             results['%s_cache_hit' % title] = hits
       
    51             results['%s_cache_miss' % title] = misses
       
    52             results['%s_cache_hit_percent' % title] = (hits * 100) / (hits + misses)
       
    53         results['type_source_cache_size'] = len(repo._type_source_cache)
       
    54         results['extid_cache_size'] = len(repo._extid_cache)
       
    55         results['sql_no_cache'] = repo.system_source.no_cache
       
    56         results['nb_open_sessions'] = len(repo._sessions)
       
    57         results['nb_active_threads'] = threading.activeCount()
       
    58         looping_tasks = repo._tasks_manager._looping_tasks
       
    59         results['looping_tasks'] = [(t.name, t.interval) for t in looping_tasks]
       
    60         results['available_cnxsets'] = repo._cnxsets_pool.qsize()
       
    61         results['threads'] = [t.name for t in threading.enumerate()]
       
    62         return results
       
    63 
       
    64 
       
    65 class GcStatsService(Service):
       
    66     """Return a dictionary containing some statistics about the repository
       
    67     resources usage.
       
    68     """
       
    69 
       
    70     __regid__  = 'repo_gc_stats'
       
    71     __select__ = match_user_groups('managers')
       
    72 
       
    73     def call(self, nmax=20):
       
    74         """Return a dictionary containing some statistics about the repository
       
    75         memory usage.
       
    76 
       
    77         This is a public method, not requiring a session id.
       
    78 
       
    79         nmax is the max number of (most) referenced object returned as
       
    80         the 'referenced' result
       
    81         """
       
    82 
       
    83         from cubicweb._gcdebug import gc_info
       
    84         from cubicweb.appobject import AppObject
       
    85         from cubicweb.rset import ResultSet
       
    86         from cubicweb.web.request import CubicWebRequestBase
       
    87         from rql.stmts import Union
       
    88 
       
    89         lookupclasses = (AppObject,
       
    90                          Union, ResultSet,
       
    91                          CubicWebRequestBase)
       
    92         try:
       
    93             from cubicweb.server.session import Session, InternalSession
       
    94             lookupclasses += (InternalSession, Session)
       
    95         except ImportError:
       
    96             pass  # no server part installed
       
    97 
       
    98         results = {}
       
    99         counters, ocounters, garbage = gc_info(lookupclasses,
       
   100                                                viewreferrersclasses=())
       
   101         values = sorted(counters.items(), key=lambda x: x[1], reverse=True)
       
   102         results['lookupclasses'] = values
       
   103         values = sorted(ocounters.items(), key=lambda x: x[1], reverse=True)[:nmax]
       
   104         results['referenced'] = values
       
   105         results['unreachable'] = garbage
       
   106         return results
       
   107 
       
   108 
       
   109 class RegisterUserService(Service):
       
   110     """check if a user with the given login exists, if not create it with the
       
   111     given password. This service is designed to be used for anonymous
       
   112     registration on public web sites.
       
   113 
       
   114     To use it, do:
       
   115      with self.appli.repo.internal_cnx() as cnx:
       
   116         cnx.call_service('register_user',
       
   117                          login=login,
       
   118                          password=password,
       
   119                          **cwuserkwargs)
       
   120     """
       
   121     __regid__ = 'register_user'
       
   122     __select__ = Service.__select__ & match_kwargs('login', 'password')
       
   123     default_groups = ('users',)
       
   124 
       
   125     def call(self, login, password, email=None, groups=None, **cwuserkwargs):
       
   126         cnx = self._cw
       
   127         errmsg = cnx._('the value "%s" is already used, use another one')
       
   128 
       
   129         if (cnx.execute('CWUser X WHERE X login %(login)s', {'login': login},
       
   130                         build_descr=False)
       
   131             or cnx.execute('CWUser X WHERE X use_email C, C address %(login)s',
       
   132                            {'login': login}, build_descr=False)):
       
   133             qname = role_name('login', 'subject')
       
   134             raise ValidationError(None, {qname: errmsg % login})
       
   135 
       
   136         if isinstance(password, text_type):
       
   137             # password should *always* be utf8 encoded
       
   138             password = password.encode('UTF8')
       
   139         cwuserkwargs['login'] = login
       
   140         cwuserkwargs['upassword'] = password
       
   141         # we have to create the user
       
   142         user = cnx.create_entity('CWUser', **cwuserkwargs)
       
   143         if groups is None:
       
   144             groups = self.default_groups
       
   145         assert groups, "CWUsers must belong to at least one CWGroup"
       
   146         group_names = ', '.join('%r' % group for group in groups)
       
   147         cnx.execute('SET X in_group G WHERE X eid %%(x)s, G name IN (%s)' % group_names,
       
   148                     {'x': user.eid})
       
   149 
       
   150         if email or '@' in login:
       
   151             d = {'login': login, 'email': email or login}
       
   152             if cnx.execute('EmailAddress X WHERE X address %(email)s', d,
       
   153                            build_descr=False):
       
   154                 qname = role_name('address', 'subject')
       
   155                 raise ValidationError(None, {qname: errmsg % d['email']})
       
   156             cnx.execute('INSERT EmailAddress X: X address %(email)s, '
       
   157                         'U primary_email X, U use_email X '
       
   158                         'WHERE U login %(login)s', d, build_descr=False)
       
   159 
       
   160         return user
       
   161 
       
   162 
       
   163 class SourceSynchronizationService(Service):
       
   164     """Force synchronization of a datafeed source"""
       
   165     __regid__ = 'source-sync'
       
   166     __select__ = Service.__select__ & match_user_groups('managers')
       
   167 
       
   168     def call(self, source_eid):
       
   169         source_entity = self._cw.entity_from_eid(source_eid)
       
   170         repo = self._cw.repo # Service are repo side only.
       
   171         with repo.internal_cnx() as cnx:
       
   172             source = repo.sources_by_uri[source_entity.name]
       
   173             source.pull_data(cnx)
       
   174