Commit cd295a1d authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Add json/optional outputs to server commands

Refs: #3732
parent 395fbf9e
......@@ -19,7 +19,7 @@ Changes:
- Disallow moving deleted objects by version [#3737]
This operation was implemented by accident, due to the symetry between
move and copy
- Rename file-meta commands to file-metadata
- Rename file/server-meta commands to file/server-metadata
- Rename image-[add|del]member commands to members-[add|delete]
- Remove update option from imagre-register
- In image-compute split properties to properties-list and properties-get
......@@ -33,10 +33,13 @@ Changes:
unregister, members add/delete/set
-image compute:
delete, properties delete
- server: rename, delete, reboot, start, shutdown, firewall-set
- 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:
......@@ -54,4 +57,5 @@ Features:
- Add a download_to_string method in pithos client [#3608]
- Add an upload_from_string method in pithos client [#3608]
- Add pithos client method create_container [#3756]
- Add server-firewall-get command to get a VMs firewall profile
......@@ -137,19 +137,22 @@ server (Compute/Cyclades)
.. code-block:: text
addmeta : Add server metadata
addr : List a server's nic address
console : Get a VNC console
create : Create a server
delete : Delete a server
delmeta : Delete server metadata
firewall: Set the server's firewall profile
firewall: Manage server's firewall profile
set : Set the server's firewall profile
get : Get the server's firewall profile
info : Get server details
list : List servers
metadata: Manage Server Metadata
list : List server metadata
set : Add / update server metadata
delete: Delete a piece of server metadata
meta : Get a server's metadata
reboot : Reboot a server
rename : Update a server's name
setmeta : Update server's metadata
shutdown: Shutdown a server
start : Start a server
stats : Get server statistics
......
......@@ -116,14 +116,17 @@ server commands
* console Get a VNC console to access an existing server (VM)
* create Create a server (aka Virtual Machine)
* delete Delete a server (VM)
* delmeta Delete server (VM) metadata
* firewall Set the server (VM) firewall profile on VMs public network
* firewall Set the server (VM) firewall profile for public networks
* set Set the firewall profile
* get Get the firewall profile
* info Detailed information on a Virtual Machine
* list List Virtual Machines accessible by user
* meta Get a server's metadatum
* metadata Manage a server metadata
* list List server metadata
* set Add or update server metadata
* delete Delete a piece of server metadata
* reboot Reboot a server (VM)
* rename Set/update a server (VM) name
* setmeta set server (VM) metadata
* shutdown Shutdown an active server (VM)
* start Start an existing server (VM)
* stats Get server (VM) statistics
......
......@@ -146,19 +146,21 @@ To see the command groups, use -h or --help like in example 1.3.1. In the same w
Options:
- - - -
addmeta : Add server metadata
addr : List a server's nic address
console : Get a VNC console
create : Create a server
delete : Delete a server
delmeta : Delete server metadata
firewall: Set the server's firewall profile
firewall: Manage the server's firewall profile
set: Set the server's firewall profile
get: Get the server's firewall profile
info : Get server details
list : List servers
meta : Get a server's metadata
metadata: Manage server metadata
list : Get a server metadata
set : Add or update server metadata
delete: Delete a piece of server metadata
reboot : Reboot a server
rename : Update a server's name
setmeta : Update server's metadata
shutdown: Shutdown a server
start : Start a server
stats : Get server statistics
......
......@@ -33,12 +33,12 @@
from kamaki.cli import command
from kamaki.cli.command_tree import CommandTree
from kamaki.cli.utils import print_dict, print_list, print_items
from kamaki.cli.utils import print_dict, print_items, print_json
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
from kamaki.cli.commands import _command_init, errors, _optional_output_cmd
from base64 import b64encode
from os.path import exists
......@@ -94,7 +94,8 @@ 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')
enum=FlagArgument('Enumerate results', '--enumerate'),
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
def _make_results_pretty(self, servers):
......@@ -117,6 +118,9 @@ 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']:
self._make_results_pretty(servers)
......@@ -145,6 +149,10 @@ 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):
addr_dict = {}
if 'attachments' in server:
......@@ -165,8 +173,8 @@ class server_info(_init_cyclades):
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
server = self.client.get_server_details(server_id)
self._print(server)
printer = print_json if self['json_output'] else self._print
printer(self.client.get_server_details(server_id))
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -218,8 +226,9 @@ class server_create(_init_cyclades):
arguments = dict(
personality=PersonalityArgument(
' /// '.join(howto_personality),
('-p', '--personality'))
(80 * ' ').join(howto_personality),
('-p', '--personality')),
json_output=FlagArgument('show output in json', ('-j', '--json'))
)
@errors.generic.all
......@@ -227,12 +236,12 @@ class server_create(_init_cyclades):
@errors.plankton.id
@errors.cyclades.flavor_id
def _run(self, name, flavor_id, image_id):
r = self.client.create_server(
printer = print_json if self['json_output'] else print_dict
printer(self.client.create_server(
name,
int(flavor_id),
image_id,
self['personality'])
print_dict(r)
self['personality']))
def main(self, name, flavor_id, image_id):
super(self.__class__, self)._run()
......@@ -240,7 +249,7 @@ class server_create(_init_cyclades):
@command(server_cmds)
class server_rename(_init_cyclades):
class server_rename(_init_cyclades, _optional_output_cmd):
"""Set/update a server (VM) name
VM names are not unique, therefore multiple servers may share the same name
"""
......@@ -249,7 +258,8 @@ class server_rename(_init_cyclades):
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id, new_name):
self.client.update_server_name(int(server_id), new_name)
self._optional_output(
self.client.update_server_name(int(server_id), new_name))
def main(self, server_id, new_name):
super(self.__class__, self)._run()
......@@ -257,14 +267,14 @@ class server_rename(_init_cyclades):
@command(server_cmds)
class server_delete(_init_cyclades):
class server_delete(_init_cyclades, _optional_output_cmd):
"""Delete a server (VM)"""
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self.client.delete_server(int(server_id))
self._optional_output(self.client.delete_server(int(server_id)))
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -272,7 +282,7 @@ class server_delete(_init_cyclades):
@command(server_cmds)
class server_reboot(_init_cyclades):
class server_reboot(_init_cyclades, _optional_output_cmd):
"""Reboot a server (VM)"""
arguments = dict(
......@@ -283,7 +293,8 @@ class server_reboot(_init_cyclades):
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self.client.reboot_server(int(server_id), self['hard'])
self._optional_output(
self.client.reboot_server(int(server_id), self['hard']))
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -291,14 +302,14 @@ class server_reboot(_init_cyclades):
@command(server_cmds)
class server_start(_init_cyclades):
class server_start(_init_cyclades, _optional_output_cmd):
"""Start an existing server (VM)"""
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self.client.start_server(int(server_id))
self._optional_output(self.client.start_server(int(server_id)))
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -306,14 +317,14 @@ class server_start(_init_cyclades):
@command(server_cmds)
class server_shutdown(_init_cyclades):
class server_shutdown(_init_cyclades, _optional_output_cmd):
"""Shutdown an active server (VM)"""
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
self.client.shutdown_server(int(server_id))
self._optional_output(self.client.shutdown_server(int(server_id)))
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -329,12 +340,16 @@ 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):
r = self.client.get_server_console(int(server_id))
print_dict(r)
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_server_console(int(server_id)))
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -343,6 +358,11 @@ class server_console(_init_cyclades):
@command(server_cmds)
class server_firewall(_init_cyclades):
"""Manage server (VM) firewall profiles for public networks"""
@command(server_cmds)
class server_firewall_set(_init_cyclades, _optional_output_cmd):
"""Set the server (VM) firewall profile on VMs public network
Values for profile:
- DISABLED: Shutdown firewall
......@@ -355,25 +375,49 @@ class server_firewall(_init_cyclades):
@errors.cyclades.server_id
@errors.cyclades.firewall
def _run(self, server_id, profile):
self.client.set_firewall_profile(
server_id=int(server_id),
profile=('%s' % profile).upper())
self._optional_output(self.client.set_firewall_profile(
server_id=int(server_id), profile=('%s' % profile).upper()))
def main(self, server_id, profile):
super(self.__class__, self)._run()
self._run(server_id=server_id, profile=profile)
@command(server_cmds)
class server_firewall_get(_init_cyclades):
"""Get the server (VM) firewall profile for its public network"""
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
print(self.client.get_firewall_profile(server_id))
def main(self, server_id):
super(self.__class__, self)._run()
self._run(server_id=server_id)
@command(server_cmds)
class server_addr(_init_cyclades):
"""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'))
)
@errors.generic.all
@errors.cyclades.connection
@errors.cyclades.server_id
def _run(self, server_id):
reply = self.client.list_server_nics(int(server_id))
print_list(reply, with_enumeration=len(reply) > 1)
if self['json_output']:
print_json(reply)
else:
print_items(
reply,
with_enumeration=self['enum'] and len(reply) > 1)
def main(self, server_id):
super(self.__class__, self)._run()
......@@ -381,18 +425,25 @@ class server_addr(_init_cyclades):
@command(server_cmds)
class server_meta(_init_cyclades):
"""Get a server's metadatum
Metadata are formed as key:value pairs where key is used to retrieve them
"""
class server_metadata(_init_cyclades):
"""Manage Server metadata (key:value pairs of server attributes)"""
@command(server_cmds)
class server_metadata_list(_init_cyclades):
"""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=''):
r = self.client.get_server_metadata(int(server_id), key)
print_dict(r)
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_server_metadata(int(server_id), key))
def main(self, server_id, key=''):
super(self.__class__, self)._run()
......@@ -400,26 +451,46 @@ class server_meta(_init_cyclades):
@command(server_cmds)
class server_setmeta(_init_cyclades):
"""set server (VM) metadata
Metadata are formed as key:value pairs, both needed to set one
class server_metadata_set(_init_cyclades):
"""Set / update server(VM) metadata
Metadata should be given in key/value pairs in key=value format
For example:
/server metadata set <server id> key1=value1 key2=value2
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, key, val):
metadata = {key: val}
r = self.client.update_server_metadata(int(server_id), **metadata)
print_dict(r)
def main(self, server_id, key, val):
def _run(self, server_id, keyvals):
metadata = dict()
print('TO ANALYZE:', keyvals)
for keyval in keyvals:
k, sep, v = keyval.partition('=')
if sep and k:
metadata[k] = v
else:
raiseCLIError(
'Invalid piece of metadata %s' % keyval,
importance=2, details=[
'Correct metadata format: key=val',
'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))
def main(self, server_id, *key_equals_val):
super(self.__class__, self)._run()
self._run(server_id=server_id, key=key, val=val)
self._run(server_id=server_id, keyvals=key_equals_val)
@command(server_cmds)
class server_delmeta(_init_cyclades):
class server_metadata_delete(_init_cyclades, _optional_output_cmd):
"""Delete server (VM) metadata"""
@errors.generic.all
......@@ -427,7 +498,8 @@ class server_delmeta(_init_cyclades):
@errors.cyclades.server_id
@errors.cyclades.metadata
def _run(self, server_id, key):
self.client.delete_server_metadata(int(server_id), key)
self._optional_output(
self.client.delete_server_metadata(int(server_id), key))
def main(self, server_id, key):
super(self.__class__, self)._run()
......@@ -438,12 +510,16 @@ class server_delmeta(_init_cyclades):
class server_stats(_init_cyclades):
"""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):
r = self.client.get_server_stats(int(server_id))
print_dict(r, exclude=('serverRef',))
printer = print_json if self['json_output'] else print_dict
printer(self.client.get_server_stats(int(server_id)))
def main(self, server_id):
super(self.__class__, self)._run()
......
......@@ -115,16 +115,22 @@ class ComputeClient(ComputeRestClient):
:param server_id: integer (str or int)
:param new_name: (str)
:returns: (dict) response headers
"""
req = {'server': {'name': new_name}}
self.servers_put(server_id, json_data=req)
r = self.servers_put(server_id, json_data=req)
return r.headers
def delete_server(self, server_id):
"""Submit a deletion request for a server specified by id
:param server_id: integer (str or int)
:returns: (dict) response headers
"""
self.servers_delete(server_id)
r = self.servers_delete(server_id)
return r.headers
def reboot_server(self, server_id, hard=False):
"""
......@@ -134,7 +140,8 @@ class ComputeClient(ComputeRestClient):
"""
boot_type = 'HARD' if hard else 'SOFT'
req = {'reboot': {'type': boot_type}}
self.servers_post(server_id, 'action', json_data=req)
r = self.servers_post(server_id, 'action', json_data=req)
return r.headers
def get_server_metadata(self, server_id, key=''):
"""
......@@ -183,8 +190,11 @@ class ComputeClient(ComputeRestClient):
:param server_id: integer (str or int)
:param key: (str) the meta key
:returns: (dict) response headers
"""
self.servers_delete(server_id, 'meta/' + key)
r = self.servers_delete(server_id, 'meta/' + key)
return r.headers
def list_flavors(self, detail=False):
"""
......@@ -279,6 +289,8 @@ class ComputeClient(ComputeRestClient):
:param image_id: (str)
:param key: (str) metadatum key
:returns: (dict) response headers
"""
command = path4url('meta', key)
r = self.images_delete(image_id, command)
......
......@@ -45,17 +45,23 @@ class CycladesClient(CycladesRestClient):
"""Submit a startup request
:param server_id: integer (str or int)
:returns: (dict) response headers
"""
req = {'start': {}}
self.servers_post(server_id, 'action', json_data=req, success=202)
r = self.servers_post(server_id, 'action', json_data=req, success=202)
return r.headers
def shutdown_server(self, server_id):
"""Submit a shutdown request
:param server_id: integer (str or int)
:returns: (dict) response headers
"""
req = {'shutdown': {}}
self.servers_post(server_id, 'action', json_data=req, success=202)
r = self.servers_post(server_id, 'action', json_data=req, success=202)
return r.headers
def get_server_console(self, server_id):
"""
......@@ -89,9 +95,12 @@ class CycladesClient(CycladesRestClient):
:param server_id: integer (str or int)
:param profile: (str) ENABLED | DISABLED | PROTECTED
:returns: (dict) response headers
"""
req = {'firewallProfile': {'profile': profile}}
self.servers_post(server_id, 'action', json_data=req, success=202)
r = self.servers_post(server_id, 'action', json_data=req, success=202)
return r.headers
def list_servers(self, detail=False, changes_since=None):
"""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment