Commit 7acbda7b authored by Iustin Pop's avatar Iustin Pop

Add a new gnt-node command list-drbd

This uses confd to query the DRBD minors, which is very special; no
other command currently does so.

Since the backend is only implemented in the Haskell version of confd,
we have checks that 1) confd is enable, and 2) hs confd is also
enabled. If by mistake people do manage to query Python confd, the
error message will be clean:

  Query gave non-ok status '2': not implemented

So nothing breaks in an "ugly" way.
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarAgata Murawska <agatamurawska@google.com>
parent d81ec8b7
...@@ -38,6 +38,8 @@ from ganeti import errors ...@@ -38,6 +38,8 @@ from ganeti import errors
from ganeti import netutils from ganeti import netutils
from cStringIO import StringIO from cStringIO import StringIO
from ganeti import confd
from ganeti.confd import client as confd_client
#: default list of field for L{ListNodes} #: default list of field for L{ListNodes}
_LIST_DEF_FIELDS = [ _LIST_DEF_FIELDS = [
...@@ -877,6 +879,98 @@ def SetNodeParams(opts, args): ...@@ -877,6 +879,98 @@ def SetNodeParams(opts, args):
return 0 return 0
class ReplyStatus(object):
"""Class holding a reply status for synchronous confd clients.
"""
def __init__(self):
self.failure = True
self.answer = False
def ListDrbd(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 len(args) != 1:
ToStderr("Please give one (and only one) node.")
return constants.EXIT_FAILURE
if not constants.ENABLE_CONFD:
ToStderr("Error: this command requires confd support, but it has not"
" been enabled at build time.")
return constants.EXIT_FAILURE
if not constants.HS_CONFD:
ToStderr("Error: this command requires the Haskell version of confd,"
" but it has not been enabled at build time.")
return constants.EXIT_FAILURE
status = ReplyStatus()
def ListDrbdConfdCallback(reply):
"""Callback for confd queries"""
if reply.type == confd_client.UPCALL_REPLY:
answer = reply.server_reply.answer
reqtype = reply.orig_request.type
if reqtype == constants.CONFD_REQ_NODE_DRBD:
if reply.server_reply.status != constants.CONFD_REPL_STATUS_OK:
ToStderr("Query gave non-ok status '%s': %s" %
(reply.server_reply.status,
reply.server_reply.answer))
status.failure = True
return
if not confd.HTNodeDrbd(answer):
ToStderr("Invalid response from server: expected %s, got %s",
confd.HTNodeDrbd, answer)
status.failure = True
else:
status.failure = False
status.answer = answer
else:
ToStderr("Unexpected reply %s!?", reqtype)
status.failure = True
node = args[0]
hmac = utils.ReadFile(constants.CONFD_HMAC_KEY)
filter_callback = confd_client.ConfdFilterCallback(ListDrbdConfdCallback)
counting_callback = confd_client.ConfdCountingCallback(filter_callback)
cf_client = confd_client.ConfdClient(hmac, [constants.IP4_ADDRESS_LOCALHOST],
counting_callback)
req = confd_client.ConfdClientRequest(type=constants.CONFD_REQ_NODE_DRBD,
query=node)
def DoConfdRequestReply(req):
counting_callback.RegisterQuery(req.rsalt)
cf_client.SendRequest(req, async=False)
while not counting_callback.AllAnswered():
if not cf_client.ReceiveReply():
ToStderr("Did not receive all expected confd replies")
break
DoConfdRequestReply(req)
if status.failure:
return constants.EXIT_FAILURE
fields = ["node", "minor", "instance", "disk", "role", "peer"]
headers = {"node": "Node", "minor": "Minor", "instance": "Instance",
"disk": "Disk", "role": "Role", "peer": "PeerNode"}
data = GenerateTable(separator=opts.separator, headers=headers,
fields=fields, data=sorted(status.answer),
numfields=["minor"])
for line in data:
ToStdout(line)
return constants.EXIT_SUCCESS
commands = { commands = {
"add": ( "add": (
AddNode, [ArgHost(min=1, max=1)], AddNode, [ArgHost(min=1, max=1)],
...@@ -988,6 +1082,10 @@ commands = { ...@@ -988,6 +1082,10 @@ commands = {
Health, ARGS_MANY_NODES, Health, ARGS_MANY_NODES,
[NOHDR_OPT, SEP_OPT, PRIORITY_OPT, OOB_TIMEOUT_OPT], [NOHDR_OPT, SEP_OPT, PRIORITY_OPT, OOB_TIMEOUT_OPT],
"[<node_name>...]", "List health of node(s) using out-of-band"), "[<node_name>...]", "List health of node(s) using out-of-band"),
"list-drbd": (
ListDrbd, ARGS_ONE_NODE,
[NOHDR_OPT, SEP_OPT],
"[<node_name>]", "Query the list of used DRBD minors on the given node"),
} }
#: dictionary with aliases for commands #: dictionary with aliases for commands
......
# #
# #
# Copyright (C) 2009 Google Inc. # Copyright (C) 2009, 2012 Google Inc.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -25,11 +25,20 @@ ...@@ -25,11 +25,20 @@
from ganeti import constants from ganeti import constants
from ganeti import errors from ganeti import errors
from ganeti import ht
_FOURCC_LEN = 4 _FOURCC_LEN = 4
#: Items in the individual rows of the NodeDrbd query
_HTNodeDrbdItems = [ht.TString, ht.TInt, ht.TString,
ht.TString, ht.TString, ht.TString]
#: Type for the (top-level) result of NodeDrbd query
HTNodeDrbd = ht.TListOf(ht.TAnd(ht.TList, ht.TIsLength(len(_HTNodeDrbdItems)),
ht.TItems(_HTNodeDrbdItems)))
def PackMagic(payload): def PackMagic(payload):
"""Prepend the confd magic fourcc to a payload. """Prepend the confd magic fourcc to a payload.
......
...@@ -174,6 +174,7 @@ TOOLSDIR = _autoconf.TOOLSDIR ...@@ -174,6 +174,7 @@ TOOLSDIR = _autoconf.TOOLSDIR
CONF_DIR = SYSCONFDIR + "/ganeti" CONF_DIR = SYSCONFDIR + "/ganeti"
USER_SCRIPTS_DIR = CONF_DIR + "/scripts" USER_SCRIPTS_DIR = CONF_DIR + "/scripts"
ENABLE_CONFD = _autoconf.ENABLE_CONFD ENABLE_CONFD = _autoconf.ENABLE_CONFD
HS_CONFD = _autoconf.HS_CONFD
#: Lock file for watcher, locked in shared mode by watcher; lock in exclusive #: Lock file for watcher, locked in shared mode by watcher; lock in exclusive
# mode to block watcher (see L{cli._RunWhileClusterStoppedHelper.Call} # mode to block watcher (see L{cli._RunWhileClusterStoppedHelper.Call}
......
...@@ -235,6 +235,37 @@ If no node names are given, then all nodes are queried. Otherwise, ...@@ -235,6 +235,37 @@ If no node names are given, then all nodes are queried. Otherwise,
only the given nodes will be listed. only the given nodes will be listed.
LIST-DRBD
~~~~~~~~~
**list-drbd** [\--no-headers] [\--separator=*SEPARATOR*] node
Lists the mapping of DRBD minors for a given node. This outputs a static
list of fields (it doesn't accept the ``--output`` option), as follows:
``Node``
The (full) name of the node we are querying
``Minor``
The DRBD minor
``Instance``
The instance the DRBD minor belongs to
``Disk``
The disk index that the DRBD minor belongs to
``Role``
Either ``primary`` or ``secondary``, denoting the role of the node for
the instance (note: this is not the live status of the DRBD device,
but the configuration value)
``PeerNode``
The node that the minor is connected to on the other end
This command can be used as a reverse lookup (from node and minor) to a
given instance, which can be useful when debugging DRBD issues.
Note that this command queries Ganeti via :manpage:`ganeti-confd(8)`, so
it won't be available if support for ``confd`` has not been enabled at
build time; furthermore, in Ganeti 2.6 this is only available via the
Haskell version of confd (again selected at build time).
LIST-FIELDS LIST-FIELDS
~~~~~~~~~~~ ~~~~~~~~~~~
......
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