Commit e1d3a85d authored by Ilias Tsitsimpis's avatar Ilias Tsitsimpis
Browse files

Merge pull request #13 from saxtouri/feature-renamings

Rename packages, classes, methods and variables
so that they comply with PEP8 standards.
parents 8fdfa852 2afb8137
......@@ -24,4 +24,5 @@ Features:
- Resources can be reassigned to projects
- Update account API commands to reflect changes in synnefo 0.16
- Implement a get_endpoint_url method and use it
- Rename kamaki.clients.Client.base_url --> endpoint_url, keep BW compatibility [#9]
......@@ -4,7 +4,7 @@ Adding Commands
Kamaki commands are implemented as python classes, which wear a decorator
called *command*. The decorator lives in *kamaki.cli* and its purpose is to
update the *CommandTree* structure. The *CommandTree* class (
*kamaki.cli.commant_tree*) manages command namespaces for kamaki.
*kamaki.cli.cmdtree*) manages command namespaces for kamaki.
For demonstration purposes, the following set of kamaki commands will be
implemented in this document::
......@@ -96,7 +96,7 @@ application to load the list of commands from the *_commands* array.
The command decorator
---------------------
All commands are specified by subclasses of *kamaki.cli.commands._command_init*
All commands are specified by subclasses of *kamaki.cli.cmds.CommandInit*
These classes are called "command specifications".
The *command* decorator mines all the information needed to build namespaces
......@@ -229,7 +229,7 @@ or more usually and elegantly:
Accessing run-time arguments
----------------------------
To access run-time arguments, command classes extend the *_command_init*
To access run-time arguments, command classes extend the *CommandInit*
interface, which implements *__item__* accessors to handle run-time argument
values. In other words, one may get the runtime value of an argument by calling
*self[<argument>]*.
......@@ -237,10 +237,10 @@ values. In other words, one may get the runtime value of an argument by calling
.. code-block:: python
from kamaki.cli.argument import ValueArgument
from kamaki.cli.commands import _command_init
from kamaki.cli.commands import CommandInit
@command(_mygrp1_commands)
class mygrp1_list_details(_command_init):
class mygrp1_list_details(CommandInit):
"""List of details"""
arguments = dict(
......@@ -266,7 +266,7 @@ required at command specification level:
...
@command(_mygrp1_commands)
class mygrp1_list_details(_command_init):
class mygrp1_list_details(CommandInit):
"""List of details"""
arguments = dict(
......@@ -336,7 +336,7 @@ Letting kamaki know
Assume that the command specifications presented so far be stored in a file
named *grps.py*.
The developer should move the file *grps.py* to *kamaki/cli/commands*, the
The developer should move the file *grps.py* to *kamaki/cli/cmds*, the
default place for command specifications
These lines should be contained in the kamaki configuration file for a new
......@@ -370,8 +370,8 @@ Summary: create a command set
# File: grps.py
from kamaki.cli.commands import _command_init
from kamaki.cli.command_tree import CommandTree
from kamaki.cli.cmds import CommandInit
from kamaki.cli.cmdtree import CommandTree
from kamaki.cli.argument import ValueArgument, FlagArgument
...
......@@ -388,14 +388,14 @@ Summary: create a command set
@command(_mygrp1_commands)
class mygrp1_list(_command_init):
class mygrp1_list(CommandInit):
"""List mygrp1 objects.
There are two versions: short and detailed
"""
@command(_mygrp1_commands)
class mygrp1_list_all(_command_init):
class mygrp1_list_all(CommandInit):
"""show a list"""
def _run():
......@@ -406,7 +406,7 @@ Summary: create a command set
@command(_mygrp1_commands)
class mygrp1_list_details(_command_init):
class mygrp1_list_details(CommandInit):
"""show list of details"""
arguments = dict(
......@@ -426,7 +426,7 @@ Summary: create a command set
@command(_mygrp2_commands)
class mygrp2_list_all(_command_init):
class mygrp2_list_all(CommandInit):
"""list all subjects"""
arguments = dict(
......@@ -445,7 +445,7 @@ Summary: create a command set
@command(_mygrp2_commands)
class mygrp2_info(_command_init):
class mygrp2_info(CommandInit):
"""get information for subject with id"""
def _run(self, grp_id, grp_name):
......
......@@ -23,7 +23,7 @@ There is a client for every API. An external applications should instantiate
the kamaki clients that fit their needs.
For example, to manage virtual servers and stored objects / files, an
application would probably need the CycladesClient and PithosClient
application would probably need the CycladesComputeClient and PithosClient
respectively.
.. code-block:: python
......@@ -32,16 +32,16 @@ respectively.
Example 1.1: Instantiate Cyclades and Pithos clients
from kamaki.clients.cyclades import CycladesClient
from kamaki.clients.cyclades import CycladesComputeClient
from kamaki.clients.pithos import PithosClient
cyclades = CycladesClient(computeURL, token)
cyclades = CycladesComputeClient(computeURL, token)
pithos = PithosClient(object-storeURL, token, account, container)
.. note:: *cyclades* and *pithos* clients inherit ComputeClient from *compute*
and StorageClient from *storage*, respectively. Separate ComputeClient or
StorageClient objects should be used only when implementing applications for
strict OpenStack Compute or Storage services.
StorageClient objects should be used only when implementing applications
for strict OpenStack Compute or Storage services.
Using endpoints to get the authentication url
---------------------------------------------
......@@ -59,7 +59,7 @@ stored in ``service_type`` attribute in the client class.
The values of ``service_type`` for each client are shown bellow::
storage.StorageClient, pithos.PithosClient --> object-store
compute.ComputeClient, cyclades.CycladesClient --> compute
compute.ComputeClient, cyclades.CycladesComputeClient --> compute
network.NetworkClient, cyclades.CycladesNetworkClient --> network
image.ImageClient --> image
astakos.AstakosClient --> identity
......@@ -87,7 +87,7 @@ will be used to initialize a *cyclades* and a *pithos* client respectively.
Example 1.3: Retrieve cyclades and pithos URLs
cyclades_URL = astakos.get_endpoint_url(CycladesClient.service_type)
cyclades_URL = astakos.get_endpoint_url(CycladesComputeClient.service_type)
pithos_URL = astakos.get_endpoint_url(PithosClent.service_type)
It's time to initialize both clients.
......@@ -97,10 +97,10 @@ It's time to initialize both clients.
Example 1.3.1 Initialize cyclades and pithos clients
from kamaki.clients.cyclades import CycladesClient
from kamaki.clients.cyclades import CycladesComputeClient
from kamaki.clients.pithos import PithosClient
cyclades = CycladesClient(cyclades_URL, TOKEN)
cyclades = CycladesComputeClient(cyclades_URL, TOKEN)
pithos = PithosClient(pithos_URL, TOKEN)
# Also, setup the account UUID and container for pithos client
......@@ -172,7 +172,7 @@ The following example concatenates examples 1.1 to 1.4 plus error handling
from kamaki.clients import ClientError
from kamaki.clients.astakos import AstakosClient
from kamaki.clients.cyclades import CycladesClient
from kamaki.clients.cyclades import CycladesComputeClient
from kamaki.clients.pithos import PithosClient
try:
......@@ -182,12 +182,12 @@ The following example concatenates examples 1.1 to 1.4 plus error handling
raise
try:
CYCLADES_URL = astakos.get_endpoint_url(CycladesClient.service_type)
CYCLADES_URL = astakos.get_endpoint_url(CycladesComputeClient.service_type)
except ClientError:
print('Failed to get endpoints for cyclades')
try:
cyclades = CycladesClient(CYCLADES_URL, TOKEN)
cyclades = CycladesComputeClient(CYCLADES_URL, TOKEN)
except ClientError:
print('Failed to initialize Cyclades client')
......@@ -231,15 +231,15 @@ Batch-create servers
#! /usr/bin/python
from kamaki.clients.astakos import AstakosClient
from kamaki.clients.cyclades import CycladesClient
from kamaki.clients.cyclades import CycladesComputeClient
AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
TOKEN = 'replace this with your token'
astakos = AstakosClient(AUTHENTICATION_URL, TOKEN)
CYCLADES_URL = astakos.get_endpoint_url(CycladesClient.service_type)
cyclades = CycladesClient(CYCLADES_URL, TOKEN)
CYCLADES_URL = astakos.get_endpoint_url(CycladesComputeClient.service_type)
cyclades = CycladesComputeClient(CYCLADES_URL, TOKEN)
# (name, flavor-id, image-id)
servers = [
......@@ -310,7 +310,8 @@ Two servers and a private network
#! /user/bin/python
from kamaki.clients.astakos import AstakosClient
from kamaki.clients.cyclades import CycladesClient, CycladesNetworkClient
from kamaki.clients.cyclades import (
CycladesComputeClient, CycladesNetworkClient)
AUTHENTICATION_URL = 'https://accounts.example.com/identity/v2.0'
TOKEN = 'replace this with your token'
......@@ -322,8 +323,8 @@ Two servers and a private network
net = network.create_network(type='MAC_FILTERED', name='My private network')
CYCLADES_URL = astakos.get_endpoint_url(CycladesClient.service_type)
cyclades = CycladesClient(CYCLADES_URL, TOKEN)
CYCLADES_URL = astakos.get_endpoint_url(CycladesComputeClient.service_type)
cyclades = CycladesComputeClient(CYCLADES_URL, TOKEN)
FLAVOR_ID = 'put your flavor id here'
IMAGE_ID = 'put your image id here'
......
......@@ -10,7 +10,7 @@ astakos
^^^^^^^
Features: user, project, quota, resource, commission, endpoint, service
.. automodule:: kamaki.cli.commands.astakos
.. automodule:: kamaki.cli.cmds.astakos
:members:
:undoc-members:
......@@ -19,7 +19,7 @@ cyclades
Features server, flavor
.. automodule:: kamaki.cli.commands.cyclades
.. automodule:: kamaki.cli.cmds.cyclades
:members:
:undoc-members:
......@@ -28,7 +28,7 @@ pithos
Features file, container, sharer, group
.. automodule:: kamaki.cli.commands.pithos
.. automodule:: kamaki.cli.cmds.pithos
:members:
:undoc-members:
......@@ -37,7 +37,7 @@ image
Features (image, imagecompute)
.. automodule:: kamaki.cli.commands.image
.. automodule:: kamaki.cli.cmds.image
:members:
:undoc-members:
......@@ -47,7 +47,7 @@ network
Features network, port, subnet, ip
.. automodule:: kamaki.cli.commands.network
.. automodule:: kamaki.cli.cmds.network
:members:
:undoc-members:
......@@ -57,7 +57,7 @@ Kamaki commands
config
""""""
.. automodule:: kamaki.cli.commands.config
.. automodule:: kamaki.cli.cmds.config
:members:
:undoc-members:
......@@ -65,7 +65,7 @@ config
errors
^^^^^^
.. automodule:: kamaki.cli.commands.errors
.. automodule:: kamaki.cli.cmds.errors
:members:
:show-inheritance:
:undoc-members:
......
......@@ -86,7 +86,7 @@ This is the plan:
try:
endpoints = dict(
astakos=AUTH_URL,
cyclades=auth.get_endpoint_url(CycladesClient.service_type),
cyclades=auth.get_endpoint_url(CycladesComputeClient.service_type),
network=auth.get_endpoint_url(CycladesNetworkClient.service_type),
pithos=auth.get_endpoint_url(PithosClient.service_type),
plankton=auth.get_endpoint_url(ImageClient.service_type)
......@@ -209,7 +209,7 @@ Here is the plan:
.. code-block:: python
# 4. Create virtual cluster
from kamaki.clients.cyclades import CycladesClient
from kamaki.clients.cyclades import CycladesComputeClient
FLAVOR_ID = 42
IMAGE_ID = image['id']
......@@ -218,7 +218,7 @@ Here is the plan:
# 4.1 Initialize a cyclades client
try:
cyclades = CycladesClient(endpoints['cyclades'], AUTH_TOKEN)
cyclades = CycladesComputeClient(endpoints['cyclades'], AUTH_TOKEN)
except ClientError:
stderr.write('Failed to initialize cyclades client\n')
raise
......@@ -640,7 +640,7 @@ logging more. We also added some command line interaction candy.
# Astakos implements identity and account APIs - The endpoint
# URL is the same for both services
astakos=auth.get_endpoint_url('identity'),
cyclades=auth.get_endpoint_url(CycladesClient.service_type),
cyclades=auth.get_endpoint_url(CycladesComputeClient.service_type),
network=auth.get_endpoint_url(CycladesNetworkClient.service_type),
pithos=auth.get_endpoint_url(PithosClient.service_type),
plankton=auth.get_endpoint_url(ImageClient.service_type)
......@@ -743,11 +743,11 @@ logging more. We also added some command line interaction candy.
# Compute / Cyclades
def init_cyclades(endpoint, token):
from kamaki.clients.cyclades import CycladesClient
from kamaki.clients.cyclades import CycladesComputeClient
print(' Initialize a cyclades client')
try:
return CycladesClient(endpoint, token)
return CycladesComputeClient(endpoint, token)
except ClientError:
log.debug('Failed to initialize cyclades client')
raise
......
......@@ -36,20 +36,20 @@ from sys import argv, exit, stdout, stderr
from os.path import basename, exists
from inspect import getargspec
from kamaki.cli.argument import ArgumentParseManager
from kamaki.cli.argument import (
ArgumentParseManager, ConfigArgument, ValueArgument, FlagArgument,
RuntimeConfigArgument, VersionArgument, Argument)
from kamaki.cli.history import History
from kamaki.cli.utils import print_dict, red, magenta, yellow, pref_enc
from kamaki.cli.utils import (
print_dict, magenta, red, yellow, suggest_missing, remove_colors, pref_enc)
from kamaki.cli.errors import CLIError, CLICmdSpecError
from kamaki.cli import logger
from kamaki.clients.astakos import CachedAstakosClient
from kamaki.clients import ClientError
_help = False
_debug = False
_verbose = False
_colors = False
kloger = None
filelog = None
# command auxiliary methods
......@@ -64,42 +64,25 @@ def _arg2syntax(arg):
'_', ' ')
def _construct_command_syntax(cls):
spec = getargspec(cls.main.im_func)
args = spec.args[1:]
n = len(args) - len(spec.defaults or ())
required = ' '.join(['<%s>' % _arg2syntax(x) for x in args[:n]])
optional = ' '.join(['[%s]' % _arg2syntax(x) for x in args[n:]])
cls.syntax = ' '.join([required, optional])
if spec.varargs:
cls.syntax += ' <%s ...>' % spec.varargs
def _num_of_matching_terms(basic_list, attack_list):
if not attack_list:
return len(basic_list)
matching_terms = 0
for i, term in enumerate(basic_list):
try:
if term != attack_list[i]:
break
except IndexError:
break
matching_terms += 1
return matching_terms
def _update_best_match(name_terms, prefix=[]):
global _best_match
if prefix:
pref_list = prefix if isinstance(prefix, list) else prefix.split('_')
else:
pref_list = []
_best_match, pref_list = [], []
num_of_matching_terms = _num_of_matching_terms(name_terms, pref_list)
global _best_match
if not prefix:
_best_match = []
if pref_list:
num_of_matching_terms = 0
for i, term in enumerate(name_terms):
try:
if term == pref_list[i]:
num_of_matching_terms += 1
else:
break
except IndexError:
break
else:
num_of_matching_terms = len(name_terms)
if num_of_matching_terms and len(_best_match) <= num_of_matching_terms:
if len(_best_match) < num_of_matching_terms:
......@@ -113,7 +96,7 @@ def command(cmd_tree, prefix='', descedants_depth=1):
e.g., spec_cmd0_cmd1 will be command spec cmd0
:param cmd_tree: is initialized in cmd_spec file and is the structure
where commands are loaded. Var name should be _commands
where commands are loaded. Var name should be "namespaces"
:param prefix: if given, load only commands prefixed with prefix,
:param descedants_depth: is the depth of the tree descedants of the
prefix command. It is used ONLY if prefix and if prefix is not
......@@ -133,8 +116,6 @@ def command(cmd_tree, prefix='', descedants_depth=1):
name_terms = cls_name.split('_')
if not _update_best_match(name_terms, prefix):
# if _debug:
# kloger.warning('%s failed to update_best_match' % cls_name)
return None
global _best_match
......@@ -154,7 +135,15 @@ def command(cmd_tree, prefix='', descedants_depth=1):
except AttributeError:
raise CLICmdSpecError(
'No commend in %s (acts as cmd description)' % cls.__name__)
_construct_command_syntax(cls)
# Build command syntax help
spec = getargspec(cls.main.im_func)
args = spec.args[1:]
n = len(args) - len(spec.defaults or ())
required = ' '.join(['<%s>' % _arg2syntax(x) for x in args[:n]])
optional = ' '.join(['[%s]' % _arg2syntax(x) for x in args[n:]])
cls.syntax = ' '.join([required, optional])
if spec.varargs:
cls.syntax += ' <%s ...>' % spec.varargs
cmd_tree.add_command(
cls_name, cls.description, cls, cls.long_description)
......@@ -163,6 +152,8 @@ def command(cmd_tree, prefix='', descedants_depth=1):
cmd_spec_locations = [
'kamaki.cli.cmds',
'kamaki.cmds',
'kamaki.cli.commands',
'kamaki.commands',
'kamaki.cli',
......@@ -173,13 +164,9 @@ cmd_spec_locations = [
# Generic init auxiliary functions
def _setup_logging(silent=False, debug=False, verbose=False):
def _setup_logging(debug=False, verbose=False):
"""handle logging for clients package"""
if silent:
logger.add_stream_logger(__name__, logging.CRITICAL)
return
sfmt, rfmt = '> %(message)s', '< %(message)s'
if debug:
print('Logging location: %s' % logger.get_log_filename())
......@@ -190,7 +177,8 @@ def _setup_logging(silent=False, debug=False, verbose=False):
logger.add_stream_logger('kamaki.clients.send', logging.INFO, sfmt)
logger.add_stream_logger('kamaki.clients.recv', logging.INFO, rfmt)
logger.add_stream_logger(__name__, logging.INFO)
logger.add_stream_logger(__name__, logging.WARNING)
else:
logger.add_stream_logger(__name__, logging.WARNING)
global kloger
kloger = logger.get_logger(__name__)
......@@ -228,26 +216,21 @@ def _init_session(arguments, is_non_API=False):
"""
:returns: cloud name
"""
global _help
_help = arguments['help'].value
global _debug
_debug = arguments['debug'].value
global _verbose
_verbose = arguments['verbose'].value
_cnf = arguments['config']
_silent = arguments['silent'].value
_setup_logging(_silent, _debug, _verbose)
_setup_logging(_debug, _verbose)
if _help or is_non_API:
return None
_check_config_version(_cnf.value)
global _colors
_colors = _cnf.value.get('global', 'colors')
if not (stdout.isatty() and _colors == 'on'):
from kamaki.cli.utils import remove_colors
remove_colors()
cloud = arguments['cloud'].value or _cnf.value.get(
......@@ -302,19 +285,19 @@ def init_cached_authenticator(config_argument, cloud, logger):
_cnf = config_argument.value
url = _cnf.get_cloud(cloud, 'url')
tokens = _cnf.get_cloud(cloud, 'token').split()
auth_base, failed = None, []
astakos, failed = None, []
for token in tokens:
try:
if auth_base:
auth_base.authenticate(token)
if astakos:
astakos.authenticate(token)
else:
tmp_base = CachedAstakosClient(url, token)
from kamaki.cli.commands import _command_init
fake_cmd = _command_init(dict(config=config_argument))
fake_cmd.client = auth_base
from kamaki.cli.cmds import CommandInit
fake_cmd = CommandInit(dict(config=config_argument))
fake_cmd.client = astakos
fake_cmd._set_log_params()
tmp_base.authenticate(token)
auth_base = tmp_base
astakos = tmp_base
except ClientError as ce:
if ce.status in (401, ):
logger.warning(
......@@ -331,7 +314,7 @@ def init_cached_authenticator(config_argument, cloud, logger):
_cnf.set_cloud(cloud, 'token', ' '.join(tokens))
_cnf.write()
if tokens:
return auth_base
return astakos
logger.warning('WARNING: cloud.%s.token is now empty' % cloud)
except AssertionError as ae:
logger.warning('WARNING: Failed to load authenticator [%s]' % ae)
......@@ -367,11 +350,11 @@ def _groups_help(arguments):
descriptions = {}
acceptable_groups = arguments['config'].groups
for cmd_group, spec in arguments['config'].cli_specs:
pkg = _load_spec_module(spec, arguments, '_commands')
pkg = _load_spec_module(spec, arguments, 'namespaces')
if pkg:
cmds = getattr(pkg, '_commands')
namespaces = getattr(pkg, 'namespaces')
try:
for cmd_tree in cmds:
for cmd_tree in namespaces:
if cmd_tree.name in acceptable_groups:
descriptions[cmd_tree.name] = cmd_tree.description
except TypeError:
......@@ -388,14 +371,14 @@ def _load_all_commands(cmd_tree, arguments):
_cnf = arguments['config']
for cmd_group, spec in _cnf.cli_specs:
try:
spec_module = _load_spec_module(spec, arguments, '_commands')
spec_commands = getattr(spec_module, '_commands')
spec_module = _load_spec_module(spec, arguments, 'namespaces')
namespaces = getattr(spec_module, 'namespaces')
except AttributeError:
if _debug:
global kloger
kloger.warning('No valid description for %s' % cmd_group)
continue
for spec_tree in spec_commands:
for spec_tree in namespaces:
if spec_tree.name == cmd_group:
cmd_tree.add_tree(spec_tree)
break
......@@ -453,8 +436,6 @@ def exec_cmd(instance, cmd_args, help_method):
print(magenta('Syntax error'))
if _debug:
raise err
if _verbose:
print(unicode(err))
help_method()
else:
raise
......@@ -504,8 +485,22 @@ def main(func):
for arg in reversed(internal_argv):
argv.insert(0, arg)
argv.pop()
parser = ArgumentParseManager(exe)
_config_arg = ConfigArgument('Path to config file')
parser = ArgumentParseManager(exe, arguments=dict(
config=_config_arg,
cloud=ValueArgument(
'Chose a cloud to connect to', ('--cloud')),
help=Argument(0, 'Show help message', ('-h', '--help')),
debug=FlagArgument('Include debug output', ('-d', '--debug')),
verbose=FlagArgument(
'More info at response', ('-v', '--verbose')),
version=VersionArgument(
'Print current version', ('-V', '--version')),
options=RuntimeConfigArgument(
_config_arg,
'Override a config value', ('-o', '--options')))
)
if parser.arguments['version'].value:
exit(0)
......@@ -513,13 +508,11 @@ def main(func):
log_file = _cnf.get('global', 'log_file')
if log_file:
logger.set_log_filename(log_file)
global filelog
filelog = logger.add_file_logger(__name__.split('.')[0])
filelog.info('%s\n- - -' % ' '.join(argv))
from kamaki.cli.utils import suggest_missing