"POWER_DELAY_OPT",
"PREALLOC_WIPE_DISKS_OPT",
"PRIMARY_IP_VERSION_OPT",
+ "PRIMARY_ONLY_OPT",
"PRIORITY_OPT",
"RAPI_CERT_OPT",
"READD_OPT",
"RESERVED_LVS_OPT",
"ROMAN_OPT",
"SECONDARY_IP_OPT",
+ "SECONDARY_ONLY_OPT",
"SELECT_OS_OPT",
"SEP_OPT",
"SHOWCMD_OPT",
"SRC_DIR_OPT",
"SRC_NODE_OPT",
"SUBMIT_OPT",
+ "STARTUP_PAUSED_OPT",
"STATIC_OPT",
"SYNC_OPT",
+ "TAG_ADD_OPT",
"TAG_SRC_OPT",
"TIMEOUT_OPT",
+ "TO_GROUP_OPT",
"UIDPOOL_OPT",
"USEUNITS_OPT",
"USE_REPL_NET_OPT",
"""
if ":" not in value:
- ident, rest = value, ''
+ ident, rest = value, ""
else:
ident, rest = value.split(":", 1)
" (defaults to one space)"))
USEUNITS_OPT = cli_option("--units", default=None,
- dest="units", choices=('h', 'm', 'g', 't'),
+ dest="units", choices=("h", "m", "g", "t"),
help="Specify units for output (one of h/m/g/t)")
FIELDS_OPT = cli_option("-o", "--output", dest="output", action="store",
help=("Ignore offline nodes and do as much"
" as possible"))
+TAG_ADD_OPT = cli_option("--tags", dest="tags",
+ default=None, help="Comma-separated list of instance"
+ " tags")
+
TAG_SRC_OPT = cli_option("--from", dest="tags_source",
default=None, help="File with tag names")
" times, if not given defaults to all nodes)",
completion_suggest=OPT_COMPL_ONE_NODE)
-NODEGROUP_OPT = cli_option("-g", "--node-group",
+NODEGROUP_OPT_NAME = "--node-group"
+NODEGROUP_OPT = cli_option("-g", NODEGROUP_OPT_NAME,
dest="nodegroup",
help="Node group (name or uuid)",
metavar="<nodegroup>",
" [%s]" % constants.DEFAULT_VG),
metavar="VG", default=None)
-YES_DOIT_OPT = cli_option("--yes-do-it", dest="yes_do_it",
+YES_DOIT_OPT = cli_option("--yes-do-it", "--ya-rly", dest="yes_do_it",
help="Destroy cluster", action="store_true")
NOVOTING_OPT = cli_option("--no-voting", dest="no_voting",
help="Perform but do not record the change"
" in the configuration")
+PRIMARY_ONLY_OPT = cli_option("-p", "--primary-only",
+ default=False, action="store_true",
+ help="Evacuate primary instances only")
+
+SECONDARY_ONLY_OPT = cli_option("-s", "--secondary-only",
+ default=False, action="store_true",
+ help="Evacuate secondary instances only"
+ " (applies only to internally mirrored"
+ " disk templates, e.g. %s)" %
+ utils.CommaJoin(constants.DTS_INT_MIRROR))
+
+STARTUP_PAUSED_OPT = cli_option("--paused", dest="startup_paused",
+ action="store_true", default=False,
+ help="Pause instance at startup")
+
+TO_GROUP_OPT = cli_option("--to", dest="to", metavar="<group>",
+ help="Destination node group (name or uuid)",
+ default=None, action="append",
+ completion_suggest=OPT_COMPL_ONE_NODEGROUP)
+
#: Options provided by all commands
COMMON_OPTS = [DEBUG_OPT]
OSPARAMS_OPT,
OS_SIZE_OPT,
SUBMIT_OPT,
+ TAG_ADD_OPT,
DRY_RUN_OPT,
PRIORITY_OPT,
]
"""Splits the value of a --node option.
"""
- if value and ':' in value:
- return value.split(':', 1)
+ if value and ":" in value:
+ return value.split(":", 1)
else:
return (value, None)
"""
if os_variants:
- return ['%s+%s' % (os_name, v) for v in os_variants]
+ return ["%s+%s" % (os_name, v) for v in os_variants]
else:
return [os_name]
"""
if choices is None:
- choices = [('y', True, 'Perform the operation'),
- ('n', False, 'Do not perform the operation')]
+ 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 choices argument to AskUser")
for entry in choices:
- if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
+ if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == "?":
raise errors.ProgrammerError("Invalid choices element to AskUser")
answer = choices[-1][1]
try:
chars = [entry[0] for entry in choices]
chars[-1] = "[%s]" % chars[-1]
- chars.append('?')
+ chars.append("?")
maps = dict([(entry[0], entry[1]) for entry in choices])
while True:
f.write(text)
- f.write('\n')
+ f.write("\n")
f.write("/".join(chars))
f.write(": ")
line = f.readline(2).strip().lower()
if line in maps:
answer = maps[line]
break
- elif line == '?':
+ elif line == "?":
for entry in choices:
f.write(" %s - %s\n" % (entry[0], entry[2]))
f.write("\n")
ToStderr("Job %s is waiting in queue", job_id)
self.notified_queued = True
- elif status == constants.JOB_STATUS_WAITLOCK and not self.notified_waitlock:
+ elif status == constants.JOB_STATUS_WAITING and not self.notified_waitlock:
ToStderr("Job %s is trying to acquire all necessary locks", job_id)
self.notified_waitlock = True
retcode = 0
else:
obuf.write("Unhandled exception: %s" % msg)
- return retcode, obuf.getvalue().rstrip('\n')
+ return retcode, obuf.getvalue().rstrip("\n")
def GenericMain(commands, override=None, aliases=None):
" disk %d" % didx)
disks[didx] = ddict
+ if opts.tags is not None:
+ tags = opts.tags.split(",")
+ else:
+ tags = []
+
utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES)
utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
force_variant=force_variant,
src_node=src_node,
src_path=src_path,
+ tags=tags,
no_install=no_install,
identify_defaults=identify_defaults)
"""
# Pause watcher by acquiring an exclusive lock on watcher state file
self.feedback_fn("Blocking watcher")
- watcher_block = utils.FileLock.Open(constants.WATCHER_STATEFILE)
+ watcher_block = utils.FileLock.Open(constants.WATCHER_LOCK_FILE)
try:
# TODO: Currently, this just blocks. There's no timeout.
# TODO: Should it be a shared lock?
if separator is None:
mlens = [0 for name in fields]
- format_str = ' '.join(format_fields)
+ format_str = " ".join(format_fields)
else:
format_str = separator.replace("%", "%%").join(format_fields)
for line in data:
args = []
if line is None:
- line = ['-' for _ in fields]
+ line = ["-" for _ in fields]
for idx in range(len(fields)):
if separator is None:
args.append(mlens[idx])
if not names:
names = None
- if (force_filter or
- (names and len(names) == 1 and qlang.MaybeFilter(names[0]))):
- try:
- (filter_text, ) = names
- except ValueError:
- raise errors.OpPrereqError("Exactly one argument must be given as a"
- " filter")
-
- logging.debug("Parsing '%s' as filter", filter_text)
- filter_ = qlang.ParseFilter(filter_text)
- else:
- filter_ = qlang.MakeSimpleFilter("name", names)
+ filter_ = qlang.MakeFilter(names, force_filter)
response = cl.Query(resource, fields, filter_)
"""
if not isinstance (ts, (tuple, list)) or len(ts) != 2:
- return '?'
+ return "?"
sec, usec = ts
return time.strftime("%F %T", time.localtime(sec)) + ".%06d" % usec
if not value:
raise errors.OpPrereqError("Empty time specification passed")
suffix_map = {
- 's': 1,
- 'm': 60,
- 'h': 3600,
- 'd': 86400,
- 'w': 604800,
+ "s": 1,
+ "m": 60,
+ "h": 3600,
+ "d": 86400,
+ "w": 604800,
}
if value[-1] not in suffix_map:
try:
def GetOnlineNodes(nodes, cl=None, nowarn=False, secondary_ips=False,
- filter_master=False):
+ filter_master=False, nodegroup=None):
"""Returns the names of online nodes.
This function will also log a warning on stderr with the names of
@param filter_master: if True, do not return the master node in the list
(useful in coordination with secondary_ips where we cannot check our
node name against the list)
+ @type nodegroup: string
+ @param nodegroup: If set, only return nodes in this node group
"""
if cl is None:
cl = GetClient()
- if secondary_ips:
- name_idx = 2
- else:
- name_idx = 0
+ filter_ = []
+
+ if nodes:
+ filter_.append(qlang.MakeSimpleFilter("name", nodes))
+
+ if nodegroup is not None:
+ filter_.append([qlang.OP_OR, [qlang.OP_EQUAL, "group", nodegroup],
+ [qlang.OP_EQUAL, "group.uuid", nodegroup]])
if filter_master:
- master_node = cl.QueryConfigValues(["master_node"])[0]
- filter_fn = lambda x: x != master_node
+ filter_.append([qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
+
+ if filter_:
+ if len(filter_) > 1:
+ final_filter = [qlang.OP_AND] + filter_
+ else:
+ assert len(filter_) == 1
+ final_filter = filter_[0]
else:
- filter_fn = lambda _: True
+ final_filter = None
+
+ result = cl.Query(constants.QR_NODE, ["name", "offline", "sip"], final_filter)
+
+ def _IsOffline(row):
+ (_, (_, offline), _) = row
+ return offline
+
+ def _GetName(row):
+ ((_, name), _, _) = row
+ return name
+
+ def _GetSip(row):
+ (_, _, (_, sip)) = row
+ return sip
+
+ (offline, online) = compat.partition(result.data, _IsOffline)
- result = cl.QueryNodes(names=nodes, fields=["name", "offline", "sip"],
- use_locking=False)
- offline = [row[0] for row in result if row[1]]
if offline and not nowarn:
- ToStderr("Note: skipping offline node(s): %s" % utils.CommaJoin(offline))
- return [row[name_idx] for row in result if not row[1] and filter_fn(row[0])]
+ ToStderr("Note: skipping offline node(s): %s" %
+ utils.CommaJoin(map(_GetName, offline)))
+
+ if secondary_ips:
+ fn = _GetSip
+ else:
+ fn = _GetName
+
+ return map(fn, online)
def _ToStream(stream, txt, *args):
stream.write(txt % args)
else:
stream.write(txt)
- stream.write('\n')
+ stream.write("\n")
stream.flush()
except IOError, err:
if err.errno == errno.EPIPE:
self.feedback_fn = feedback_fn
self._counter = itertools.count()
+ @staticmethod
+ def _IfName(name, fmt):
+ """Helper function for formatting name.
+
+ """
+ if name:
+ return fmt % name
+
+ return ""
+
def QueueJob(self, name, *ops):
"""Record a job for later submit.
@type name: string
@param name: a description of the job, will be used in WaitJobSet
+
"""
SetGenericOpcodeOpts(ops, self.opts)
self.queue.append((self._counter.next(), name, ops))
+ def AddJobId(self, name, status, job_id):
+ """Adds a job ID to the internal queue.
+
+ """
+ self.jobs.append((self._counter.next(), status, job_id, name))
+
def SubmitPending(self, each=False):
"""Submit all pending jobs.
for job_data, status in zip(self.jobs, result):
if (isinstance(status, list) and status and
status[0] in (constants.JOB_STATUS_QUEUED,
- constants.JOB_STATUS_WAITLOCK,
+ constants.JOB_STATUS_WAITING,
constants.JOB_STATUS_CANCELING)):
# job is still present and waiting
continue
# first, remove any non-submitted jobs
self.jobs, failures = compat.partition(self.jobs, lambda x: x[1])
for idx, _, jid, name in failures:
- ToStderr("Failed to submit job for %s: %s", name, jid)
+ ToStderr("Failed to submit job%s: %s", self._IfName(name, " for %s"), jid)
results.append((idx, False, jid))
while self.jobs:
(idx, _, jid, name) = self._ChooseJob()
- ToStdout("Waiting for job %s for %s...", jid, name)
+ ToStdout("Waiting for job %s%s ...", jid, self._IfName(name, " for %s"))
try:
job_result = PollJob(jid, cl=self.cl, feedback_fn=self.feedback_fn)
success = True
except errors.JobLost, err:
_, job_result = FormatError(err)
- ToStderr("Job %s for %s has been archived, cannot check its result",
- jid, name)
+ ToStderr("Job %s%s has been archived, cannot check its result",
+ jid, self._IfName(name, " for %s"))
success = False
except (errors.GenericError, luxi.ProtocolError), err:
_, job_result = FormatError(err)
success = False
# the error message will always be shown, verbose or not
- ToStderr("Job %s for %s has failed: %s", jid, name, job_result)
+ ToStderr("Job %s%s has failed: %s",
+ jid, self._IfName(name, " for %s"), job_result)
results.append((idx, success, job_result))