diff --git a/Makefile.am b/Makefile.am
index ff7e00fb1e8e89403ecaf18e59a4b58f03b84bb2..3d2ae8e94d2ab0e42e285f132bfa2301f34b24f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -160,6 +160,7 @@ EXTRA_DIST = \
$(MAINTAINERCLEANFILES) \
NEWS \
DEVNOTES \
+ pylintrc \
autotools/docbook-wrapper \
devel/upload.in \
$(docdot) \
diff --git a/daemons/ganeti-masterd b/daemons/ganeti-masterd
index 5cf8c61c93679b30cfb41fb3c1a0178eb130d560..e5f291a4767e59da39293ffa5548112b957ba2cb 100755
--- a/daemons/ganeti-masterd
+++ b/daemons/ganeti-masterd
@@ -195,6 +195,7 @@ class ClientRqHandler(SocketServer.BaseRequestHandler):
def send_message(self, msg):
#print "sending", msg
+ # TODO: sendall is not guaranteed to send everything
self.request.sendall(msg + self.EOM)
@@ -403,6 +404,10 @@ def ParseOptions():
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
@@ -479,7 +484,7 @@ def main():
ssconf.CheckMaster(options.debug)
# we believe we are the master, let's ask the other nodes...
- if options.no_voting:
+ if options.no_voting and not options.yes_do_it:
sys.stdout.write("The 'no voting' option has been selected.\n")
sys.stdout.write("This is dangerous, please confirm by"
" typing uppercase 'yes': ")
@@ -488,7 +493,7 @@ def main():
if confirmation != "YES":
print "Aborting."
return
- else:
+ elif not options.no_voting:
if not CheckAgreement():
return
diff --git a/doc/hooks.rst b/doc/hooks.rst
index 7dbe7d5e92ad0bafb1f1a74bbd5cd3dbeadbd3a7..b2f05ce5b00f9bf68dd219749e9813d4734b970e 100644
--- a/doc/hooks.rst
+++ b/doc/hooks.rst
@@ -104,7 +104,7 @@ The scripts will be run as follows:
be left
-All informations about the cluster is passed using environment
+All information about the cluster is passed using environment
variables. Different operations will have sligthly different
environments, but most of the variables are common.
diff --git a/doc/iallocator.rst b/doc/iallocator.rst
index 58719a0b87ea55f2e1289e0c1e46c22571a8c602..467f79da9d2f59e070b5d29c8faa7bb914f1af26 100644
--- a/doc/iallocator.rst
+++ b/doc/iallocator.rst
@@ -233,7 +233,7 @@ The response message is much more simple than the input one. It is
also a dict having three keys:
success
- a boolean value denoting if the allocation was successfull or not
+ a boolean value denoting if the allocation was successful or not
info
a string with information from the scripts; if the allocation fails,
diff --git a/lib/backend.py b/lib/backend.py
index 0860e0f8467478f50993d1e88ad5d0be6171b60f..456ba69e65246d958f3737b712cc45c3b9b6fadc 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -174,7 +174,7 @@ def GetMasterInfo():
master_node = cfg.GetMasterNode()
except errors.ConfigurationError, err:
_Fail("Cluster configuration incomplete: %s", err, exc=True)
- return master_netdev, master_ip, master_node
+ return (master_netdev, master_ip, master_node)
def StartMaster(start_daemons):
@@ -337,7 +337,7 @@ def LeaveCluster():
def GetNodeInfo(vgname, hypervisor_type):
- """Gives back a hash with different informations about the node.
+ """Gives back a hash with different information about the node.
@type vgname: C{string}
@param vgname: the name of the volume group to ask for disk space information
@@ -609,7 +609,7 @@ def GetInstanceList(hypervisor_list):
def GetInstanceInfo(instance, hname):
- """Gives back the informations about an instance as a dictionary.
+ """Gives back the information about an instance as a dictionary.
@type instance: string
@param instance: the instance name
@@ -764,7 +764,7 @@ def RunRenameInstance(instance, old_name):
def _GetVGInfo(vg_name):
- """Get informations about the volume group.
+ """Get information about the volume group.
@type vg_name: str
@param vg_name: the volume group which we query
@@ -931,7 +931,7 @@ def InstanceShutdown(instance):
# test every 10secs for 2min
time.sleep(1)
- for dummy in range(11):
+ for _ in range(11):
if instance.name not in GetInstanceList([hv_name]):
break
time.sleep(10)
@@ -1242,7 +1242,7 @@ def BlockdevAssemble(disk, owner, as_primary):
def BlockdevShutdown(disk):
"""Shut down a block device.
- First, if the device is assembled (Attach() is successfull), then
+ First, if the device is assembled (Attach() is successful), then
the device is shutdown. Then the children of the device are
shutdown.
@@ -1348,7 +1348,7 @@ def BlockdevGetmirrorstatus(disks):
def _RecursiveFindBD(disk):
"""Check if a device is activated.
- If so, return informations about the real device.
+ If so, return information about the real device.
@type disk: L{objects.Disk}
@param disk: the disk object we need to find
@@ -1368,7 +1368,7 @@ def _RecursiveFindBD(disk):
def BlockdevFind(disk):
"""Check if a device is activated.
- If it is, return informations about the real device.
+ If it is, return information about the real device.
@type disk: L{objects.Disk}
@param disk: the disk to find
@@ -2068,7 +2068,7 @@ def RemoveFileStorageDir(file_storage_dir):
@param file_storage_dir: the directory we should cleanup
@rtype: tuple (success,)
@return: tuple of one element, C{success}, denoting
- whether the operation was successfull
+ whether the operation was successful
"""
file_storage_dir = _TransformFileStorageDir(file_storage_dir)
@@ -2254,7 +2254,8 @@ def DemoteFromMC():
if utils.IsProcessAlive(utils.ReadPidFile(pid_file)):
_Fail("The master daemon is running, will not demote")
try:
- utils.CreateBackup(constants.CLUSTER_CONF_FILE)
+ if os.path.isfile(constants.CLUSTER_CONF_FILE):
+ utils.CreateBackup(constants.CLUSTER_CONF_FILE)
except EnvironmentError, err:
if err.errno != errno.ENOENT:
_Fail("Error while backing up cluster file: %s", err, exc=True)
diff --git a/lib/bdev.py b/lib/bdev.py
index 5f94d5078c123f59a93c18b91cf7c9a8bd8eb083..4971b53b1e33d1a9164c811f4aae5f62c2d0b0fe 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -161,7 +161,7 @@ class BlockDev(object):
"""Remove this device.
This makes sense only for some of the device types: LV and file
- storeage. Also note that if the device can't attach, the removal
+ storage. Also note that if the device can't attach, the removal
can't be completed.
"""
@@ -444,7 +444,7 @@ class LogicalVolume(BlockDev):
def Assemble(self):
"""Assemble the device.
- We alway run `lvchange -ay` on the LV to ensure it's active before
+ We always run `lvchange -ay` on the LV to ensure it's active before
use, as there were cases when xenvg was not active after boot
(also possibly after disk issues).
@@ -828,7 +828,13 @@ class BaseDRBD(BlockDev):
bytes = sectors * 512
if bytes < 128 * 1024 * 1024: # less than 128MiB
_ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
- if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
+ # the maximum *valid* size of the meta device when living on top
+ # of LVM is hard to compute: it depends on the number of stripes
+ # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
+ # (normal size), but an eight-stripe 128MB PE will result in a 1GB
+ # size meta device; as such, we restrict it to 1GB (a little bit
+ # too generous, but making assumptions about PE size is hard)
+ if bytes > 1024 * 1024 * 1024:
_ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
def Rename(self, new_id):
@@ -1252,14 +1258,14 @@ class DRBD8(BaseDRBD):
If sync_percent is None, it means all is ok
- If estimated_time is None, it means we can't esimate
+ If estimated_time is None, it means we can't estimate
the time needed, otherwise it's the time left in seconds.
We set the is_degraded parameter to True on two conditions:
network not connected or local disk missing.
- We compute the ldisk parameter based on wheter we have a local
+ We compute the ldisk parameter based on whether we have a local
disk or not.
@rtype: tuple
@@ -1329,14 +1335,14 @@ class DRBD8(BaseDRBD):
ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor)
timeout_limit = time.time() + self._NET_RECONFIG_TIMEOUT
- sleep_time = 0.100 # we start the retry time at 100 miliseconds
+ sleep_time = 0.100 # we start the retry time at 100 milliseconds
while time.time() < timeout_limit:
status = self.GetProcStatus()
if status.is_standalone:
break
# retry the disconnect, it seems possible that due to a
# well-time disconnect on the peer, my disconnect command might
- # be ingored and forgotten
+ # be ignored and forgotten
ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor) or \
ever_disconnected
time.sleep(sleep_time)
@@ -1641,7 +1647,7 @@ class FileStorage(BlockDev):
def Shutdown(self):
"""Shutdown the device.
- This is a no-op for the file type, as we don't deacivate
+ This is a no-op for the file type, as we don't deactivate
the file on shutdown.
"""
diff --git a/lib/bootstrap.py b/lib/bootstrap.py
index a3811a889d3b4585b01f2e5b7b03f2b758555896..e576c93524aca2e618f9123430d6c720645da9dc 100644
--- a/lib/bootstrap.py
+++ b/lib/bootstrap.py
@@ -79,24 +79,27 @@ def _GenerateSelfSignedSslCert(file_name, validity=(365 * 5)):
"""
(fd, tmp_file_name) = tempfile.mkstemp(dir=os.path.dirname(file_name))
try:
- # Set permissions before writing key
- os.chmod(tmp_file_name, 0600)
-
- result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
- "-days", str(validity), "-nodes", "-x509",
- "-keyout", tmp_file_name, "-out", tmp_file_name,
- "-batch"])
- if result.failed:
- raise errors.OpExecError("Could not generate SSL certificate, command"
- " %s had exitcode %s and error message %s" %
- (result.cmd, result.exit_code, result.output))
-
- # Make read-only
- os.chmod(tmp_file_name, 0400)
-
- os.rename(tmp_file_name, file_name)
+ try:
+ # Set permissions before writing key
+ os.chmod(tmp_file_name, 0600)
+
+ result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
+ "-days", str(validity), "-nodes", "-x509",
+ "-keyout", tmp_file_name, "-out", tmp_file_name,
+ "-batch"])
+ if result.failed:
+ raise errors.OpExecError("Could not generate SSL certificate, command"
+ " %s had exitcode %s and error message %s" %
+ (result.cmd, result.exit_code, result.output))
+
+ # Make read-only
+ os.chmod(tmp_file_name, 0400)
+
+ os.rename(tmp_file_name, file_name)
+ finally:
+ utils.RemoveFile(tmp_file_name)
finally:
- utils.RemoveFile(tmp_file_name)
+ os.close(fd)
def _InitGanetiServerSetup():
@@ -384,13 +387,17 @@ def SetupNodeDaemon(cluster_name, node, ssh_key_check):
(node, result.fail_reason, result.output))
-def MasterFailover():
+def MasterFailover(no_voting=False):
"""Failover the master node.
This checks that we are not already the master, and will cause the
current master to cease being master, and the non-master to become
new master.
+ @type no_voting: boolean
+ @param no_voting: force the operation without remote nodes agreement
+ (dangerous)
+
"""
sstore = ssconf.SimpleStore()
@@ -412,18 +419,20 @@ def MasterFailover():
" master candidates is:\n"
"%s" % ('\n'.join(mc_no_master)))
- vote_list = GatherMasterVotes(node_list)
-
- if vote_list:
- voted_master = vote_list[0][0]
- if voted_master is None:
- raise errors.OpPrereqError("Cluster is inconsistent, most nodes did not"
- " respond.")
- elif voted_master != old_master:
- raise errors.OpPrereqError("I have wrong configuration, I believe the"
- " master is %s but the other nodes voted for"
- " %s. Please resync the configuration of"
- " this node." % (old_master, voted_master))
+ if not no_voting:
+ vote_list = GatherMasterVotes(node_list)
+
+ if vote_list:
+ voted_master = vote_list[0][0]
+ if voted_master is None:
+ raise errors.OpPrereqError("Cluster is inconsistent, most nodes did"
+ " not respond.")
+ elif voted_master != old_master:
+ raise errors.OpPrereqError("I have a wrong configuration, I believe"
+ " the master is %s but the other nodes"
+ " voted %s. Please resync the configuration"
+ " of this node." %
+ (old_master, voted_master))
# end checks
rcode = 0
@@ -448,7 +457,8 @@ def MasterFailover():
# cluster info
cfg.Update(cluster_info)
- result = rpc.RpcRunner.call_node_start_master(new_master, True)
+ # 2.0.X: Don't start the master if no_voting is true
+ result = rpc.RpcRunner.call_node_start_master(new_master, not no_voting)
msg = result.RemoteFailMsg()
if msg:
logging.error("Could not start the master role on the new master"
@@ -490,7 +500,7 @@ def GatherMasterVotes(node_list):
@type node_list: list
@param node_list: the list of nodes to query for master info; the current
- node wil be removed if it is in the list
+ node will be removed if it is in the list
@rtype: list
@return: list of (node, votes)
diff --git a/lib/cli.py b/lib/cli.py
index 6b40c72d0a48dfcdfc0d43f4cff9610649e26103..f9a9628e0e14a3a0961d92d00da924e618a1cd29 100644
--- a/lib/cli.py
+++ b/lib/cli.py
@@ -341,7 +341,7 @@ keyval_option = KeyValOption
def _ParseArgs(argv, commands, aliases):
"""Parser for the command line arguments.
- This function parses the arguements and returns the function which
+ This function parses the arguments and returns the function which
must be executed together with its (modified) arguments.
@param argv: the command line
@@ -459,10 +459,10 @@ def AskUser(text, choices=None):
choices = [('y', True, 'Perform the operation'),
('n', False, 'Do not perform the operation')]
if not choices or not isinstance(choices, list):
- raise errors.ProgrammerError("Invalid choiches argument to AskUser")
+ raise errors.ProgrammerError("Invalid choices argument to AskUser")
for entry in choices:
if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
- raise errors.ProgrammerError("Invalid choiches element to AskUser")
+ raise errors.ProgrammerError("Invalid choices element to AskUser")
answer = choices[-1][1]
new_text = []
@@ -778,7 +778,7 @@ def GenericMain(commands, override=None, aliases=None):
except (errors.GenericError, luxi.ProtocolError,
JobSubmittedException), err:
result, err_msg = FormatError(err)
- logging.exception("Error durring command processing")
+ logging.exception("Error during command processing")
ToStderr(err_msg)
return result
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 6f0b518434d718479cdea1095d5070b414abd1ea..25e598bd0d4c53c58d48b7a27c2f54ee249fd631 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -68,7 +68,7 @@ class LogicalUnit(object):
def __init__(self, processor, op, context, rpc):
"""Constructor for LogicalUnit.
- This needs to be overriden in derived classes in order to check op
+ This needs to be overridden in derived classes in order to check op
validity.
"""
@@ -118,7 +118,7 @@ class LogicalUnit(object):
CheckPrereq, doing these separate is better because:
- ExpandNames is left as as purely a lock-related function
- - CheckPrereq is run after we have aquired locks (and possible
+ - CheckPrereq is run after we have acquired locks (and possible
waited for them)
The function is allowed to change the self.op attribute so that
@@ -456,7 +456,7 @@ def _CheckNodeNotDrained(lu, node):
def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
memory, vcpus, nics, disk_template, disks,
- bep, hvp, hypervisor):
+ bep, hvp, hypervisor_name):
"""Builds instance related env variables for hooks
This builds the hook environment from individual variables.
@@ -479,15 +479,15 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
@param nics: list of tuples (ip, mac, mode, link) representing
the NICs the instance has
@type disk_template: string
- @param disk_template: the distk template of the instance
+ @param disk_template: the disk template of the instance
@type disks: list
@param disks: the list of (size, mode) pairs
@type bep: dict
@param bep: the backend parameters for the instance
@type hvp: dict
@param hvp: the hypervisor parameters for the instance
- @type hypervisor: string
- @param hypervisor: the hypervisor for the instance
+ @type hypervisor_name: string
+ @param hypervisor_name: the hypervisor for the instance
@rtype: dict
@return: the hook environment for this instance
@@ -506,7 +506,7 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
"INSTANCE_MEMORY": memory,
"INSTANCE_VCPUS": vcpus,
"INSTANCE_DISK_TEMPLATE": disk_template,
- "INSTANCE_HYPERVISOR": hypervisor,
+ "INSTANCE_HYPERVISOR": hypervisor_name,
}
if nics:
@@ -654,7 +654,7 @@ class LUDestroyCluster(NoHooksLU):
This checks whether the cluster is empty.
- Any errors are signalled by raising errors.OpPrereqError.
+ Any errors are signaled by raising errors.OpPrereqError.
"""
master = self.cfg.GetMasterNode()
@@ -705,7 +705,7 @@ class LUVerifyCluster(LogicalUnit):
Test list:
- compares ganeti version
- - checks vg existance and size > 20G
+ - checks vg existence and size > 20G
- checks config file checksum
- checks ssh to other nodes
@@ -787,8 +787,8 @@ class LUVerifyCluster(LogicalUnit):
else:
# not candidate and this is not a must-have file
bad = True
- feedback_fn(" - ERROR: non master-candidate has old/wrong file"
- " '%s'" % file_name)
+ feedback_fn(" - ERROR: file '%s' should not exist on non master"
+ " candidates (and the file is outdated)" % file_name)
else:
# all good, except non-master/non-must have combination
if not node_is_mc and not must_have_file:
@@ -944,7 +944,7 @@ class LUVerifyCluster(LogicalUnit):
if bep[constants.BE_AUTO_BALANCE]:
needed_mem += bep[constants.BE_MEMORY]
if nodeinfo['mfree'] < needed_mem:
- feedback_fn(" - ERROR: not enough memory on node %s to accomodate"
+ feedback_fn(" - ERROR: not enough memory on node %s to accommodate"
" failovers should node %s fail" % (node, prinode))
bad = True
return bad
@@ -963,7 +963,7 @@ class LUVerifyCluster(LogicalUnit):
def BuildHooksEnv(self):
"""Build hooks env.
- Cluster-Verify hooks just rone in the post phase and their failure makes
+ Cluster-Verify hooks just ran in the post phase and their failure makes
the output be logged in the verify output and the verification to fail.
"""
@@ -1231,7 +1231,7 @@ class LUVerifyCluster(LogicalUnit):
return not bad
def HooksCallBack(self, phase, hooks_results, feedback_fn, lu_result):
- """Analize the post-hooks' result
+ """Analyze the post-hooks' result
This method analyses the hook result, handles it, and sends some
nicely-formatted feedback back to the user.
@@ -1337,7 +1337,6 @@ class LUVerifyDisks(NoHooksLU):
node_lvs = self.rpc.call_lv_list(nodes, vg_name)
- to_act = set()
for node in nodes:
# node_volume
node_res = node_lvs[node]
@@ -1453,7 +1452,7 @@ def _RecursiveCheckIfLVMBased(disk):
@type disk: L{objects.Disk}
@param disk: the disk to check
- @rtype: booleean
+ @rtype: boolean
@return: boolean indicating whether a LD_LV dev_type was found or not
"""
@@ -1909,7 +1908,7 @@ class LURemoveNode(LogicalUnit):
- it does not have primary or secondary instances
- it's not the master
- Any errors are signalled by raising errors.OpPrereqError.
+ Any errors are signaled by raising errors.OpPrereqError.
"""
node = self.cfg.GetNodeInfo(self.cfg.ExpandNodeName(self.op.node_name))
@@ -2239,7 +2238,7 @@ class LUAddNode(LogicalUnit):
- it is resolvable
- its parameters (single/dual homed) matches the cluster
- Any errors are signalled by raising errors.OpPrereqError.
+ Any errors are signaled by raising errors.OpPrereqError.
"""
node_name = self.op.node_name
@@ -2293,7 +2292,7 @@ class LUAddNode(LogicalUnit):
raise errors.OpPrereqError("The master has a private ip but the"
" new node doesn't have one")
- # checks reachablity
+ # checks reachability
if not utils.TcpPing(primary_ip, constants.DEFAULT_NODED_PORT):
raise errors.OpPrereqError("Node not reachable by ping")
@@ -2305,14 +2304,24 @@ class LUAddNode(LogicalUnit):
" based ping to noded port")
cp_size = self.cfg.GetClusterInfo().candidate_pool_size
- mc_now, _ = self.cfg.GetMasterCandidateStats()
- master_candidate = mc_now < cp_size
+ if self.op.readd:
+ exceptions = [node]
+ else:
+ exceptions = []
+ mc_now, mc_max = self.cfg.GetMasterCandidateStats(exceptions)
+ # the new node will increase mc_max with one, so:
+ mc_max = min(mc_max + 1, cp_size)
+ self.master_candidate = mc_now < mc_max
- self.new_node = objects.Node(name=node,
- primary_ip=primary_ip,
- secondary_ip=secondary_ip,
- master_candidate=master_candidate,
- offline=False, drained=False)
+ if self.op.readd:
+ self.new_node = self.cfg.GetNodeInfo(node)
+ assert self.new_node is not None, "Can't retrieve locked node %s" % node
+ else:
+ self.new_node = objects.Node(name=node,
+ primary_ip=primary_ip,
+ secondary_ip=secondary_ip,
+ master_candidate=self.master_candidate,
+ offline=False, drained=False)
def Exec(self, feedback_fn):
"""Adds the new node to the cluster.
@@ -2321,6 +2330,20 @@ class LUAddNode(LogicalUnit):
new_node = self.new_node
node = new_node.name
+ # for re-adds, reset the offline/drained/master-candidate flags;
+ # we need to reset here, otherwise offline would prevent RPC calls
+ # later in the procedure; this also means that if the re-add
+ # fails, we are left with a non-offlined, broken node
+ if self.op.readd:
+ new_node.drained = new_node.offline = False
+ self.LogInfo("Readding a node, the offline/drained flags were reset")
+ # if we demote the node, we do cleanup later in the procedure
+ new_node.master_candidate = self.master_candidate
+
+ # notify the user about any possible mc promotion
+ if new_node.master_candidate:
+ self.LogInfo("Node will be a master candidate")
+
# check connectivity
result = self.rpc.call_version([node])[node]
result.Raise("Can't get version information from node %s" % node)
@@ -2386,6 +2409,15 @@ class LUAddNode(LogicalUnit):
if self.op.readd:
_RedistributeAncillaryFiles(self)
self.context.ReaddNode(new_node)
+ # make sure we redistribute the config
+ self.cfg.Update(new_node)
+ # and make sure the new node will not have old files around
+ if not new_node.master_candidate:
+ result = self.rpc.call_node_demote_from_mc(new_node.name)
+ msg = result.RemoteFailMsg()
+ if msg:
+ self.LogWarning("Node failed to demote itself from master"
+ " candidate status: %s" % msg)
else:
_RedistributeAncillaryFiles(self, additional_nodes=[node])
self.context.AddNode(new_node)
@@ -2505,6 +2537,10 @@ class LUSetNodeParams(LogicalUnit):
node.master_candidate = False
changed_mc = True
result.append(("master_candidate", "auto-demotion due to drain"))
+ rrc = self.rpc.call_node_demote_from_mc(node.name)
+ msg = rrc.RemoteFailMsg()
+ if msg:
+ self.LogWarning("Node failed to demote itself: %s" % msg)
if node.offline:
node.offline = False
result.append(("offline", "clear offline status due to drain"))
@@ -2593,8 +2629,8 @@ class LUQueryClusterInfo(NoHooksLU):
"master": cluster.master_node,
"default_hypervisor": cluster.default_hypervisor,
"enabled_hypervisors": cluster.enabled_hypervisors,
- "hvparams": dict([(hvname, cluster.hvparams[hvname])
- for hvname in cluster.enabled_hypervisors]),
+ "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor])
+ for hypervisor_name in cluster.enabled_hypervisors]),
"beparams": cluster.beparams,
"nicparams": cluster.nicparams,
"candidate_pool_size": cluster.candidate_pool_size,
@@ -2757,7 +2793,7 @@ def _StartInstanceDisks(lu, instance, force):
"""Start the disks of an instance.
"""
- disks_ok, dummy = _AssembleInstanceDisks(lu, instance,
+ disks_ok, _ = _AssembleInstanceDisks(lu, instance,
ignore_secondaries=force)
if not disks_ok:
_ShutdownInstanceDisks(lu, instance)
@@ -2943,7 +2979,7 @@ class LUStartupInstance(LogicalUnit):
_CheckNodeOnline(self, instance.primary_node)
bep = self.cfg.GetClusterInfo().FillBE(instance)
- # check bridges existance
+ # check bridges existence
_CheckInstanceBridgesExist(self, instance)
remote_info = self.rpc.call_instance_info(instance.primary_node,
@@ -3022,7 +3058,7 @@ class LURebootInstance(LogicalUnit):
_CheckNodeOnline(self, instance.primary_node)
- # check bridges existance
+ # check bridges existence
_CheckInstanceBridgesExist(self, instance)
def Exec(self, feedback_fn):
@@ -3762,7 +3798,7 @@ class LUFailoverInstance(LogicalUnit):
logging.info("Starting instance %s on node %s",
instance.name, target_node)
- disks_ok, dummy = _AssembleInstanceDisks(self, instance,
+ disks_ok, _ = _AssembleInstanceDisks(self, instance,
ignore_secondaries=True)
if not disks_ok:
_ShutdownInstanceDisks(self, instance)
@@ -5501,7 +5537,6 @@ class LUReplaceDisks(LogicalUnit):
logging.debug("Allocated minors %s" % (minors,))
self.proc.LogStep(4, steps_total, "changing drbd configuration")
for idx, (dev, new_minor) in enumerate(zip(instance.disks, minors)):
- size = dev.size
info("activating a new drbd on %s for disk/%d" % (new_node, idx))
# create new devices on new_node; note that we create two IDs:
# one without port, so the drbd will be activated without
@@ -6077,7 +6112,7 @@ class LUSetInstanceParams(LogicalUnit):
This only checks the instance list against the existing names.
"""
- force = self.force = self.op.force
+ self.force = self.op.force
# checking the new params on the primary/secondary nodes
@@ -6435,7 +6470,7 @@ class LUExportInstance(LogicalUnit):
# remove it from its current node. In the future we could fix this by:
# - making a tasklet to search (share-lock all), then create the new one,
# then one to remove, after
- # - removing the removal operation altoghether
+ # - removing the removal operation altogether
self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
def DeclareLocks(self, level):
@@ -7097,7 +7132,6 @@ class IAllocator(object):
"""
if call_fn is None:
call_fn = self.lu.rpc.call_iallocator_runner
- data = self.in_text
result = call_fn(self.lu.cfg.GetMasterNode(), name, self.in_text)
result.Raise("Failure while running the iallocator script")
diff --git a/lib/config.py b/lib/config.py
index 21f6c18baadb7850b9a10e436315cb77bf3bdbad..acdca2ea0580eee573d2a2151493dd72f78f7387 100644
--- a/lib/config.py
+++ b/lib/config.py
@@ -474,8 +474,8 @@ class ConfigWriter:
def _AppendUsedPorts(instance_name, disk, used):
duplicates = []
if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
- nodeA, nodeB, dummy, minorA, minorB = disk.logical_id[:5]
- for node, port in ((nodeA, minorA), (nodeB, minorB)):
+ node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
+ for node, port in ((node_a, minor_a), (node_b, minor_b)):
assert node in used, ("Node '%s' of instance '%s' not found"
" in node list" % (node, instance_name))
if port in used[node]:
@@ -796,7 +796,7 @@ class ConfigWriter:
self._config_data.instances.keys())
def _UnlockedGetInstanceInfo(self, instance_name):
- """Returns informations about an instance.
+ """Returns information about an instance.
This function is for internal use, when the config lock is already held.
@@ -808,9 +808,9 @@ class ConfigWriter:
@locking.ssynchronized(_config_lock, shared=1)
def GetInstanceInfo(self, instance_name):
- """Returns informations about an instance.
+ """Returns information about an instance.
- It takes the information from the configuration file. Other informations of
+ It takes the information from the configuration file. Other information of
an instance are taken from the live systems.
@param instance_name: name of the instance, e.g.
@@ -945,15 +945,19 @@ class ConfigWriter:
for node in self._UnlockedGetNodeList()])
return my_dict
- def _UnlockedGetMasterCandidateStats(self):
+ def _UnlockedGetMasterCandidateStats(self, exceptions=None):
"""Get the number of current and maximum desired and possible candidates.
+ @type exceptions: list
+ @param exceptions: if passed, list of nodes that should be ignored
@rtype: tuple
@return: tuple of (current, desired and possible)
"""
mc_now = mc_max = 0
- for node in self._config_data.nodes.itervalues():
+ for node in self._config_data.nodes.values():
+ if exceptions and node.name in exceptions:
+ continue
if not (node.offline or node.drained):
mc_max += 1
if node.master_candidate:
@@ -962,16 +966,18 @@ class ConfigWriter:
return (mc_now, mc_max)
@locking.ssynchronized(_config_lock, shared=1)
- def GetMasterCandidateStats(self):
+ def GetMasterCandidateStats(self, exceptions=None):
"""Get the number of current and maximum possible candidates.
This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
+ @type exceptions: list
+ @param exceptions: if passed, list of nodes that should be ignored
@rtype: tuple
@return: tuple of (current, max)
"""
- return self._UnlockedGetMasterCandidateStats()
+ return self._UnlockedGetMasterCandidateStats(exceptions)
@locking.ssynchronized(_config_lock)
def MaintainCandidatePool(self):
@@ -1203,7 +1209,7 @@ class ConfigWriter:
@locking.ssynchronized(_config_lock, shared=1)
def GetClusterInfo(self):
- """Returns informations about the cluster
+ """Returns information about the cluster
@rtype: L{objects.Cluster}
@return: the cluster object
diff --git a/lib/http/__init__.py b/lib/http/__init__.py
index 008cf9cb6712592dd08a718fe66a256df6cf29db..c98fa586dd10323f265ec11cd01f0b5e884f93e7 100644
--- a/lib/http/__init__.py
+++ b/lib/http/__init__.py
@@ -367,15 +367,12 @@ def SocketOperation(sock, op, arg1, timeout):
# TODO: event_poll/event_check/override
if op in (SOCKOP_SEND, SOCKOP_HANDSHAKE):
event_poll = select.POLLOUT
- event_check = select.POLLOUT
elif op == SOCKOP_RECV:
event_poll = select.POLLIN
- event_check = select.POLLIN | select.POLLPRI
elif op == SOCKOP_SHUTDOWN:
event_poll = None
- event_check = None
# The timeout is only used when OpenSSL requests polling for a condition.
# It is not advisable to have no timeout for shutdown.
@@ -744,7 +741,7 @@ class HttpMessageWriter(object):
def HasMessageBody(self):
"""Checks whether the HTTP message contains a body.
- Can be overriden by subclasses.
+ Can be overridden by subclasses.
"""
return bool(self._msg.body)
@@ -937,7 +934,7 @@ class HttpMessageReader(object):
def ParseStartLine(self, start_line):
"""Parses the start line of a message.
- Must be overriden by subclass.
+ Must be overridden by subclass.
@type start_line: string
@param start_line: Start line string
diff --git a/lib/http/auth.py b/lib/http/auth.py
index 8a8d7201e604bcc99ea8e9cce57f844affa2498d..670b897b3a8d1fc6253a158f398cbd14999ead7c 100644
--- a/lib/http/auth.py
+++ b/lib/http/auth.py
@@ -23,12 +23,10 @@
"""
import logging
-import time
import re
import base64
import binascii
-from ganeti import constants
from ganeti import utils
from ganeti import http
@@ -80,7 +78,7 @@ class HttpServerRequestAuthentication(object):
def GetAuthRealm(self, req):
"""Returns the authentication realm for a request.
- MAY be overriden by a subclass, which then can return different realms for
+ MAY be overridden by a subclass, which then can return different realms for
different paths. Returning "None" means no authentication is needed for a
request.
@@ -195,7 +193,7 @@ class HttpServerRequestAuthentication(object):
def Authenticate(self, req, user, password):
"""Checks the password for a user.
- This function MUST be overriden by a subclass.
+ This function MUST be overridden by a subclass.
"""
raise NotImplementedError()
diff --git a/lib/http/client.py b/lib/http/client.py
index 776fadeae37545f8659f1bb6cc896feddc840e90..717581f6f4295dfdab325a6bae15780d3793c4cc 100644
--- a/lib/http/client.py
+++ b/lib/http/client.py
@@ -22,23 +22,13 @@
"""
-import BaseHTTPServer
-import cgi
-import logging
-import OpenSSL
import os
import select
import socket
-import sys
-import time
-import signal
import errno
import threading
-from ganeti import constants
-from ganeti import serializer
from ganeti import workerpool
-from ganeti import utils
from ganeti import http
diff --git a/lib/http/server.py b/lib/http/server.py
index b74eb3674121dc64958c3a81916971fde2aaad19..0afdcd00d3da52e9d25541f250a8d04a20b7c28b 100644
--- a/lib/http/server.py
+++ b/lib/http/server.py
@@ -31,9 +31,6 @@ import socket
import time
import signal
-from ganeti import constants
-from ganeti import serializer
-from ganeti import utils
from ganeti import http
@@ -498,7 +495,7 @@ class HttpServer(http.HttpBase):
# As soon as too many children run, we'll not respond to new
# requests. The real solution would be to add a timeout for children
# and killing them after some time.
- pid, status = os.waitpid(0, 0)
+ pid, _ = os.waitpid(0, 0)
except os.error:
pid = None
if pid and pid in self._children:
@@ -536,14 +533,14 @@ class HttpServer(http.HttpBase):
def PreHandleRequest(self, req):
"""Called before handling a request.
- Can be overriden by a subclass.
+ Can be overridden by a subclass.
"""
def HandleRequest(self, req):
"""Handles a request.
- Must be overriden by subclass.
+ Must be overridden by subclass.
"""
raise NotImplementedError()
diff --git a/lib/hypervisor/hv_fake.py b/lib/hypervisor/hv_fake.py
index 52d85bc96352a12c11e16b6be3161dc70a4aa1c0..38599d76bf12daa8ec3b9952ac6e3c538c177149 100644
--- a/lib/hypervisor/hv_fake.py
+++ b/lib/hypervisor/hv_fake.py
@@ -25,7 +25,6 @@
import os
import os.path
-import re
from ganeti import utils
from ganeti import constants
diff --git a/lib/hypervisor/hv_xen.py b/lib/hypervisor/hv_xen.py
index b3390f4f15325e04567d4ee661b02c151227a40f..552ff2f41eb5f29ac8896ffa5dd980d80d96a32f 100644
--- a/lib/hypervisor/hv_xen.py
+++ b/lib/hypervisor/hv_xen.py
@@ -94,7 +94,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
@return: list of (name, id, memory, vcpus, state, time spent)
"""
- for dummy in range(5):
+ for _ in range(5):
result = utils.RunCmd(["xm", "list"])
if not result.failed:
break
diff --git a/lib/jqueue.py b/lib/jqueue.py
index ed2b9a90858089e207ab5ea453d677277d41d750..74139b7b5aac54bfc78d2eecec128531f4a3fd47 100644
--- a/lib/jqueue.py
+++ b/lib/jqueue.py
@@ -69,7 +69,7 @@ def TimeStampNow():
class _QueuedOpCode(object):
- """Encasulates an opcode object.
+ """Encapsulates an opcode object.
@ivar log: holds the execution log and consists of tuples
of the form C{(log_serial, timestamp, level, message)}
@@ -286,7 +286,7 @@ class _QueuedJob(object):
"""Selectively returns the log entries.
@type newer_than: None or int
- @param newer_than: if this is None, return all log enties,
+ @param newer_than: if this is None, return all log entries,
otherwise return only the log entries with serial higher
than this value
@rtype: list
@@ -469,7 +469,7 @@ class _JobQueueWorkerPool(workerpool.WorkerPool):
class JobQueue(object):
- """Quue used to manaage the jobs.
+ """Queue used to manage the jobs.
@cvar _RE_JOB_FILE: regex matching the valid job file names
@@ -657,7 +657,7 @@ class JobQueue(object):
Since we aim to keep consistency should this node (the current
master) fail, we will log errors if our rpc fail, and especially
- log the case when more than half of the nodes failes.
+ log the case when more than half of the nodes fails.
@param result: the data as returned from the rpc call
@type nodes: list
@@ -940,7 +940,7 @@ class JobQueue(object):
and in the future we might merge them.
@type drain_flag: boolean
- @param drain_flag: wheter to set or unset the drain flag
+ @param drain_flag: Whether to set or unset the drain flag
"""
if drain_flag:
diff --git a/lib/jstore.py b/lib/jstore.py
index 4d9189e39eee121bd861a713a20116f2387ba904..5c5996807e7881d8f1ead4a4bb551a6a653f6188 100644
--- a/lib/jstore.py
+++ b/lib/jstore.py
@@ -22,9 +22,7 @@
"""Module implementing the job queue handling."""
import os
-import logging
import errno
-import re
from ganeti import constants
from ganeti import errors
diff --git a/lib/locking.py b/lib/locking.py
index 647e14f4c030b393dc3addfe144bfa645c5e8627..16f302e73a0e72647d87f16271c91719af6317a2 100644
--- a/lib/locking.py
+++ b/lib/locking.py
@@ -297,7 +297,7 @@ class SharedLock:
# Whenever we want to acquire a full LockSet we pass None as the value
-# to acquire. Hide this behing this nicely named constant.
+# to acquire. Hide this behind this nicely named constant.
ALL_SET = None
@@ -689,7 +689,7 @@ BGL = 'BGL'
class GanetiLockManager:
"""The Ganeti Locking Library
- The purpouse of this small library is to manage locking for ganeti clusters
+ The purpose of this small library is to manage locking for ganeti clusters
in a central place, while at the same time doing dynamic checks against
possible deadlocks. It will also make it easier to transition to a different
lock type should we migrate away from python threads.
@@ -774,7 +774,7 @@ class GanetiLockManager:
"""Acquire a set of resource locks, at the same level.
@param level: the level at which the locks shall be acquired;
- it must be a memmber of LEVELS.
+ it must be a member of LEVELS.
@param names: the names of the locks which shall be acquired
(special lock names, or instance/node names)
@param shared: whether to acquire in shared mode; by default
@@ -809,7 +809,7 @@ class GanetiLockManager:
mode, before releasing them.
@param level: the level at which the locks shall be released;
- it must be a memmber of LEVELS
+ it must be a member of LEVELS
@param names: the names of the locks which shall be released
(defaults to all the locks acquired at that level)
@@ -827,7 +827,7 @@ class GanetiLockManager:
"""Add locks at the specified level.
@param level: the level at which the locks shall be added;
- it must be a memmber of LEVELS_MOD.
+ it must be a member of LEVELS_MOD.
@param names: names of the locks to acquire
@param acquired: whether to acquire the newly added locks
@param shared: whether the acquisition will be shared
diff --git a/lib/luxi.py b/lib/luxi.py
index 1c3ca6d3aa5a7fb7a3f4d954132dbc60fdb4f1ba..11ea61d29d830724401f0f189bf95abbdc1654dd 100644
--- a/lib/luxi.py
+++ b/lib/luxi.py
@@ -187,12 +187,13 @@ class Transport:
raise EncodingError("Message terminator found in payload")
self._CheckSocket()
try:
+ # TODO: sendall is not guaranteed to send everything
self.socket.sendall(msg + self.eom)
except socket.timeout, err:
raise TimeoutError("Sending timeout: %s" % str(err))
def Recv(self):
- """Try to receive a messae from the socket.
+ """Try to receive a message from the socket.
In case we already have messages queued, we just return from the
queue. Otherwise, we try to read data with a _rwtimeout network
@@ -205,10 +206,16 @@ class Transport:
while not self._msgs:
if time.time() > etime:
raise TimeoutError("Extended receive timeout")
- try:
- data = self.socket.recv(4096)
- except socket.timeout, err:
- raise TimeoutError("Receive timeout: %s" % str(err))
+ while True:
+ try:
+ data = self.socket.recv(4096)
+ except socket.error, err:
+ if err.args and err.args[0] == errno.EAGAIN:
+ continue
+ raise
+ except socket.timeout, err:
+ raise TimeoutError("Receive timeout: %s" % str(err))
+ break
if not data:
raise ConnectionClosedError("Connection closed while reading")
new_msgs = (self._buffer + data).split(self.eom)
@@ -278,7 +285,7 @@ class Client(object):
old_transp = self.transport
self.transport = None
old_transp.Close()
- except Exception, err:
+ except Exception:
pass
def CallMethod(self, method, args):
diff --git a/lib/mcpu.py b/lib/mcpu.py
index 0844b7409dac85337df362f18b9c2331ec8ea5c6..78faa37ddbf5e3344f68310efce853a8a4529308 100644
--- a/lib/mcpu.py
+++ b/lib/mcpu.py
@@ -168,7 +168,7 @@ class Processor(object):
self.context.glm.add(level, add_locks, acquired=1, shared=share)
except errors.LockError:
raise errors.OpPrereqError(
- "Coudn't add locks (%s), probably because of a race condition"
+ "Couldn't add locks (%s), probably because of a race condition"
" with another job, who added them first" % add_locks)
try:
try:
@@ -197,7 +197,7 @@ class Processor(object):
@type run_notifier: callable (no arguments) or None
@param run_notifier: this function (if callable) will be called when
we are about to call the lu's Exec() method, that
- is, after we have aquired all locks
+ is, after we have acquired all locks
"""
if not isinstance(op, opcodes.OpCode):
diff --git a/lib/objects.py b/lib/objects.py
index 5ae1bb97881ec816d441c347114cd95e221e5338..106b704aa09f5e27de6f2cb9cb8eca9b627c7858 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -578,10 +578,10 @@ class Disk(ConfigObject):
"""Checks that this disk is correctly configured.
"""
- errs = []
+ all_errors = []
if self.mode not in constants.DISK_ACCESS_SET:
- errs.append("Disk access mode '%s' is invalid" % (self.mode, ))
- return errs
+ all_errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
+ return all_errors
class Instance(TaggableObject):
diff --git a/lib/rapi/baserlib.py b/lib/rapi/baserlib.py
index a5522a5948ef1e38603c8fd01d2f80edad2c9652..be1524ed34a393b27ceeb7a32b48b6a36507b8ef 100644
--- a/lib/rapi/baserlib.py
+++ b/lib/rapi/baserlib.py
@@ -25,8 +25,6 @@
import logging
-import ganeti.cli
-
from ganeti import luxi
from ganeti import rapi
from ganeti import http
@@ -247,7 +245,7 @@ class R_Generic(object):
val = 0
try:
val = int(val)
- except (ValueError, TypeError), err:
+ except (ValueError, TypeError):
raise http.HttpBadRequest("Invalid value for the"
" '%s' parameter" % (name,))
return val
diff --git a/lib/rpc.py b/lib/rpc.py
index b19c048cca24331e151a1e6d5c25f3a69eda973e..ed11ae4e41e0ac4af682306bf2a6ce7d4baabc42 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -31,7 +31,6 @@
# R0904: Too many public methods
import os
-import socket
import logging
import zlib
import base64
@@ -83,7 +82,7 @@ class RpcResult(object):
calls we can't raise an exception just because one one out of many
failed, and therefore we use this class to encapsulate the result.
- @ivar data: the data payload, for successfull results, or None
+ @ivar data: the data payload, for successful results, or None
@type failed: boolean
@ivar failed: whether the operation failed at transport level (not
application level on the remote node)
@@ -169,7 +168,7 @@ class Client:
list of nodes, will contact (in parallel) all nodes, and return a
dict of results (key: node name, value: result).
- One current bug is that generic failure is still signalled by
+ One current bug is that generic failure is still signaled by
'False' result, which is not good. This overloading of values can
cause bugs.
@@ -228,7 +227,7 @@ class Client:
@return: List of RPC results
"""
- assert _http_manager, "RPC module not intialized"
+ assert _http_manager, "RPC module not initialized"
_http_manager.ExecRequests(self.nc.values())
@@ -277,9 +276,9 @@ class RpcRunner(object):
@type instance: L{objects.Instance}
@param instance: an Instance object
@type hvp: dict or None
- @param hvp: a dictionary with overriden hypervisor parameters
+ @param hvp: a dictionary with overridden hypervisor parameters
@type bep: dict or None
- @param bep: a dictionary with overriden backend parameters
+ @param bep: a dictionary with overridden backend parameters
@rtype: dict
@return: the instance dict, with the hvparams filled with the
cluster defaults
diff --git a/lib/ssh.py b/lib/ssh.py
index 40df9996500dd1228399f37b89366bed03a27202..f0362b4b81933a3939d0f2975def532948be2ea2 100644
--- a/lib/ssh.py
+++ b/lib/ssh.py
@@ -201,7 +201,7 @@ class SshRunner:
connected to).
This is used to detect problems in ssh known_hosts files
- (conflicting known hosts) and incosistencies between dns/hosts
+ (conflicting known hosts) and inconsistencies between dns/hosts
entries and local machine names
@param node: nodename of a host to check; can be short or
diff --git a/lib/utils.py b/lib/utils.py
index b552915e324e042c57010a0cabef6e582973ba4d..aace5eccea89b3cf5e1e8258736f6bc950c8d629 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -27,7 +27,6 @@ the command line scripts.
"""
-import sys
import os
import time
import subprocess
@@ -59,7 +58,6 @@ from ganeti import constants
_locksheld = []
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
-debug = False
debug_locks = False
#: when set to True, L{RunCmd} is disabled
@@ -136,7 +134,7 @@ def RunCmd(cmd, env=None, output=None, cwd='/'):
directory for the command; the default will be /
@rtype: L{RunResult}
@return: RunResult instance
- @raise erors.ProgrammerError: if we call this when forks are disabled
+ @raise errors.ProgrammerError: if we call this when forks are disabled
"""
if no_fork:
@@ -665,7 +663,7 @@ def TryConvert(fn, val):
"""
try:
nv = fn(val)
- except (ValueError, TypeError), err:
+ except (ValueError, TypeError):
nv = val
return nv
@@ -679,7 +677,7 @@ def IsValidIP(ip):
@type ip: str
@param ip: the address to be checked
@rtype: a regular expression match object
- @return: a regular epression match object, or None if the
+ @return: a regular expression match object, or None if the
address is not valid
"""
@@ -712,7 +710,7 @@ def BuildShellCmd(template, *args):
This function will check all arguments in the args list so that they
are valid shell parameters (i.e. they don't contain shell
- metacharaters). If everything is ok, it will return the result of
+ metacharacters). If everything is ok, it will return the result of
template % args.
@type template: str
@@ -1041,7 +1039,7 @@ def ShellQuoteArgs(args):
@type args: list
@param args: list of arguments to be quoted
@rtype: str
- @return: the quoted arguments concatenaned with spaces
+ @return: the quoted arguments concatenated with spaces
"""
return ' '.join([ShellQuote(i) for i in args])
@@ -1058,7 +1056,7 @@ def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
@type port: int
@param port: the port to connect to
@type timeout: int
- @param timeout: the timeout on the connection attemp
+ @param timeout: the timeout on the connection attempt
@type live_port_needed: boolean
@param live_port_needed: whether a closed port will cause the
function to return failure, as if there was a timeout
@@ -1075,7 +1073,7 @@ def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
if source is not None:
try:
sock.bind((source, 0))
- except socket.error, (errcode, errstring):
+ except socket.error, (errcode, _):
if errcode == errno.EADDRNOTAVAIL:
success = False
@@ -1100,7 +1098,7 @@ def OwnIpAddress(address):
address.
@type address: string
- @param address: the addres to check
+ @param address: the address to check
@rtype: bool
@return: True if we own the address
@@ -1196,7 +1194,7 @@ def ReadFile(file_name, size=None):
@type size: None or int
@param size: Read at most size bytes
@rtype: str
- @return: the (possibly partial) conent of the file
+ @return: the (possibly partial) content of the file
"""
f = open(file_name, "r")
@@ -1338,14 +1336,14 @@ def FirstFree(seq, base=0):
def all(seq, pred=bool):
"Returns True if pred(x) is True for every element in the iterable"
- for elem in itertools.ifilterfalse(pred, seq):
+ for _ in itertools.ifilterfalse(pred, seq):
return False
return True
def any(seq, pred=bool):
"Returns True if pred(x) is True for at least one element in the iterable"
- for elem in itertools.ifilter(pred, seq):
+ for _ in itertools.ifilter(pred, seq):
return True
return False
@@ -1356,7 +1354,7 @@ def UniqueSequence(seq):
Element order is preserved.
@type seq: sequence
- @param seq: the sequence with the source elementes
+ @param seq: the sequence with the source elements
@rtype: list
@return: list of unique elements from seq
@@ -1368,7 +1366,7 @@ def UniqueSequence(seq):
def IsValidMac(mac):
"""Predicate to check if a MAC address is valid.
- Checks wether the supplied MAC address is formally correct, only
+ Checks whether the supplied MAC address is formally correct, only
accepts colon separated format.
@type mac: str
@@ -1815,7 +1813,7 @@ def SafeEncode(text):
"""
if isinstance(text, unicode):
- # onli if unicode; if str already, we handle it below
+ # only if unicode; if str already, we handle it below
text = text.encode('ascii', 'backslashreplace')
resu = ""
for char in text:
diff --git a/man/gnt-cluster.sgml b/man/gnt-cluster.sgml
index 21f717003f77377ff9b0f5805bc172a65622a192..2634adb2e4e7970beafe0853d31a80c937ad0f5b 100644
--- a/man/gnt-cluster.sgml
+++ b/man/gnt-cluster.sgml
@@ -465,11 +465,32 @@
<cmdsynopsis>
<command>masterfailover</command>
+ <arg>--no-voting</arg>
</cmdsynopsis>
<para>
Failover the master role to the current node.
</para>
+
+ <para>
+ The <option>--no-voting</option> option skips the remote node agreement
+ checks. This is dangerous, but necessary in some cases (for example
+ failing over the master role in a 2 node cluster with the original master
+ down). If the original master then comes up, it won't be able to start
+ its master daemon because it won't have enough votes, but so won't the
+ new master, if the master daemon ever needs a restart. You can pass
+ --no-voting to ganeti-masterd on the new master to solve this problem,
+ and gnt-cluster redist-conf to make sure the cluster is consistent again.
+ </para>
+
+ <para>
+ In version 2.0.X ganeti-masterd will not be able to start if
+ masterfailover is called with the --no-voting option (which, again,
+ should only be used on 2 nodes clusters with the former master being
+ down). In that case just start it manually passing --no-voting to it
+ as well, until you have restored cluster redundancy.
+ </para>
+
</refsect2>
<refsect2>
diff --git a/man/gnt-node.sgml b/man/gnt-node.sgml
index f4836707107a3c62520548805d59e0563e6e89fd..99f80bffd62d957cd01b81439fdfc98fea3d672a 100644
--- a/man/gnt-node.sgml
+++ b/man/gnt-node.sgml
@@ -91,7 +91,7 @@
discussion in <citerefentry>
<refentrytitle>gnt-cluster</refentrytitle>
<manvolnum>8</manvolnum> </citerefentry> for more
- informations.
+ information.
</para>
<para>
diff --git a/pylintrc b/pylintrc
new file mode 100644
index 0000000000000000000000000000000000000000..61163e7f2a3f33490e68cde8fddb35f70d98a178
--- /dev/null
+++ b/pylintrc
@@ -0,0 +1,78 @@
+# Configuration file for pylint (http://www.logilab.org/project/pylint). See
+# http://www.logilab.org/card/pylintfeatures for more detailed variable
+# descriptions.
+
+[MASTER]
+profile = no
+ignore =
+persistent = no
+cache-size = 50000
+load-plugins =
+
+[REPORTS]
+output-format = colorized
+include-ids = no
+files-output = no
+reports = no
+evaluation = 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+comment = yes
+
+[BASIC]
+required-attributes =
+no-docstring-rgx = __.*__
+module-rgx = (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+const-rgx = ((_{0,2}[A-Z][A-Z0-9_]*)|(__.*__))$
+class-rgx = _?[A-Z][a-zA-Z0-9]+$
+function-rgx = (_?([A-Z]+[a-z0-9]+([A-Z]+[a-z0-9]*)*)|main)$
+method-rgx = (_{0,2}[A-Z]+[a-z0-9]+([A-Z]+[a-z0-9]*)*|__.*__)$
+attr-rgx = [a-z_][a-z0-9_]{1,30}$
+argument-rgx = [a-z_][a-z0-9_]*$
+variable-rgx = (_?([a-z_][a-z0-9_]*)|([A-Z0-9_]+))$
+inlinevar-rgx = [A-Za-z_][A-Za-z0-9_]*$
+good-names = i,j,k,_
+bad-names = foo,bar,baz,toto,tutu,tata
+bad-functions =
+
+[TYPECHECK]
+ignore-mixin-members = yes
+zope = no
+acquired-members =
+
+[VARIABLES]
+init-import = no
+dummy-variables-rgx = _
+additional-builtins =
+
+[CLASSES]
+ignore-iface-methods =
+defining-attr-methods = __init__,__new__,setUp
+
+[DESIGN]
+max-args = 6
+max-locals = 15
+max-returns = 6
+max-branchs = 12
+max-statements = 50
+max-parents = 7
+max-attributes = 7
+min-public-methods = 2
+max-public-methods = 20
+
+[IMPORTS]
+deprecated-modules = regsub,string,TERMIOS,Bastion,rexec
+import-graph =
+ext-import-graph =
+int-import-graph =
+
+[FORMAT]
+max-line-length = 80
+max-module-lines = 1000
+indent-string = " "
+
+[MISCELLANEOUS]
+notes = FIXME,XXX,TODO
+
+[SIMILARITIES]
+min-similarity-lines = 4
+ignore-comments = yes
+ignore-docstrings = yes
diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster
index 505ffa2187a01b55ecee379dd8a0ae5a44eae718..66c9c65ebec9a66fcb897fe1ef03af608059a9ee 100755
--- a/scripts/gnt-cluster
+++ b/scripts/gnt-cluster
@@ -424,7 +424,15 @@ def MasterFailover(opts, args):
@return: the desired exit code
"""
- return bootstrap.MasterFailover()
+ if opts.no_voting:
+ usertext = ("This will perform the failover even if most other nodes"
+ " are down, or if this node is outdated. This is dangerous"
+ " as it can lead to a non-consistent cluster. Check the"
+ " gnt-cluster(8) man page before proceeding. Continue?")
+ if not AskUser(usertext):
+ return 1
+
+ return bootstrap.MasterFailover(no_voting=opts.no_voting)
def SearchTags(opts, args):
@@ -619,7 +627,12 @@ commands = {
"", "Does a check on the cluster configuration"),
'verify-disks': (VerifyDisks, ARGS_NONE, [DEBUG_OPT],
"", "Does a check on the cluster disk status"),
- 'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT],
+ 'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT,
+ make_option("--no-voting", dest="no_voting",
+ help="Skip node agreement check (dangerous)",
+ action="store_true",
+ default=False,),
+ ],
"", "Makes the current node the master"),
'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
"", "Shows the cluster version"),
diff --git a/scripts/gnt-node b/scripts/gnt-node
index 6dd4e1dbf0e2dadfa7cee8752e611e58ea1f2bd8..c2b2f88bbd1450a6d3b6a6cd83db6ba3479ab000 100755
--- a/scripts/gnt-node
+++ b/scripts/gnt-node
@@ -100,18 +100,7 @@ def AddNode(opts, args):
output = cl.QueryConfigValues(['cluster_name'])
cluster_name = output[0]
- if readd:
- # clear the offline and drain flags on the node
- ToStdout("Resetting the 'offline' and 'drained' flags due to re-add")
- op = opcodes.OpSetNodeParams(node_name=node, force=True,
- offline=False, drained=False)
-
- result = SubmitOpCode(op, cl=cl)
- if result:
- ToStdout("Modified:")
- for param, data in result:
- ToStdout(" - %-5s -> %s", param, data)
- else:
+ if not readd:
ToStderr("-- WARNING -- \n"
"Performing this operation is going to replace the ssh daemon"
" keypair\n"
diff --git a/tools/lvmstrap b/tools/lvmstrap
index 0a17a5f7c27b39b7bfe95107c1f12f60fa1dfca1..ed92a12789f1feef1b7c79159d7d8e293a071c9d 100755
--- a/tools/lvmstrap
+++ b/tools/lvmstrap
@@ -267,7 +267,7 @@ def CheckSysDev(name, devnum):
devnum: the device number, e.g. 0x803 (2051 in decimal) for sda3
Returns:
- None; failure of the check is signalled by raising a
+ None; failure of the check is signaled by raising a
SysconfigError exception
"""
@@ -447,7 +447,7 @@ def GetMountInfo():
def DevInfo(name, dev, mountinfo):
- """Computes miscellaneous informations about a block device.
+ """Computes miscellaneous information about a block device.
Args:
name: the device name, e.g. sda
@@ -476,7 +476,7 @@ def DevInfo(name, dev, mountinfo):
def ShowDiskInfo(opts):
"""Shows a nicely formatted block device list for this system.
- This function shows the user a table with the informations gathered
+ This function shows the user a table with the information gathered
by the other functions defined, in order to help the user make a
choice about which disks should be allocated to our volume group.