"""
Server
======
Run the iceprod server.
"""
import os
import sys
import logging
import importlib
import subprocess
from datetime import datetime, timedelta
import asyncio
from concurrent.futures import ThreadPoolExecutor
from iceprod.core.logger import set_log_level
from iceprod.server.config import IceProdConfig
logger = logging.getLogger('Server')
[docs]
class Server(object):
"""
The actual server.
"""
def __init__(self, config_params=None, outfile=None, errfile=None):
self.executor = ThreadPoolExecutor(max_workers=10)
self.cfg = IceProdConfig(override=config_params)
self.modules = {}
self.services = {
'daemon': {
'restart': self.restart,
'reload': self.reload,
'stop': self.stop,
'kill': self.kill,
'get_running_modules': lambda: self.modules.keys(),
},
}
self.outfile = outfile
self.errfile = errfile
set_log_level(self.cfg['logging']['level'])
for mod_name in self.cfg['modules']:
if self.cfg['modules'][mod_name]:
try:
m = importlib.import_module('iceprod.server.modules.'+mod_name)
mod = getattr(m, mod_name)(cfg=self.cfg,
executor=self.executor,
modules=self.services)
self.modules[mod_name] = mod
self.services[mod_name] = mod.service
mod.start()
except Exception:
logger.critical('cannot start module', exc_info=True)
self.kill()
raise
[docs]
async def rotate_logs(self):
current_date = datetime.utcnow()
while self.outfile and self.errfile:
if current_date.day != datetime.utcnow().day:
# rotate files
current_date = datetime.utcnow()
if self.outfile:
roll_files(sys.stdout, self.outfile)
if self.errfile:
roll_files(sys.stderr, self.errfile)
await asyncio.sleep(3600)
[docs]
def start(self):
asyncio.create_task(self.rotate_logs())
[docs]
def restart(self):
env = os.environ.copy()
extra_path = os.path.join(os.environ['I3PROD'],'bin')
env['PATH'] = extra_path+(':'+env['PATH'] if 'PATH' in env else '')
subprocess.Popen(['iceprod_server.py','restart'],
cwd=os.environ['I3PROD'], env=env)
[docs]
def reload(self):
for m in self.modules.values():
m.stop()
m.start()
[docs]
def stop(self):
for m in self.modules.values():
m.stop()
[docs]
def kill(self):
for m in self.modules.values():
m.kill()
[docs]
def roll_files(fd, filename, num_files=5):
d = datetime.utcnow()
ext = (d-timedelta(days=num_files-1)).strftime('%Y-%m-%d')
newfile = f'{filename}.{ext}'
if os.path.exists(newfile): # delete last file
os.remove(newfile)
for i in range(num_files-2, 0, -1):
ext = (d-timedelta(days=i)).strftime('%Y-%m-%d')
oldfile = f'{filename}.{ext}'
if os.path.exists(oldfile):
os.rename(oldfile, newfile)
newfile = oldfile
if os.path.exists(filename):
os.rename(filename, newfile)
# redirect file descriptor
newfd = open(filename, fd.mode)
fd.flush()
os.dup2(newfd.fileno(), fd.fileno())
if fd != sys.stdout and fd != sys.stderr:
fd.close()
return newfd