from ganeti import netutils
from ganeti import qlang
from ganeti import objects
+from ganeti import pathutils
from optparse import (OptionParser, TitledHelpFormatter,
Option, OptionValueError)
raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
kind = opts.tag_type
if kind == constants.TAG_CLUSTER:
- retval = kind, kind
+ retval = kind, None
elif kind in (constants.TAG_NODEGROUP,
constants.TAG_NODE,
constants.TAG_INSTANCE):
"""
kind, name = _ExtractTagsObject(opts, args)
- cl = GetClient()
+ cl = GetClient(query=True)
result = cl.QueryTags(kind, name)
result = list(result)
result.sort()
DRY_RUN_OPT = cli_option("--dry-run", default=False,
action="store_true",
help=("Do not execute the operation, just run the"
- " check steps and verify it it could be"
+ " check steps and verify if it could be"
" executed"))
VERBOSE_OPT = cli_option("-v", "--verbose", default=False,
completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
DEFAULT_IALLOCATOR_OPT = cli_option("-I", "--default-iallocator",
- metavar="<NAME>",
- help="Set the default instance allocator plugin",
- default=None, type="string",
- completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
+ metavar="<NAME>",
+ help="Set the default instance"
+ " allocator plugin",
+ default=None, type="string",
+ completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
OS_OPT = cli_option("-o", "--os-type", dest="os", help="What OS to run",
metavar="<os>",
completion_suggest=OPT_COMPL_ONE_OS)
OSPARAMS_OPT = cli_option("-O", "--os-parameters", dest="osparams",
- type="keyval", default={},
- help="OS parameters")
+ type="keyval", default={},
+ help="OS parameters")
FORCE_VARIANT_OPT = cli_option("--force-variant", dest="force_variant",
action="store_true", default=False,
SPECS_DISK_SIZE_OPT = cli_option("--specs-disk-size", dest="ispecs_disk_size",
type="keyval", default={},
help="Disk size specs: list of key=value,"
- " where key is one of min, max, std"
+ " where key is one of min, max, std"
" (in MB or using a unit)")
SPECS_NIC_COUNT_OPT = cli_option("--specs-nic-count", dest="ispecs_nic_count",
" where key is one of min, max, std")
IPOLICY_DISK_TEMPLATES = cli_option("--ipolicy-disk-templates",
- dest="ipolicy_disk_templates",
- type="list", default=None,
- help="Comma-separated list of"
- " enabled disk templates")
+ dest="ipolicy_disk_templates",
+ type="list", default=None,
+ help="Comma-separated list of"
+ " enabled disk templates")
IPOLICY_VCPU_RATIO = cli_option("--ipolicy-vcpu-ratio",
dest="ipolicy_vcpu_ratio",
" (excluded from allocation operations)"))
CAPAB_MASTER_OPT = cli_option("--master-capable", dest="master_capable",
- type="bool", default=None, metavar=_YORNO,
- help="Set the master_capable flag on the node")
+ type="bool", default=None, metavar=_YORNO,
+ help="Set the master_capable flag on the node")
CAPAB_VM_OPT = cli_option("--vm-capable", dest="vm_capable",
- type="bool", default=None, metavar=_YORNO,
- help="Set the vm_capable flag on the node")
+ type="bool", default=None, metavar=_YORNO,
+ help="Set the vm_capable flag on the node")
ALLOCATABLE_OPT = cli_option("--allocatable", dest="allocatable",
type="bool", default=None, metavar=_YORNO,
default=None)
USE_EXTERNAL_MIP_SCRIPT = cli_option("--use-external-mip-script",
- dest="use_external_mip_script",
- help="Specify whether to run a user-provided"
- " script for the master IP address turnup and"
- " turndown operations",
- type="bool", metavar=_YORNO, default=None)
+ dest="use_external_mip_script",
+ help="Specify whether to run a"
+ " user-provided script for the master"
+ " IP address turnup and"
+ " turndown operations",
+ type="bool", metavar=_YORNO, default=None)
GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
help="Specify the default directory (cluster-"
"wide) for storing the file-based disks [%s]" %
- constants.DEFAULT_FILE_STORAGE_DIR,
+ pathutils.DEFAULT_FILE_STORAGE_DIR,
metavar="DIR",
- default=constants.DEFAULT_FILE_STORAGE_DIR)
+ default=pathutils.DEFAULT_FILE_STORAGE_DIR)
-GLOBAL_SHARED_FILEDIR_OPT = cli_option("--shared-file-storage-dir",
- dest="shared_file_storage_dir",
- help="Specify the default directory (cluster-"
- "wide) for storing the shared file-based"
- " disks [%s]" %
- constants.DEFAULT_SHARED_FILE_STORAGE_DIR,
- metavar="SHAREDDIR",
- default=constants.DEFAULT_SHARED_FILE_STORAGE_DIR)
+GLOBAL_SHARED_FILEDIR_OPT = cli_option(
+ "--shared-file-storage-dir",
+ dest="shared_file_storage_dir",
+ help="Specify the default directory (cluster-wide) for storing the"
+ " shared file-based disks [%s]" %
+ pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR,
+ metavar="SHAREDDIR", default=pathutils.DEFAULT_SHARED_FILE_STORAGE_DIR)
NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts",
- help="Don't modify /etc/hosts",
+ help="Don't modify %s" % pathutils.ETC_HOSTS,
action="store_false", default=True)
NOMODIFY_SSH_SETUP_OPT = cli_option("--no-ssh-init", dest="modify_ssh_setup",
help="Maximum time to wait")
SHUTDOWN_TIMEOUT_OPT = cli_option("--shutdown-timeout",
- dest="shutdown_timeout", type="int",
- default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
- help="Maximum time to wait for instance shutdown")
+ dest="shutdown_timeout", type="int",
+ default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
+ help="Maximum time to wait for instance"
+ " shutdown")
INTERVAL_OPT = cli_option("--interval", dest="interval", type="int",
default=None,
" certificate"))
SPICE_CERT_OPT = cli_option("--spice-certificate", dest="spice_cert",
- default=None,
- help="File containing new SPICE certificate")
+ default=None,
+ help="File containing new SPICE certificate")
SPICE_CACERT_OPT = cli_option("--spice-ca-certificate", dest="spice_cacert",
- default=None,
- help="File containing the certificate of the CA"
- " which signed the SPICE certificate")
+ default=None,
+ help="File containing the certificate of the CA"
+ " which signed the SPICE certificate")
NEW_SPICE_CERT_OPT = cli_option("--new-spice-certificate",
- dest="new_spice_cert", default=None,
- action="store_true",
- help=("Generate a new self-signed SPICE"
- " certificate"))
+ dest="new_spice_cert", default=None,
+ action="store_true",
+ help=("Generate a new self-signed SPICE"
+ " certificate"))
NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key",
dest="new_confd_hmac_key",
" removed from the user-id pool"))
RESERVED_LVS_OPT = cli_option("--reserved-lvs", default=None,
- action="store", dest="reserved_lvs",
- help=("A comma-separated list of reserved"
- " logical volumes names, that will be"
- " ignored by cluster verify"))
+ action="store", dest="reserved_lvs",
+ help=("A comma-separated list of reserved"
+ " logical volumes names, that will be"
+ " ignored by cluster verify"))
ROMAN_OPT = cli_option("--roman",
dest="roman_integers", default=False,
constants.IP6_VERSION),
help="Cluster-wide IP version for primary IP")
+
+def _PriorityOptionCb(option, _, value, parser):
+ """Callback for processing C{--priority} option.
+
+ """
+ value = _PRIONAME_TO_VALUE[value]
+
+ setattr(parser.values, option.dest, value)
+
+
PRIORITY_OPT = cli_option("--priority", default=None, dest="priority",
metavar="|".join(name for name, _ in _PRIORITY_NAMES),
choices=_PRIONAME_TO_VALUE.keys(),
+ action="callback", type="choice",
+ callback=_PriorityOptionCb,
help="Priority for opcode processing")
HID_OS_OPT = cli_option("--hidden", dest="hidden",
help="Specify if the SoR for node is powered")
OOB_TIMEOUT_OPT = cli_option("--oob-timeout", dest="oob_timeout", type="int",
- default=constants.OOB_TIMEOUT,
- help="Maximum time to wait for out-of-band helper")
+ default=constants.OOB_TIMEOUT,
+ help="Maximum time to wait for out-of-band helper")
POWER_DELAY_OPT = cli_option("--power-delay", dest="power_delay", type="float",
default=constants.OOB_POWER_DELAY,
]
-def _ParseArgs(argv, commands, aliases, env_override):
+class _ShowUsage(Exception):
+ """Exception class for L{_ParseArgs}.
+
+ """
+ def __init__(self, exit_error):
+ """Initializes instances of this class.
+
+ @type exit_error: bool
+ @param exit_error: Whether to report failure on exit
+
+ """
+ Exception.__init__(self)
+ self.exit_error = exit_error
+
+
+class _ShowVersion(Exception):
+ """Exception class for L{_ParseArgs}.
+
+ """
+
+
+def _ParseArgs(binary, argv, commands, aliases, env_override):
"""Parser for the command line arguments.
This function parses the arguments and returns the function which
must be executed together with its (modified) arguments.
- @param argv: the command line
- @param commands: dictionary with special contents, see the design
- doc for cmdline handling
- @param aliases: dictionary with command aliases {'alias': 'target, ...}
+ @param binary: Script name
+ @param argv: Command line arguments
+ @param commands: Dictionary containing command definitions
+ @param aliases: dictionary with command aliases {"alias": "target", ...}
@param env_override: list of env variables allowed for default args
+ @raise _ShowUsage: If usage description should be shown
+ @raise _ShowVersion: If version should be shown
"""
assert not (env_override - set(commands))
+ assert not (set(aliases.keys()) & set(commands.keys()))
- if len(argv) == 0:
- binary = "<command>"
+ if len(argv) > 1:
+ cmd = argv[1]
else:
- binary = argv[0].split("/")[-1]
+ # No option or command given
+ raise _ShowUsage(exit_error=True)
- if len(argv) > 1 and argv[1] == "--version":
- ToStdout("%s (ganeti %s) %s", binary, constants.VCS_VERSION,
- constants.RELEASE_VERSION)
- # Quit right away. That way we don't have to care about this special
- # argument. optparse.py does it the same.
- sys.exit(0)
-
- if len(argv) < 2 or not (argv[1] in commands or
- argv[1] in aliases):
- # let's do a nice thing
- sortedcmds = commands.keys()
- sortedcmds.sort()
-
- ToStdout("Usage: %s {command} [options...] [argument...]", binary)
- ToStdout("%s <command> --help to see details, or man %s", binary, binary)
- ToStdout("")
-
- # compute the max line length for cmd + usage
- mlen = max([len(" %s" % cmd) for cmd in commands])
- mlen = min(60, mlen) # should not get here...
-
- # and format a nice command list
- ToStdout("Commands:")
- for cmd in sortedcmds:
- cmdstr = " %s" % (cmd,)
- help_text = commands[cmd][4]
- help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
- ToStdout("%-*s - %s", mlen, cmdstr, help_lines.pop(0))
- for line in help_lines:
- ToStdout("%-*s %s", mlen, "", line)
-
- ToStdout("")
-
- return None, None, None
+ if cmd == "--version":
+ raise _ShowVersion()
+ elif cmd == "--help":
+ raise _ShowUsage(exit_error=False)
+ elif not (cmd in commands or cmd in aliases):
+ raise _ShowUsage(exit_error=True)
# get command, unalias it, and look it up in commands
- cmd = argv.pop(1)
if cmd in aliases:
- if cmd in commands:
- raise errors.ProgrammerError("Alias '%s' overrides an existing"
- " command" % cmd)
-
if aliases[cmd] not in commands:
raise errors.ProgrammerError("Alias '%s' maps to non-existing"
" command '%s'" % (cmd, aliases[cmd]))
args_env_name = ("%s_%s" % (binary.replace("-", "_"), cmd)).upper()
env_args = os.environ.get(args_env_name)
if env_args:
- argv = utils.InsertAtPos(argv, 1, shlex.split(env_args))
+ argv = utils.InsertAtPos(argv, 2, shlex.split(env_args))
func, args_def, parser_opts, usage, description = commands[cmd]
parser = OptionParser(option_list=parser_opts + COMMON_OPTS,
formatter=TitledHelpFormatter(),
usage="%%prog %s %s" % (cmd, usage))
parser.disable_interspersed_args()
- options, args = parser.parse_args(args=argv[1:])
+ options, args = parser.parse_args(args=argv[2:])
if not _CheckArguments(cmd, args_def, args):
return None, None, None
return func, options, args
+def _FormatUsage(binary, commands):
+ """Generates a nice description of all commands.
+
+ @param binary: Script name
+ @param commands: Dictionary containing command definitions
+
+ """
+ # compute the max line length for cmd + usage
+ mlen = min(60, max(map(len, commands)))
+
+ yield "Usage: %s {command} [options...] [argument...]" % binary
+ yield "%s <command> --help to see details, or man %s" % (binary, binary)
+ yield ""
+ yield "Commands:"
+
+ # and format a nice command list
+ for (cmd, (_, _, _, _, help_text)) in sorted(commands.items()):
+ help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
+ yield " %-*s - %s" % (mlen, cmd, help_lines.pop(0))
+ for line in help_lines:
+ yield " %-*s %s" % (mlen, "", line)
+
+ yield ""
+
+
def _CheckArguments(cmd, args_def, args):
"""Verifies the arguments using the argument definition.
if hasattr(options, "dry_run"):
op.dry_run = options.dry_run
if getattr(options, "priority", None) is not None:
- op.priority = _PRIONAME_TO_VALUE[options.priority]
+ op.priority = options.priority
+
+
+def GetClient(query=False):
+ """Connects to the a luxi socket and returns a client.
+ @type query: boolean
+ @param query: this signifies that the client will only be
+ used for queries; if the build-time parameter
+ enable-split-queries is enabled, then the client will be
+ connected to the query socket instead of the masterd socket
-def GetClient():
+ """
+ if query and constants.ENABLE_SPLIT_QUERY:
+ address = pathutils.QUERY_SOCKET
+ else:
+ address = None
# TODO: Cache object?
try:
- client = luxi.Client()
+ client = luxi.Client(address=address)
except luxi.NoMasterError:
ss = ssconf.SimpleStore()
elif isinstance(err, errors.OpPrereqError):
if len(err.args) == 2:
obuf.write("Failure: prerequisites not met for this"
- " operation:\nerror type: %s, error details:\n%s" %
+ " operation:\nerror type: %s, error details:\n%s" %
(err.args[1], err.args[0]))
else:
obuf.write("Failure: prerequisites not met for this"
aliases = {}
try:
- func, options, args = _ParseArgs(sys.argv, commands, aliases, env_override)
+ (func, options, args) = _ParseArgs(binary, sys.argv, commands, aliases,
+ env_override)
+ except _ShowVersion:
+ ToStdout("%s (ganeti %s) %s", binary, constants.VCS_VERSION,
+ constants.RELEASE_VERSION)
+ return constants.EXIT_SUCCESS
+ except _ShowUsage, err:
+ for line in _FormatUsage(binary, commands):
+ ToStdout(line)
+
+ if err.exit_error:
+ return constants.EXIT_FAILURE
+ else:
+ return constants.EXIT_SUCCESS
except errors.ParameterError, err:
result, err_msg = FormatError(err)
ToStderr(err_msg)
for key, val in override.iteritems():
setattr(options, key, val)
- utils.SetupLogging(constants.LOG_COMMANDS, logname, debug=options.debug,
+ utils.SetupLogging(pathutils.LOG_COMMANDS, logname, debug=options.debug,
stderr_logging=True)
logging.info("Command line: %s", cmdline)
# No need to use SSH
result = utils.RunCmd(cmd)
else:
- result = self.ssh.Run(node_name, "root", utils.ShellQuoteArgs(cmd))
+ result = self.ssh.Run(node_name, constants.SSH_LOGIN_USER,
+ utils.ShellQuoteArgs(cmd))
if result.failed:
errmsg = ["Failed to run command %s" % result.cmd]
"""
# Pause watcher by acquiring an exclusive lock on watcher state file
self.feedback_fn("Blocking watcher")
- watcher_block = utils.FileLock.Open(constants.WATCHER_LOCK_FILE)
+ watcher_block = utils.FileLock.Open(pathutils.WATCHER_LOCK_FILE)
try:
# TODO: Currently, this just blocks. There's no timeout.
# TODO: Should it be a shared lock?
# Stop master daemons, so that no new jobs can come in and all running
# ones are finished
self.feedback_fn("Stopping master daemons")
- self._RunCmd(None, [constants.DAEMON_UTIL, "stop-master"])
+ self._RunCmd(None, [pathutils.DAEMON_UTIL, "stop-master"])
try:
# Stop daemons on all nodes
for node_name in self.online_nodes:
self.feedback_fn("Stopping daemons on %s" % node_name)
- self._RunCmd(node_name, [constants.DAEMON_UTIL, "stop-all"])
+ self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "stop-all"])
# All daemons are shut down now
try:
# Start cluster again, master node last
for node_name in self.nonmaster_nodes + [self.master_node]:
self.feedback_fn("Starting daemons on %s" % node_name)
- self._RunCmd(node_name, [constants.DAEMON_UTIL, "start-all"])
+ self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "start-all"])
finally:
# Resume watcher
watcher_block.Close()
def GenericList(resource, fields, names, unit, separator, header, cl=None,
format_override=None, verbose=False, force_filter=False,
- namefield=None, qfilter=None):
+ namefield=None, qfilter=None, isnumeric=False):
"""Generic implementation for listing all items of a resource.
@param resource: One of L{constants.QR_VIA_LUXI}
L{qlang.MakeFilter} for details)
@type qfilter: list or None
@param qfilter: Query filter (in addition to names)
+ @param isnumeric: bool
+ @param isnumeric: Whether the namefield's type is numeric, and therefore
+ any simple filters built by namefield should use integer values to
+ reflect that
"""
if not names:
names = None
- namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield)
+ namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield,
+ isnumeric=isnumeric)
if qfilter is None:
qfilter = namefilter