freezer/freezer/scheduler/daemon.py

223 lines
6.1 KiB
Python

# Copyright 2015 Hewlett-Packard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import signal
import traceback
from oslo_log import log
from tempfile import gettempdir
from time import sleep
from freezer.lib.pep3143daemon import DaemonContext
from freezer.lib.pep3143daemon import PidFile
LOG = log.getLogger(__name__)
def get_filenos(logger):
"""
Get a list of file no from logger
"""
filenos = []
for handler in logger.handlers:
filenos.append(handler.stream.fileno())
if logger.parent:
filenos += get_filenos(logger.parent)
return filenos
def is_process_running(pid):
"""
Checks whether the process is running.
:param pid: process pid to check
:return: true if the process is running
"""
try:
os.kill(pid, 0)
except OSError:
return False
else:
return True
class NoDaemon(object):
"""
A class which shares the same interface as the Daemon class,
but is used to execute the scheduler as a foreground process
"""
instance = None
exit_flag = False
def __init__(self, daemonizable):
# daemonizable has to provide start/stop (and possibly reload) methods
NoDaemon.instance = self
self.daemonizable = daemonizable
# register signal handlers
for (signal_number, handler) in self.signal_map.items():
signal.signal(signal_number, handler)
@property
def signal_map(self):
return {
signal.SIGTERM: NoDaemon.handle_program_exit,
signal.SIGINT: NoDaemon.handle_program_exit,
signal.SIGHUP: NoDaemon.handle_reload,
}
@staticmethod
def handle_program_exit(signum, frame):
LOG.info('Got signal {0}. Exiting ...'.format(signum))
NoDaemon.exit_flag = True
NoDaemon.instance.daemonizable.stop()
@staticmethod
def handle_reload(signum, frame):
NoDaemon.instance.daemonizable.reload()
def start(self, dump_stack_trace=False):
while not NoDaemon.exit_flag:
try:
LOG.info('Starting in no-daemon mode')
self.daemonizable.start()
NoDaemon.exit_flag = True
except Exception as e:
if dump_stack_trace:
LOG.error(traceback.format_exc(e))
LOG.error('Restarting procedure in no-daemon mode '
'after Fatal Error: {0}'.format(e))
sleep(10)
LOG.info('Done exiting')
def stop(self):
pass
def status(self):
pass
def restart(self):
pass
def reload(self):
pass
class Daemon(object):
"""
A class to manage all the daemon-related stuff
"""
instance = None
exit_flag = False
def __init__(self, daemonizable=None, pid_fname=None):
# daemonizable has to provide start/stop (and possibly reload) methods
Daemon.instance = self
self._pid_fname = pid_fname
self.daemonizable = daemonizable
@staticmethod
def handle_program_exit(signum, frame):
Daemon.exit_flag = True
Daemon.instance.daemonizable.stop()
@staticmethod
def handle_reload(signum, frame):
Daemon.instance.daemonizable.reload()
@property
def signal_map(self):
return {
signal.SIGTERM: Daemon.handle_program_exit,
signal.SIGHUP: Daemon.handle_reload,
}
@property
def pid_fname(self):
if not self._pid_fname:
fname = '{0}/freezer_sched_{1}.pid'.format(
gettempdir(),
os.path.split(os.path.expanduser('~'))[-1])
self._pid_fname = os.path.normpath(fname)
return self._pid_fname
@property
def pid(self):
if os.path.isfile(self.pid_fname):
with open(self.pid_fname, 'r') as f:
return int(f.read())
return None
def start(self, dump_stack_trace=False):
if os.path.exists(self.pid_fname) and \
is_process_running(self.pid):
print('freezer daemon is already running, '
'pid: {0}'.format(self.pid))
return
pidfile = PidFile(self.pid_fname)
files_preserve = get_filenos(LOG.logger)
with DaemonContext(pidfile=pidfile, signal_map=self.signal_map,
files_preserve=files_preserve):
while not Daemon.exit_flag:
try:
LOG.info('freezer daemon starting, pid: {0}'.
format(self.pid))
self.daemonizable.start()
Daemon.exit_flag = True
except Exception as e:
if dump_stack_trace:
LOG.error(traceback.format_exc(e))
LOG.error('Restarting daemonized procedure '
'after Fatal Error: {0}'.format(e))
sleep(10)
LOG.info('freezer daemon done, pid: {0}'.format(self.pid))
def stop(self):
pid = self.pid
if pid:
os.kill(self.pid, signal.SIGTERM)
else:
print('Not Running')
def restart(self):
pid = self.pid
if not pid:
self.start()
else:
self.stop()
sleep(5)
self.start()
def status(self):
pid = self.pid
if pid:
print('Running with pid: {0}'.format(pid))
else:
print('Not Running')
def reload(self):
pid = self.pid
if pid:
os.kill(pid, signal.SIGHUP)
else:
print('Not Running')