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
from ganeti import config
from ganeti import constants
from ganeti import daemon
from ganeti import mcpu
from ganeti import opcodes
from ganeti import jqueue
......@@ -383,35 +384,6 @@ class GanetiContext(object):
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():
"""Check the agreement on who is the master.
......@@ -468,17 +440,10 @@ def CheckAgreement():
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()
try:
ssconf.CheckMaster(options.debug)
......@@ -496,32 +461,20 @@ def main():
elif not options.no_voting:
if not CheckAgreement():
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:
rpc.Shutdown()
# become a daemon
if options.fork:
utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
utils.WritePidFile(daemon_name)
try:
utils.SetupLogging(constants.DAEMONS_LOGFILES[daemon_name],
debug=options.debug,
stderr_logging=not options.fork, multithreaded=True)
def ExecMASTERD(options, args):
"""Main MASTERD function, executed with the pidfile held.
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()
try:
# activate ip
......@@ -539,9 +492,27 @@ def main():
finally:
rpc.Shutdown()
finally:
utils.RemovePidFile(daemon_name)
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__":
main()
......@@ -732,83 +732,51 @@ class NodeHttpServer(http.server.HttpServer):
return backend.ValidateHVParams(hvname, hvparams)
def ParseOptions():
"""Parse the command line options.
@return: (options, args) as from OptionParser.parse_args()
def CheckNODED(options, args):
"""Initial checks whether to run exit with a failure
"""
parser = OptionParser(description="Ganeti node daemon",
usage="%prog [-f] [-d] [-b ADDRESS]",
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("-b", "--bind", dest="bind_address",
help="Bind address",
default="", metavar="ADDRESS")
options, args = parser.parse_args()
return options, args
for fname in (constants.SSL_CERT_FILE,):
if not os.path.isfile(fname):
print "config %s not there, will not run." % fname
sys.exit(constants.EXIT_NOTCLUSTER)
def main():
"""Main function for the node daemon.
def ExecNODED(options, args):
"""Main NODED function, executed with the pidfile held.
"""
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:
utils.CloseFDs()
# Prepare job queue
queue_lock = jstore.InitAndVerifyQueue(must_lock=False)
for fname in (constants.SSL_CERT_FILE,):
if not os.path.isfile(fname):
print "config %s not there, will not run." % fname
sys.exit(constants.EXIT_NOTCLUSTER)
mainloop = daemon.Mainloop()
server = NodeHttpServer(mainloop, options.bind_address, options.port,
ssl_params=ssl_params, ssl_verify_peer=True)
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.append((constants.LOG_OS_DIR, 0750))
dirs.append((constants.LOCK_DIR, 1777))
utils.EnsureDirs(dirs)
# 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)
daemon.GenericMain(constants.NODED, parser, dirs, CheckNODED, ExecNODED)
if __name__ == '__main__':
......
......@@ -177,93 +177,70 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
return result
def ParseOptions():
"""Parse the command line options.
@return: (options, args) as from OptionParser.parse_args()
def CheckRAPI(options, args):
"""Initial checks whether to run or exit with a failure
"""
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:
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)
if options.ssl and not (options.ssl_cert and options.ssl_key):
print >> sys.stderr, ("For secure mode please provide "
"--ssl-key and --ssl-cert arguments")
sys.exit(constants.EXIT_FAILURE)
if options.ssl:
if not (options.ssl_cert and options.ssl_key):
print >> sys.stderr, ("For secure mode please provide "
"--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():
"""Main function.
def ExecRAPI(options, args):
"""Main RAPI function, executed with the pidfile held.
"""
options, args = ParseOptions()
daemon_name = constants.RAPI
if options.fork:
utils.CloseFDs()
# Read SSL certificate
if options.ssl:
# Read SSL certificate
try:
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)
ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key,
ssl_cert_path=options.ssl_cert)
else:
ssl_params = None
ssconf.CheckMaster(options.debug)
port = utils.GetDaemonPort(constants.RAPI)
mainloop = daemon.Mainloop()
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,
stderr_logging=not options.fork)
def main():
"""Main function.
utils.WritePidFile(daemon_name)
try:
mainloop = daemon.Mainloop()
server = RemoteApiHttpServer(mainloop, options.bind_address, port,
ssl_params=ssl_params, ssl_verify_peer=False,
request_executor_class=
JsonErrorRequestExecutor)
server.Start()
try:
mainloop.Run()
finally:
server.Stop()
finally:
utils.RemovePidFile(daemon_name)
"""
parser = optparse.OptionParser(description="Ganeti Remote API",
usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
version="%%prog (ganeti) %s" % constants.RAPI_VERSION)
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")
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__':
......
......@@ -115,6 +115,8 @@ NODED = "ganeti-noded"
RAPI = "ganeti-rapi"
MASTERD = "ganeti-masterd"
MULTITHREADED_DAEMONS = frozenset([MASTERD])
DAEMONS_PORTS = {
# daemon-name: ("proto", "default-port")
NODED: ("tcp", 1811),
......
......@@ -26,8 +26,10 @@ import select
import signal
import errno
import time
import logging
from ganeti import utils
from ganeti import constants
class Timer(object):
......@@ -297,3 +299,65 @@ class Mainloop(object):
"""
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