Author: tmckay
Date: 2012-05-16 19:55:30 +0000 (Wed, 16 May 2012)
New Revision: 5380
Added:
trunk/cumin/bin/cumin-checkpid
Modified:
trunk/cumin/bin/cumin
trunk/cumin/bin/cumin-data
trunk/cumin/bin/cumin-report
trunk/cumin/bin/cumin-web
trunk/cumin/python/cumin/config.py
Log:
Add pidfile writing in the cumin master. Add cumin-checkpid to check for the existence of
the pidfile and return cumin startup status. This eliminates the need for the sysvinit
script to do a double start (run with init checks first, followed by a full run).
sysvinit will be updated separately.
BZ801287
Modified: trunk/cumin/bin/cumin
===================================================================
--- trunk/cumin/bin/cumin 2012-05-16 18:23:13 UTC (rev 5379)
+++ trunk/cumin/bin/cumin 2012-05-16 19:55:30 UTC (rev 5380)
@@ -54,15 +54,43 @@
home = os.environ.get("CUMIN_HOME",
os.path.normpath("/usr/share/cumin"))
syslog.syslog("cumin: master script exited with errors, see
%s/log/master.log" % home)
+# Used to write the pidfile and the startup status files.
+def write_status_path(p, value):
+ try:
+ f = open(p, "w+")
+ if value:
+ f.write(value)
+ f.close()
+ except:
+ pass
+
+def read_status_path(p):
+ try:
+ f = open(p, "r")
+ v = f.readline()
+ if v:
+ v = v[:-1]
+ except:
+ v = ""
+ return v
+
def main():
# tuple indices, for clarity
PROCESS = 0
ARGS = 1
PROG_STRING = 2
+ INIT_FILE = 3
+ wrote_status = False
+
config = CuminMasterConfig()
+ # Clear the status path file. A monitoring program will
+ # check for writes here to find out the status of init
+ # checks (after a pidfile is written)
+ write_status_path(config.get_init_status_path(), "")
+
# Trap exit from parser and save standard error for logging
# Then put stderr back to original value
r, w = os.pipe()
@@ -114,6 +142,9 @@
parser.add_option("--syslog", dest="syslog",
action="store_true", default=False,
help="Log general error notfications to syslog. Intended
for systemd")
+ parser.add_option("--p", dest="write_pid",
action="store_true", default=False,
+ help="Write the pid file (/var/run/cumin.pid). For service
starts.")
+
(options, args) = parser.parse_args()
except SystemExit:
options = args = None
@@ -123,56 +154,74 @@
sys.stderr.close()
sys.stderr = sys.__stderr__
- # Parse may have failed, in which case make a quick check for options ourselves
+ # Parse may have failed, in which case make a quick check for important
+ # options ourselves
if options:
_syslog.enabled = options.syslog
console = options.console
+ write_pid = options.write_pid
else:
_syslog.enabled = "--syslog" in sys.argv[1:]
console = "--console" in sys.argv[1:]
+ write_pid = "--p" in sys.argv[1:]
+
+ # Write the pidfile
+ if write_pid:
+ write_status_path("/var/run/cumin.pid",
str(os.getpid())+"\n")
+
+ # Set up logging
if console:
log_dest = sys.stderr
else:
log_dest = os.path.join(home, "log", "master.log")
enable_logging("cumin.master", logging.INFO, log_dest)
-
# Parser exited, either on --help or with errors
if not options:
- return log_parse_errors(r)
-
+ v = log_parse_errors(r)
+ write_status_path(config.get_init_status_path(),
+ str(v)+"\n")
+ return v
+
if len(args) != 0:
log.error("Extra arguments:" + "".join([" "+arg for
arg in args]))
_syslog.log()
+ write_status_path(config.get_init_status_path(), "1\n")
return 1
- # Get our list of cumin-web and data instances
- # create list elements to hold the process object, section arg, and app
apps = []
- if len(options.webs) > 0:
- for instance in options.webs.split(','):
- args, prog_string = get_args("cumin-web", instance,
- options.init_only, console,
- exit_s, options.timeout,
- options.web_options)
- apps.append([None, args, prog_string])
+ init_files = []
- if len(options.datas) > 0:
- for instance in options.datas.split(','):
- args, prog_string = get_args("cumin-data", instance,
- options.init_only, console,
- exit_s, options.timeout,
- options.data_options)
- apps.append([None, args, prog_string])
+ # This gives us the list of section names, plus the app name,
+ # plus extra options to pass on to the children for each app type
+ sections = [options.webs, options.datas, options.reports]
+ names = ["cumin-web", "cumin-data", "cumin-report"]
+ child_options = [options.web_options,
+ options.data_options,
+ options.report_options]
+ zipped = zip(sections, names, child_options)
- if len(options.reports) > 0:
- for instance in options.reports.split(','):
- args, prog_string = get_args("cumin-report", instance,
- options.init_only, console,
- exit_s, options.timeout,
- options.report_options)
- apps.append([None, args, prog_string])
-
+ for info in zipped:
+ instances, app, extra_options = info
+ if len(instances) > 0:
+ for instance in instances.split(','):
+ instance = instance.strip()
+
+ args, prog_string = get_args(app,
+ instance,
+ options.init_only,
+ console,
+ exit_s,
+ options.timeout,
+ extra_options)
+
+ init_path = config.get_init_status_path(instance)
+ init_files.append(init_path)
+ apps.append([None, args, prog_string, init_path])
+
+ # Clear the init file
+ write_status_path(init_path, "")
+
# Launch and babysit, do not restart if options.init_only is set
complete = 0
return_code = 0
@@ -218,6 +267,9 @@
app[PROCESS] = None
return_code = 2
complete = len(apps)
+ write_status_path(config.get_init_status_path(),
+ str(poll)+"\n")
+ wrote_status = True
break
else:
log.info("Subprocess (%s) exited with status %s (%s),
%s"\
@@ -229,7 +281,29 @@
complete += 1
else:
start(app, "Restart")
+
+ elif app[INIT_FILE] in init_files:
+ # Check the init status file for the child so that we know
+ # it has made it through init checks. When all children
+ # have made it through init checks, we write our own status.
+ value = read_status_path(app[INIT_FILE])
+ if value:
+ if value != "0":
+ # we should see an init failure soon
+ # ignore init_files and wait for the poll
+ init_files = []
+ else:
+ init_files.remove(app[INIT_FILE])
+ if len(init_files) == 0:
+ write_status_path(config.get_init_status_path(),
+ "0"+"\n")
+ wrote_status = True
finally:
+ # Hmm, exiting, but we haven't written the status file yet.
+ # If we had seen an init failure from a child we would have
+ # written the file. Let's assume it was a success.
+ if not wrote_status:
+ write_status_path(config.get_init_status_path(),
"0"+"\n")
log.info("Write termination string to all children")
complete = 0
for app in apps:
Added: trunk/cumin/bin/cumin-checkpid
===================================================================
--- trunk/cumin/bin/cumin-checkpid (rev 0)
+++ trunk/cumin/bin/cumin-checkpid 2012-05-16 19:55:30 UTC (rev 5380)
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+import sys
+import os
+from datetime import datetime, timedelta
+from time import sleep
+from cumin.config import CuminMasterConfig
+def main():
+
+ # Wait for the pidfile to exist
+ then = datetime.now()
+ while not os.path.isfile("/var/run/cumin.pid"):
+ sleep(0.25)
+ if datetime.now() - then > timedelta(seconds=10):
+ return -1
+
+ # Okay, we have a pid. Now wait for the status file.
+ # Get the path from a config object for the master.
+ config = CuminMasterConfig()
+ p = config.get_init_status_path()
+ while not os.path.isfile(p):
+ sleep(0.25)
+ if datetime.now() - then > timedelta(seconds=10):
+ return -1
+
+ # Okay, now let's read the status.
+ # It should be an int on the first line.
+ while True:
+ try:
+ f = open(p, "r")
+ v = f.readline()
+ if v:
+ return int(v[:-1])
+
+ # Okay, if the master has not written an init error
+ # status within 10 seconds, assume everything is fine
+ if datetime.now() - then > timedelta(seconds=10):
+ return 0
+
+ sleep(0.25)
+ except:
+ print "\ncumin-checkpid error, assuming service start"
+ break
+ return 0
+
+if __name__ == "__main__":
+ try:
+ sys.exit(main())
+ except KeyboardInterrupt:
+ sys.exit(0)
Property changes on: trunk/cumin/bin/cumin-checkpid
___________________________________________________________________
Added: svn:executable
+ *
Modified: trunk/cumin/bin/cumin-data
===================================================================
--- trunk/cumin/bin/cumin-data 2012-05-16 18:23:13 UTC (rev 5379)
+++ trunk/cumin/bin/cumin-data 2012-05-16 19:55:30 UTC (rev 5380)
@@ -95,9 +95,19 @@
ret = ret << 1 | passed_init
return ret
+def write_status_path(p, value):
+ try:
+ f = open(p, "w+")
+ if value:
+ f.write(value)
+ f.close()
+ except:
+ pass
+
def main():
passed_init = 1
return_code = CuminErrors.NO_ERROR
+ wrote_status = False
# Do our own simple option check so we can redirect IO early
# without worrying about other options or the behavior of optParse
@@ -135,6 +145,8 @@
try:
mint = None
+ config = None
+
setup_initial_logging()
parser = BrokeredOptionParser()
@@ -168,7 +180,6 @@
if len(args) != 0:
log.error("Extra arguments:" + "".join([" "+arg
for arg in args]))
raise ArgError
-
model_dir = [os.path.join(config.home, x) for x in ("model",
"model/admin")]
@@ -208,6 +219,10 @@
return_code, agents = process_agents(values.agents, opts.section)
mint.qmf_agents = agents
+ # Okay, record the init check status here
+ write_status_path(config.get_init_status_path(),
str(return_code)+"\n")
+ wrote_status = True
+
# If init_only was set or we failed init, don't proceed...
if not opts.init_only and not return_code:
passed_init = 0
@@ -271,6 +286,12 @@
print_exc()
return_code = CuminErrors.UNHANDLED_ERROR
+ # For parse errors, for example, we won't have a config object yet
+ # but the return code will indicate an init error anyway so a
+ # parent can check the return code.
+ if not wrote_status and config:
+ write_status_path(config.get_init_status_path(), str(return_code)+"\n")
+
def shutdown():
if mint:
mint.stop()
Modified: trunk/cumin/bin/cumin-report
===================================================================
--- trunk/cumin/bin/cumin-report 2012-05-16 18:23:13 UTC (rev 5379)
+++ trunk/cumin/bin/cumin-report 2012-05-16 19:55:30 UTC (rev 5380)
@@ -65,9 +65,20 @@
ret = ret << 1 | passed_init
return ret
+def write_status_path(p, value):
+ try:
+ f = open(p, "w+")
+ if value:
+ f.write(value)
+ f.close()
+ except:
+ pass
+
def main():
passed_init = 1
return_code = CuminErrors.NO_ERROR
+ wrote_status = False
+ config = None
# Do our own simple option check so we can redirect IO early
# without worrying about other options or the behavior of optParse
@@ -185,6 +196,10 @@
if opts.init_only and opts.delete_on_start:
mint.update_thread._delete_all_objects()
+ # Okay, record the init check status here
+ write_status_path(config.get_init_status_path(),
str(return_code)+"\n")
+ wrote_status = True
+
# If init_only was set or we failed init, don't proceed...
if not opts.init_only and not return_code:
passed_init = 0
@@ -248,6 +263,12 @@
print_exc()
return_code = CuminErrors.UNHANDLED_ERROR
+ # For parse errors, for example, we won't have a config object yet
+ # but the return code will indicate an init error anyway so a
+ # parent can check the return code.
+ if not wrote_status and config:
+ write_status_path(config.get_init_status_path(),
str(return_code)+"\n")
+
def shutdown():
if mint:
mint.stop()
Modified: trunk/cumin/bin/cumin-web
===================================================================
--- trunk/cumin/bin/cumin-web 2012-05-16 18:23:13 UTC (rev 5379)
+++ trunk/cumin/bin/cumin-web 2012-05-16 19:55:30 UTC (rev 5380)
@@ -98,9 +98,19 @@
ret = ret << 1 | passed_init
return ret
+def write_status_path(p, value):
+ try:
+ f = open(p, "w+")
+ if value:
+ f.write(value)
+ f.close()
+ except:
+ pass
+
def main():
passed_init = 1
return_code = CuminErrors.NO_ERROR
+ wrote_status = False
# Do our own simple option check so we can redirect IO early
# without worrying about other options or the behavior of optParse
@@ -143,6 +153,7 @@
try:
cumin = None
+ config = None
setup_initial_logging()
@@ -227,6 +238,10 @@
cumin.check()
cumin.init()
+ # Okay, record the init check status here
+ write_status_path(config.get_init_status_path(),
str(return_code)+"\n")
+ wrote_status = True
+
if not opts.init_only and not return_code:
passed_init = 0
@@ -280,6 +295,12 @@
traceback.print_exc()
return_code = CuminErrors.UNHANDLED_ERROR
+ # For parse errors, for example, we won't have a config object yet
+ # but the return code will indicate an init error anyway so a
+ # parent can check the return code.
+ if not wrote_status and config:
+ write_status_path(config.get_init_status_path(),
str(return_code)+"\n")
+
def shutdown():
if cumin:
cumin.stop()
Modified: trunk/cumin/python/cumin/config.py
===================================================================
--- trunk/cumin/python/cumin/config.py 2012-05-16 18:23:13 UTC (rev 5379)
+++ trunk/cumin/python/cumin/config.py 2012-05-16 19:55:30 UTC (rev 5380)
@@ -8,7 +8,7 @@
log = logging.getLogger("cumin.config")
class CuminConfig(Config):
- def __init__(self):
+ def __init__(self, section_name):
super(CuminConfig, self).__init__()
hdef = os.path.normpath("/usr/share/cumin")
@@ -17,12 +17,22 @@
if not os.path.isdir(self.home):
raise Exception("Home path '%s' is not a directory")
+ # Remember the config section we were created with
+ self.section_name = section_name
+
def get_home(self):
return self.home
def get_access_root(self):
return os.path.join(self.home, "model/access/")
+ def get_init_status_path(self, section_name=""):
+ if not section_name:
+ section_name = self.section_name
+ if section_name:
+ return os.path.join(self.home, "log", "." + section_name
+ ".init")
+ return ""
+
def parse(self):
paths = list()
@@ -39,20 +49,20 @@
file for apps which do not need any more
'''
def __init__(self):
- super(CuminCommonConfig, self).__init__()
+ super(CuminCommonConfig, self).__init__("common")
- self.section = CuminConfigSection(self, "common")
+ self.section = CuminConfigSection(self, self.section_name)
def set_log(self, log):
self.section.log_file.default = log
class CuminWebConfig(CuminConfig):
def __init__(self, section_name="web", strict_section="False"):
- super(CuminWebConfig, self).__init__()
+ super(CuminWebConfig, self).__init__(section_name)
- web = CuminWebConfigSection(self, section_name, strict_section)
+ web = CuminWebConfigSection(self, self.section_name, strict_section)
web.log_file.default = os.path.join(self.home, "log",
- section_name + ".log")
+ self.section_name + ".log")
def _common_data_section(self, data, name):
data.log_file.default = os.path.join(self.home, "log", name +
".log")
@@ -77,23 +87,24 @@
class CuminDataConfig(CuminConfig):
def __init__(self, section_name="data", strict_section=False):
- super(CuminDataConfig, self).__init__()
+ super(CuminDataConfig, self).__init__(section_name)
- data = CuminDataConfigSection(self, section_name, strict_section)
- _common_data_section(self, data, section_name)
+ data = CuminDataConfigSection(self, self.section_name, strict_section)
+ _common_data_section(self, data, self.section_name)
class CuminReportConfig(CuminConfig):
def __init__(self, section_name="report", strict_section=False):
- super(CuminReportConfig, self).__init__()
+ super(CuminReportConfig, self).__init__(section_name)
- data = CuminReportConfigSection(self, section_name, strict_section)
- _common_data_section(self, data, section_name)
+ data = CuminReportConfigSection(self,
+ self.section_name, strict_section)
+ _common_data_section(self, data, self.section_name)
class CuminMasterConfig(CuminConfig):
def __init__(self, section_name="master", strict_section=False):
- super(CuminMasterConfig, self).__init__()
+ super(CuminMasterConfig, self).__init__(section_name)
- master = ConfigSection(self, section_name, strict_section)
+ master = ConfigSection(self, self.section_name, strict_section)
param = ConfigParameter(master, "datas", str)
param.default = "data"