Skip to content

Commit

Permalink
Filtering by component status support for info command
Browse files Browse the repository at this point in the history
  • Loading branch information
maciejlach committed May 11, 2015
1 parent d198639 commit 0ac3996
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 37 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
------------------------------------------------------------------------------
yak 3.1.1 [2015.05.11]
------------------------------------------------------------------------------

- Filtering by component status support for info command

------------------------------------------------------------------------------
yak 3.1.0 [2014.12.09]
------------------------------------------------------------------------------
Expand Down
14 changes: 14 additions & 0 deletions doc/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ In order to configure `yak` paths (log directories or system location) command l
| <pre>-v VIEWER</pre> <pre>--viewer=VIEWER</pre> | | external viewer/pager
| <pre>-d DELIM</pre> <pre>--delimiter=DELIM</pre> | padded spaces | delimiter for the info command
| <pre>-f FORMAT</pre> <pre>--format=FORMAT</pre> | [see below] | format for the info command
| <pre>-F STATUS</pre> <pre>--filter=STATUS</pre> | empty | filter info result by component status
| <pre>-A ALIAS</pre> <pre>--alias=ALIAS</pre> | | define command alias
| <pre>-a ARGS</pre> <pre>--arguments=ARGS</pre> | empty | additional arguments for the processes (valid for `start`, `restart` and `console` commands)

Expand All @@ -90,6 +91,13 @@ Each component inside yak is assigned with status attribute:
| `WSFULL` | q only. If file with STDERR redirection is non-empty and finishes with one of the following: wsfull or -w abort.


Output from the `info` command can be filtered based on component status via command line parameter `-F / --filter`.

```bash
yak info -F RUNNING#DISTURBED
```


### Command aliases

Command aliases allow user to chain multiple commands and bind these with a custom name. Alias is declared and defined via command line parameter `-A / --alias`.
Expand All @@ -100,6 +108,12 @@ For example: user can define a `restart_console` command, which stops and restar
yak --alias restart_console "stop, console"
```

Define a `info_dt` alias, which only show information about components with status DISTURBED or TERMINATED:

```bash
yak --alias info_dt "info -F DISTURBED#TERMINATED"
```

For convenience, command aliases can be set via `YAK_OPTS` environmental variable. For example:
```bash
$ echo $YAK_OPTS
Expand Down
92 changes: 56 additions & 36 deletions scripts/yak.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,15 @@ def __init__(self, options):

if options.alias:
for alias, commands in options.alias.iteritems():
command_functions = []
for c in commands:
alias_eval = []
for cmd_alias in commands:
cmd_alias = shlex.split(cmd_alias)
c = cmd_alias[0]
if hasattr(self, "do_" + c):
command_functions.append(getattr(self, "do_" + c))
alias_eval.append(getattr(self, "do_" + c))
else:
raise ComponentManagerShellError("Alias: '{0}' refers to unknown command: '{1}'".format(alias, c))
setattr(self, "do_" + alias, partial(self._evaluate_alias, command_functions))
setattr(self, "do_" + alias, partial(self._evaluate_alias, alias_eval, " ".join(cmd_alias[1:])))

def _parse_format(self, formating, delimiter):
r = re.compile("\d+")
Expand Down Expand Up @@ -145,15 +147,13 @@ def parseline(self, line):
def _opt_parser(self):
opt_parser = OptionParser()
opt_parser.add_option("-a", "--arguments", default = None)
opt_parser.add_option("-F", "--filter", default = None)
return opt_parser

def _get_components_list_and_params(self, args):
def _get_components_list(self, identifiers):
components = []
ignored_components = set()

(params, identifiers) = self._opt_parser.parse_args(args = shlex.split(args))
params = vars(params)

if len(identifiers) > 0:
for id in identifiers:
if id.startswith("!"):
Expand Down Expand Up @@ -188,46 +188,53 @@ def _get_components_list_and_params(self, args):

# remove duplicates, restore runtime order, remove ignored components
components = [s for s in self._manager.dependencies_order if s in components and s not in ignored_components]
return components, params
return components

def _allow_empty_components_list(f): # @NoSelf
def expand_to_all_components(self, args):
if args:
def _error_handler(f): # @NoSelf
def tracked_command(self, args):
try:
return f(self, args)
except:
print get_short_exc_info()
ComponentManagerShell.logger.error(get_full_exc_info(), extra = {"user": get_username()})
return 1
return tracked_command

def _cmd_line_split(f): # @NoSelf
def line_split(self, args):
(params, identifiers) = self._opt_parser.parse_args(args = shlex.split(args))
params = vars(params)
f(self, identifiers, params)
return line_split

def _allow_empty_components_list(f): # @NoSelf
def expand_to_all_components(self, identifiers, params):
if identifiers:
return f(self, identifiers, params)
else:
return f(self, "*")
return f(self, ["*"], params)
return expand_to_all_components

def _multiple_components_allowed(f): # @NoSelf
def multi_components_command(self, args):
if args:
ComponentManagerShell.logger.info("%s %s", f.func_name[3:], args, extra = {"user": get_username()})
def multi_components_command(self, identifiers, params):
if identifiers:
ComponentManagerShell.logger.info("%s %s %s", f.func_name[3:], identifiers, params, extra = {"user": get_username()})
self._manager.reload()
return f(self, *self._get_components_list_and_params(args))
return f(self, self._get_components_list(identifiers), params)
else:
raise ComponentManagerShellError("Command: '{0}' requires group, namespace or component id(s)".format(f.__name__[3:]))
return multi_components_command

