Skip to content

Commit

Permalink
Merge pull request #87 from jathanism/grep
Browse files Browse the repository at this point in the history
Fixes #73: Added support for grep-friendliness of CLI output
  • Loading branch information
diggyk committed Mar 11, 2016
2 parents bd3eaf8 + 3f21d24 commit 8a25bfa
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 34 deletions.
44 changes: 43 additions & 1 deletion pynsot/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@
'interfaces': ['name', 'device'],
}

# Mapping of resource_names to what we want objects to look like when formatted
# to stdout for grep-friendliness. These are string interpolation style.
GREP_FORMATS = {
'devices': '%(hostname)s',
'networks': '%(network_address)s/%(prefix_length)s',
'attributes': '%(name)s:%(resource_name)s',
'interfaces': '%(device)s:%(name)s',
}


__all__ = (
'NsotCLI', 'App', 'app'
Expand Down Expand Up @@ -319,6 +328,34 @@ def format_field(self, field, field_data):

return field_data

def format_object_for_grep(self, obj):
"""
Formats an object... for grep.
:param obj:
Object
"""
return GREP_FORMATS[self.resource_name] % obj

def print_grep(self, objects):
"""
Print a list of objects in a grep-friendly format.
Attributes are displayed in key=value style, one per line.
:param objects:
List of objects
"""
output = []
for obj in objects:
prefix = self.format_object_for_grep(obj)
attrs = obj['attributes']
keys = sorted(attrs)
for k in keys:
output.append('%s %s=%s' % (prefix, k, attrs[k]))

click.echo('\n'.join(output))

def print_list(self, objects, display_fields):
"""
Print a list of objects in a table format.
Expand Down Expand Up @@ -507,6 +544,8 @@ def list(self, data, display_fields=None, resource=None,
log.debug('listing %s' % data)
obj_id = data.get('id') # If obj_id, it's a single object

grep = data.pop('grep', False)

# If a resource object is provided, call it instead, and only rebase if
# we haven't provided our own resource.
if resource is None:
Expand Down Expand Up @@ -554,7 +593,10 @@ def list(self, data, display_fields=None, resource=None,

if objects:
objects = self.get_paginated_results(objects)
self.print_list(objects, display_fields)
if grep:
self.print_grep(objects)
else:
self.print_list(objects, display_fields)
else:
pretty_dict = self.pretty_dict(data)
t_ = 'No %s found matching args: %s!'
Expand Down
28 changes: 19 additions & 9 deletions pynsot/commands/cmd_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
getattr(ctx.obj, ctx.info_name)(ctx.params)
"""

__author__ = 'Jathan McCollum'
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan@dropbox.com'
__copyright__ = 'Copyright (c) 2015 Dropbox, Inc.'

from __future__ import unicode_literals

from ..vendor import click

from . import callbacks


__author__ = 'Jathan McCollum'
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan@dropbox.com'
__copyright__ = 'Copyright (c) 2015-2016 Dropbox, Inc.'


# Ordered list of 2-tuples of (field, display_name) used to translate object
# field names oto their human-readable form when calling .print_list().
DISPLAY_FIELDS = (
Expand Down Expand Up @@ -120,6 +121,14 @@ def add(ctx, attributes, bulk_add, hostname, site_id):
default=False,
show_default=True,
)
@click.option(
'-g',
'--grep',
is_flag=True,
help='Display list results in a grep-friendly format.',
default=False,
show_default=True,
)
@click.option(
'-H',
'--hostname',
Expand Down Expand Up @@ -161,7 +170,8 @@ def add(ctx, attributes, bulk_add, hostname, site_id):
callback=callbacks.process_site_id,
)
@click.pass_context
def list(ctx, attributes, delimited, hostname, id, limit, offset, query, site_id):
def list(ctx, attributes, delimited, grep, hostname, id, limit, offset, query,
site_id):
"""
List existing Devices for a Site.
Expand Down Expand Up @@ -280,8 +290,8 @@ def remove(ctx, id, site_id):
'attr_action',
flag_value='replace',
help=(
'Causes attributes to be replaced instead of updated. If combined with '
'--multi, the entire list will be replaced.'
'Causes attributes to be replaced instead of updated. If combined '
' with --multi, the entire list will be replaced.'
),
)
@click.option(
Expand Down
29 changes: 19 additions & 10 deletions pynsot/commands/cmd_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@
getattr(ctx.obj, ctx.info_name)(ctx.params)
"""

__author__ = 'Jathan McCollum'
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan@dropbox.com'
__copyright__ = 'Copyright (c) 2015 Dropbox, Inc.'

from __future__ import unicode_literals

from ..vendor import click

from . import callbacks
from .cmd_networks import DISPLAY_FIELDS as NETWORK_DISPLAY_FIELDS


__author__ = 'Jathan McCollum'
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan@dropbox.com'
__copyright__ = 'Copyright (c) 2015-2016 Dropbox, Inc.'


# Ordered list of 2-tuples of (field, display_name) used to translate object
# field names oto their human-readable form when calling .print_list().
DISPLAY_FIELDS = (
Expand Down Expand Up @@ -152,6 +153,14 @@ def add(ctx, attributes, bulk_add, device_id, name):
type=str,
help='Filter by Interfaces matching this description.',
)
@click.option(
'-g',
'--grep',
is_flag=True,
help='Display list results in a grep-friendly format.',
default=False,
show_default=True,
)
@click.option(
'-i',
'--id',
Expand Down Expand Up @@ -212,15 +221,15 @@ def add(ctx, attributes, bulk_add, device_id, name):
help='Filter by integer of the interface type (e.g. 6 for ethernet)',
)
@click.pass_context
def list(ctx, attributes, delimited, device, description, id, limit, name, offset,
parent_id, query, site_id, speed, type):
def list(ctx, attributes, delimited, device, description, grep, id, limit,
name, offset, parent_id, query, site_id, speed, type):
"""
List existing Interfaces for a Site.
You must provide a Site ID using the -s/--site-id option.
When listing Interfaces, all objects are displayed by default. You optionally
may lookup a single Interfaces by ID using the -i/--id option.
When listing Interfaces, all objects are displayed by default. You
optionally may lookup a single Interfaces by ID using the -i/--id option.
You may limit the number of results using the -l/--limit option.
"""
Expand Down
42 changes: 29 additions & 13 deletions pynsot/commands/cmd_networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
getattr(ctx.obj, ctx.info_name)(ctx.params)
"""

__author__ = 'Jathan McCollum'
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan@dropbox.com'
__copyright__ = 'Copyright (c) 2015 Dropbox, Inc.'

from __future__ import unicode_literals

from ..vendor import click

from . import callbacks


__author__ = 'Jathan McCollum'
__maintainer__ = 'Jathan McCollum'
__email__ = 'jathan@dropbox.com'
__copyright__ = 'Copyright (c) 2015-2016 Dropbox, Inc.'


# Ordered list of 2-tuples of (field, display_name) used to translate object
# field names oto their human-readable form when calling .print_list().
DISPLAY_FIELDS = (
Expand Down Expand Up @@ -144,6 +145,14 @@ def add(ctx, attributes, bulk_add, cidr, state, site_id):
default=False,
show_default=True,
)
@click.option(
'-g',
'--grep',
is_flag=True,
help='Display list results in a grep-friendly format.',
default=False,
show_default=True,
)
@click.option(
'-i',
'--id',
Expand All @@ -165,6 +174,13 @@ def add(ctx, attributes, bulk_add, cidr, state, site_id):
default=True,
show_default=True,
)
@click.option(
'-V',
'--ip-version',
metavar='IP_VERSION',
type=click.Choice(['4', '6']),
help='Filter to Networks matchin this IP version.',
)
@click.option(
'-l',
'--limit',
Expand Down Expand Up @@ -219,9 +235,9 @@ def add(ctx, attributes, bulk_add, cidr, state, site_id):
callback=callbacks.process_site_id,
)
@click.pass_context
def list(ctx, attributes, cidr, delimited, id, include_ips, include_networks,
limit, network_address, offset, prefix_length, query, root_only,
state, site_id):
def list(ctx, attributes, cidr, delimited, grep, id, include_ips,
include_networks, ip_version, limit, network_address, offset,
prefix_length, query, root_only, state, site_id):
"""
List existing Networks for a Site.
Expand Down Expand Up @@ -331,8 +347,8 @@ def remove(ctx, id, site_id):
'--cidr',
metavar='CIDR',
help=(
'A network or IP address in CIDR notation. Used for lookup only in '
'place of -i/--id!'
'A network or IP address in CIDR notation. Used for lookup only in '
'place of -i/--id!'
)
)
@click.option(
Expand Down Expand Up @@ -378,8 +394,8 @@ def remove(ctx, id, site_id):
'attr_action',
flag_value='replace',
help=(
'Causes attributes to be replaced instead of updated. If combined with '
'--multi, the entire list will be replaced.'
'Causes attributes to be replaced instead of updated. If combined '
'with --multi, the entire list will be replaced.'
),
)
@click.option(
Expand Down
2 changes: 1 addition & 1 deletion pynsot/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.19.1'
__version__ = '0.19.2'
20 changes: 20 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,14 @@ def test_devices_list(config):
expected_output = 'foo-bar1,foo-bar2\n'
assert result.output == expected_output

# Grep-friendly output (--grep)
result = runner.invoke(
app, shlex.split('devices list -s 1 -a owner=jathan -g')
)
expected_output = u'foo-bar1 owner=jathan\nfoo-bar2 owner=jathan\n'
assert result.exit_code == 0
assert result.output == expected_output


def test_devices_update(config):
"""Test ``nsot devices update -s 1 -i 1 -a monitored``"""
Expand Down Expand Up @@ -799,6 +807,18 @@ def test_networks_list(config):
expected_output = '10.0.0.0/8,10.0.0.0/24\n'
assert result.output == expected_output

# Set query display comma-delimited (--delimited)
result = runner.invoke(
app, shlex.split('networks list -s 1 -a owner=jathan -g')
)
assert result.exit_code == 0

expected_output = (
'10.0.0.0/8 owner=jathan\n'
'10.0.0.0/24 owner=jathan\n'
)
assert result.output == expected_output


def test_networks_subcommands(config):
"""Test supernets/subnets"""
Expand Down

0 comments on commit 8a25bfa

Please sign in to comment.