diff options
Diffstat (limited to 'scripts/scriptmgr.py')
-rwxr-xr-x | scripts/scriptmgr.py | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/scripts/scriptmgr.py b/scripts/scriptmgr.py new file mode 100755 index 00000000..2ee742b2 --- /dev/null +++ b/scripts/scriptmgr.py @@ -0,0 +1,220 @@ +#! /usr/bin/env python + +"""Bulk start/stop of scripts. + +Reads a bunch of config files and maps them to scripts, then handles those. +""" + +import sys, os, skytools, signal, glob, ConfigParser, time + +command_usage = """ +%prog [options] INI CMD [subcmd args] + +commands: + start [-a | jobname ..] start a job + stop [-a | jobname ..] stop a job + restart [-a | jobname ..] restart job(s) + reload [-a | jobname ..] send reload signal + status +""" + +def job_sort_cmp(j1, j2): + d1 = j1['service'] + j1['job_name'] + d2 = j2['service'] + j2['job_name'] + if d1 < d2: return -1 + elif d1 > d2: return 1 + else: return 0 + +class ScriptMgr(skytools.DBScript): + def init_optparse(self, p = None): + p = skytools.DBScript.init_optparse(self, p) + p.add_option("-a", "--all", action="store_true", help="apply command to all jobs") + p.set_usage(command_usage.strip()) + return p + + def load_jobs(self): + self.svc_list = [] + self.svc_map = {} + self.config_list = [] + + # load services + svc_list = self.cf.sections() + svc_list.remove(self.service_name) + for svc_name in svc_list: + cf = self.cf.clone(svc_name) + disabled = cf.getboolean('disabled', 0) + defscript = None + if disabled: + defscript = '/disabled' + svc = { + 'service': svc_name, + 'script': cf.getfile('script', defscript), + 'cwd': cf.getfile('cwd'), + 'disabled': cf.getboolean('disabled', 0), + 'args': cf.get('args', ''), + } + self.svc_list.append(svc) + self.svc_map[svc_name] = svc + + # generate config list + for tmp in self.cf.getlist('config_list'): + tmp = os.path.expanduser(tmp) + tmp = os.path.expandvars(tmp) + for fn in glob.glob(tmp): + self.config_list.append(fn) + + # read jobs + for fn in self.config_list: + raw = ConfigParser.SafeConfigParser({'job_name':'?', 'service_name':'?'}) + raw.read(fn) + + # skip its own config + if raw.has_section(self.service_name): + continue + + got = 0 + for sect in raw.sections(): + if sect in self.svc_map: + got = 1 + self.add_job(fn, sect) + if not got: + self.log.warning('Cannot find service for %s' % fn) + + def add_job(self, cf_file, service_name): + svc = self.svc_map[service_name] + cf = skytools.Config(service_name, cf_file) + disabled = svc['disabled'] + if not disabled: + disabled = cf.getboolean('disabled', 0) + job = { + 'disabled': disabled, + 'config': cf_file, + 'cwd': svc['cwd'], + 'script': svc['script'], + 'args': svc['args'], + 'service': svc['service'], + 'job_name': cf.get('job_name'), + 'pidfile': cf.getfile('pidfile'), + } + self.job_list.append(job) + self.job_map[job['job_name']] = job + + def cmd_status(self): + for job in self.job_list: + os.chdir(job['cwd']) + cf = skytools.Config(job['service'], job['config']) + pidfile = cf.getfile('pidfile') + name = job['job_name'] + svc = job['service'] + if job['disabled']: + name += " (disabled)" + + if os.path.isfile(pidfile): + print " OK [%s] %s" % (svc, name) + else: + print " STOPPED [%s] %s" % (svc, name) + + def cmd_info(self): + for job in self.job_list: + print job + + def cmd_start(self, job_name): + job = self.job_map[job_name] + if job['disabled']: + self.log.info("Skipping %s" % job_name) + return 0 + self.log.info('Starting %s' % job_name) + os.chdir(job['cwd']) + pidfile = job['pidfile'] + if os.path.isfile(pidfile): + self.log.warning("Script %s seems running") + return 0 + cmd = "%(script)s %(config)s %(args)s -d" % job + res = os.system(cmd) + self.log.debug(res) + if res != 0: + self.log.error('startup failed: %s' % job_name) + return 1 + else: + return 0 + + def cmd_stop(self, job_name): + job = self.job_map[job_name] + if job['disabled']: + self.log.info("Skipping %s" % job_name) + return + self.log.info('Stopping %s' % job_name) + self.signal_job(job, signal.SIGINT) + + def cmd_reload(self, job_name): + job = self.job_map[job_name] + if job['disabled']: + self.log.info("Skipping %s" % job_name) + return + self.log.info('Reloading %s' % job_name) + self.signal_job(job, signal.SIGHUP) + + def signal_job(self, job, sig): + os.chdir(job['cwd']) + pidfile = job['pidfile'] + if os.path.isfile(pidfile): + pid = int(open(pidfile).read()) + os.kill(pid, sig) + else: + self.log.warning("Job %s not running" % job['job_name']) + + def work(self): + self.set_single_loop(1) + self.job_list = [] + self.job_map = {} + self.load_jobs() + + if len(self.args) < 2: + print "need command" + sys.exit(1) + + jobs = self.args[2:] + if len(jobs) == 0 and self.options.all: + for job in self.job_list: + jobs.append(job['job_name']) + + self.job_list.sort(job_sort_cmp) + + cmd = self.args[1] + if cmd == "status": + self.cmd_status() + return + elif cmd == "info": + self.cmd_info() + return + + if len(jobs) == 0: + print "no jobs given?" + sys.exit(1) + + if cmd == "start": + err = 0 + for n in jobs: + err += self.cmd_start(n) + if err > 0: + self.log.error('some scripts failed') + sys.exit(1) + elif cmd == "stop": + for n in jobs: + self.cmd_stop(n) + elif cmd == "restart": + for n in jobs: + self.cmd_stop(n) + time.sleep(2) + self.cmd_start(n) + elif cmd == "reload": + for n in self.jobs: + self.cmd_reload(n) + else: + print "unknown command:", cmd + sys.exit(1) + +if __name__ == '__main__': + script = ScriptMgr('scriptmgr', sys.argv[1:]) + script.start() + |