summaryrefslogtreecommitdiff
path: root/scripts/scriptmgr.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/scriptmgr.py')
-rwxr-xr-xscripts/scriptmgr.py220
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()
+