Commit 224ff0f7 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

gnt-node add: Use prepare-node-join

This patch changes “gnt-node add” to use the newly added
“prepare-node-join” tool. Hereby Paramiko is no longer a hard dependency
for setting up SSH on nodes.

In “”, a positional parameter is no longer passed as a
keyword parameter.
Signed-off-by: default avatarMichael Hanselmann <>
Reviewed-by: default avatarIustin Pop <>
parent f712208d
......@@ -25,6 +25,10 @@ Version 2.7.0 beta1
- The parsing of the variants file for OSes (see
:manpage:`ganeti-os-interface(8)` has been slightly changed: now empty
lines and comment lines are ignored for better readability.
- The ``setup-ssh`` tool added in Ganeti 2.2 has been replaced.
``gnt-node add`` now invokes a new tool on the destination node, named
``prepare-node-join``, to configure the SSH daemon. Paramiko is no
longer necessary to configure nodes' SSH daemons via ``gnt-node add``.
Version 2.6.1
......@@ -503,7 +503,7 @@ def ClusterCopyFile(opts, args):
srun = ssh.SshRunner(cluster_name=cluster_name)
srun = ssh.SshRunner(cluster_name)
for node in results:
if not srun.CopyFileToNode(node, filename):
ToStderr("Copy of file %s to node %s failed", filename, node)
......@@ -27,6 +27,8 @@
# C0103: Invalid name gnt-node
import itertools
import errno
import tempfile
from ganeti.cli import *
from ganeti import cli
......@@ -37,6 +39,8 @@ from ganeti import constants
from ganeti import errors
from ganeti import netutils
from ganeti import pathutils
from ganeti import serializer
from ganeti import ssh
from cStringIO import StringIO
from ganeti import confd
......@@ -134,37 +138,111 @@ def ConvertStorageType(user_storage_type):
def _RunSetupSSH(options, nodes):
"""Wrapper around utils.RunCmd to call setup-ssh
def _TryReadFile(path):
"""Tries to read a file.
@param options: The command line options
@param nodes: The nodes to setup
If the file is not found, C{None} is returned.
@type path: string
@param path: Filename
@rtype: None or string
@todo: Consider adding a generic ENOENT wrapper
return utils.ReadFile(path)
except EnvironmentError, err:
if err.errno == errno.ENOENT:
return None
assert nodes, "Empty node list"
def _ReadSshKeys(keyfiles, _tostderr_fn=ToStderr):
"""Reads SSH keys according to C{keyfiles}.
@type keyfiles: dict
@param keyfiles: Dictionary with keys of L{constants.SSHK_ALL} and two-values
tuples (private and public key file)
@rtype: list
@return: List of three-values tuples (L{constants.SSHK_ALL}, private and
public key as strings)
result = []
cmd = [pathutils.SETUP_SSH]
for (kind, (private_file, public_file)) in keyfiles.items():
private_key = _TryReadFile(private_file)
public_key = _TryReadFile(public_file)
# Pass --debug|--verbose to the external script if set on our invocation
# --debug overrides --verbose
if public_key and private_key:
result.append((kind, private_key, public_key))
elif public_key or private_key:
_tostderr_fn("Couldn't find a complete set of keys for kind '%s'; files"
" '%s' and '%s'", kind, private_file, public_file)
return result
def _SetupSSH(options, cluster_name, node):
"""Configures a destination node's SSH daemon.
@param options: Command line options
@type cluster_name
@param cluster_name: Cluster name
@type node: string
@param node: Destination node name
if options.force_join:
ToStderr("The \"--force-join\" option is no longer supported and will be"
" ignored.")
cmd = [pathutils.PREPARE_NODE_JOIN]
# Pass --debug/--verbose to the external script if set on our invocation
if options.debug:
elif options.verbose:
if options.verbose:
if not options.ssh_key_check:
if options.force_join:
host_keys = _ReadSshKeys(constants.SSH_DAEMON_KEYFILES)
(_, root_keyfiles) = \
ssh.GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False)
root_keys = _ReadSshKeys(root_keyfiles)
(_, cert_pem) = \
data = {
constants.SSHS_CLUSTER_NAME: cluster_name,
constants.SSHS_SSH_HOST_KEY: host_keys,
constants.SSHS_SSH_ROOT_KEY: root_keys,
srun = ssh.SshRunner(cluster_name)
scmd = srun.BuildCmd(node, constants.SSH_LOGIN_USER,
batch=False, ask_key=options.ssh_key_check,
strict_host_check=options.ssh_key_check, quiet=False,
tempfh = tempfile.TemporaryFile()
result = utils.RunCmd(cmd, interactive=True)
result = utils.RunCmd(scmd, interactive=True, input_fd=tempfh)
if result.failed:
errmsg = ("Command '%s' failed with exit code %s; output %r" %
(result.cmd, result.exit_code, result.output))
raise errors.OpExecError(errmsg)
raise errors.OpExecError("Command '%s' failed: %s" %
(result.cmd, result.fail_reason))
......@@ -206,8 +284,7 @@ def AddNode(opts, args):
sip = opts.secondary_ip
# read the cluster name from the master
output = cl.QueryConfigValues(["cluster_name"])
cluster_name = output[0]
(cluster_name, ) = cl.QueryConfigValues(["cluster_name"])
if not readd and opts.node_setup:
ToStderr("-- WARNING -- \n"
......@@ -218,7 +295,7 @@ def AddNode(opts, args):
"and grant full intra-cluster ssh root access to/from it\n", node)
if opts.node_setup:
_RunSetupSSH(opts, [node])
_SetupSSH(opts, cluster_name, node)
bootstrap.SetupNodeDaemon(cluster_name, node, opts.ssh_key_check)
......@@ -44,6 +44,7 @@ IMPORT_EXPORT_DAEMON = _autoconf.PKGLIBDIR + "/import-export"
KVM_CONSOLE_WRAPPER = _autoconf.PKGLIBDIR + "/tools/kvm-console-wrapper"
KVM_IFUP = _autoconf.PKGLIBDIR + "/kvm-ifup"
SETUP_SSH = _autoconf.TOOLSDIR + "/setup-ssh"
PREPARE_NODE_JOIN = _autoconf.PKGLIBDIR + "/prepare-node-join"
XM_CONSOLE_WRAPPER = _autoconf.PKGLIBDIR + "/tools/xm-console-wrapper"
......@@ -52,10 +52,6 @@ secondary IP again, it will reused from the cluster. Also, the
drained and offline flags of the node will be cleared before
re-adding it.
The ``--force-join`` option is to proceed with adding a node even if it already
appears to belong to another cluster. This is used during cluster merging, for
The ``-g (--node-group)`` option is used to add the new node into a
specific node group, specified by UUID or name. If only one node group
exists you can skip this option, otherwise it's mandatory.
......@@ -651,7 +651,7 @@ class Merger(object):
for node in data.nodes:"Readding node %s", node)
result = utils.RunCmd(["gnt-node", "add", "--readd",
"--no-ssh-key-check", "--force-join", node])
"--no-ssh-key-check", node])
if result.failed:
logging.error("%s failed to be readded. Reason: %s, output: %s",
node, result.fail_reason, result.output)
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