#
##############################################################################
-import SimpleXMLRPCServer
-import SocketServer
import logging
import logging.handlers
-import os
-import signal
-import socket
import sys
import threading
import time
-import xmlrpclib
import release
+from pprint import pformat
+import warnings
class Service(object):
- """ Base class for *Local* services
-
+ """ Base class for *Local* services
+
Functionality here is trusted, no authentication.
"""
_services = {}
raise Exception("No group for local services")
#GROUPS.setdefault(name, {})[self.__name] = self
- def service_exist(self,name):
- return Service._services.has_key(name)
+ @classmethod
+ def exists(cls, name):
+ return name in cls._services
+
+ @classmethod
+ def remove(cls, name):
+ if cls.exists(name):
+ cls._services.pop(name)
def exportMethod(self, method):
if callable(method):
Any instance of this class will behave like the single instance
of Service(name)
"""
+ __logger = logging.getLogger('service')
def __init__(self, name):
self.__name = name
try:
for method_name, method_definition in self._service._methods.items():
setattr(self, method_name, method_definition)
except KeyError, keyError:
- Logger().notifyChannel('module', LOG_ERROR, 'This service does not exist: %s' % (str(keyError),) )
+ self.__logger.error('This service does not exist: %s' % (str(keyError),) )
raise
+
def __call__(self, method, *params):
return getattr(self, method)(*params)
LOG_NOTSET = 'notset'
LOG_DEBUG_RPC = 'debug_rpc'
LOG_DEBUG = 'debug'
-LOG_DEBUG2 = 'debug2'
+LOG_TEST = 'test'
LOG_INFO = 'info'
LOG_WARNING = 'warn'
LOG_ERROR = 'error'
LOG_CRITICAL = 'critical'
-# add new log level below DEBUG
-logging.DEBUG2 = logging.DEBUG - 1
-logging.DEBUG_RPC = logging.DEBUG2 - 1
+logging.DEBUG_RPC = logging.DEBUG - 2
+logging.addLevelName(logging.DEBUG_RPC, 'DEBUG_RPC')
+
+logging.TEST = logging.INFO - 5
+logging.addLevelName(logging.TEST, 'TEST')
def init_logger():
import os
mapping = {
'DEBUG_RPC': ('blue', 'white'),
- 'DEBUG2': ('green', 'white'),
'DEBUG': ('blue', 'default'),
'INFO': ('green', 'default'),
+ 'TEST': ('white', 'blue'),
'WARNING': ('yellow', 'default'),
'ERROR': ('red', 'default'),
'CRITICAL': ('white', 'red'),
class Logger(object):
+ def __init__(self):
+ warnings.warn("The netsvc.Logger API shouldn't be used anymore, please "
+ "use the standard `logging.getLogger` API instead",
+ PendingDeprecationWarning, stacklevel=2)
+ super(Logger, self).__init__()
def notifyChannel(self, name, level, msg):
+ warnings.warn("notifyChannel API shouldn't be used anymore, please use "
+ "the standard `logging` module instead",
+ PendingDeprecationWarning, stacklevel=2)
from service.web_services import common
log = logging.getLogger(tools.ustr(name))
- if level == LOG_DEBUG2 and not hasattr(log, level):
- fct = lambda msg, *args, **kwargs: log.log(logging.DEBUG2, msg, *args, **kwargs)
- setattr(log, LOG_DEBUG2, fct)
+ if level in [LOG_DEBUG_RPC, LOG_TEST] and not hasattr(log, level):
+ fct = lambda msg, *args, **kwargs: log.log(getattr(logging, level.upper()), msg, *args, **kwargs)
+ setattr(log, level, fct)
- if level == LOG_DEBUG_RPC and not hasattr(log, level):
- fct = lambda msg, *args, **kwargs: log.log(logging.DEBUG_RPC, msg, *args, **kwargs)
- setattr(log, LOG_DEBUG_RPC, fct)
level_method = getattr(log, level)
try:
msg = tools.ustr(msg).strip()
- if level in (LOG_ERROR,LOG_CRITICAL) and tools.config.get_misc('debug','env_info',False):
+ if level in (LOG_ERROR, LOG_CRITICAL) and tools.config.get_misc('debug','env_info',False):
msg = common().exp_get_server_environment() + "\n" + msg
result = msg.split('\n')
_timers = {}
_logger = Logger()
+ __logger = logging.getLogger('timer')
+
def setAlarm(self, fn, dt, db_name, *args, **kwargs):
wait = dt - time.time()
if wait > 0:
- self._logger.notifyChannel('timers', LOG_DEBUG, "Job scheduled in %.3g seconds for %s.%s" % (wait, fn.im_class.__name__, fn.func_name))
+ self.__logger.debug("Job scheduled in %.3g seconds for %s.%s" % (wait, fn.im_class.__name__, fn.func_name))
timer = threading.Timer(wait, fn, args, kwargs)
timer.start()
self._timers.setdefault(db_name, []).append(timer)
class Server:
""" Generic interface for all servers with an event loop etc.
Override this to impement http, net-rpc etc. servers.
-
+
Servers here must have threaded behaviour. start() must not block,
there is no run().
"""
__is_started = False
__servers = []
-
+
+
+ __logger = logging.getLogger('server')
+
def __init__(self):
if Server.__is_started:
raise Exception('All instances of servers must be inited before the startAll()')
Server.__servers.append(self)
def start(self):
- print "called stub Server.start"
- pass
-
+ self.__logger.debug("called stub Server.start")
+
def stop(self):
- print "called stub Server.stop"
- pass
+ self.__logger.debug("called stub Server.stop")
def stats(self):
""" This function should return statistics about the server """
def startAll(cls):
if cls.__is_started:
return
- Logger().notifyChannel("services", LOG_INFO,
- "Starting %d services" % len(cls.__servers))
+ cls.__logger.info("Starting %d services" % len(cls.__servers))
for srv in cls.__servers:
srv.start()
cls.__is_started = True
-
+
@classmethod
def quitAll(cls):
if not cls.__is_started:
return
- Logger().notifyChannel("services", LOG_INFO,
- "Stopping %d services" % len(cls.__servers))
+ cls.__logger.info("Stopping %d services" % len(cls.__servers))
for srv in cls.__servers:
srv.stop()
cls.__is_started = False
@classmethod
def allStats(cls):
- res = ''
- if cls.__is_started:
- res += "Servers started\n"
- else:
- res += "Servers stopped\n"
- for srv in cls.__servers:
- try:
- res += srv.stats() + "\n"
- except:
- pass
- return res
+ res = ["Servers %s" % ('stopped', 'started')[cls.__is_started]]
+ res.extend(srv.stats() for srv in cls.__servers)
+ return '\n'.join(res)
class OpenERPDispatcherException(Exception):
def __init__(self, exception, traceback):
class OpenERPDispatcher:
def log(self, title, msg):
- from pprint import pformat
Logger().notifyChannel('%s' % title, LOG_DEBUG_RPC, pformat(msg))
def dispatch(self, service_name, method, params):
self.log('service', service_name)
self.log('method', method)
self.log('params', params)
- if hasattr(self,'auth_provider'):
- auth = self.auth_provider
- else:
- auth = None
+ auth = getattr(self, 'auth_provider', None)
result = ExportService.getService(service_name).dispatch(method, auth, params)
self.log('result', result)
# We shouldn't marshall None,
return result
except Exception, e:
self.log('exception', tools.exception_to_unicode(e))
- if hasattr(e, 'traceback'):
- tb = e.traceback
- else:
- tb = sys.exc_info()
+ tb = getattr(e, 'traceback', sys.exc_info())
tb_s = "".join(traceback.format_exception(*tb))
if tools.config['debug_mode']:
import pdb
pdb.post_mortem(tb[2])
raise OpenERPDispatcherException(e, tb_s)
-class GenericXMLRPCRequestHandler(OpenERPDispatcher):
- def _dispatch(self, method, params):
- try:
- service_name = self.path.split("/")[-1]
- return self.dispatch(service_name, method, params)
- except OpenERPDispatcherException, e:
- raise xmlrpclib.Fault(tools.exception_to_unicode(e.exception), e.traceback)
-
-class SSLSocket(object):
- def __init__(self, socket):
- if not hasattr(socket, 'sock_shutdown'):
- from OpenSSL import SSL
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.use_privatekey_file(tools.config['secure_pkey_file'])
- ctx.use_certificate_file(tools.config['secure_cert_file'])
- self.socket = SSL.Connection(ctx, socket)
- else:
- self.socket = socket
-
- def shutdown(self, how):
- return self.socket.sock_shutdown(how)
-
- def __getattr__(self, name):
- return getattr(self.socket, name)
-
-class SimpleXMLRPCRequestHandler(GenericXMLRPCRequestHandler, SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
- rpc_paths = map(lambda s: '/xmlrpc/%s' % s, GROUPS.get('web-services', {}).keys())
-
-class SecureXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
- def setup(self):
- self.connection = SSLSocket(self.request)
- self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
- self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-
-class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
- def server_bind(self):
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self)
-
-class SecureThreadedXMLRPCServer(SimpleThreadedXMLRPCServer):
- def __init__(self, server_address, HandlerClass, logRequests=1):
- SimpleThreadedXMLRPCServer.__init__(self, server_address, HandlerClass, logRequests)
- self.socket = SSLSocket(socket.socket(self.address_family, self.socket_type))
- self.server_bind()
- self.server_activate()
-
-class HttpDaemon(threading.Thread):
- def __init__(self, interface, port, secure=False):
- threading.Thread.__init__(self)
- self.__port = port
- self.__interface = interface
- self.secure = bool(secure)
- handler_class = (SimpleXMLRPCRequestHandler, SecureXMLRPCRequestHandler)[self.secure]
- server_class = (SimpleThreadedXMLRPCServer, SecureThreadedXMLRPCServer)[self.secure]
-
- if self.secure:
- from OpenSSL.SSL import Error as SSLError
- else:
- class SSLError(Exception): pass
- try:
- self.server = server_class((interface, port), handler_class, 0)
- except SSLError, e:
- Logger().notifyChannel('xml-rpc-ssl', LOG_CRITICAL, "Can not load the certificate and/or the private key files")
- sys.exit(1)
- except Exception, e:
- Logger().notifyChannel('xml-rpc', LOG_CRITICAL, "Error occur when starting the server daemon: %s" % (e,))
- sys.exit(1)
-
-
- def attach(self, path, gw):
- pass
-
- def stop(self):
- self.running = False
- if os.name != 'nt':
- try:
- self.server.socket.shutdown(
- hasattr(socket, 'SHUT_RDWR') and socket.SHUT_RDWR or 2)
- except socket.error, e:
- if e.errno != 57: raise
- # OSX, socket shutdowns both sides if any side closes it
- # causing an error 57 'Socket is not connected' on shutdown
- # of the other side (or something), see
- # http://bugs.python.org/issue4397
- Logger().notifyChannel(
- 'server', LOG_DEBUG,
- '"%s" when shutting down server socket, '
- 'this is normal under OS X'%e)
- self.server.socket.close()
-
- def run(self):
- self.server.register_introspection_functions()
-
- self.running = True
- while self.running:
- self.server.handle_request()
- return True
-
- # If the server need to be run recursively
- #
- #signal.signal(signal.SIGALRM, self.my_handler)
- #signal.alarm(6)
- #while True:
- # self.server.handle_request()
- #signal.alarm(0) # Disable the alarm
-
-import tiny_socket
-class TinySocketClientThread(threading.Thread, OpenERPDispatcher):
- def __init__(self, sock, threads):
- threading.Thread.__init__(self)
- self.sock = sock
- self.threads = threads
-
- def run(self):
- import select
- self.running = True
- try:
- ts = tiny_socket.mysocket(self.sock)
- except:
- self.sock.close()
- self.threads.remove(self)
- return False
- while self.running:
- try:
- msg = ts.myreceive()
- except:
- self.sock.close()
- self.threads.remove(self)
- return False
- try:
- result = self.dispatch(msg[0], msg[1], msg[2:])
- ts.mysend(result)
- except OpenERPDispatcherException, e:
- new_e = Exception(tools.exception_to_unicode(e.exception)) # avoid problems of pickeling
- ts.mysend(new_e, exception=True, traceback=e.traceback)
-
- self.sock.close()
- self.threads.remove(self)
- return True
-
- def stop(self):
- self.running = False
-
-
-class TinySocketServerThread(threading.Thread):
- def __init__(self, interface, port, secure=False):
- threading.Thread.__init__(self)
- self.__port = port
- self.__interface = interface
- self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.socket.bind((self.__interface, self.__port))
- self.socket.listen(5)
- self.threads = []
-
- def run(self):
- import select
- try:
- self.running = True
- while self.running:
- (clientsocket, address) = self.socket.accept()
- ct = TinySocketClientThread(clientsocket, self.threads)
- self.threads.append(ct)
- ct.start()
- self.socket.close()
- except Exception, e:
- self.socket.close()
- return False
-
- def stop(self):
- self.running = False
- for t in self.threads:
- t.stop()
- try:
- if hasattr(socket, 'SHUT_RDWR'):
- self.socket.shutdown(socket.SHUT_RDWR)
- else:
- self.socket.shutdown(2)
- self.socket.close()
- except:
- return False
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: