Commit 04ccf5e9 authored by Guido Trotter's avatar Guido Trotter
Browse files

Collapse daemon's main function



With three ganeti daemons, and one or two more coming, the daemon's main
function started becoming too much cut&pasted code. Collapsing most of
it in a daemon.GenericMain function. Some more code could be collapsed
between the two http-based daemons, but since the new daemons won't be
http-based we won't do it right now.

As a bonus a functionality for overriding the network port on the
command line for all network based nodes is added.
Signed-off-by: default avatarGuido Trotter <ultrotter@google.com>
parent 83052f9e
...@@ -43,6 +43,7 @@ from optparse import OptionParser ...@@ -43,6 +43,7 @@ from optparse import OptionParser
from ganeti import config from ganeti import config
from ganeti import constants from ganeti import constants
from ganeti import daemon
from ganeti import mcpu from ganeti import mcpu
from ganeti import opcodes from ganeti import opcodes
from ganeti import jqueue from ganeti import jqueue
...@@ -383,35 +384,6 @@ class GanetiContext(object): ...@@ -383,35 +384,6 @@ class GanetiContext(object):
self.glm.remove(locking.LEVEL_NODE, name) self.glm.remove(locking.LEVEL_NODE, name)
def ParseOptions():
"""Parse the command line options.
@return: (options, args) as from OptionParser.parse_args()
"""
parser = OptionParser(description="Ganeti master daemon",
usage="%prog [-f] [-d]",
version="%%prog (ganeti) %s" %
constants.RELEASE_VERSION)
parser.add_option("-f", "--foreground", dest="fork",
help="Don't detach from the current terminal",
default=True, action="store_false")
parser.add_option("-d", "--debug", dest="debug",
help="Enable some debug messages",
default=False, action="store_true")
parser.add_option("--no-voting", dest="no_voting",
help="Do not check that the nodes agree on this node"
" being the master and start the daemon unconditionally",
default=False, action="store_true")
parser.add_option("--yes-do-it", dest="yes_do_it",
help="Override interactive check for --no-voting",
default=False, action="store_true")
options, args = parser.parse_args()
return options, args
def CheckAgreement(): def CheckAgreement():
"""Check the agreement on who is the master. """Check the agreement on who is the master.
...@@ -468,17 +440,10 @@ def CheckAgreement(): ...@@ -468,17 +440,10 @@ def CheckAgreement():
return result return result
def CheckMASTERD(options, args):
"""Initial checks whether to run or exit with a failure
def main(): """
"""Main function"""
options, args = ParseOptions()
utils.no_fork = True
daemon_name = constants.MASTERD
if options.fork:
utils.CloseFDs()
rpc.Init() rpc.Init()
try: try:
ssconf.CheckMaster(options.debug) ssconf.CheckMaster(options.debug)
...@@ -496,32 +461,20 @@ def main(): ...@@ -496,32 +461,20 @@ def main():
elif not options.no_voting: elif not options.no_voting:
if not CheckAgreement(): if not CheckAgreement():
return return
dirs = [(constants.RUN_GANETI_DIR, constants.RUN_DIRS_MODE),
(constants.SOCKET_DIR, constants.SOCKET_DIR_MODE),
]
utils.EnsureDirs(dirs)
# This is safe to do as the pid file guarantees against
# concurrent execution.
utils.RemoveFile(constants.MASTER_SOCKET)
master = IOServer(constants.MASTER_SOCKET, ClientRqHandler)
finally: finally:
rpc.Shutdown() rpc.Shutdown()
# become a daemon
if options.fork:
utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
utils.WritePidFile(daemon_name) def ExecMASTERD(options, args):
try: """Main MASTERD function, executed with the pidfile held.
utils.SetupLogging(constants.DAEMONS_LOGFILES[daemon_name],
debug=options.debug,
stderr_logging=not options.fork, multithreaded=True)
logging.info("Ganeti master daemon startup") """
# This is safe to do as the pid file guarantees against
# concurrent execution.
utils.RemoveFile(constants.MASTER_SOCKET)
master = IOServer(constants.MASTER_SOCKET, ClientRqHandler)
try:
rpc.Init() rpc.Init()
try: try:
# activate ip # activate ip
...@@ -539,9 +492,27 @@ def main(): ...@@ -539,9 +492,27 @@ def main():
finally: finally:
rpc.Shutdown() rpc.Shutdown()
finally: finally:
utils.RemovePidFile(daemon_name)
utils.RemoveFile(constants.MASTER_SOCKET) utils.RemoveFile(constants.MASTER_SOCKET)
def main():
"""Main function"""
parser = OptionParser(description="Ganeti master daemon",
usage="%prog [-f] [-d]",
version="%%prog (ganeti) %s" %
constants.RELEASE_VERSION)
parser.add_option("--no-voting", dest="no_voting",
help="Do not check that the nodes agree on this node"
" being the master and start the daemon unconditionally",
default=False, action="store_true")
parser.add_option("--yes-do-it", dest="yes_do_it",
help="Override interactive check for --no-voting",
default=False, action="store_true")
dirs = [(constants.RUN_GANETI_DIR, constants.RUN_DIRS_MODE),
(constants.SOCKET_DIR, constants.SOCKET_DIR_MODE),
]
daemon.GenericMain(constants.MASTERD, parser, dirs,
CheckMASTERD, ExecMASTERD)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
...@@ -732,83 +732,51 @@ class NodeHttpServer(http.server.HttpServer): ...@@ -732,83 +732,51 @@ class NodeHttpServer(http.server.HttpServer):
return backend.ValidateHVParams(hvname, hvparams) return backend.ValidateHVParams(hvname, hvparams)
def ParseOptions(): def CheckNODED(options, args):
"""Parse the command line options. """Initial checks whether to run exit with a failure
@return: (options, args) as from OptionParser.parse_args()
""" """
parser = OptionParser(description="Ganeti node daemon", for fname in (constants.SSL_CERT_FILE,):
usage="%prog [-f] [-d] [-b ADDRESS]", if not os.path.isfile(fname):
version="%%prog (ganeti) %s" % print "config %s not there, will not run." % fname
constants.RELEASE_VERSION) sys.exit(constants.EXIT_NOTCLUSTER)
parser.add_option("-f", "--foreground", dest="fork",
help="Don't detach from the current terminal",
default=True, action="store_false")
parser.add_option("-d", "--debug", dest="debug",
help="Enable some debug messages",
default=False, action="store_true")
parser.add_option("-b", "--bind", dest="bind_address",
help="Bind address",
default="", metavar="ADDRESS")
options, args = parser.parse_args()
return options, args
def main(): def ExecNODED(options, args):
"""Main function for the node daemon. """Main NODED function, executed with the pidfile held.
""" """
global queue_lock global queue_lock
daemon_name = constants.NODED
options, args = ParseOptions() # Read SSL certificate
ssl_params = http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
ssl_cert_path=constants.SSL_CERT_FILE)
if options.fork: # Prepare job queue
utils.CloseFDs() queue_lock = jstore.InitAndVerifyQueue(must_lock=False)
for fname in (constants.SSL_CERT_FILE,): mainloop = daemon.Mainloop()
if not os.path.isfile(fname): server = NodeHttpServer(mainloop, options.bind_address, options.port,
print "config %s not there, will not run." % fname ssl_params=ssl_params, ssl_verify_peer=True)
sys.exit(constants.EXIT_NOTCLUSTER) server.Start()
try:
mainloop.Run()
finally:
server.Stop()
port = utils.GetDaemonPort(constants.NODED)
def main():
"""Main function for the node daemon.
"""
parser = OptionParser(description="Ganeti node daemon",
usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
version="%%prog (ganeti) %s" %
constants.RELEASE_VERSION)
dirs = [(val, constants.RUN_DIRS_MODE) for val in constants.SUB_RUN_DIRS] dirs = [(val, constants.RUN_DIRS_MODE) for val in constants.SUB_RUN_DIRS]
dirs.append((constants.LOG_OS_DIR, 0750)) dirs.append((constants.LOG_OS_DIR, 0750))
dirs.append((constants.LOCK_DIR, 1777)) dirs.append((constants.LOCK_DIR, 1777))
utils.EnsureDirs(dirs) daemon.GenericMain(constants.NODED, parser, dirs, CheckNODED, ExecNODED)
# become a daemon
if options.fork:
utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
utils.WritePidFile(daemon_name)
try:
utils.SetupLogging(logfile=constants.DAEMONS_LOGFILES[daemon_name],
debug=options.debug,
stderr_logging=not options.fork)
logging.info("ganeti node daemon startup")
# Read SSL certificate
ssl_params = http.HttpSslParams(ssl_key_path=constants.SSL_CERT_FILE,
ssl_cert_path=constants.SSL_CERT_FILE)
# Prepare job queue
queue_lock = jstore.InitAndVerifyQueue(must_lock=False)
mainloop = daemon.Mainloop()
server = NodeHttpServer(mainloop, options.bind_address, port,
ssl_params=ssl_params, ssl_verify_peer=True)
server.Start()
try:
mainloop.Run()
finally:
server.Stop()
finally:
utils.RemovePidFile(daemon_name)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -177,93 +177,70 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication, ...@@ -177,93 +177,70 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
return result return result
def ParseOptions(): def CheckRAPI(options, args):
"""Parse the command line options. """Initial checks whether to run or exit with a failure
@return: (options, args) as from OptionParser.parse_args()
""" """
parser = optparse.OptionParser(description="Ganeti Remote API",
usage="%prog [-d]",
version="%%prog (ganeti) %s" %
constants.RAPI_VERSION)
parser.add_option("-d", "--debug", dest="debug",
help="Enable some debug messages",
default=False, action="store_true")
parser.add_option("--no-ssl", dest="ssl",
help="Do not secure HTTP protocol with SSL",
default=True, action="store_false")
parser.add_option("-K", "--ssl-key", dest="ssl_key",
help="SSL key",
default=constants.RAPI_CERT_FILE, type="string")
parser.add_option("-C", "--ssl-cert", dest="ssl_cert",
help="SSL certificate",
default=constants.RAPI_CERT_FILE, type="string")
parser.add_option("-f", "--foreground", dest="fork",
help="Don't detach from the current terminal",
default=True, action="store_false")
parser.add_option("-b", "--bind", dest="bind_address",
help="Bind address",
default="", metavar="ADDRESS")
options, args = parser.parse_args()
if len(args) != 0: if len(args) != 0:
print >> sys.stderr, "Usage: %s [-d]" % sys.argv[0] print >> sys.stderr, "Usage: %s [-f] [-d] [-p port] [-b ADDRESS]" % \
sys.argv[0]
sys.exit(constants.EXIT_FAILURE) sys.exit(constants.EXIT_FAILURE)
if options.ssl and not (options.ssl_cert and options.ssl_key): if options.ssl:
print >> sys.stderr, ("For secure mode please provide " if not (options.ssl_cert and options.ssl_key):
"--ssl-key and --ssl-cert arguments") print >> sys.stderr, ("For secure mode please provide "
sys.exit(constants.EXIT_FAILURE) "--ssl-key and --ssl-cert arguments")
sys.exit(constants.EXIT_FAILURE)
for fname in (options.ssl_cert, options.ssl_key):
if not os.path.isfile(fname):
print >> sys.stderr, "config %s not there, will not run." % fname
sys.exit(constants.EXIT_FAILURE)
return options, args ssconf.CheckMaster(options.debug)
def main(): def ExecRAPI(options, args):
"""Main function. """Main RAPI function, executed with the pidfile held.
""" """
options, args = ParseOptions() # Read SSL certificate
daemon_name = constants.RAPI
if options.fork:
utils.CloseFDs()
if options.ssl: if options.ssl:
# Read SSL certificate ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key,
try: ssl_cert_path=options.ssl_cert)
ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key,
ssl_cert_path=options.ssl_cert)
except Exception, err:
sys.stderr.write("Can't load the SSL certificate/key: %s\n" % (err,))
sys.exit(constants.EXIT_FAILURE)
else: else:
ssl_params = None ssl_params = None
ssconf.CheckMaster(options.debug) mainloop = daemon.Mainloop()
port = utils.GetDaemonPort(constants.RAPI) server = RemoteApiHttpServer(mainloop, options.bind_address, options.port,
ssl_params=ssl_params, ssl_verify_peer=False,
request_executor_class=JsonErrorRequestExecutor)
server.Start()
try:
mainloop.Run()
finally:
server.Stop()
if options.fork:
utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
utils.SetupLogging(constants.DAEMONS_LOGFILES[daemon_name], debug=options.debug, def main():
stderr_logging=not options.fork) """Main function.
utils.WritePidFile(daemon_name) """
try: parser = optparse.OptionParser(description="Ganeti Remote API",
mainloop = daemon.Mainloop() usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
server = RemoteApiHttpServer(mainloop, options.bind_address, port, version="%%prog (ganeti) %s" % constants.RAPI_VERSION)
ssl_params=ssl_params, ssl_verify_peer=False, parser.add_option("--no-ssl", dest="ssl",
request_executor_class= help="Do not secure HTTP protocol with SSL",
JsonErrorRequestExecutor) default=True, action="store_false")
server.Start() parser.add_option("-K", "--ssl-key", dest="ssl_key",
try: help="SSL key",
mainloop.Run() default=constants.RAPI_CERT_FILE, type="string")
finally: parser.add_option("-C", "--ssl-cert", dest="ssl_cert",
server.Stop() help="SSL certificate",
finally: default=constants.RAPI_CERT_FILE, type="string")
utils.RemovePidFile(daemon_name)
dirs = [(val, constants.RUN_DIRS_MODE) for val in constants.SUB_RUN_DIRS]
dirs.append((constants.LOG_OS_DIR, 0750))
daemon.GenericMain(constants.RAPI, parser, dirs, CheckRAPI, ExecRAPI)
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -115,6 +115,8 @@ NODED = "ganeti-noded" ...@@ -115,6 +115,8 @@ NODED = "ganeti-noded"
RAPI = "ganeti-rapi" RAPI = "ganeti-rapi"
MASTERD = "ganeti-masterd" MASTERD = "ganeti-masterd"
MULTITHREADED_DAEMONS = frozenset([MASTERD])
DAEMONS_PORTS = { DAEMONS_PORTS = {
# daemon-name: ("proto", "default-port") # daemon-name: ("proto", "default-port")
NODED: ("tcp", 1811), NODED: ("tcp", 1811),
......
...@@ -26,8 +26,10 @@ import select ...@@ -26,8 +26,10 @@ import select
import signal import signal
import errno import errno
import time import time
import logging
from ganeti import utils from ganeti import utils
from ganeti import constants
class Timer(object): class Timer(object):
...@@ -297,3 +299,65 @@ class Mainloop(object): ...@@ -297,3 +299,65 @@ class Mainloop(object):
""" """
self._timer_remove.append(timer_id) self._timer_remove.append(timer_id)
def GenericMain(daemon_name, optionparser, dirs, check_fn, exec_fn):
"""Shared main function for daemons.
@type daemon_name: string
@param daemon_name: daemon name
@type optionparser: L{optparse.OptionParser}
@param optionparser: initialized optionparser with daemon-specific options
(common -f -d options will be handled by this module)
@type options: object @param options: OptionParser result, should contain at
least the fork and the debug options
@type dirs: list of strings
@param dirs: list of directories that must exist for this daemon to work
@type check_fn: function which accepts (options, args)
@param check_fn: function that checks start conditions and exits if they're
not met
@type exec_fn: function which accepts (options, args)
@param exec_fn: function that's executed with the daemon's pid file held, and
runs the daemon itself.
"""
optionparser.add_option("-f", "--foreground", dest="fork",
help="Don't detach from the current terminal",
default=True, action="store_false")
optionparser.add_option("-d", "--debug", dest="debug",
help="Enable some debug messages",
default=False, action="store_true")
if daemon_name in constants.DAEMONS_PORTS:
# for networked daemons we also allow choosing the bind port and address.
# by default we use the port provided by utils.GetDaemonPort, and bind to
# 0.0.0.0 (which is represented by and empty bind address.
port = utils.GetDaemonPort(daemon_name)
optionparser.add_option("-p", "--port", dest="port",
help="Network port (%s default)." % port,
default=port, type="int")
optionparser.add_option("-b", "--bind", dest="bind_address",
help="Bind address",
default="", metavar="ADDRESS")
multithread = utils.no_fork = daemon_name in constants.MULTITHREADED_DAEMONS
options, args = optionparser.parse_args()
check_fn(options, args)
utils.EnsureDirs(dirs)
if options.fork:
utils.CloseFDs()
utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
utils.WritePidFile(daemon_name)
try:
utils.SetupLogging(logfile=constants.DAEMONS_LOGFILES[daemon_name],
debug=options.debug,
stderr_logging=not options.fork,
multithreaded=multithread)
logging.info("%s daemon startup" % daemon_name)
exec_fn(options, args)
finally:
utils.RemovePidFile(daemon_name)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment