Commit a6070ef7 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

query2: Add new field status “offline”



This allows “gnt-node list” to show the difference between modes marked
offline and nodes with e.g. RPC errors (“(nodata)”). node1 is the
master, node2's node daemon crashed and node3 is marked offline:

$ gnt-node list -o name,offline,dtotal,dfree
Node              Offline    DTotal     DFree
node1.example.com N            1.3T      1.3T
node2.example.com N        (nodata)  (nodata)
node3.example.com Y       (offline) (offline)
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent ac63b093
......@@ -170,6 +170,8 @@ The result is a dictionary with the following entries:
Value unavailable for item (numeric 3)
Used if, for example, NIC 3 is requested for an instance with only
one network interface. Value must be ``None``.
Resource offline (numeric 4)
Used if resource is marked offline. Value must be ``None``.
Example response after requesting the fields ``name``, ``mfree``,
``xyz``, ``mtotal``, ``nic0.ip``, ``nic1.ip`` and ``nic2.ip``::
......
......@@ -2402,6 +2402,9 @@ class _QueryColumnFormatter:
if status == constants.QRFS_UNAVAIL:
return "(unavail)"
if status == constants.QRFS_OFFLINE:
return "(offline)"
raise NotImplementedError("Unknown status %s" % status)
......
......@@ -978,21 +978,25 @@ QFT_ALL = frozenset([
QFT_OTHER,
])
# Query result field status (don't change values as they're used by clients)
# Query result field status (don't change or reuse values as they're used by
# clients)
#: Normal field status
QRFS_NORMAL = 0
#: Unknown field
QRFS_UNKNOWN = 1
#: No data (e.g. node offline)
#: No data (e.g. RPC error), can be used instead of L{QRFS_OFFLINE}
QRFS_NODATA = 2
#: Value unavailable for item
QRFS_UNAVAIL = 3
#: Resource marked offline
QRFS_OFFLINE = 4
QRFS_ALL = frozenset([
QRFS_NORMAL,
QRFS_UNKNOWN,
QRFS_NODATA,
QRFS_UNAVAIL,
QRFS_OFFLINE,
])
# max dynamic devices
......
......@@ -421,14 +421,19 @@ def _GetNodeGroup(ctx, node):
return (constants.QRFS_NORMAL, ng.name)
def _GetLiveNodeField(field, kind, ctx, _):
def _GetLiveNodeField(field, kind, ctx, node):
"""Gets the value of a "live" field from L{NodeQueryData}.
@param field: Live field name
@param kind: Data kind, one of L{constants.QFT_ALL}
@type ctx: L{NodeQueryData}
@type node: L{objects.Node}
@param node: Node object
"""
if node.offline:
return (constants.QRFS_OFFLINE, None)
if not ctx.curlive_data:
return (constants.QRFS_NODATA, None)
......@@ -571,6 +576,8 @@ def _GetInstOperState(ctx, inst):
@param inst: Instance object
"""
# Can't use QRFS_OFFLINE here as it would describe the instance to be offline
# when we actually don't know due to missing data
if inst.primary_node in ctx.bad_nodes:
return (constants.QRFS_NODATA, None)
else:
......@@ -594,6 +601,8 @@ def _GetInstLiveData(name):
"""
if (inst.primary_node in ctx.bad_nodes or
inst.primary_node in ctx.offline_nodes):
# Can't use QRFS_OFFLINE here as it would describe the instance to be
# offline when we actually don't know due to missing data
return (constants.QRFS_NODATA, None)
if inst.name in ctx.live_data:
......
......@@ -379,24 +379,29 @@ class TestFormatQueryResult(unittest.TestCase):
kind=constants.QFT_BOOL),
objects.QueryFieldDefinition(name="nodata", title="NoData",
kind=constants.QFT_TEXT),
objects.QueryFieldDefinition(name="offline", title="OffLine",
kind=constants.QFT_TEXT),
]
response = objects.QueryResponse(fields=fields, data=[
[(constants.QRFS_NORMAL, 1), (constants.QRFS_UNKNOWN, None),
(constants.QRFS_NORMAL, False), (constants.QRFS_NORMAL, "")],
(constants.QRFS_NORMAL, False), (constants.QRFS_NORMAL, ""),
(constants.QRFS_OFFLINE, None)],
[(constants.QRFS_NORMAL, 2), (constants.QRFS_UNKNOWN, None),
(constants.QRFS_NODATA, None), (constants.QRFS_NORMAL, "x")],
(constants.QRFS_NODATA, None), (constants.QRFS_NORMAL, "x"),
(constants.QRFS_OFFLINE, None)],
[(constants.QRFS_NORMAL, 3), (constants.QRFS_UNKNOWN, None),
(constants.QRFS_NORMAL, False), (constants.QRFS_UNAVAIL, None)],
(constants.QRFS_NORMAL, False), (constants.QRFS_UNAVAIL, None),
(constants.QRFS_OFFLINE, None)],
])
self.assertEqual(cli.FormatQueryResult(response, header=True,
separator="|"),
(cli.QR_UNKNOWN, [
"ID|unk|Unavail|NoData",
"1|(unknown)|N|",
"2|(unknown)|(nodata)|x",
"3|(unknown)|N|(unavail)",
"ID|unk|Unavail|NoData|OffLine",
"1|(unknown)|N||(offline)",
"2|(unknown)|(nodata)|x|(offline)",
"3|(unknown)|N|(unavail)|(offline)",
]))
def testNoData(self):
......@@ -433,23 +438,25 @@ class TestFormatQueryResult(unittest.TestCase):
kind=constants.QFT_BOOL),
objects.QueryFieldDefinition(name="nodata", title="NoData",
kind=constants.QFT_TEXT),
objects.QueryFieldDefinition(name="offline", title="OffLine",
kind=constants.QFT_TEXT),
]
response = objects.QueryResponse(fields=fields, data=[
[(constants.QRFS_NORMAL, 1), (constants.QRFS_NORMAL, False),
(constants.QRFS_NORMAL, "")],
(constants.QRFS_NORMAL, ""), (constants.QRFS_OFFLINE, None)],
[(constants.QRFS_NORMAL, 2), (constants.QRFS_NODATA, None),
(constants.QRFS_NORMAL, "x")],
(constants.QRFS_NORMAL, "x"), (constants.QRFS_NORMAL, "abc")],
[(constants.QRFS_NORMAL, 3), (constants.QRFS_NORMAL, False),
(constants.QRFS_UNAVAIL, None)],
(constants.QRFS_UNAVAIL, None), (constants.QRFS_OFFLINE, None)],
])
self.assertEqual(cli.FormatQueryResult(response, header=False,
separator="|"),
(cli.QR_INCOMPLETE, [
"1|N|",
"2|(nodata)|x",
"3|N|(unavail)",
"1|N||(offline)",
"2|(nodata)|x|abc",
"3|N|(unavail)|(offline)",
]))
def testInvalidFieldType(self):
......
......@@ -434,16 +434,17 @@ class TestNodeQuery(unittest.TestCase):
def testGetLiveNodeField(self):
nodes = [
objects.Node(name="node1", drained=False),
objects.Node(name="node2", drained=True),
objects.Node(name="node3", drained=False),
objects.Node(name="node1", drained=False, offline=False),
objects.Node(name="node2", drained=True, offline=False),
objects.Node(name="node3", drained=False, offline=False),
objects.Node(name="node4", drained=False, offline=True),
]
live_data = dict.fromkeys([node.name for node in nodes], {})
# No data
nqd = query.NodeQueryData(None, None, None, None, None, None)
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
nqd, None),
nqd, nodes[0]),
(constants.QRFS_NODATA, None))
# Missing field
......@@ -452,7 +453,7 @@ class TestNodeQuery(unittest.TestCase):
"other": 2,
})
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
ctx, None),
ctx, nodes[0]),
(constants.QRFS_UNAVAIL, None))
# Wrong format/datatype
......@@ -461,13 +462,20 @@ class TestNodeQuery(unittest.TestCase):
"other": 2,
})
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
ctx, None),
ctx, nodes[0]),
(constants.QRFS_UNAVAIL, None))
# Offline node
assert nodes[3].offline
ctx = _QueryData(None, curlive_data={})
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
ctx, nodes[3]),
(constants.QRFS_OFFLINE, None))
# Wrong field type
ctx = _QueryData(None, curlive_data={"hello": 123})
self.assertRaises(AssertionError, query._GetLiveNodeField,
"hello", constants.QFT_BOOL, ctx, None)
"hello", constants.QFT_BOOL, ctx, nodes[0])
class TestInstanceQuery(unittest.TestCase):
......
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