Files
tryton/trytond/server.py
T

233 lines
8.8 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
"""
%prog [options]
"""
import logging
import logging.config
import logging.handlers
import sys
import os
import signal
import time
from getpass import getpass
import threading
from trytond.config import config, parse_listen
from trytond import backend
from trytond.pool import Pool
from trytond.monitor import monitor
from .transaction import Transaction
class TrytonServer(object):
def __init__(self, options):
config.update_etc(options.configfile)
if options.logconf:
logging.config.fileConfig(options.logconf)
logging.getLogger('server').info('using %s as logging '
'configuration file', options.logconf)
else:
logformat = ('%(process)s %(thread)s [%(asctime)s] '
'%(levelname)s %(name)s %(message)s')
if options.verbose:
if options.dev:
level = logging.DEBUG
else:
level = logging.INFO
else:
level = logging.ERROR
logging.basicConfig(level=level, format=logformat)
self.logger = logging.getLogger(__name__)
if options.configfile:
self.logger.info('using %s as configuration file',
options.configfile)
else:
self.logger.info('using default configuration')
self.logger.info('initialising distributed objects services')
self.xmlrpcd = []
self.jsonrpcd = []
self.webdavd = []
self.options = options
if time.tzname[0] != 'UTC':
self.logger.error('timezone is not set to UTC')
def run(self):
"Run the server and never return"
init = {}
signal.signal(signal.SIGINT, lambda *a: self.stop())
signal.signal(signal.SIGTERM, lambda *a: self.stop())
if hasattr(signal, 'SIGQUIT'):
signal.signal(signal.SIGQUIT, lambda *a: self.stop())
if hasattr(signal, 'SIGUSR1'):
signal.signal(signal.SIGUSR1, lambda *a: self.restart())
if self.options.pidfile:
with open(self.options.pidfile, 'w') as fd_pid:
fd_pid.write("%d" % (os.getpid()))
if not self.options.update:
self.start_servers()
for db_name in self.options.database_names:
init[db_name] = False
try:
with Transaction().start(db_name, 0) as transaction:
cursor = transaction.cursor
if self.options.update:
if not cursor.test():
self.logger.info("init db")
backend.get('Database').init(cursor)
init[db_name] = True
cursor.commit()
elif not cursor.test():
raise Exception("'%s' is not a Tryton database!" %
db_name)
except Exception:
self.stop(False)
raise
for db_name in self.options.database_names:
if self.options.update:
with Transaction().start(db_name, 0) as transaction:
cursor = transaction.cursor
if not cursor.test():
raise Exception("'%s' is not a Tryton database!"
% db_name)
cursor.execute('SELECT code FROM ir_lang '
'WHERE translatable')
lang = [x[0] for x in cursor.fetchall()]
else:
lang = None
Pool(db_name).init(update=self.options.update, lang=lang)
for db_name in self.options.database_names:
if init[db_name]:
# try to read password from environment variable
# TRYTONPASSFILE, empty TRYTONPASSFILE ignored
passpath = os.getenv('TRYTONPASSFILE')
password = ''
if passpath:
try:
with open(passpath) as passfile:
password = passfile.readline()[:-1]
except Exception, err:
sys.stderr.write('Can not read password '
'from "%s": "%s"\n' % (passpath, err))
if not password:
while True:
password = getpass('Admin Password for %s: ' % db_name)
password2 = getpass('Admin Password Confirmation: ')
if password != password2:
sys.stderr.write('Admin Password Confirmation '
'doesn\'t match Admin Password!\n')
continue
if not password:
sys.stderr.write('Admin Password is required!\n')
continue
break
with Transaction().start(db_name, 0) as transaction:
pool = Pool()
User = pool.get('res.user')
admin, = User.search([('login', '=', 'admin')])
User.write([admin], {
'password': password,
})
transaction.cursor.commit()
if self.options.update:
self.logger.info('Update/Init succeed!')
logging.shutdown()
sys.exit(0)
threads = {}
while True:
if self.options.cron:
for dbname in Pool.database_list():
thread = threads.get(dbname)
if thread and thread.is_alive():
continue
pool = Pool(dbname)
if not pool.lock.acquire(0):
continue
try:
try:
Cron = pool.get('ir.cron')
except KeyError:
continue
finally:
pool.lock.release()
thread = threading.Thread(
target=Cron.run,
args=(dbname,), kwargs={})
thread.start()
threads[dbname] = thread
if self.options.dev:
for _ in range(60):
if monitor([self.options.configfile]
if self.options.configfile else []):
self.restart()
time.sleep(1)
else:
time.sleep(60)
def start_servers(self):
ssl = config.get('ssl', 'privatekey')
# Launch Server
if config.get('jsonrpc', 'listen'):
from trytond.protocols.jsonrpc import JSONRPCDaemon
for hostname, port in parse_listen(
config.get('jsonrpc', 'listen')):
self.jsonrpcd.append(JSONRPCDaemon(hostname, port, ssl))
self.logger.info("starting JSON-RPC%s protocol on %s:%d",
ssl and ' SSL' or '', hostname or '*', port)
if config.get('xmlrpc', 'listen'):
from trytond.protocols.xmlrpc import XMLRPCDaemon
for hostname, port in parse_listen(
config.get('xmlrpc', 'listen')):
self.xmlrpcd.append(XMLRPCDaemon(hostname, port, ssl))
self.logger.info("starting XML-RPC%s protocol on %s:%d",
ssl and ' SSL' or '', hostname or '*', port)
if config.get('webdav', 'listen'):
from trytond.protocols.webdav import WebDAVServerThread
for hostname, port in parse_listen(
config.get('webdav', 'listen')):
self.webdavd.append(WebDAVServerThread(hostname, port, ssl))
self.logger.info("starting WebDAV%s protocol on %s:%d",
ssl and ' SSL' or '', hostname or '*', port)
for servers in (self.xmlrpcd, self.jsonrpcd, self.webdavd):
for server in servers:
server.start()
def stop(self, exit=True):
for servers in (self.xmlrpcd, self.jsonrpcd, self.webdavd):
for server in servers:
server.stop()
server.join()
if exit:
if self.options.pidfile:
os.unlink(self.options.pidfile)
logging.getLogger('server').info('stopped')
logging.shutdown()
sys.exit(0)
def restart(self):
self.stop(False)
args = ([sys.executable] + ['-W%s' % o for o in sys.warnoptions]
+ sys.argv)
if sys.platform == "win32":
args = ['"%s"' % arg for arg in args]
os.execv(sys.executable, args)