Commit 545c6c29 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Implement an optional json output 4 outputing cmds

Refs: #3732
parent 6dfd55cb
......@@ -7,6 +7,7 @@ Bug Fixes:
- Restore 2nd level command syntax in shell [#3736]
- Allow copy of deleted objects by refering to older version [#3737]
- Add image.add_member missing content-length header
- Unquote http respons headers
Changes:
......@@ -34,13 +35,22 @@ Changes:
-image compute:
delete, properties delete
- server: rename, delete, reboot, start, shutdown, firewall-set
- network: rename, delete, connect
- Add optional json for methods with output [#3732]
- file:
list, hashmap, permissions-get, info, metadata-get, quota,
containerlimit-get, group-list, sharers, versions
- server: list, info, create, console, addr, metadata-list/set, stats
- image: list, meta, register, shared, list
- image compute: list, info, properties-list/get/add/set
- flavor: list, info
- network: info, list, create
- astakos: authenticate
- Transliterate methods to list-get-set-delete command groups:
- file: permissions, versioning, group and metadata
- image: members, member
- image compute: properties
- server: firewall, metadata
Features:
- A logger module container a set of basic loging method for kamaki [#3668]
......
......@@ -326,7 +326,7 @@ file (Storage/Pithos+)
download : Download a file or directory
group : Manage access groups and group members
delete: Delete a user group
get : Get groups and group members
list : List groups and group members
set : Set a user group
hashmap : Get the hashmap of an object
info : Get information for account [, container [or object]]
......
......@@ -191,7 +191,7 @@ file commands
* download Download a file or directory
* group Manage access groups and group members
* delete Delete a user group
* get Get groups and group members
* list List groups and group members
* set Set a user group
* hashmap Get the hashmap of an object
* info Get information for account [, container [or object]]
......
......@@ -45,6 +45,8 @@ class _command_init(object):
arguments.update(self.arguments)
if isinstance(self, _optional_output_cmd):
arguments.update(self.oo_arguments)
if isinstance(self, _optional_json):
arguments.update(self.oj_arguments)
self.arguments = dict(arguments)
try:
self.config = self['config']
......@@ -129,6 +131,9 @@ class _command_init(object):
return self[argterm]
# feature classes - inherit them to get special features for your commands
class _optional_output_cmd(object):
oo_arguments = dict(
......@@ -141,3 +146,16 @@ class _optional_output_cmd(object):
print_json(r)
elif self['with_output']:
print_items([r] if isinstance(r, dict) else r)
class _optional_json(object):
oj_arguments = dict(
json_output=FlagArgument('show headers in json', ('-j', '--json'))
)
def _print(self, output, print_method=print_items, **print_method_kwargs):
if self['json_output']:
print_json(output)
else:
print_method(output, **print_method_kwargs)
......@@ -33,10 +33,8 @@
from kamaki.cli import command
from kamaki.clients.astakos import AstakosClient
from kamaki.cli.utils import print_dict, print_json
from kamaki.cli.commands import _command_init, errors
from kamaki.cli.commands import _command_init, errors, _optional_json
from kamaki.cli.command_tree import CommandTree
from kamaki.cli.argument import FlagArgument
user_cmds = CommandTree('user', 'Astakos API commands')
_commands = [user_cmds]
......@@ -60,7 +58,7 @@ class _user_init(_command_init):
@command(user_cmds)
class user_authenticate(_user_init):
class user_authenticate(_user_init, _optional_json):
"""Authenticate a user
Get user information (e.g. unique account name) from token
Token should be set in settings:
......@@ -69,16 +67,13 @@ class user_authenticate(_user_init):
Token can also be provided as a parameter
"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@errors.generic.all
@errors.user.authenticate
def _run(self, custom_token=None):
super(self.__class__, self)._run()
printer = print_json if self['json_output'] else print_dict
printer(self.client.authenticate(custom_token))
self._print(
[self.client.authenticate(custom_token)],
title=('uuid', 'name',), with_redundancy=True)
def main(self, custom_token=None):
self._run(custom_token)
......@@ -38,7 +38,8 @@ from kamaki.cli.errors import raiseCLIError, CLISyntaxError
from kamaki.clients.cyclades import CycladesClient, ClientError
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
from kamaki.cli.argument import ProgressBarArgument, DateArgument, IntArgument
from kamaki.cli.commands import _command_init, errors, _optional_output_cmd
from kamaki.cli.commands import _command_init, errors
from kamaki.cli.commands import _optional_output_cmd, _optional_json
from base64 import b64encode
from os.path import exists
......@@ -80,7 +81,7 @@ class _init_cyclades(_command_init):
@command(server_cmds)
class server_list(_init_cyclades):
class server_list(_init_cyclades, _optional_json):
"""List Virtual Machines accessible by user"""
__doc__ += about_authentication
......@@ -94,8 +95,7 @@ class server_list(_init_cyclades):
more=FlagArgument(
'output results in pages (-n to set items per page, default 10)',
'--more'),
enum=FlagArgument('Enumerate results', '--enumerate'),
json_output=FlagArgument('show output in json', ('-j', '--json'))
enum=FlagArgument('Enumerate results', '--enumerate')
)
def _make_results_pretty(self, servers):
......@@ -118,21 +118,16 @@ class server_list(_init_cyclades):
@errors.cyclades.date
def _run(self):
servers = self.client.list_servers(self['detail'], self['since'])
if self['json_output']:
print_json(servers)
return
if self['detail']:
if self['detail'] and not self['json_output']:
self._make_results_pretty(servers)
kwargs = dict(with_enumeration=self['enum'])
if self['more']:
print_items(
servers,
page_size=self['limit'] if self['limit'] else 10,
with_enumeration=self['enum'])
else:
print_items(
servers[:self['limit'] if self['limit'] else len(servers)],
with_enumeration=self['enum'])
kwargs['page_size'] = self['limit'] if self['limit'] else 10
elif self['limit']:
servers = servers[:self['limit']]
self._print(servers, **kwargs)
def main(self):
super(self.__class__, self)._run()
......@@ -140,7 +135,7 @@ class server_list(_init_cyclades):
@command(server_cmds)
class server_info(_init_cyclades):
class server_info(_init_cyclades, _optional_json):
"""Detailed information on a Virtual Machine
Contains:
- name, id, status, create/update dates
......@@ -149,11 +144,7 @@ class server_info(_init_cyclades):
- hardware flavor and os image ids
"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
def _print(self, server):
def _pretty(self, server):
addr_dict = {}
if 'attachments' in server:
atts = server.pop('attachments')
......@@ -173,8 +164,7 @@ class server_info(_init_cyclades):
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
printer = print_json if self['json_output'] else self._print
printer(self.client.get_server_details(server_id))
self._print(self.client.get_server_details(server_id), self._pretty)
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -216,7 +206,7 @@ class PersonalityArgument(KeyValueArgument):
@command(server_cmds)
class server_create(_init_cyclades):
class server_create(_init_cyclades, _optional_json):
"""Create a server (aka Virtual Machine)
Parameters:
- name: (single quoted text)
......@@ -226,9 +216,7 @@ class server_create(_init_cyclades):
arguments = dict(
personality=PersonalityArgument(
(80 * ' ').join(howto_personality),
('-p', '--personality')),
json_output=FlagArgument('show output in json', ('-j', '--json'))
(80 * ' ').join(howto_personality), ('-p', '--personality'))
)
@errors.generic.all
......@@ -236,12 +224,8 @@ class server_create(_init_cyclades):
@errors.plankton.id
@errors.cyclades.flavor_id
def _run(self, name, flavor_id, image_id):
printer = print_json if self['json_output'] else print_dict
printer(self.client.create_server(
name,
int(flavor_id),
image_id,
self['personality']))
self._print([self.client.create_server(
name, int(flavor_id), image_id, self['personality'])])
def main(self, name, flavor_id, image_id):
super(self.__class__, self)._run()
......@@ -332,7 +316,7 @@ class server_shutdown(_init_cyclades, _optional_output_cmd):
@command(server_cmds)
class server_console(_init_cyclades):
class server_console(_init_cyclades, _optional_json):
"""Get a VNC console to access an existing server (VM)
Console connection information provided (at least):
- host: (url or address) a VNC host
......@@ -340,16 +324,11 @@ class server_console(_init_cyclades):
- password: for VNC authorization
"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_server_console(int(server_id)))
self._print([self.client.get_server_console(int(server_id))])
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -399,12 +378,11 @@ class server_firewall_get(_init_cyclades):
@command(server_cmds)
class server_addr(_init_cyclades):
class server_addr(_init_cyclades, _optional_json):
"""List the addresses of all network interfaces on a server (VM)"""
arguments = dict(
enum=FlagArgument('Enumerate results', '--enumerate'),
json_output=FlagArgument('show output in json', ('-j', '--json'))
enum=FlagArgument('Enumerate results', '--enumerate')
)
@errors.generic.all
......@@ -412,12 +390,8 @@ class server_addr(_init_cyclades):
@errors.cyclades.server_id
def _run(self, server_id):
reply = self.client.list_server_nics(int(server_id))
if self['json_output']:
print_json(reply)
else:
print_items(
reply,
with_enumeration=self['enum'] and len(reply) > 1)
self._print(
reply, with_enumeration=self['enum'] and len(reply) > 1)
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -430,20 +404,16 @@ class server_metadata(_init_cyclades):
@command(server_cmds)
class server_metadata_list(_init_cyclades):
class server_metadata_list(_init_cyclades, _optional_json):
"""Get server metadata"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
@errors.cyclades.metadata
def _run(self, server_id, key=''):
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_server_metadata(int(server_id), key))
self._print(
[self.client.get_server_metadata(int(server_id), key)], title=())
def main(self, server_id, key=''):
super(self.__class__, self)._run()
......@@ -451,7 +421,7 @@ class server_metadata_list(_init_cyclades):
@command(server_cmds)
class server_metadata_set(_init_cyclades):
class server_metadata_set(_init_cyclades, _optional_json):
"""Set / update server(VM) metadata
Metadata should be given in key/value pairs in key=value format
For example:
......@@ -459,16 +429,12 @@ class server_metadata_set(_init_cyclades):
Old, unreferenced metadata will remain intact
"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id, keyvals):
assert keyvals, 'Please, add some metadata ( key=value)'
metadata = dict()
print('TO ANALYZE:', keyvals)
for keyval in keyvals:
k, sep, v = keyval.partition('=')
if sep and k:
......@@ -481,8 +447,9 @@ class server_metadata_set(_init_cyclades):
'For example:',
'/server metadata set <server id>'
'key1=value1 key2=value2'])
printer = print_json if self['json_output'] else print_dict
printer(self.client.update_server_metadata(int(server_id), **metadata))
self._print(
[self.client.update_server_metadata(int(server_id), **metadata)],
title=())
def main(self, server_id, *key_equals_val):
super(self.__class__, self)._run()
......@@ -507,19 +474,14 @@ class server_metadata_delete(_init_cyclades, _optional_output_cmd):
@command(server_cmds)
class server_stats(_init_cyclades):
class server_stats(_init_cyclades, _optional_json):
"""Get server (VM) statistics"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_server_stats(int(server_id)))
self._print([self.client.get_server_stats(int(server_id))])
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -566,7 +528,7 @@ class server_wait(_init_cyclades):
@command(flavor_cmds)
class flavor_list(_init_cyclades):
class flavor_list(_init_cyclades, _optional_json):
"""List available hardware flavors"""
arguments = dict(
......@@ -575,19 +537,15 @@ class flavor_list(_init_cyclades):
more=FlagArgument(
'output results in pages (-n to set items per page, default 10)',
'--more'),
enum=FlagArgument('Enumerate results', '--enumerate'),
json_output=FlagArgument('show output in json', ('-j', '--json'))
enum=FlagArgument('Enumerate results', '--enumerate')
)
@errors.generic.all
@errors.cyclades.connection
def _run(self):
flavors = self.client.list_flavors(self['detail'])
if self['json_output']:
print_json(flavors)
return
pg_size = 10 if self['more'] and not self['limit'] else self['limit']
print_items(
self._print(
flavors,
with_redundancy=self['detail'],
page_size=pg_size,
......@@ -599,21 +557,16 @@ class flavor_list(_init_cyclades):
@command(flavor_cmds)
class flavor_info(_init_cyclades):
class flavor_info(_init_cyclades, _optional_json):
"""Detailed information on a hardware flavor
To get a list of available flavors and flavor ids, try /flavor list
"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.flavor_id
def _run(self, flavor_id):
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_flavor_details(int(flavor_id)))
self._print([self.client.get_flavor_details(int(flavor_id))])
def main(self, flavor_id):
super(self.__class__, self)._run()
......@@ -621,15 +574,11 @@ class flavor_info(_init_cyclades):
@command(network_cmds)
class network_info(_init_cyclades):
class network_info(_init_cyclades, _optional_json):
"""Detailed information on a network
To get a list of available networks and network ids, try /network list
"""
arguments = dict(
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@classmethod
def _make_result_pretty(self, net):
if 'attachments' in net:
......@@ -642,11 +591,9 @@ class network_info(_init_cyclades):
@errors.cyclades.network_id
def _run(self, network_id):
network = self.client.get_network_details(int(network_id))
if self['json_output']:
print_json(network)
return
self._make_result_pretty(network)
print_dict(network, exclude=('id'))
#print_dict(network, exclude=('id'))
self._print(network, print_dict, exclude=('id'))
def main(self, network_id):
super(self.__class__, self)._run()
......@@ -654,7 +601,7 @@ class network_info(_init_cyclades):
@command(network_cmds)
class network_list(_init_cyclades):
class network_list(_init_cyclades, _optional_json):
"""List networks"""
arguments = dict(
......@@ -663,8 +610,7 @@ class network_list(_init_cyclades):
more=FlagArgument(
'output results in pages (-n to set items per page, default 10)',
'--more'),
enum=FlagArgument('Enumerate results', '--enumerate'),
json_output=FlagArgument('show output in json', ('-j', '--json'))
enum=FlagArgument('Enumerate results', '--enumerate')
)
def _make_results_pretty(self, nets):
......@@ -675,21 +621,14 @@ class network_list(_init_cyclades):
@errors.cyclades.connection
def _run(self):
networks = self.client.list_networks(self['detail'])
if self['json_output']:
print_json(networks)
return
if self['detail']:
self._make_results_pretty(networks)
kwargs = dict(with_enumeration=self['enum'])
if self['more']:
print_items(
networks,
page_size=self['limit'] or 10, with_enumeration=self['enum'])
kwargs['page_size'] = self['limit'] or 10
elif self['limit']:
print_items(
networks[:self['limit']],
with_enumeration=self['enum'])
else:
print_items(networks, with_enumeration=self['enum'])
networks = networks[:self['limit']]
self._print(networks, **kwargs)
def main(self):
super(self.__class__, self)._run()
......@@ -697,7 +636,7 @@ class network_list(_init_cyclades):
@command(network_cmds)
class network_create(_init_cyclades):
class network_create(_init_cyclades, _optional_json):
"""Create an (unconnected) network"""
arguments = dict(
......@@ -708,21 +647,19 @@ class network_create(_init_cyclades):
'Valid network types are '
'CUSTOM, IP_LESS_ROUTED, MAC_FILTERED (default), PHYSICAL_VLAN',
'--with-type',
default='MAC_FILTERED'),
json_output=FlagArgument('show output in json', ('-j', '--json'))
default='MAC_FILTERED')
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.network_max
def _run(self, name):
printer = print_json if self['json_output'] else print_dict
printer(self.client.create_network(
self._print([self.client.create_network(
name,
cidr=self['cidr'],
gateway=self['gateway'],
dhcp=self['dhcp'],
type=self['type']))
type=self['type'])])
def main(self, name):
super(self.__class__, self)._run()
......
......@@ -38,7 +38,8 @@ from kamaki.clients.image import ImageClient
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
from kamaki.cli.argument import IntArgument
from kamaki.cli.commands.cyclades import _init_cyclades
from kamaki.cli.commands import _command_init, errors, _optional_output_cmd
from kamaki.cli.commands import _command_init, errors
from kamaki.cli.commands import _optional_output_cmd, _optional_json
image_cmds = CommandTree(
......@@ -73,7 +74,7 @@ class _init_image(_command_init):
@command(image_cmds)
class image_list(_init_image):
class image_list(_init_image, _optional_json):
"""List images accessible by user"""
arguments = dict(
......@@ -104,8 +105,7 @@ class image_list(_init_image):
more=FlagArgument(
'output results in pages (-n to set items per page, default 10)',
'--more'),
enum=FlagArgument('Enumerate results', '--enumerate'),
json_output=FlagArgument('Show results in json', ('-j', '--json'))
enum=FlagArgument('Enumerate results', '--enumerate')
)
def _filtered_by_owner(self, detail, *list_params):
......@@ -148,18 +148,13 @@ class image_list(_init_image):
else:
images = self.client.list_public(detail, filters, order)
if self['json_output']:
print_json(images)
return
images = self._filtered_by_name(images)
kwargs = dict(with_enumeration=self['enum'])
if self['more']:
print_items(
images,
with_enumeration=self['enum'], page_size=self['limit'] or 10)
kwargs['page_size'] = self['limit'] or 10
elif self['limit']:
print_items(images[:self['limit']], with_enumeration=self['enum'])
else:
print_items(images, with_enumeration=self['enum'])
images = images[:self['limit']]
self._print(images, **kwargs)
def main(self):
super(self.__class__, self)._run()
......@@ -167,7 +162,7 @@ class image_list(_init_image):
@command(image_cmds)
class image_meta(_init_image):
class image_meta(_init_image, _optional_json):
"""Get image metadata
Image metadata include:
- image file information (location, size, etc.)
......@@ -175,16 +170,11 @@ class image_meta(_init_image):
- image os properties (os, fs, etc.)
"""
arguments = dict(
json_output=FlagArgument('Show results in json', ('-j', '--json'))
)
@errors.generic.all
@errors.plankton.connection
@errors.plankton.id
def _run(self, image_id):
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_meta(image_id))
self._print([self.client.get_meta(image_id)])
def main(self, image_id):