Commit 362adf50 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Decide a cloud term, use it if no cloud.url

Refs: #3934
parent ce9ccb40
......@@ -195,15 +195,17 @@ def _setup_logging(silent=False, debug=False, verbose=False, include=False):
def _check_config_version(cnf):
guess = cnf.guess_version()
if guess < 3.0:
print('Configuration file "%s" is not updated to v3.0' % (
print('Config file format version >= 3.0 is required')
print('Configuration file "%s" format is not up to date' % (
print('Calculating changes while preserving information ...')
print('but kamaki can fix this:')
print('Calculating changes while preserving information')
lost_terms = cnf.rescue_old_file()
if lost_terms:
print 'The following information will not be preserved:'
print '...', '\n... '.join(lost_terms)
print('... DONE')
print('Kamaki is ready to transform config file to version 3.0')
if lost_terms:
print 'The following information will NOT be preserved:'
print '\t', '\n\t'.join(lost_terms)
print('Kamaki is ready to convert the config file to version 3.0')
stdout.write('Overwrite file %s ? [Y, y] ' % cnf.path)
from sys import stdin
reply = stdin.readline()
......@@ -214,10 +216,12 @@ def _check_config_version(cnf):
print('... ABORTING')
raise CLIError(
'Invalid format for config file %s' % cnf.path,
importance=3, details=['Please, update config file to v3.0'])
importance=3, details=[
'Please, update config file to v3.0',
'For automatic conversion, rerun and say Y'])
def _init_session(arguments):
def _init_session(arguments, is_non_API=False):
global _help
_help = arguments['help'].value
global _debug
......@@ -228,44 +232,50 @@ def _init_session(arguments):
_verbose = arguments['verbose'].value
_cnf = arguments['config']
raise CLIError(
'Your file is OK, but i am not ready to proceed',
importance=3, details=['DO NOT PANIC, EXIT THE BUILDING QUIETLY'])
global _colors
_colors = _cnf.get('global', 'colors')
_colors = _cnf.value.get_global('colors')
if not (stdout.isatty() and _colors == 'on'):
from kamaki.cli.utils import remove_colors
_silent = arguments['silent'].value
_setup_logging(_silent, _debug, _verbose, _include)
picked_cloud = arguments['cloud'].value
if picked_cloud:
global_url = _cnf.get('remotes', picked_cloud)
if not global_url:
raise CLIError(
'No remote cloud "%s" in kamaki configuration' % picked_cloud,
importance=3, details=[
'To check if this remote cloud alias is declared:',
' /config get remotes.%s' % picked_cloud,
'To set a remote authentication URI aliased as "%s"' % (
' /config set remotes.%s <URI>' % picked_cloud
global_url = _cnf.get('global', 'auth_url')
global_token = _cnf.get('global', 'token')
if _help or is_non_API:
return None
cloud = arguments['cloud'].value or 'default'
if not cloud in _cnf.value.keys('remote'):
raise CLIError(
'No cloud remote "%s" is configured' % cloud,
importance=3, details=[
'To configure a new cloud remote, find and set the',
'single authentication URL and token:',
' kamaki config set remote.%s.url <URL>' % cloud,
' kamaki config set remote.%s.token <t0k3n>' % cloud])
url = _cnf.get_remote(cloud, 'url')
if not url:
'WARNING: No remote.%s.url, use service urls instead' % cloud)
return cloud
token = _cnf.get_remote(cloud, 'token')
if not token:
raise CLIError(
'No authentication token provided for %s cloud' % cloud,
importance=3, details=[
'Get and set a token for %s cloud:' % cloud,
' kamaki config set remote.%s.token <t0k3n>' % cloud])
from kamaki.clients.astakos import AstakosClient as AuthCachedClient
return AuthCachedClient(global_url, global_token)
return AuthCachedClient(url, token)
except AssertionError as ae:
kloger.warning('WARNING: Failed to load auth_url %s [ %s ]' % (
global_url, ae))
'WARNING: Failed to load auth_url %s [ %s ]' % (url, ae))
return None
def _load_spec_module(spec, arguments, module):
#spec_name = arguments['config'].get('cli', spec)
if not spec:
return None
pkg = None
......@@ -304,7 +314,6 @@ def _groups_help(arguments):
def _load_all_commands(cmd_tree, arguments):
_cnf = arguments['config']
#specs = [spec for spec in _cnf.get_groups() if _cnf.get(spec, 'cli')]
for cmd_group, spec in _cnf.get_cli_specs():
spec_module = _load_spec_module(spec, arguments, '_commands')
......@@ -411,7 +420,7 @@ def set_command_params(parameters):
def run_one_cmd(exe_string, parser, auth_base):
global _history
_history = History(
parser.arguments['config'].get('history', 'file'))
_history.add(' '.join([exe_string] + argv[1:]))
from kamaki.cli import one_command, parser, _help)
......@@ -424,6 +433,16 @@ def run_shell(exe_string, parser, auth_base):, parser)
def is_non_API(parser):
nonAPIs = ('history', 'config')
for term in parser.unparsed:
if not term.startswith('-'):
if term in nonAPIs:
return True
return False
return False
def main():
exe = basename(argv[0])
......@@ -432,25 +451,25 @@ def main():
if parser.arguments['version'].value:
log_file = parser.arguments['config'].get('global', 'log_file')
log_file = parser.arguments['config'].get_global('log_file')
if log_file:
global filelog
filelog = logger.add_file_logger(__name__.split('.')[0])'* Initial Call *\n%s\n- - -' % ' '.join(argv))
auth_base = _init_session(parser.arguments)
remote_base = _init_session(parser.arguments, is_non_API(parser))
from kamaki.cli.utils import suggest_missing
if parser.unparsed:
run_one_cmd(exe, parser, auth_base)
run_one_cmd(exe, parser, remote_base)
elif _help:
run_shell(exe, parser, auth_base)
run_shell(exe, parser, remote_base)
except CLIError as err:
if _debug:
......@@ -167,14 +167,25 @@ class ConfigArgument(Argument):
return self.value.get(group, term)
def get_groups(self):
return self.value.keys('cli')
suffix = '_cli'
slen = len(suffix)
return [term[:-slen] for term in self.value.keys('global') if (
def get_cli_specs(self):
return self.value.items('cli')
suffix = '_cli'
slen = len(suffix)
return [(k[:-slen], v) for k, v in self.value.items('global') if (
def get_global(self, option):
return self.value.get_global(option)
def get_remote(self, remote, option):
return self.value.get_remote(remote, option)
_config_arg = ConfigArgument(
1, 'Path to configuration file',
('-c', '--config'))
1, 'Path to configuration file', ('-c', '--config'))
class CmdLineConfigArgument(Argument):
......@@ -302,7 +302,7 @@ class Shell(Cmd):
self.auth_base = auth_base
self._parser = parser
self._history = History(
parser.arguments['config'].get('history', 'file'))
if path:
cmd = self.cmd_tree.get_command(path)
intro = cmd.path.replace('_', ' ')
......@@ -34,13 +34,15 @@
from kamaki.cli.logger import get_logger
from kamaki.cli.utils import print_json, print_items
from kamaki.cli.argument import FlagArgument
from kamaki.cli.errors import CLIError
from kamaki.clients import Client
log = get_logger(__name__)
class _command_init(object):
def __init__(self, arguments={}, auth_base=None):
def __init__(self, arguments={}, auth_base_or_remote=None):
if hasattr(self, 'arguments'):
if isinstance(self, _optional_output_cmd):
......@@ -52,20 +54,25 @@ class _command_init(object):
self.config = self['config']
except KeyError:
self.auth_base = auth_base or getattr(self, 'auth_base', None)
if isinstance(auth_base_or_remote, Client):
self.auth_base = auth_base_or_remote
elif not getattr(self, 'auth_base', None):
self.remote = auth_base_or_remote
if not self.remote:
raise CLIError('CRITICAL: No cloud specified', 3)
def _set_log_params(self):
self.client.LOG_TOKEN, self.client.LOG_DATA = (
self['config'].get('global', 'log_token') == 'on',
self['config'].get('global', 'log_data') == 'on')
self['config'].get_global('log_token').lower() == 'on',
self['config'].get_global('log_data').lower() == 'on')
except Exception as e:
log.warning('Failed to read custom log settings:'
'%s\n defaults for token and data logging are off' % e)
def _update_max_threads(self):
max_threads = int(self['config'].get('global', 'max_threads'))
max_threads = int(self['config'].get_global('max_threads'))
assert max_threads > 0
self.client.MAX_THREADS = max_threads
except Exception as e:
......@@ -79,7 +79,7 @@ DEFAULTS = {
# 'livetest_cli': 'livetest',
# 'astakos_cli': 'snf-astakos'
'default': {
'url': '',
......@@ -220,8 +220,8 @@ class Config(RawConfigParser):
return 2.0
log.warning('........ nope')
log.warning('Config file heuristic 2: at least 1 remote section ?')
if 'remotes' in sections:
for r in self.keys('remotes'):
if 'remote' in sections:
for r in self.keys('remote'):
log.warning('... found remote "%s"' % r)
return 3.0
log.warning('........ nope')
......@@ -238,18 +238,24 @@ class Config(RawConfigParser):
:raises KeyError: if remote or remote's option does not exist
r = self.get('remotes', remote)
r = self.get('remote', remote)
if not r:
raise KeyError('Remote "%s" does not exist' % remote)
return r[option]
def get_global(self, option):
return self.get('global', option)
def set_remote(self, remote, option, value):
d = self.get('remotes', remote)
d = self.get('remote', remote)
except KeyError:
d[option] = value
self.set('remotes', remote, d)
self.set('remote', remote, d)
def set_global(self, option, value):
self.set('global', option, value)
def _load_defaults(self):
for section, options in DEFAULTS.items():
......@@ -305,10 +311,10 @@ class Config(RawConfigParser):
self._overrides[section][option] = value
def write(self):
for r, d in self.items('remotes'):
for r, d in self.items('remote'):
for k, v in d.items():
self.set('remote "%s"' % r, k, v)
with open(self.path, 'w') as f:
os.chmod(self.path, 0600)
......@@ -55,20 +55,38 @@ class CLIError(Exception):
self.importance = 0
class CLIUnimplemented(CLIError):
def __init__(
message='I \'M SORRY, DAVE.\nI \'M AFRAID I CAN\'T DO THAT.',
' _ |',
' _-- --_ |',
' -- -- |',
' -- . -- |',
' -_ _- |',
' -_ _- |',
' - |'],
super(CLIUnimplemented, self).__init__(message, details, importance)
class CLIBaseUrlError(CLIError):
def __init__(self, message='', details=[], importance=2, service=None):
message = message or 'No url for %s' % service.lower()
details = details or [
'Two options to resolve this:',
'A. (recommended) Let kamaki discover the endpoint URLs for all',
'services by setting a single Authentication URL:',
' /config set auth_url <AUTH_URL>',
'services by setting a single Authentication URL and token:',
' /config set remote.default.url <AUTH_URL>',
' /config set remote.default.token <t0k3n>',
'B. (advanced users) Explicitly set a valid %s endpoint URL' % (
'Note: auth_url option has a higher priority, so delete it to',
'Note: url option has a higher priority, so delete it to',
'make that work',
' /config delete auth_url',
' /config set %s.url <%s_URL>' % (service, service.upper())]
' /config delete remote.default.url',
' /config set remote.%s.url <%s_URL>' % (
service, service.upper())]
super(CLIBaseUrlError, self).__init__(message, details, importance)
......@@ -55,7 +55,7 @@ def _get_best_match_from_cmd_tree(cmd_tree, unparsed):
return None
def run(auth_base, parser, _help):
def run(remote_base, parser, _help):
group = get_command_group(list(parser.unparsed), parser.arguments)
if not group:
......@@ -68,7 +68,7 @@ def run(auth_base, parser, _help):
global _best_match
_best_match = []
group_spec = parser.arguments['config'].get('cli', group)
group_spec = parser.arguments['config'].get('global', '%s_cli' % group)
spec_module = _load_spec_module(group_spec, parser.arguments, '_commands')
if spec_module is None:
raise CLIUnknownCommand(
......@@ -96,7 +96,7 @@ def run(auth_base, parser, _help):
cls = cmd.get_class()
executable = cls(parser.arguments, auth_base)
executable = cls(parser.arguments, remote_base)
#parsed, unparsed = parse_known_args(parser, executable.arguments)
for term in _best_match:
......@@ -39,15 +39,10 @@ from json import dumps
from kamaki.cli.errors import raiseCLIError
suggest = dict(
suggest = dict(ansicolors=dict(
description='Add colors to console responses'),
description='Add progress bars to some commands'))
description='Add colors to console responses'))
from colors import magenta, red, yellow, bold
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