X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/0623d351313854d09cc58ff8657e95d206dc958d..30e4e7419ba8ad4178afdd4731658c154bc59e78:/daemons/ganeti-noded diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded index 67081e8..fce0d29 100755 --- a/daemons/ganeti-noded +++ b/daemons/ganeti-noded @@ -21,14 +21,16 @@ """Ganeti node daemon""" -# functions in this module need to have a given name structure, so: -# pylint: disable-msg=C0103 +# pylint: disable-msg=C0103,W0142 + +# C0103: Functions in this module need to have a given name structure, +# and the name of the daemon doesn't match + +# W0142: Used * or ** magic, since we do use it extensively in this +# module import os import sys -import traceback -import SocketServer -import errno import logging import signal @@ -42,8 +44,9 @@ from ganeti import jstore from ganeti import daemon from ganeti import http from ganeti import utils +from ganeti import storage -import ganeti.http.server +import ganeti.http.server # pylint: disable-msg=W0611 queue_lock = None @@ -73,6 +76,9 @@ class NodeHttpServer(http.server.HttpServer): This class holds all methods exposed over the RPC interface. """ + # too many public methods, and unused args - all methods get params + # due to the API + # pylint: disable-msg=R0904,W0613 def __init__(self, *args, **kwargs): http.server.HttpServer.__init__(self, *args, **kwargs) self.noded_pid = os.getpid() @@ -93,7 +99,9 @@ class NodeHttpServer(http.server.HttpServer): raise http.HttpNotFound() try: - return method(req.request_body) + rvalue = method(req.request_body) + return True, rvalue + except backend.RPCFail, err: # our custom failure exception; str(err) works fine if the # exception was constructed with a single argument, and in @@ -107,9 +115,9 @@ class NodeHttpServer(http.server.HttpServer): # And return the error's arguments, which must be already in # correct tuple format return err.args - except: + except Exception, err: logging.exception("Error in RPC call") - raise + return False, "Error while executing backend function: %s" % str(err) # the new block devices -------------------------- @@ -199,8 +207,9 @@ class NodeHttpServer(http.server.HttpServer): """ disks = [objects.Disk.FromDict(dsk_s) - for dsk_s in params] - return backend.BlockdevGetmirrorstatus(disks) + for dsk_s in params] + return [status.ToDict() + for status in backend.BlockdevGetmirrorstatus(disks)] @staticmethod def perspective_blockdev_find(params): @@ -210,7 +219,12 @@ class NodeHttpServer(http.server.HttpServer): """ disk = objects.Disk.FromDict(params[0]) - return backend.BlockdevFind(disk) + + result = backend.BlockdevFind(disk) + if result is None: + return None + + return result.ToDict() @staticmethod def perspective_blockdev_snapshot(params): @@ -241,6 +255,23 @@ class NodeHttpServer(http.server.HttpServer): disks = [objects.Disk.FromDict(cf) for cf in params[1]] return backend.BlockdevClose(params[0], disks) + @staticmethod + def perspective_blockdev_getsize(params): + """Compute the sizes of the given block devices. + + """ + disks = [objects.Disk.FromDict(cf) for cf in params[0]] + return backend.BlockdevGetsize(disks) + + @staticmethod + def perspective_blockdev_export(params): + """Compute the sizes of the given block devices. + + """ + disk = objects.Disk.FromDict(params[0]) + dest_node, dest_path, cluster_name = params[1:] + return backend.BlockdevExport(disk, dest_node, dest_path, cluster_name) + # blockdev/drbd specific methods ---------- @staticmethod @@ -338,12 +369,12 @@ class NodeHttpServer(http.server.HttpServer): # volume -------------------------- @staticmethod - def perspective_volume_list(params): + def perspective_lv_list(params): """Query the list of logical volumes in a given volume group. """ vgname = params[0] - return True, backend.GetVolumeList(vgname) + return backend.GetVolumeList(vgname) @staticmethod def perspective_vg_list(params): @@ -352,6 +383,32 @@ class NodeHttpServer(http.server.HttpServer): """ return backend.ListVolumeGroups() + # Storage -------------------------- + + @staticmethod + def perspective_storage_list(params): + """Get list of storage units. + + """ + (su_name, su_args, name, fields) = params + return storage.GetStorage(su_name, *su_args).List(name, fields) + + @staticmethod + def perspective_storage_modify(params): + """Modify a storage unit. + + """ + (su_name, su_args, name, changes) = params + return storage.GetStorage(su_name, *su_args).Modify(name, changes) + + @staticmethod + def perspective_storage_execute(params): + """Execute an operation on a storage unit. + + """ + (su_name, su_args, name, op) = params + return storage.GetStorage(su_name, *su_args).Execute(name, op) + # bridge -------------------------- @staticmethod @@ -399,7 +456,8 @@ class NodeHttpServer(http.server.HttpServer): """ instance = objects.Instance.FromDict(params[0]) - return backend.InstanceShutdown(instance) + timeout = params[1] + return backend.InstanceShutdown(instance, timeout) @staticmethod def perspective_instance_start(params): @@ -451,7 +509,8 @@ class NodeHttpServer(http.server.HttpServer): """ instance = objects.Instance.FromDict(params[0]) reboot_type = params[1] - return backend.InstanceReboot(instance, reboot_type) + shutdown_timeout = params[2] + return backend.InstanceReboot(instance, reboot_type, shutdown_timeout) @staticmethod def perspective_instance_info(params): @@ -480,7 +539,7 @@ class NodeHttpServer(http.server.HttpServer): """Query the list of running instances. """ - return True, backend.GetInstanceList(params[0]) + return backend.GetInstanceList(params[0]) # node -------------------------- @@ -497,7 +556,7 @@ class NodeHttpServer(http.server.HttpServer): """Checks if a node has the given ip address. """ - return True, utils.OwnIpAddress(params[0]) + return utils.OwnIpAddress(params[0]) @staticmethod def perspective_node_info(params): @@ -527,7 +586,7 @@ class NodeHttpServer(http.server.HttpServer): """Promote this node to master status. """ - return backend.StartMaster(params[0]) + return backend.StartMaster(params[0], params[1]) @staticmethod def perspective_node_stop_master(params): @@ -541,7 +600,7 @@ class NodeHttpServer(http.server.HttpServer): """Cleanup after leaving a cluster. """ - return backend.LeaveCluster() + return backend.LeaveCluster(params[0]) @staticmethod def perspective_node_volumes(params): @@ -608,7 +667,7 @@ class NodeHttpServer(http.server.HttpServer): """Query detailed information about existing OSes. """ - return [os_obj.ToDict() for os_obj in backend.DiagnoseOS()] + return backend.DiagnoseOS() @staticmethod def perspective_os_get(params): @@ -616,10 +675,7 @@ class NodeHttpServer(http.server.HttpServer): """ name = params[0] - try: - os_obj = backend.OSFromDisk(name) - except errors.InvalidOS, err: - os_obj = objects.OS.FromInvalidOS(err) + os_obj = backend.OSFromDisk(name) return os_obj.ToDict() # hooks ----------------------- @@ -652,7 +708,10 @@ class NodeHttpServer(http.server.HttpServer): """ duration = params[0] - return utils.TestDelay(duration) + status, rval = utils.TestDelay(duration) + if not status: + raise backend.RPCFail(rval) + return rval # file storage --------------- @@ -730,86 +789,54 @@ 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(_, args): + """Initial checks whether to run or exit with a failure. """ - parser = OptionParser(description="Ganeti node daemon", - usage="%prog [-f] [-d] [-b ADDRESS]", - version="%%prog (ganeti) %s" % - constants.RELEASE_VERSION) + if args: # noded doesn't take any arguments + print >> sys.stderr, ("Usage: %s [-f] [-d] [-p port] [-b ADDRESS]" % + sys.argv[0]) + sys.exit(constants.EXIT_FAILURE) + - 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") +def ExecNoded(options, _): + """Main node daemon function, executed with the PID file held. - options, args = parser.parse_args() - return options, args + """ + global queue_lock # pylint: disable-msg=W0603 + + # Read SSL certificate + if options.ssl: + ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key, + ssl_cert_path=options.ssl_cert) + else: + ssl_params = None + + # Prepare job queue + queue_lock = jstore.InitAndVerifyQueue(must_lock=False) + + 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() def main(): """Main function for the node daemon. """ - global queue_lock - - options, args = ParseOptions() - utils.debug = options.debug - - if options.fork: - utils.CloseFDs() - - for fname in (constants.SSL_CERT_FILE,): - if not os.path.isfile(fname): - print "config %s not there, will not run." % fname - sys.exit(5) - - try: - port = utils.GetNodeDaemonPort() - except errors.ConfigurationError, err: - print "Cluster configuration incomplete: '%s'" % str(err) - sys.exit(5) - + 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.LOG_NODESERVER) - - utils.WritePidFile(constants.NODED_PID) - try: - utils.SetupLogging(logfile=constants.LOG_NODESERVER, 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(constants.NODED_PID) + daemon.GenericMain(constants.NODED, parser, dirs, CheckNoded, ExecNoded) if __name__ == '__main__':