node: Add --field option (#922)
This lets the user add fields to be printed in the table and allows printing arbitrary data that wasn't possible to show before, except when using `--json`.
This commit is contained in:
committed by
tamarrow
parent
965c888dc4
commit
faa645e79f
@@ -4,7 +4,7 @@ Description:
|
||||
Usage:
|
||||
dcos node --help
|
||||
dcos node --info
|
||||
dcos node [--json]
|
||||
dcos node [--json | --field=<field>...]
|
||||
dcos node --version
|
||||
dcos node diagnostics (--list | --status | --cancel) [--json]
|
||||
dcos node diagnostics create (<nodes>)...
|
||||
@@ -52,6 +52,9 @@ Options:
|
||||
Show DC/OS component logs.
|
||||
--config-file=<path>
|
||||
Path to SSH configuration file.
|
||||
--field=<field>
|
||||
Name of extra field to include in the output of `dcos node`.
|
||||
Can be repeated multiple times to add several fields.
|
||||
--filter=<filter>
|
||||
Filter logs by field and value. Filter must be a string separated by colon.
|
||||
For example: --filter _PID:0 --filter _UID:1.
|
||||
|
||||
@@ -115,7 +115,7 @@ def _cmds():
|
||||
|
||||
cmds.Command(
|
||||
hierarchy=['node'],
|
||||
arg_keys=['--json'],
|
||||
arg_keys=['--json', '--field'],
|
||||
function=_list)
|
||||
]
|
||||
|
||||
@@ -458,12 +458,15 @@ def _info():
|
||||
return 0
|
||||
|
||||
|
||||
def _list(json_):
|
||||
def _list(json_, extra_field_names):
|
||||
"""List DC/OS nodes
|
||||
|
||||
:param json_: If true, output json.
|
||||
Otherwise, output a human readable table.
|
||||
:type json_: bool
|
||||
:param extra_field_names: List of additional field names to include in
|
||||
table output
|
||||
:type extra_field_names: [str]
|
||||
:returns: process return code
|
||||
:rtype: int
|
||||
"""
|
||||
@@ -473,7 +476,16 @@ def _list(json_):
|
||||
if json_:
|
||||
emitter.publish(slaves)
|
||||
else:
|
||||
table = tables.slave_table(slaves)
|
||||
for extra_field_name in extra_field_names:
|
||||
field_name = extra_field_name.split(':')[-1]
|
||||
if len(slaves) > 0:
|
||||
try:
|
||||
tables._dotted_itemgetter(field_name)(slaves[0])
|
||||
except KeyError:
|
||||
emitter.publish(errors.DefaultError(
|
||||
'Field "%s" is invalid.' % field_name))
|
||||
return
|
||||
table = tables.slave_table(slaves, extra_field_names)
|
||||
output = six.text_type(table)
|
||||
if output:
|
||||
emitter.publish(output)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import copy
|
||||
import datetime
|
||||
import operator
|
||||
import posixpath
|
||||
|
||||
import textwrap
|
||||
@@ -839,11 +840,13 @@ def auth_provider_table(providers):
|
||||
return tb
|
||||
|
||||
|
||||
def slave_table(slaves):
|
||||
def slave_table(slaves, field_names=()):
|
||||
"""Returns a PrettyTable representation of the provided DC/OS slaves
|
||||
|
||||
:param slaves: slaves to render. dicts from /mesos/state-summary
|
||||
:type slaves: [dict]
|
||||
:param field_names: Extra fields to add to the table
|
||||
:type slaves: [str]
|
||||
:rtype: PrettyTable
|
||||
"""
|
||||
|
||||
@@ -853,10 +856,46 @@ def slave_table(slaves):
|
||||
('ID', lambda s: s['id'])
|
||||
])
|
||||
|
||||
tb = table(fields, slaves, sortby="HOSTNAME")
|
||||
for field_name in field_names:
|
||||
if field_name.upper() in fields:
|
||||
continue
|
||||
if ':' in field_name:
|
||||
heading, field_name = field_name.split(':', 1)
|
||||
else:
|
||||
heading = field_name
|
||||
fields[heading.upper()] = _dotted_itemgetter(field_name.lower())
|
||||
|
||||
sortby = list(fields.keys())[0]
|
||||
tb = table(fields, slaves, sortby=sortby)
|
||||
return tb
|
||||
|
||||
|
||||
def _dotted_itemgetter(field_name):
|
||||
"""Returns a func that gets the value in a nested dict where the
|
||||
`field_name` is a dotted path to the key.
|
||||
|
||||
Example:
|
||||
|
||||
>>> from dcoscli.tables import _dotted_itemgetter
|
||||
>>> d1 = {'a': {'b': {'c': 21}}}
|
||||
>>> d2 = {'a': {'b': {'c': 22}}}
|
||||
>>> func = _dotted_itemgetter('a.b.c')
|
||||
>>> func(d1)
|
||||
21
|
||||
>>> func(d2)
|
||||
22
|
||||
|
||||
:param field_name: dotted path to key in nested dict
|
||||
:type field_name: str
|
||||
:rtype: callable
|
||||
"""
|
||||
|
||||
if '.' not in field_name:
|
||||
return operator.itemgetter(field_name)
|
||||
head, tail = field_name.split('.', 1)
|
||||
return lambda d: _dotted_itemgetter(tail)(d[head])
|
||||
|
||||
|
||||
def _format_unix_timestamp(ts):
|
||||
""" Formats a unix timestamp in a `dcos task ls --long` format.
|
||||
|
||||
@@ -976,7 +1015,10 @@ def truncate_table(fields, objs, limits, **kwargs):
|
||||
:type function: function
|
||||
:rtype: PrettyTable
|
||||
"""
|
||||
result = str(function(obj))
|
||||
try:
|
||||
result = str(function(obj))
|
||||
except KeyError:
|
||||
result = 'N/A'
|
||||
if (limits is not None and limits.get(key) is not None):
|
||||
result = textwrap.\
|
||||
shorten(result, width=limits.get(key), placeholder='...')
|
||||
|
||||
@@ -45,6 +45,17 @@ def test_node_table():
|
||||
assert len(stdout.decode('utf-8').split('\n')) > 2
|
||||
|
||||
|
||||
def test_node_table_field_option():
|
||||
returncode, stdout, stderr = exec_command(
|
||||
['dcos', 'node', '--field=disk_used:used_resources.disk'])
|
||||
|
||||
assert returncode == 0
|
||||
assert stderr == b''
|
||||
lines = stdout.decode('utf-8').splitlines()
|
||||
assert len(lines) > 2
|
||||
assert lines[0].split() == ['HOSTNAME', 'IP', 'ID', 'DISK_USED']
|
||||
|
||||
|
||||
def test_node_log_empty():
|
||||
stderr = b"You must choose one of --leader or --mesos-id.\n"
|
||||
assert_command(['dcos', 'node', 'log'], returncode=1, stderr=stderr)
|
||||
|
||||
Reference in New Issue
Block a user