# 02110-1301, USA.
+# pylint: disable-msg=W0401,W0614
+# W0401: Wildcard import ganeti.cli
+# W0614: Unused import %s from wildcard import (since we need cli)
+
import sys
from optparse import make_option
from ganeti.cli import *
from ganeti import opcodes
-from ganeti import logger
from ganeti import utils
from ganeti import constants
from ganeti import errors
+from ganeti import bootstrap
+#: default list of field for L{ListNodes}
_LIST_DEF_FIELDS = [
"name", "dtotal", "dfree",
"mtotal", "mnode", "mfree",
"pinst_cnt", "sinst_cnt",
]
+
+@UsesRPC
def AddNode(opts, args):
- """Add node cli-to-processor bridge."""
- logger.ToStderr("-- WARNING -- \n"
- "Performing this operation is going to replace the ssh daemon keypair\n"
- "on the target machine (%s) with the ones of the current one\n"
- "and grant full intra-cluster ssh root access to/from it\n" % args[0])
+ """Add a node to the cluster.
+
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should contain only one element, the new node name
+ @rtype: int
+ @return: the desired exit code
+
+ """
+ cl = GetClient()
+ dns_data = utils.HostInfo(args[0])
+ node = dns_data.name
+
+ if not opts.readd:
+ try:
+ output = cl.QueryNodes(names=[node], fields=['name'])
+ except (errors.OpPrereqError, errors.OpExecError):
+ pass
+ else:
+ ToStderr("Node %s already in the cluster (as %s)"
+ " - please use --readd", args[0], output[0][0])
+ return 1
+
+ # read the cluster name from the master
+ output = cl.QueryConfigValues(['cluster_name'])
+ cluster_name = output[0]
+
+ ToStderr("-- WARNING -- \n"
+ "Performing this operation is going to replace the ssh daemon"
+ " keypair\n"
+ "on the target machine (%s) with the ones of the"
+ " current one\n"
+ "and grant full intra-cluster ssh root access to/from it\n", node)
+
+ bootstrap.SetupNodeDaemon(cluster_name, node, opts.ssh_key_check)
+
op = opcodes.OpAddNode(node_name=args[0], secondary_ip=opts.secondary_ip,
readd=opts.readd)
SubmitOpCode(op)
def ListNodes(opts, args):
"""List nodes and their properties.
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should be an empty list
+ @rtype: int
+ @return: the desired exit code
+
"""
if opts.output is None:
selected_fields = _LIST_DEF_FIELDS
else:
selected_fields = opts.output.split(",")
- op = opcodes.OpQueryNodes(output_fields=selected_fields, names=[])
- output = SubmitOpCode(op)
+ output = GetClient().QueryNodes([], selected_fields)
if not opts.no_headers:
headers = {
"bootid": "BootID",
"ctotal": "CTotal",
"tags": "Tags",
+ "serial_no": "SerialNo",
+ "master_candidate": "MasterC",
+ "master": "IsMaster",
}
else:
headers = None
- if opts.human_readable:
- unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
- else:
- unitfields = None
+ unitfields = ["dtotal", "dfree", "mtotal", "mnode", "mfree"]
numfields = ["dtotal", "dfree",
"mtotal", "mnode", "mfree",
"pinst_cnt", "sinst_cnt",
- "ctotal"]
+ "ctotal", "serial_no"]
list_type_fields = ("pinst_list", "sinst_list", "tags")
# change raw values to nicer strings
val = row[idx]
if field in list_type_fields:
val = ",".join(val)
+ elif field in ('master', 'master_candidate'):
+ if val:
+ val = 'Y'
+ else:
+ val = 'N'
elif val is None:
val = "?"
row[idx] = str(val)
data = GenerateTable(separator=opts.separator, headers=headers,
fields=selected_fields, unitfields=unitfields,
- numfields=numfields, data=output)
+ numfields=numfields, data=output, units=opts.units)
for line in data:
- logger.ToStdout(line)
+ ToStdout(line)
return 0
def EvacuateNode(opts, args):
"""Relocate all secondary instance from a node.
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should be an empty list
+ @rtype: int
+ @return: the desired exit code
+
"""
force = opts.force
selected_fields = ["name", "sinst_list"]
src_node)
if not sinst:
- logger.ToStderr("No secondary instances on node %s, exiting." % src_node)
+ ToStderr("No secondary instances on node %s, exiting.", src_node)
return constants.EXIT_SUCCESS
sinst = utils.NiceSort(sinst)
mode=constants.REPLACE_DISK_ALL,
disks=["sda", "sdb"])
try:
- logger.ToStdout("Replacing disks for instance %s" % iname)
+ ToStdout("Replacing disks for instance %s", iname)
SubmitOpCode(op)
- logger.ToStdout("Instance %s has been relocated" % iname)
+ ToStdout("Instance %s has been relocated", iname)
good_cnt += 1
except errors.GenericError, err:
nret, msg = FormatError(err)
retcode |= nret
- logger.ToStderr("Error replacing disks for instance %s: %s" %
- (iname, msg))
+ ToStderr("Error replacing disks for instance %s: %s", iname, msg)
bad_cnt += 1
if retcode == constants.EXIT_SUCCESS:
- logger.ToStdout("All %d instance(s) relocated successfully." % good_cnt)
+ ToStdout("All %d instance(s) relocated successfully.", good_cnt)
else:
- logger.ToStdout("There were errors during the relocation:\n"
- "%d error(s) out of %d instance(s)." %
- (bad_cnt, good_cnt + bad_cnt))
+ ToStdout("There were errors during the relocation:\n"
+ "%d error(s) out of %d instance(s).", bad_cnt, good_cnt + bad_cnt)
return retcode
def FailoverNode(opts, args):
"""Failover all primary instance on a node.
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should be an empty list
+ @rtype: int
+ @return: the desired exit code
+
"""
force = opts.force
selected_fields = ["name", "pinst_list"]
node, pinst = result[0]
if not pinst:
- logger.ToStderr("No primary instances on node %s, exiting." % node)
+ ToStderr("No primary instances on node %s, exiting.", node)
return 0
pinst = utils.NiceSort(pinst)
op = opcodes.OpFailoverInstance(instance_name=iname,
ignore_consistency=opts.ignore_consistency)
try:
- logger.ToStdout("Failing over instance %s" % iname)
+ ToStdout("Failing over instance %s", iname)
SubmitOpCode(op)
- logger.ToStdout("Instance %s has been failed over" % iname)
+ ToStdout("Instance %s has been failed over", iname)
good_cnt += 1
except errors.GenericError, err:
nret, msg = FormatError(err)
retcode |= nret
- logger.ToStderr("Error failing over instance %s: %s" % (iname, msg))
+ ToStderr("Error failing over instance %s: %s", iname, msg)
bad_cnt += 1
if retcode == 0:
- logger.ToStdout("All %d instance(s) failed over successfully." % good_cnt)
+ ToStdout("All %d instance(s) failed over successfully.", good_cnt)
else:
- logger.ToStdout("There were errors during the failover:\n"
- "%d error(s) out of %d instance(s)." %
- (bad_cnt, good_cnt + bad_cnt))
+ ToStdout("There were errors during the failover:\n"
+ "%d error(s) out of %d instance(s).", bad_cnt, good_cnt + bad_cnt)
return retcode
def ShowNodeConfig(opts, args):
"""Show node information.
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should either be an empty list, in which case
+ we show information about all nodes, or should contain
+ a list of nodes to be queried for information
+ @rtype: int
+ @return: the desired exit code
+
"""
op = opcodes.OpQueryNodes(output_fields=["name", "pip", "sip",
"pinst_list", "sinst_list"],
result = SubmitOpCode(op)
for name, primary_ip, secondary_ip, pinst, sinst in result:
- logger.ToStdout("Node name: %s" % name)
- logger.ToStdout(" primary ip: %s" % primary_ip)
- logger.ToStdout(" secondary ip: %s" % secondary_ip)
+ ToStdout("Node name: %s", name)
+ ToStdout(" primary ip: %s", primary_ip)
+ ToStdout(" secondary ip: %s", secondary_ip)
if pinst:
- logger.ToStdout(" primary for instances:")
+ ToStdout(" primary for instances:")
for iname in pinst:
- logger.ToStdout(" - %s" % iname)
+ ToStdout(" - %s", iname)
else:
- logger.ToStdout(" primary for no instances")
+ ToStdout(" primary for no instances")
if sinst:
- logger.ToStdout(" secondary for instances:")
+ ToStdout(" secondary for instances:")
for iname in sinst:
- logger.ToStdout(" - %s" % iname)
+ ToStdout(" - %s", iname)
else:
- logger.ToStdout(" secondary for no instances")
+ ToStdout(" secondary for no instances")
return 0
def RemoveNode(opts, args):
- """Remove node cli-to-processor bridge."""
+ """Remove a node from the cluster.
+
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should contain only one element, the name of
+ the node to be removed
+ @rtype: int
+ @return: the desired exit code
+
+ """
op = opcodes.OpRemoveNode(node_name=args[0])
SubmitOpCode(op)
+ return 0
def ListVolumes(opts, args):
"""List logical volumes on node(s).
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should either be an empty list, in which case
+ we list data for all nodes, or contain a list of nodes
+ to display data only for those
+ @rtype: int
+ @return: the desired exit code
+
"""
if opts.output is None:
selected_fields = ["node", "phys", "vg",
else:
headers = None
- if opts.human_readable:
- unitfields = ["size"]
- else:
- unitfields = None
+ unitfields = ["size"]
numfields = ["size"]
data = GenerateTable(separator=opts.separator, headers=headers,
fields=selected_fields, unitfields=unitfields,
- numfields=numfields, data=output)
+ numfields=numfields, data=output, units=opts.units)
for line in data:
- logger.ToStdout(line)
+ ToStdout(line)
+
+ return 0
+
+def SetNodeParams(opts, args):
+ """Modifies a node.
+
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should contain only one element, the node name
+ @rtype: int
+ @return: the desired exit code
+
+ """
+ if opts.master_candidate is None:
+ ToStderr("Please give at least one of the parameters.")
+ return 1
+
+ candidate = opts.master_candidate == 'yes'
+ op = opcodes.OpSetNodeParams(node_name=args[0],
+ master_candidate=candidate,
+ force=opts.force)
+
+ # even if here we process the result, we allow submit only
+ result = SubmitOrSend(op, opts)
+
+ if result:
+ ToStdout("Modified node %s", args[0])
+ for param, data in result:
+ ToStdout(" - %-5s -> %s", param, data)
return 0
make_option("--readd", dest="readd",
default=False, action="store_true",
help="Readd old node after replacing it"),
+ make_option("--no-ssh-key-check", dest="ssh_key_check",
+ default=True, action="store_false",
+ help="Disable SSH key fingerprint checking"),
],
- "[-s ip] [--readd] <node_name>", "Add a node to the cluster"),
+ "[-s ip] [--readd] [--no-ssh-key-check] <node_name>",
+ "Add a node to the cluster"),
'evacuate': (EvacuateNode, ARGS_FIXED(2),
[DEBUG_OPT, FORCE_OPT],
"[-f] <src> <dst>",
'info': (ShowNodeConfig, ARGS_ANY, [DEBUG_OPT],
"[<node_name>...]", "Show information about the node(s)"),
'list': (ListNodes, ARGS_NONE,
- [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
+ [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT,
+ SUBMIT_OPT],
"", "Lists the nodes in the cluster. The available fields"
" are (see the man page for details): name, pinst_cnt, pinst_list,"
" sinst_cnt, sinst_list, pip, sip, dtotal, dfree, mtotal, mnode,"
- " mfree, bootid, cpu_count. The default field list is"
+ " mfree, bootid, cpu_count, serial_no."
+ " The default field list is"
" (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
),
+ 'modify': (SetNodeParams, ARGS_ONE,
+ [DEBUG_OPT, FORCE_OPT,
+ SUBMIT_OPT,
+ make_option("-C", "--master-candidate", dest="master_candidate",
+ choices=('yes', 'no'), default=None,
+ help="Set the master_candidate flag on the node"),
+ ],
+ "<instance>", "Alters the parameters of an instance"),
'remove': (RemoveNode, ARGS_ONE, [DEBUG_OPT],
"<node_name>", "Removes a node from the cluster"),
'volumes': (ListVolumes, ARGS_ANY,