Commit b13dfb92 authored by Iustin Pop's avatar Iustin Pop

Merge branch 'devel-2.1' into stable-2.1

parents b8313b29 82187135
......@@ -411,6 +411,10 @@ lib/_autoconf.py: Makefile stamp-directories
echo ''; \
echo '"""'; \
echo ''; \
echo '# pylint: disable-msg=C0301,C0324'; \
echo '# because this is autogenerated, we do not want'; \
echo '# style warnings' ; \
echo ''; \
echo "PACKAGE_VERSION = '$(PACKAGE_VERSION)'"; \
echo "VERSION_MAJOR = '$(VERSION_MAJOR)'"; \
echo "VERSION_MINOR = '$(VERSION_MINOR)'"; \
......@@ -469,6 +473,10 @@ ganeti:
check-local:
$(CHECK_PYTHON_CODE) $(check_python_code)
.PHONY: lint
lint: ganeti
pylint $(LINT_OPTS) ganeti $(dist_sbin_SCRIPTS) $(dist_tools_SCRIPTS)
# a dist hook rule for catching revision control directories
distcheck-hook:
if find $(top_distdir) -name .svn -or -name .git | grep .; then \
......
......@@ -26,12 +26,20 @@ It uses UDP+HMAC for authentication with a global cluster key.
"""
# pylint: disable-msg=C0103
# C0103: Invalid name ganeti-confd
import os
import sys
import logging
import pyinotify
import time
try:
# pylint: disable-msg=E0611
from pyinotify import pyinotify
except ImportError:
import pyinotify
from optparse import OptionParser
from ganeti import asyncnotifier
......@@ -40,7 +48,6 @@ from ganeti.confd import server as confd_server
from ganeti import constants
from ganeti import errors
from ganeti import daemon
from ganeti import ssconf
class ConfdAsyncUDPServer(daemon.AsyncUDPSocket):
......@@ -63,7 +70,7 @@ class ConfdAsyncUDPServer(daemon.AsyncUDPSocket):
self.port = port
self.processor = processor
self.bind((bind_address, port))
logging.debug("listening on ('%s':%d)" % (bind_address, port))
logging.debug("listening on ('%s':%d)", bind_address, port)
# this method is overriding a daemon.AsyncUDPSocket method
def handle_datagram(self, payload_in, ip, port):
......@@ -84,23 +91,25 @@ class ConfdAsyncUDPServer(daemon.AsyncUDPSocket):
class ConfdInotifyEventHandler(pyinotify.ProcessEvent):
def __init__(self, watch_manager, callback,
file=constants.CLUSTER_CONF_FILE):
filename=constants.CLUSTER_CONF_FILE):
"""Constructor for ConfdInotifyEventHandler
@type watch_manager: L{pyinotify.WatchManager}
@param watch_manager: ganeti-confd inotify watch manager
@type callback: function accepting a boolean
@param callback: function to call when an inotify event happens
@type file: string
@param file: config file to watch
@type filename: string
@param filename: config file to watch
"""
# no need to call the parent's constructor
self.watch_manager = watch_manager
self.callback = callback
# pylint: disable-msg=E1103
# pylint for some reason doesn't see the below constants
self.mask = pyinotify.EventsCodes.IN_IGNORED | \
pyinotify.EventsCodes.IN_MODIFY
self.file = file
self.file = filename
self.watch_handle = None
def enable(self):
......@@ -130,7 +139,7 @@ class ConfdInotifyEventHandler(pyinotify.ProcessEvent):
# IN_IGNORED event from inotify, because of the file removal (which is
# contextual with the replacement). In such a case we need to create
# another watcher for the "new" file.
logging.debug("Received 'ignored' inotify event for %s" % event.path)
logging.debug("Received 'ignored' inotify event for %s", event.path)
self.watch_handle = None
try:
......@@ -140,7 +149,7 @@ class ConfdInotifyEventHandler(pyinotify.ProcessEvent):
# going to realod the file after setting up the new watch.
self.callback(False)
except errors.ConfdFatalError, err:
logging.critical("Critical error, shutting down: %s" % err)
logging.critical("Critical error, shutting down: %s", err)
sys.exit(constants.EXIT_FAILURE)
except:
# we need to catch any exception here, log it, but proceed, because even
......@@ -153,12 +162,12 @@ class ConfdInotifyEventHandler(pyinotify.ProcessEvent):
# usually happen in Ganeti, as the config file is normally replaced by a
# new one, at filesystem level, rather than actually modified (see
# utils.WriteFile)
logging.debug("Received 'modify' inotify event for %s" % event.path)
logging.debug("Received 'modify' inotify event for %s", event.path)
try:
self.callback(True)
except errors.ConfdFatalError, err:
logging.critical("Critical error, shutting down: %s" % err)
logging.critical("Critical error, shutting down: %s", err)
sys.exit(constants.EXIT_FAILURE)
except:
# we need to catch any exception here, log it, but proceed, because even
......@@ -167,7 +176,7 @@ class ConfdInotifyEventHandler(pyinotify.ProcessEvent):
logging.error("Unexpected exception", exc_info=True)
def process_default(self, event):
logging.error("Received unhandled inotify event: %s" % event)
logging.error("Received unhandled inotify event: %s", event)
class ConfdConfigurationReloader(object):
......@@ -315,10 +324,14 @@ class ConfdConfigurationReloader(object):
self._ResetTimer()
def CheckConfd(options, args):
def CheckConfd(_, args):
"""Initial checks whether to run exit with a failure.
"""
if args: # confd doesn't take any arguments
print >> sys.stderr, ("Usage: %s [-f] [-d] [-b ADDRESS]" % sys.argv[0])
sys.exit(constants.EXIT_FAILURE)
# TODO: collapse HMAC daemons handling in daemons GenericMain, when we'll
# have more than one.
if not os.path.isfile(constants.HMAC_CLUSTER_KEY):
......@@ -326,10 +339,13 @@ def CheckConfd(options, args):
sys.exit(constants.EXIT_FAILURE)
def ExecConfd(options, args):
def ExecConfd(options, _):
"""Main confd function, executed with PID file held
"""
# TODO: clarify how the server and reloader variables work (they are
# not used)
# pylint: disable-msg=W0612
mainloop = daemon.Mainloop()
# Asyncronous confd UDP server
......@@ -340,7 +356,7 @@ def ExecConfd(options, args):
# If enabling the processor has failed, we can still go on, but confd will
# be disabled
logging.warning("Confd is starting in disabled mode")
pass
server = ConfdAsyncUDPServer(options.bind_address, options.port, processor)
# Configuration reloader
......
......@@ -26,6 +26,8 @@ inheritance from parent classes requires it.
"""
# pylint: disable-msg=C0103
# C0103: Invalid name ganeti-masterd
import os
import sys
......@@ -61,6 +63,7 @@ EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
class ClientRequestWorker(workerpool.BaseWorker):
# pylint: disable-msg=W0221
def RunTask(self, server, request, client_address):
"""Process the request.
......@@ -70,7 +73,7 @@ class ClientRequestWorker(workerpool.BaseWorker):
try:
server.finish_request(request, client_address)
server.close_request(request)
except:
except: # pylint: disable-msg=W0702
server.handle_error(request, client_address)
server.close_request(request)
......@@ -108,7 +111,7 @@ class IOServer(SocketServer.UnixStreamServer):
self.request_workers.AddTask(self, request, client_address)
@utils.SignalHandled([signal.SIGINT, signal.SIGTERM])
def serve_forever(self, signal_handlers=None):
def serve_forever(self, signal_handlers=None): # pylint: disable-msg=W0221
"""Handle one request at a time until told to quit."""
assert isinstance(signal_handlers, dict) and \
len(signal_handlers) > 0, \
......@@ -141,6 +144,8 @@ class ClientRqHandler(SocketServer.BaseRequestHandler):
READ_SIZE = 4096
def setup(self):
# pylint: disable-msg=W0201
# setup() is the api for initialising for this class
self._buffer = ""
self._msgs = collections.deque()
self._ops = ClientOps(self.server)
......@@ -204,11 +209,13 @@ class ClientOps:
def __init__(self, server):
self.server = server
def handle_request(self, method, args):
def handle_request(self, method, args): # pylint: disable-msg=R0911
queue = self.server.context.jobqueue
# TODO: Parameter validation
# TODO: Rewrite to not exit in each 'if/elif' branch
if method == luxi.REQ_SUBMIT_JOB:
logging.info("Received new job")
ops = [opcodes.OpCode.LoadOpCode(state) for state in args]
......@@ -292,6 +299,12 @@ class ClientOps:
op = opcodes.OpQueryClusterInfo()
return self._Query(op)
elif method == luxi.REQ_QUERY_TAGS:
kind, name = args
logging.info("Received tags query request")
op = opcodes.OpGetTags(kind=kind, name=name)
return self._Query(op)
elif method == luxi.REQ_QUEUE_SET_DRAIN_FLAG:
drain_flag = args
logging.info("Received queue drain flag change request to %s",
......@@ -333,6 +346,8 @@ class GanetiContext(object):
This class creates and holds common objects shared by all threads.
"""
# pylint: disable-msg=W0212
# we do want to ensure a singleton here
_instance = None
def __init__(self):
......@@ -498,12 +513,12 @@ def _RunInSeparateProcess(fn):
# Call function
result = int(bool(fn()))
assert result in (0, 1)
except:
except: # pylint: disable-msg=W0702
logging.exception("Error while calling function in separate process")
# 0 and 1 are reserved for the return value
result = 33
os._exit(result)
os._exit(result) # pylint: disable-msg=W0212
# Parent process
......@@ -529,6 +544,10 @@ def CheckMasterd(options, args):
"""Initial checks whether to run or exit with a failure.
"""
if args: # masterd doesn't take any arguments
print >> sys.stderr, ("Usage: %s [-f] [-d]" % sys.argv[0])
sys.exit(constants.EXIT_FAILURE)
ssconf.CheckMaster(options.debug)
# If CheckMaster didn't fail we believe we are the master, but we have to
......@@ -544,7 +563,7 @@ def CheckMasterd(options, args):
confirmation = sys.stdin.readline().strip()
if confirmation != "YES":
print >>sys.stderr, "Aborting."
print >> sys.stderr, "Aborting."
sys.exit(constants.EXIT_FAILURE)
return
......@@ -555,7 +574,7 @@ def CheckMasterd(options, args):
sys.exit(constants.EXIT_FAILURE)
def ExecMasterd (options, args):
def ExecMasterd (options, args): # pylint: disable-msg=W0613
"""Main master daemon function, executed with the PID file held.
"""
......
......@@ -21,12 +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 SocketServer
import logging
import signal
......@@ -42,7 +46,7 @@ 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
......@@ -72,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()
......@@ -782,11 +789,21 @@ class NodeHttpServer(http.server.HttpServer):
return backend.ValidateHVParams(hvname, hvparams)
def ExecNoded(options, args):
def CheckNoded(_, args):
"""Initial checks whether to run or exit with a failure.
"""
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)
def ExecNoded(options, _):
"""Main node daemon function, executed with the PID file held.
"""
global queue_lock
global queue_lock # pylint: disable-msg=W0603
# Read SSL certificate
if options.ssl:
......@@ -819,7 +836,7 @@ def main():
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))
daemon.GenericMain(constants.NODED, parser, dirs, None, ExecNoded)
daemon.GenericMain(constants.NODED, parser, dirs, CheckNoded, ExecNoded)
if __name__ == '__main__':
......
......@@ -18,28 +18,29 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
""" Ganeti Remote API master script.
"""Ganeti Remote API master script.
"""
import glob
# pylint: disable-msg=C0103,W0142
# C0103: Invalid name ganeti-watcher
import logging
import optparse
import sys
import os
import os.path
import signal
from ganeti import constants
from ganeti import errors
from ganeti import http
from ganeti import daemon
from ganeti import ssconf
from ganeti import utils
from ganeti import luxi
from ganeti import serializer
from ganeti.rapi import connector
import ganeti.http.auth
import ganeti.http.auth # pylint: disable-msg=W0611
import ganeti.http.server
......@@ -105,7 +106,7 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
method = req.request_method.upper()
try:
ctx.handler_fn = getattr(ctx.handler, method)
except AttributeError, err:
except AttributeError:
raise http.HttpBadRequest("Method %s is unsupported for path %s" %
(method, req.request_path))
......@@ -182,15 +183,15 @@ def CheckRapi(options, args):
"""Initial checks whether to run or exit with a failure.
"""
if len(args) != 0:
print >> sys.stderr, "Usage: %s [-f] [-d] [-p port] [-b ADDRESS]" % \
sys.argv[0]
if args: # rapi doesn't take any arguments
print >> sys.stderr, ("Usage: %s [-f] [-d] [-p port] [-b ADDRESS]" %
sys.argv[0])
sys.exit(constants.EXIT_FAILURE)
ssconf.CheckMaster(options.debug)
def ExecRapi(options, args):
def ExecRapi(options, _):
"""Main remote API function, executed with the PID file held.
"""
......
......@@ -27,11 +27,14 @@ by a node reboot. Run from cron or similar.
"""
# pylint: disable-msg=C0103,W0142
# C0103: Invalid name ganeti-watcher
import os
import sys
import time
import logging
import errno
from optparse import OptionParser
from ganeti import utils
......@@ -115,7 +118,7 @@ class WatcherState(object):
self._data = {}
else:
self._data = serializer.Load(state_data)
except Exception, msg:
except Exception, msg: # pylint: disable-msg=W0703
# Ignore errors while loading the file and treat it as empty
self._data = {}
logging.warning(("Invalid state file. Using defaults."
......@@ -331,7 +334,7 @@ class Watcher(object):
"""
arch_count, left_count = client.AutoArchiveJobs(age)
logging.debug("Archived %s jobs, left %s" % (arch_count, left_count))
logging.debug("Archived %s jobs, left %s", arch_count, left_count)
def CheckDisks(self, notepad):
"""Check all nodes for restarted ones.
......@@ -369,7 +372,7 @@ class Watcher(object):
try:
logging.info("Activating disks for instance %s", instance.name)
instance.ActivateDisks()
except Exception:
except Exception: # pylint: disable-msg=W0703
logging.exception("Error while activating disks for instance %s",
instance.name)
......@@ -400,7 +403,7 @@ class Watcher(object):
instance.name, last)
instance.Restart()
self.started_instances.add(instance.name)
except Exception:
except Exception: # pylint: disable-msg=W0703
logging.exception("Error while restarting instance %s",
instance.name)
......@@ -464,10 +467,14 @@ def main():
"""Main function.
"""
global client
global client # pylint: disable-msg=W0603
options, args = ParseOptions()
if args: # watcher doesn't take any arguments
print >> sys.stderr, ("Usage: %s [-f] " % sys.argv[0])
sys.exit(constants.EXIT_FAILURE)
utils.SetupLogging(constants.LOG_WATCHER, debug=options.debug,
stderr_logging=options.debug)
......
#!/bin/bash
# Copyright (C) 2009 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# This is a test script to ease development and testing on test clusters.
# It should not be used to update production environments.
# Usage: release v2.0.5
# Alternative: URL=file:///my/git/repo release e5823b7e2cd8a3...
# It will clone the given repository from the default or passed URL,
# checkout the given reference (a tag or branch) and then create a
# release archive; you will need to copy the archive and delete the
# temporary directory at the end
set -e
: ${URL:=git://git.ganeti.org/ganeti.git}
TAG="$1"
TMPDIR=`mktemp -d`
cd $TMPDIR
echo "Cloning the repository under $TMPDIR ..."
git clone -q "$URL" dist
cd dist
git checkout $TAG
./autogen.sh
./configure
VERSION=$(sed -n -e '/^PACKAGE_VERSION =/ s/^PACKAGE_VERSION = // p' Makefile)
make distcheck
fakeroot make dist
tar tzvf ganeti-$VERSION.tar.gz
sha1sum ganeti-$VERSION.tar.gz
echo "The archive is at $PWD/ganeti-$VERSION.tar.gz"
echo "Please copy it and remove the temporary directory when done."
......@@ -100,7 +100,9 @@ echo ---
# and now put it under $prefix on the target node(s)
for host; do
echo Uploading code to ${host}...
rsync -v -rlDc --exclude="*.py[oc]" --exclude="*.pdf" --exclude="*.html" \
rsync -v -rlDc \
-e "ssh -oBatchMode=yes" \
--exclude="*.py[oc]" --exclude="*.pdf" --exclude="*.html" \
"$TXD/" \
root@${host}:/ &
done
......@@ -109,7 +111,7 @@ wait
if test -z "${NO_RESTART}"; then
for host; do
echo Restarting ganeti-noded on ${host}...
ssh root@${host} /etc/init.d/ganeti restart &
ssh -oBatchMode=yes root@${host} /etc/init.d/ganeti restart &
done
wait
fi
......@@ -20,3 +20,5 @@
# empty file for package definition
"""Ganeti python modules"""
......@@ -22,15 +22,20 @@
"""Asynchronous pyinotify implementation"""
import pyinotify
import asyncore
try:
# pylint: disable-msg=E0611
from pyinotify import pyinotify
except ImportError:
import pyinotify
class AsyncNotifier(asyncore.file_dispatcher):
"""An asyncore dispatcher for inotify events.
"""
# pylint: disable-msg=W0622,W0212
def __init__(self, watch_manager, default_proc_fun=None, map=None):
"""Initializes this class.
......
......@@ -392,7 +392,7 @@ def LeaveCluster(modify_ssh_setup):
utils.RemoveFile(constants.HMAC_CLUSTER_KEY)
utils.RemoveFile(constants.RAPI_CERT_FILE)
utils.RemoveFile(constants.SSL_CERT_FILE)
except:
except: # pylint: disable-msg=W0702
logging.exception("Error while removing cluster secrets")
result = utils.RunCmd([constants.DAEMON_UTIL, "stop", constants.CONFD])
......@@ -551,6 +551,10 @@ def VerifyNode(what, cluster_name):
tmpr.append("The procfs filesystem doesn't seem to be mounted"
" under /proc, missing required directory /proc/sys and"
" the file /proc/sysrq-trigger")
if constants.NV_TIME in what:
result[constants.NV_TIME] = utils.SplitTime(time.time())
return result
......@@ -1191,6 +1195,8 @@ def BlockdevCreate(disk, size, owner, on_primary, info):
it's not required to return anything.
"""
# TODO: remove the obsolete 'size' argument
# pylint: disable-msg=W0613
clist = []
if disk.children:
for child in disk.children:
......@@ -1202,6 +1208,7 @@ def BlockdevCreate(disk, size, owner, on_primary, info):
# we need the children open in case the device itself has to
# be assembled