def _single_component_allowed(f): # @NoSelf
def single_component_command(self, arg):
ComponentManagerShell.logger.info("%s %s", f.func_name[3:], arg, extra = {"user": get_username()})
components, params = self._get_components_list_and_params(arg)
def single_component_command(self, identifiers, params):
ComponentManagerShell.logger.info("%s %s %s", f.func_name[3:], identifiers, params, extra = {"user": get_username()})
components = self._get_components_list(identifiers)
if len(components) != 1:
raise ComponentManagerShellError("Command: '{0}' can only be performed on single component".format(f.__name__[3:]))
self._manager.reload()
return f(self, components[0], params)
return single_component_command

def _error_handler(f): # @NoSelf
def tracked_command(self, args):
try:
return f(self, args)
except:
print get_short_exc_info()
ComponentManagerShell.logger.error(get_full_exc_info(), extra = {"user": get_username()})
return 1
return tracked_command

# utility functions
def _apply_command(self, command, components, **kwargs):
failed = []
Expand Down Expand Up @@ -269,9 +276,9 @@ def _format_parameter(self, key, value, default = ""):
return value

# shell commands
def _evaluate_alias(self, commands, args):
def _evaluate_alias(self, commands, params, args):
for command in commands:
ret_val = command(args)
ret_val = command("{0} {1}".format(args, params) if params else args)
if ret_val:
return ret_val

Expand Down Expand Up @@ -305,18 +312,23 @@ def do_quit(self, args):
sys.exit(0)

@_error_handler
@_cmd_line_split
@_allow_empty_components_list
@_multiple_components_allowed
def do_info(self, components, params):
status_filter = params["filter"].upper().split("#") if params["filter"] else None

print self._info_header
for component_uid in sorted(components):
parameters = dict()
component = self._manager.components[component_uid]
for attr in self._info_parameters:
parameters[attr] = self._format_parameter(attr, getattr(component, to_underscore(attr).lower(), ""), "")
print self._info_format.format(**parameters)
if not status_filter or component.status in status_filter:
for attr in self._info_parameters:
parameters[attr] = self._format_parameter(attr, getattr(component, to_underscore(attr).lower(), ""), "")
print self._info_format.format(**parameters)

@_error_handler
@_cmd_line_split
@_multiple_components_allowed
def do_details(self, components, params):
print HLINE
Expand All @@ -332,12 +344,14 @@ def do_details(self, components, params):
print HLINE

@_error_handler
@_cmd_line_split
@_multiple_components_allowed
def do_start(self, components, params):
print "Starting components..."
return self._apply_command(self._manager.start, components, **params)

@_error_handler
@_cmd_line_split
@_multiple_components_allowed
def do_stop(self, components, params):
print "Stopping components..."
Expand All @@ -348,28 +362,33 @@ def do_restart(self, args):
return retval if retval else self.do_start(args)

@_error_handler
@_cmd_line_split
@_single_component_allowed
def do_console(self, component, params):
print "Starting interactive console..."
return 0 if self._manager.console(component, **params) else 1

@_error_handler
@_cmd_line_split
@_multiple_components_allowed
def do_interrupt(self, components, params):
print "Interrupting components..."
return self._apply_command(self._manager.interrupt, reversed(components))

@_error_handler
@_cmd_line_split
@_single_component_allowed
def do_out(self, component, params):
return show_file(self._manager.components[component].stdout)

@_error_handler
@_cmd_line_split
@_single_component_allowed
def do_err(self, component, params):
return show_file(self._manager.components[component].stderr)

@_error_handler
@_cmd_line_split
@_single_component_allowed
def do_log(self, component, params):
return show_file(self._manager.components[component].log)
Expand Down Expand Up @@ -407,6 +426,7 @@ def get_opt_parser():
opt_parser.add_option("-v", "--viewer", help = "external viewer")
opt_parser.add_option("-d", "--delimiter", help = "column delimiter for the info command [default: padded spaces]", default = " ")
opt_parser.add_option("-f", "--format", help = "display format for info command", default = "uid:18#pid:5#port:6#status:11#started:19#stopped:19")
opt_parser.add_option("-F", "--filter", help = "status filter for info command", default = "")
opt_parser.add_option("-A", "--alias", help = "define command alias e.g.: --alias restart_console \"stop, console\"", action = "callback", callback = define_aliases, nargs = 2, type = "str")
opt_parser.add_option("-a", "--arguments", help = "additional arguments passed to process - valid only for 'start', 'restart' and 'console' commands", default = "")
return opt_parser
Expand Down Expand Up @@ -457,7 +477,7 @@ def init_logging(logfile):
if len(args) == 0:
exit_status = shell.cmdloop()
elif len(args) >= 1:
exit_status = shell.onecmd(" ".join(args) + (" -a \"" + options.arguments + "\"" if options.arguments else ""))
exit_status = shell.onecmd(" ".join(args) + (" -a \"" + options.arguments + "\"" if options.arguments else "") + (" -F \"" + options.filter + "\"" if options.filter else ""))
except (SystemExit, KeyboardInterrupt):
exit_status = 0
except:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def run(self):

setup(
name = "yak",
version = os.environ.get("version", "3.1.0"),
version = os.environ.get("version", "3.1.1"),
description = "process components for enterprise components",

license = "Apache License Version 2.0",
Expand Down

0 comments on commit 0ac3996

Please sign in to comment.