Commit d3b18b8e authored by René Nussbaumer's avatar René Nussbaumer
Browse files

Adding functionality to check feasability of joining the host

This checks if the host might possibily belong already to another
cluster. If this is the case we abort without any further action
unless we are forced by --force-join.
Signed-off-by: default avatarRené Nussbaumer <>
Reviewed-by: default avatarMichael Hanselmann <>
parent 887c7aa6
......@@ -38,6 +38,7 @@ from ganeti import cli
from ganeti import constants
from ganeti import errors
from ganeti import netutils
from ganeti import ssconf
from ganeti import ssh
from ganeti import utils
......@@ -48,6 +49,54 @@ class RemoteCommandError(errors.GenericError):
class JoinCheckError(errors.GenericError):
"""Exception raised if join check fails.
def _CheckJoin(transport):
"""Checks if a join is safe or dangerous.
Note: This function relies on the fact, that all
hosts have the same configuration at compile time of
Ganeti. So that the constants do not mismatch.
@param transport: The paramiko transport instance
@return: True if the join is safe; False otherwise
sftp = transport.open_sftp_client()
ss = ssconf.SimpleStore()
ss_cluster_name_path = ss.KeyToFilename(constants.SS_CLUSTER_NAME)
cluster_files = {
ss_cluster_name_path: utils.ReadFile(ss_cluster_name_path),
constants.NODED_CERT_FILE: utils.ReadFile(constants.NODED_CERT_FILE),
remote_noded_file = _ReadSftpFile(sftp, constants.NODED_CERT_FILE)
except IOError:
# We can just assume that the file doesn't exist as such error reporting
# is lacking from paramiko
# We don't have the noded certificate. As without the cert, the
# noded is not running, we are on the safe bet to say that this
# node doesn't belong to a cluster
return True
remote_cluster_name = _ReadSftpFile(sftp, ss_cluster_name_path)
except IOError:
# This can indicate that a previous join was not successful
# So if the noded cert was found and matches we are fine
return cluster_files[constants.NODED_CERT_FILE] == remote_noded_file
return (cluster_files[constants.NODED_CERT_FILE] == remote_noded_file and
cluster_files[ss_cluster_name_path] == remote_cluster_name)
def _RunRemoteCommand(transport, command):
"""Invokes and wait for the command over SSH.
......@@ -84,6 +133,21 @@ def _InvokeDaemonUtil(transport, command):
_RunRemoteCommand(transport, "%s %s" % (constants.DAEMON_UTIL, command))
def _ReadSftpFile(sftp, filename):
"""Reads a file over sftp.
@param sftp: An open paramiko SFTP client
@param filename: The filename of the file to read
@return: The content of the file
remote_file =, "r")
def _WriteSftpFile(sftp, name, perm, data):
"""SFTPs data to a remote file.
......@@ -159,8 +223,8 @@ def ParseOptions():
program = os.path.basename(sys.argv[0])
parser = optparse.OptionParser(usage=("%prog [--debug|--verbose] <node>"
" <node...>"), prog=program)
parser = optparse.OptionParser(usage=("%prog [--debug|--verbose] [--force]"
" <node> <node...>"), prog=program)
......@@ -172,6 +236,9 @@ def ParseOptions():
parser.add_option(optparse.Option("--key-type", dest="key_type",
choices=("rsa", "dsa"), default="dsa",
help="The private key type (rsa or dsa)"))
parser.add_option(optparse.Option("-j", "--force-join", dest="force_join",
action="store_true", default=False,
help="Force the join of the host"))
(options, args) = parser.parse_args()
......@@ -368,6 +435,12 @@ def main():
if not _CheckJoin(transport):
if options.force_join:
logging.warning("Host %s failed join check, forced to continue",
raise JoinCheckError("Host %s failed join check" % host)
except errors.GenericError, err:
logging.error("While doing setup on host %s an error occured: %s",
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