Commit 8df337ec authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Merge branch 'feature-output' into develop

parents 69e16d8f 83c3ba87
......@@ -74,6 +74,7 @@ class astakos_authenticate(_astakos_init):
'If not, set a token:',
' 1.(permanent): /config set token <token>',
' 2.(temporary): rerun with <token> parameter'])
raiseCLIError(ce)
except Exception as err:
raiseCLIError(err)
print_dict(reply)
......@@ -32,16 +32,32 @@
# or implied, of GRNET S.A.
from kamaki.cli import command
from kamaki.cli.argument import FlagArgument
from kamaki.cli.commands import _command_init
from kamaki.cli.command_tree import CommandTree
config_cmds = CommandTree('config', 'Configuration commands')
_commands = [config_cmds]
about_options = '\nAbout options:\
\n syntax: [group.]option\
\n example: store.account\
\n special case: <option> is equivalent to global.<option>\
\n configuration file syntax:\
\n [group]\
\n option=value\
\n (more options can be set per group)'
@command(config_cmds)
class config_list(_command_init):
"""List configuration options"""
"""List all configuration options
FAQ:
Q: I haven't set any options!
A: Defaults are used (override with /config set )
Q: There are more options than I have set
A: Default options remain if not explicitly replaced or deleted
"""
def main(self):
for section in sorted(self.config.sections()):
......@@ -52,7 +68,10 @@ class config_list(_command_init):
@command(config_cmds)
class config_get(_command_init):
"""Show a configuration option"""
"""Show a configuration option
"""
__doc__ += about_options
def main(self, option):
section, sep, key = option.rpartition('.')
......@@ -66,6 +85,8 @@ class config_get(_command_init):
class config_set(_command_init):
"""Set a configuration option"""
__doc__ += about_options
def main(self, option, value):
section, sep, key = option.rpartition('.')
section = section or 'global'
......@@ -76,11 +97,20 @@ class config_set(_command_init):
@command(config_cmds)
class config_delete(_command_init):
"""Delete a configuration option (and use the default value)"""
"""Delete a configuration option
Default values are not removed by default. To alter this behavior in a
session, use --default.
"""
arguments = dict(
default=FlagArgument(
'Remove default value as well (persists until end of sesion)',
'--default')
)
def main(self, option):
section, sep, key = option.rpartition('.')
section = section or 'global'
self.config.remove_option(section, key)
self.config.remove_option(section, key, self['default'])
self.config.write()
self.config.reload()
This diff is collapsed.
......@@ -36,9 +36,10 @@ from kamaki.cli.command_tree import CommandTree
from kamaki.cli.errors import raiseCLIError
from kamaki.cli.utils import print_dict, print_items, bold
from kamaki.clients.image import ImageClient, ClientError
from kamaki.cli.argument import\
FlagArgument, ValueArgument, KeyValueArgument, IntArgument
from kamaki.cli.argument import FlagArgument, ValueArgument, KeyValueArgument
from kamaki.cli.argument import IntArgument
from kamaki.cli.commands.cyclades_cli import _init_cyclades
from kamaki.cli.commands.cyclades_cli import raise_if_connection_error
from kamaki.cli.commands import _command_init
......@@ -79,7 +80,11 @@ class image_public(_init_image):
order=ValueArgument(
'order by FIELD ( - to reverse order)',
'--order',
default='')
default=''),
limit=IntArgument('limit the number of images in list', '-n'),
more=FlagArgument(
'output results in pages (-n to set items per page, default 10)',
'--more')
)
def main(self):
......@@ -100,9 +105,24 @@ class image_public(_init_image):
detail = self['detail']
try:
images = self.client.list_public(detail, filters, order)
except ClientError as ce:
raise_if_connection_error(ce, base_url='image.url')
raiseCLIError(ce)
except Exception as err:
raiseCLIError(err)
print_items(images, title=('name',), with_enumeration=True)
if self['more']:
print_items(
images,
title=('name',),
with_enumeration=True,
page_size=self['limit'] if self['limit'] else 10)
elif self['limit']:
print_items(
images[:self['limit']],
title=('name',),
with_enumeration=True)
else:
print_items(images, title=('name',), with_enumeration=True)
@command(image_cmds)
......
......@@ -128,8 +128,10 @@ class Config(RawConfigParser):
self.add_section(section)
RawConfigParser.set(self, section, option, value)
def remove_option(self, section, option):
def remove_option(self, section, option, also_remove_default=False):
try:
if also_remove_default:
DEFAULTS[section].pop(option)
RawConfigParser.remove_option(self, section, option)
except NoSectionError:
pass
......
......@@ -31,7 +31,7 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from sys import stdout
from sys import stdout, stdin
from re import compile as regex_compile
from kamaki.cli.errors import raiseCLIError
......@@ -194,7 +194,8 @@ def print_list(l,
def print_items(items,
title=('id', 'name'),
with_enumeration=False,
with_redundancy=False):
with_redundancy=False,
page_size=0):
"""print dict or list items in a list, using some values as title
Objects of next level don't inherit enumeration (default: off) or titles
......@@ -202,7 +203,17 @@ def print_items(items,
:param title: (tuple) keys to use their values as title
:param with_enumeration: (boolean) enumerate items (order id on title)
:param with_redundancy: (boolean) values in title also appear on body
:param page_size: (int) show results in pages of page_size items, enter to
continue
"""
if not items:
return
try:
page_size = int(page_size) if int(page_size) > 0 else len(items)
except:
page_size = len(items)
num_of_pages = len(items) // page_size
num_of_pages += 1 if len(items) % page_size else 0
for i, item in enumerate(items):
if with_enumeration:
stdout.write('%s. ' % (i + 1))
......@@ -221,6 +232,14 @@ def print_items(items,
print_list(item, ident=1)
else:
print(' %s' % item)
if num_of_pages and len(items) > (i + 1) and 0 == (i + 1) % page_size:
num_of_pages -= 1
print('(%s listed - %s more - "enter" to continue)' % (
i + 1,
len(items) - (i + 1)))
c = ' '
while c != '\n':
c = stdin.read(1)
def format_size(size):
......
......@@ -215,7 +215,7 @@ class Client(object):
if not errstr:
errstr = ('%s' % type(err))[7:-2]
raise ClientError('%s\n' % errstr,
status=getattr(err, 'status', 0))
status=getattr(err, 'status', 0) or getattr(err, 'errno', 0))
self.http_client.reset_headers()
self.http_client.reset_params()
......
......@@ -98,7 +98,8 @@ class ComputeClient(ComputeClientApi):
r = self.servers_post(json_data=req)
except ClientError as err:
try:
tmp_err = err.details.split(',')
tmp_err = err.details if isinstance(err.details, list)\
else unicode(err.details).split(',')
tmp_err = tmp_err[0].split(':')
tmp_err = tmp_err[2].split('"')
err.message = tmp_err[1]
......@@ -112,7 +113,7 @@ class ComputeClient(ComputeClientApi):
:param server_id: integer (str or int)
:param new_name: (str)
:param new_name: (str)
"""
req = {'server': {'name': new_name}}
r = self.servers_put(server_id, json_data=req)
......
......@@ -33,10 +33,18 @@
class HTTPConnectionError(Exception):
def __init__(self, message):
errno = None
def __init__(self, message, errno=None):
super(HTTPConnectionError, self).__init__(message)
if errno:
self.errno = errno
class HTTPResponseError(Exception):
def __init__(self, message):
errno = None
def __init__(self, message, errno=None):
super(HTTPResponseError, self).__init__(message)
if errno:
self.errno = errno
......@@ -36,7 +36,7 @@ from objpool.http import get_http_connection
from kamaki.clients.connection import HTTPConnection, HTTPResponse
from kamaki.clients.connection.errors import HTTPConnectionError
from kamaki.clients.connection.errors import HTTPResponseError
from socket import gaierror
from socket import gaierror, error
from json import loads
......@@ -175,13 +175,14 @@ class KamakiHTTPConnection(HTTPConnection):
url=str(self.path),
headers=http_headers,
body=data)
except IOError as ioe:
raise HTTPConnectionError(
'Cannot connect to %s: %s' % (self.url, ioe.strerror),
errno=ioe.errno)
except Exception as err:
from traceback import format_stack
from kamaki.clients import recvlog
recvlog.debug('\n'.join(['%s' % type(err)] + format_stack()))
conn.close()
if isinstance(err, gaierror):
raise HTTPConnectionError(
'Cannot connect to %s - %s' % (self.url, err))
raise
return KamakiHTTPResponse(conn)
......@@ -93,6 +93,18 @@ class CycladesClient(CycladesClientApi):
r = self.servers_post(server_id, 'action', json_data=req, success=202)
r.release()
def list_servers(self, detail=False, changes_since=None):
"""
:param detail: (bool) append full server details to each item if true
:param changes_since: (date)
:returns: list of server ids and names
"""
detail = 'detail' if detail else ''
r = self.servers_get(command=detail, changes_since=changes_since)
return r.json['servers']['values']
def list_server_nics(self, server_id):
"""
:param server_id: integer (str or int)
......@@ -212,10 +224,13 @@ class CycladesClient(CycladesClientApi):
server_nets = self.list_server_nics(server_id)
nets = [(net['id'], net['network_id']) for net in server_nets\
if nic_id == net['id']]
num_of_disconnections = 0
for (nic_id, network_id) in nets:
req = {'remove': {'attachment': unicode(nic_id)}}
r = self.networks_post(network_id, 'action', json_data=req)
r.release()
num_of_disconnections += 1
return num_of_disconnections
def disconnect_network_nics(self, netid):
"""
......
......@@ -39,6 +39,30 @@ import json
class CycladesClientApi(ComputeClient):
"""GRNet Cyclades REST API Client"""
def servers_get(self,
server_id='',
command='',
success=200,
changes_since=None,
**kwargs):
"""GET base_url/servers[/server_id][/command] request
:param server_id: integer (as int or str)
:param command: 'ips', 'stats', or ''
:param success: success code or list or tupple of accepted success
codes. if server response code is not in this list, a ClientError
raises
:param changes_since: (date)
:returns: request response
"""
path = path4url('servers', server_id, command)
self.set_param('changes-since', changes_since, changes_since)
return self.get(path, success=success, **kwargs)
def networks_get(self,
network_id='', command='', success=(200, 203), **kwargs):
"""GET base_url/networks[/network_id][/command] request
......
......@@ -159,6 +159,17 @@ class StorageClient(Client):
r = self.put(path, data=data, success=201)
r.release()
def create_object(self, obj):
"""
:param obj: (str) directory-object name
"""
self._assert_container()
path = path4url(self.account, self.container, obj)
self.set_header('Content-Type', 'application/directory')
self.set_header('Content-length', '0')
r = self.put(path, success=201)
r.release()
def create_directory(self, obj):
"""
:param obj: (str) directory-object name
......
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