Hi Justin, thanks for your first patch.

As per our workflows, we use git for our version control and have a
reviewboard for reviewing patches and an issue tracker for tracking
bugs/features.

Git:
https://cwiki.apache.org/confluence/display/CLOUDSTACK/Git

Review board:
https://reviews.apache.org

For filtering only passed key names, we can cut down the whole if-else tree
using filter (which you may see I'd used at several places) and reduce list.

Something like; filter(lamba key: <put here condition to evaluate key, like
key in my set of filters>, dictionary.keys()), using this filtered failsafe
list of keys, we can generate dictionary, using a map; map(lambda x:
myjson[x] = jsondictionary[x], failsafekeys) etc.

I can help with this, thanks for your patch. Since it's small I can help
apply that for future please use review board or share git formatted patch
which would apply cleanly on master.

Regards.

On Mon, Apr 1, 2013 at 10:42 AM, Justin Grudzien <grudz...@gmail.com> wrote:

> Greetings,
>
> I am posting my first patch. I am not sure exactly how to submit a patch
> but I figure I will paste it here and someone will tell me the right
> method. I am keeping track of the work that I am doing and as Rohit
> suggested I will update the mailing list as I progress. This first patch
> adds basic JSON output with filtering. I have tested it on cloudmonkey
> 4.1.0-0 and 4.1.0-snapshot3 and all seems well. Comments are appreciated.
>
> --- README ---
> Added
> 1. display = [default|json|tabularize] has been added in the config to
> replace
>     tabularize = [true|false]
> 2. tabularize is deprecated but we will still set it as "false" once the
> user
>     removes it out of their config to avoid throwing an error. This will be
>     removed in the next major version.
> 3. display = "default" is added to the [ui] section of the config if it is
> not
>     present.
> 4. You can now output JSON formatted text by setting the config display =
> json
> 5. You can now filter text in JSON output mode. (i.e. list users
>     account=grudzien filter=account,id,email). Filtered output returns a
>     properly formatted JSON document.
>
> Removed
> 1. Removed the printing of attr keys in read_config().
>
> Deprecated
> 1. tabularize = [true|false] is now messaged as deprecated.
>
> ToDo
> 1. Need to format empty return messages with proper JSON messaging.
> 2. Need to standardize JSON output messaging.
>     A. Add return code [Error|Success] to all calls.
>     B. Add called API verb.
> 3. Count is not decreased in results when filtering completely eliminates a
>     result.
> 4. JSON print needs to be implemented manually to support colors. Right now
>     json.dumps() pretty prints the output.
> 5. Color tagging needs to be added to JSON printing.
> 6. Error messages need to have proper JSON formatting.
> 7. Various help text has grammar or consistency errors that need fixing.
> 8. Make color a passable option to a command instead of a config parameter.
>     (i.e. list users account=grudzien color=true)
>     A. This will require reworking the result_filter passable argument to
> the
>         various print methods since they only expect filter= input.
> 9. The API in CloudStack 4.0.1 does not all return proper JSON formatted
> text.
>     As a result some of the JSON printing looks funny. I will get the later
>     versions into a lab and test them to make sure the formatting bugs are
> in
>     newer revisions and submit them as bugs.
> --- README ---
>
> --- PATCH ---
> diff -rupN cloudstack/tools/cli/cloudmonkey/cloudmonkey.py
> cloudstackmodified/tools/cli/cloudmonkey/cloudmonkey.py
> --- cloudstack/tools/cli/cloudmonkey/cloudmonkey.py 2013-03-31
> 16:58:26.000000000 -0500
> +++ cloudstackmodified/tools/cli/cloudmonkey/cloudmonkey.py 2013-03-31
> 22:03:38.000000000 -0500
> @@ -27,6 +27,7 @@ try:
>      import shlex
>      import sys
>      import types
> +    import copy
>
>      from cachemaker import loadcache, savecache, monkeycache,
> splitverbsubject
>      from config import __version__, __description__, __projecturl__
> @@ -162,6 +163,44 @@ class CloudMonkeyShell(cmd.Cmd, object):
>                  self.monkeyprint(printer)
>              return PrettyTable(toprow)
>
> +        # method: print_result_json( result, result_filter )
> +        # parameters: result - raw results from the API call
> +        #             result_filter - filterset
> +        # description: prints result as a json object
> +        def print_result_json(result, result_filter=None):
> +            tfilter = {} # temp var to hold a dict of the filters
> +            tresult = copy.deepcopy(result) # dupe the result to filter
> +            if result_filter != None:
> +                for res in result_filter:
> +                    tfilter[ res ] = 1
> +                myresults = {}
> +                for okey, oval in result.iteritems():
> +                    if isinstance( oval, dict ):
> +                        for tkey in x:
> +                            if tkey not in tfilter:
> +                                try:
> +                                    del( tresult[okey][x][tkey] )
> +                                except:
> +                                    pass
> +                    elif isinstance( oval, list ):
> +                        for x in range( len( oval ) ):
> +                            if isinstance( oval[x], dict ):
> +                                for tkey in oval[x]:
> +                                    if tkey not in tfilter:
> +                                        try:
> +                                            del( tresult[okey][x][tkey] )
> +                                        except:
> +                                            pass
> +                            else:
> +                                try:
> +                                    del( tresult[ okey ][ x ] )
> +                                except:
> +                                    pass
> +            print json.dumps(tresult,
> +                             sort_keys=True,
> +                             indent=2,
> +                             separators=(',', ': '))
> +
>          def print_result_tabular(result, result_filter=None):
>              toprow = None
>              printer = None
> @@ -183,6 +222,12 @@ class CloudMonkeyShell(cmd.Cmd, object):
>                  self.monkeyprint(printer)
>
>          def print_result_as_dict(result, result_filter=None):
> +
> +            # tabularize overrides self.display
> +            if self.display == "json" and not self.tabularize == "true":
> +                print_result_json(result, result_filter)
> +                return
> +
>              for key in sorted(result.keys(), key=lambda x:
>                                x not in ['id', 'count', 'name'] and x):
>                  if not (isinstance(result[key], list) or
> @@ -195,7 +240,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
>          def print_result_as_list(result, result_filter=None):
>              for node in result:
>                  # Tabular print if it's a list of dict and tabularize is
> true
> -                if isinstance(node, dict) and self.tabularize == 'true':
> +                if isinstance(node, dict) and (self.display ==
> 'tabularize' or self.tabularize == 'true'):
>                      print_result_tabular(result, result_filter)
>                      break
>                  self.print_result(node)
> @@ -318,7 +363,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
>                      autocompletions = uuids
>                      search_string = value
>
> -        if self.tabularize == "true" and subject != "":
> +        if (self.display == "tabularize" or self.display == "json" or
> self.tabularize == "true") and subject != "":
>              autocompletions.append("filter=")
>          return [s for s in autocompletions if s.startswith(search_string)]
>
> @@ -459,7 +504,6 @@ class CloudMonkeyShell(cmd.Cmd, object):
>          self.monkeyprint("Bye!")
>          return self.do_EOF(args)
>
> -
>  class MonkeyParser(OptionParser):
>      def format_help(self, formatter=None):
>          if formatter is None:
> @@ -473,7 +517,6 @@ class MonkeyParser(OptionParser):
>          result.append("\nTry cloudmonkey [help|?]\n")
>          return "".join(result)
>
> -
>  def main():
>      parser = MonkeyParser()
>      parser.add_option("-c", "--config-file",
> diff -rupN cloudstack/tools/cli/cloudmonkey/config.py
> cloudstackmodified/tools/cli/cloudmonkey/config.py
> --- cloudstack/tools/cli/cloudmonkey/config.py 2013-03-31
> 16:58:26.000000000 -0500
> +++ cloudstackmodified/tools/cli/cloudmonkey/config.py 2013-03-31
> 23:09:01.000000000 -0500
> @@ -56,7 +56,8 @@ config_fields['core']['log_file'] = expa
>  # ui
>  config_fields['ui']['color'] = 'true'
>  config_fields['ui']['prompt'] = '> '
> -config_fields['ui']['tabularize'] = 'false'
> +config_fields['ui']['tabularize'] = 'false' # deprecate - REMOVE
> +config_fields['ui']['display'] = 'default' # default display mechanism
>
>  # server
>  config_fields['server']['host'] = 'localhost'
> @@ -111,9 +112,19 @@ def read_config(get_attr, set_attr, conf
>      for section in config_fields.keys():
>          for key in config_fields[section].keys():
>              try:
> +                if( key == "tabularize" ): # this key is deprecated
> +                    print "\ntabularize config parameter is deprecated:",
> +                    print "please switch to display =",
> +                    print "[default,json,tabularize]\n"
>                  set_attr(key, config.get(section, key))
>              except Exception:
> -                missing_keys.append(key)
> +                if( key == "tabularize" ): # this key is deprecated
> +                    set_attr( key, "false" ) # set default
> +                elif( key == "display" ): # this key is deprecated
> +                    config = write_config(get_attr, config_file, True)
> +                    set_attr( key, "default" ) # set default
> +                else:
> +                    missing_keys.append(key)
>
>      if len(missing_keys) > 0:
>          print "Please fix `%s` in %s" % (', '.join(missing_keys),
> config_file)
> --- PATCH ---
>
> Justin
>
>
> On Sun, Mar 31, 2013 at 2:15 AM, Rohit Yadav <bhais...@apache.org> wrote:
>
> > Hey Justin, thanks for posting this on community ML.
> >
> > On Sat, Mar 30, 2013 at 9:20 AM, Justin Grudzien <grudz...@gmail.com>
> > wrote:
> >
> > > My company is building a private cloud and we are moving to cloudstack.
> > As
> > > we begun investigating the cloudmonkey CLI we found that the output was
> > > slightly hard to read. I have begun working on some optimizations that
> I
> > > think will benefit the community and I reached out to Rohit, who
> > > recommended that I join this list and present my ideas. Here is what I
> am
> > > proposing:
> > >
> > > 1. Add json output to cloudmonkey
> > > I have accomplished this by adding a config parameter called display,
> > which
> > > can be set to json, tabularize, or default. I have removed the
> tabularize
> > > parameter.
> > >
> >
> > +1
> >
> >
> > >
> > > Justins-MacBook-Pro:cloudmonkey grudzien$ cloudmonkey list users
> > > account=grudzien
> > > {
> > >   "count": 1,
> > >   "user": [
> > >     {
> > >       "account": "grudzien",
> > >       "accountid": "b799783d-e5bb-460a-be0e-3966bd69edda",
> > >       "accounttype": 1,
> > >       "apikey": "*nokey*",
> > >       "created": "2013-03-27T16:09:17-0500",
> > >       "domain": "ROOT",
> > >       "domainid": "7e61c32f-9873-4944-947a-dcc00d3bebdc",
> > >       "email": "grudz...@gmail.com",
> > >       "firstname": "Justin",
> > >       "id": "265930bc-62ef-41f8-849c-e58593ca4b1f",
> > >       "lastname": "Grudzien",
> > >       "secretkey": "*nokey*",
> > >       "state": "enabled",
> > >       "username": "grudzien"
> > >     }
> > >   ]
> > > }
> > >
> > > 2. Add filtering as a standard parameter for all output types.
> > > The only thing that has filtering now is the tabular output and grep
> > breaks
> > > the json.
> > >
> > > Justins-MacBook-Pro:cloudmonkey grudzien$ cloudmonkey list users
> > > account=grudzien filter=account,email,username,state
> > > {
> > >   "count": 1,
> > >   "user": [
> > >     {
> > >       "account": "grudzien",
> > >       "email": "grudz...@gmail.com",
> > >       "state": "enabled",
> > >       "username": "grudzien"
> > >     }
> > >   ]
> > > }
> > >
> >
> > Awesome.
> >
> >
> > >
> > > 3. Add color to the json output
> > > I was thinking of colorizing the keys in the key/value pairs to
> increase
> > > readability.
> > >
> >
> > This is easily doable, all we have to do is define a regex rule in
> > printer.py for the regex type "txt": as key and ": xxx, as value.
> >
> >
> > > 4. Move the color option from the config file to the command line.
> > > There are two reasons for this. First, I want to be able to wrap a
> script
> > > around cloudmonkey and not have to worry about colorization that will
> > > impede me processing the output and second I think it would be more
> > useful
> > > to use the highlighting on demand rather than having to back out of the
> > > shell to edit a config file.
> > >
> >
> > I did not understand this one. What exactly are you proposing? You can
> > always set color to false or true. Are you talking about color themes?
> >
> >
> > > 5. Standardize messaging for the output types.
> > > Right now certain kinds of messaging is presented differently for an
> > output
> > > type. For example, if I issue an api command that doesn't exist it
> > displays
> > > a generic error message, regardless of the output type selected.
> Ideally,
> > > all output would be in the specified format.
> > >
> >
> > Oh man, that would be just great! Pl. share your patch.
> >
> >
> > >
> > > I have the first two working and am planning on implementing the others
> > as
> > > I flesh them out. I will submit a patch when I feel it is ready. Any
> > early
> > > feedback on whether these changes will be useful to others is
> > appreciated.
> > >
> >
> > You can submit patches as you finish them, don't wait till all of 5
> things
> > are done. I can help review them and merge them.
> >
> > Thanks for your proposal and sharing them.
> >
> > Cheers.
> >
> >
> >
> > > Justin
> > >
> >
>

Reply via email to