Commit 144b3551 authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Change term "remote" to "cloud" when proper

Refs: #3934
parent 1b46f2b0
......@@ -223,7 +223,7 @@ def _check_config_version(cnf):
def _init_session(arguments, is_non_API=False):
"""
:returns: (AuthCachedClient, str) authenticator and cloud remote name
:returns: (AuthCachedClient, str) authenticator and cloud name
"""
global _help
_help = arguments['help'].value
......@@ -248,23 +248,23 @@ def _init_session(arguments, is_non_API=False):
return None, None
cloud = arguments['cloud'].value or 'default'
if not cloud in _cnf.value.keys('remote'):
if not cloud in _cnf.value.keys('cloud'):
raise CLIError(
'No cloud remote "%s" is configured' % cloud,
'No cloud "%s" is configured' % cloud,
importance=3, details=[
'To configure a new cloud remote, find and set the',
'To configure a new cloud, 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])
' kamaki config set cloud.%s.url <URL>' % cloud,
' kamaki config set cloud.%s.token <t0k3n>' % cloud])
auth_args = dict()
for term in ('url', 'token'):
auth_args[term] = _cnf.get_remote(cloud, term)
auth_args[term] = _cnf.get_cloud(cloud, term)
if not auth_args[term]:
raise CLIError(
'No authentication %s provided for %s cloud' % (term, cloud),
importance=3, details=[
'Get and set a %s for %s cloud:' % (term, cloud),
' kamaki config set remote.%s.%s <t0k3n>' % (term, cloud)
' kamaki config set cloud.%s.%s <t0k3n>' % (term, cloud)
])
from kamaki.clients.astakos import AstakosClient as AuthCachedClient
......
......@@ -181,8 +181,8 @@ class ConfigArgument(Argument):
def get_global(self, option):
return self.value.get_global(option)
def get_remote(self, remote, option):
return self.value.get_remote(remote, option)
def get_cloud(self, cloud, option):
return self.value.get_cloud(cloud, option)
_config_arg = ConfigArgument(
1, 'Path to configuration file', ('-c', '--config'))
......@@ -409,7 +409,7 @@ class ProgressBarArgument(FlagArgument):
_arguments = dict(
config=_config_arg,
cloud=ValueArgument('Chose a remote cloud to connect to', ('--cloud')),
cloud=ValueArgument('Chose a cloud to connect to', ('--cloud')),
help=Argument(0, 'Show help message', ('-h', '--help')),
debug=FlagArgument('Include debug output', ('-d', '--debug')),
include=FlagArgument(
......
......@@ -76,19 +76,19 @@ class _command_init(object):
@DontRaiseKeyError
def _custom_url(self, service):
return self.config.get_remote(self.cloud, '%s_url' % service)
return self.config.get_cloud(self.cloud, '%s_url' % service)
@DontRaiseKeyError
def _custom_token(self, service):
return self.config.get_remote(self.cloud, '%s_token' % service)
return self.config.get_cloud(self.cloud, '%s_token' % service)
@DontRaiseKeyError
def _custom_type(self, service):
return self.config.get_remote(self.cloud, '%s_type' % service)
return self.config.get_cloud(self.cloud, '%s_type' % service)
@DontRaiseKeyError
def _custom_version(self, service):
return self.config.get_remote(self.cloud, '%s_version' % service)
return self.config.get_cloud(self.cloud, '%s_version' % service)
def _set_log_params(self):
try:
......
......@@ -53,7 +53,7 @@ class _user_init(_command_init):
base_url = self._custom_url('astakos')
if base_url:
token = self._custom_token('astakos')\
or self.config.get_remote(self.cloud, 'token')
or self.config.get_cloud(self.cloud, 'token')
self.client = AstakosClient(base_url=base_url, token=token)
return
else:
......@@ -72,10 +72,10 @@ 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:
* check if a token is set /config get remote.default.token
* permanently set a token /config set remote.default.token <token>
* check if a token is set /config get cloud.default.token
* permanently set a token /config set cloud.default.token <token>
Token can also be provided as a parameter
(In case of another named cloud remote, use its name instead of default)
(In case of another named cloud, use its name instead of default)
"""
@staticmethod
......
......@@ -50,12 +50,12 @@ about_options = '\nAbout options:\
\n. [group]\
\n. option=value\
\n. (more options can be set per group)\
\n. special case: named cloud remotes.\
\n. special case: named clouds.\
\n. E.g. for a cloud "demo":\
\n. [remote "demo"]\
\n. [cloud "demo"]\
\n. url = <http://single/authentication/url/for/demo/site>\
\n. token = <auth_token_from_demo_site>\
\n. which are referenced as remote.demo.url , remote.demo.token'
\n. which are referenced as cloud.demo.url , cloud.demo.token'
@command(config_cmds)
......@@ -73,7 +73,7 @@ class config_list(_command_init):
for section in sorted(self.config.sections()):
items = self.config.items(section)
for key, val in sorted(items):
if section in ('remote',):
if section in ('cloud',):
prefix = '%s.%s' % (section, key)
for k, v in val.items():
print('%s..%s = %s' % (prefix, k, v))
......@@ -97,15 +97,15 @@ class config_get(_command_init):
match = False
for k in self.config.keys(key):
match = True
if option != 'remote':
if option != 'cloud':
stdout.write('%s.%s =' % (option, k))
self._run('%s.%s' % (option, k))
if match:
return
section = 'global'
prefix = 'remote.'
prefix = 'cloud.'
get, section = (
self.config.get_remote, section[len(prefix):]) if (
self.config.get_cloud, section[len(prefix):]) if (
section.startswith(prefix)) else (self.config.get, section)
value = get(section, key)
if isinstance(value, dict):
......@@ -127,16 +127,16 @@ class config_set(_command_init):
@errors.generic.all
def _run(self, option, value):
section, sep, key = option.rpartition('.')
prefix = 'remote.'
prefix = 'cloud.'
if section.startswith(prefix):
self.config.set_remote(section[len(prefix):], key, value)
elif section in ('remote',):
self.config.set_cloud(section[len(prefix):], key, value)
elif section in ('cloud',):
raise CLISyntaxError(
'Invalid syntax for cloud definition', importance=2, details=[
'To define a cloud remote "%s"' % key,
'To define a cloud "%s"' % key,
'set the cloud\'s authentication url and token:',
' /config set remote.%s.url <URL>' % key,
' /config set remote.%s.token <t0k3n>' % key])
' /config set cloud.%s.url <URL>' % key,
' /config set cloud.%s.token <t0k3n>' % key])
else:
section = section or 'global'
self.config.set(section, key, value)
......@@ -164,11 +164,11 @@ class config_delete(_command_init):
def _run(self, option):
section, sep, key = option.rpartition('.')
section = section or 'global'
prefix = 'remote.'
prefix = 'cloud.'
if section.startswith(prefix):
remote = section[len(prefix):]
cloud = section[len(prefix):]
try:
self.config.remove_from_remote(remote, key)
self.config.remove_from_cloud(cloud, key)
except KeyError:
raise CLIError('Field %s does not exist' % option)
else:
......
......@@ -53,7 +53,7 @@ _commands = [server_cmds, flavor_cmds, network_cmds]
about_authentication = '\nUser Authentication:\
\n* to check authentication: /user authenticate\
\n* to set authentication token: /config set remote.default.token <token>'
\n* to set authentication token: /config set cloud.default.token <token>'
howto_personality = [
'Defines a file to be injected to VMs personality.',
......@@ -75,7 +75,7 @@ class _init_cyclades(_command_init):
if base_url:
token = self._custom_token(service)\
or self._custom_token('cyclades')\
or self.config.get_remote('token')
or self.config.get_cloud('token')
self.client = CycladesClient(
base_url=base_url, token=token)
return
......
......@@ -39,8 +39,7 @@ from kamaki.cli import _debug, kloger
from kamaki.cli.utils import format_size
CLOUDNAME = [
'Note: If you use a named cloud remote, use its name',
'instead of "default"']
'Note: If you use a named cloud, use its name instead of "default"']
class generic(object):
......@@ -71,9 +70,9 @@ class generic(object):
'Make sure a valid token is provided:',
' to check if token is valid: /user authenticate',
' to set token:',
' /config set remote.default.token <token>',
' /config set cloud.default.token <token>',
' to get current token:',
' /config get remote.default.token'] + CLOUDNAME)
' /config get cloud.default.token'] + CLOUDNAME)
elif ce.status in range(-12, 200) + [302, 401, 403, 500]:
raiseCLIError(ce, importance=3, details=[
'Check if service is up'])
......@@ -86,9 +85,9 @@ class generic(object):
raiseCLIError(ce, msg, details=[
'Check if authentication url is correct',
' check current url:',
' /config get remote.default.url',
' /config get cloud.default.url',
' set new auth. url:',
' /config set remote.default.url'] + CLOUDNAME)
' /config set cloud.default.url'] + CLOUDNAME)
raise
return _raise
......@@ -96,9 +95,9 @@ class generic(object):
class user(object):
_token_details = [
'To check default token: /config get remote.default.token',
'To check default token: /config get cloud.default.token',
'If set/update a token:',
'* (permanent): /config set remote.default.token <token>',
'* (permanent): /config set cloud.default.token <token>',
'* (temporary): re-run with <token> parameter'] + CLOUDNAME
@classmethod
......@@ -112,15 +111,15 @@ class user(object):
if not getattr(client, 'token', False):
kloger.warning(
'No permanent token (try:'
' kamaki config set remote.default.token <tkn>)')
' kamaki config set cloud.default.token <tkn>)')
if not getattr(client, 'base_url', False):
msg = 'Missing synnefo authentication URL'
raise CLIError(msg, importance=3, details=[
'Check if authentication url is correct',
' check current url:',
' /config get remote.default.url',
' /config get cloud.default.url',
' set new auth. url:',
' /config set remote.default.url'] + CLOUDNAME)
' /config set cloud.default.url'] + CLOUDNAME)
return r
return _raise
......
......@@ -80,7 +80,7 @@ class _init_image(_command_init):
if img_url:
token = self._custom_token('image')\
or self._custom_token('plankton')\
or self.config.get_remote(self.cloud, 'token')
or self.config.get_cloud(self.cloud, 'token')
self.client = ImageClient(base_url=img_url, token=token)
return
if getattr(self, 'auth_base', False):
......
......@@ -153,11 +153,11 @@ class _pithos_init(_command_init):
@DontRaiseKeyError
def _custom_container(self):
return self.config.get_remote(self.cloud, 'pithos_container')
return self.config.get_cloud(self.cloud, 'pithos_container')
@DontRaiseKeyError
def _custom_uuid(self):
return self.config.get_remote(self.cloud, 'pithos_uuid')
return self.config.get_cloud(self.cloud, 'pithos_uuid')
def _set_account(self):
self.account = self._custom_uuid()
......
......@@ -61,7 +61,7 @@ class _astakos_init(_command_init):
def _run(self):
self.cloud = self.cloud if self.cloud else 'default'
self.token = self['token'] or self._custom_token('astakos')\
or self.config.get_remote(self.cloud, 'token')
or self.config.get_cloud(self.cloud, 'token')
if getattr(self, 'auth_base', False):
astakos_endpoints = self.auth_base.get_service_endpoints(
self._custom_type('astakos') or 'identity',
......@@ -82,10 +82,10 @@ class astakos_authenticate(_astakos_init, _optional_json):
"""Authenticate a user
Get user information (e.g. unique account name) from token
Token should be set in settings:
* check if a token is set /config get remote.default.token
* permanently set a token /config set remote.default.token <token>
* check if a token is set /config get cloud.default.token
* permanently set a token /config set cloud.default.token <token>
Token can also be provided as a parameter
(To use a named cloud remote, use its name instead of "default")
(To use a named cloud, use its name instead of "default")
"""
arguments = dict(
......
......@@ -39,6 +39,7 @@ from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
from re import match
from kamaki.cli.errors import CLISyntaxError
from kamaki import __version__
try:
from collections import OrderedDict
......@@ -55,12 +56,16 @@ HISTORY_PATH = os.path.expanduser('~/.kamaki.history')
# Name of a shell variable to bypass the CONFIG_PATH value
CONFIG_ENV = 'KAMAKI_CONFIG'
HEADER = """
# Kamaki configuration file v3 (kamaki >= v0.9)
"""
version = ''
for c in '%s' % __version__:
if c not in '0.123456789':
break
version += c
HEADER = '# Kamaki configuration file v%s' % version
DEFAULTS = {
'global': {
'default_cloud': '',
'colors': 'off',
'log_file': os.path.expanduser('~/.kamaki.log'),
'log_token': 'off',
......@@ -79,20 +84,20 @@ DEFAULTS = {
# 'livetest_cli': 'livetest',
# 'astakos_cli': 'snf-astakos'
},
'remote':
'cloud':
{
'default': {
'url': '',
'token': ''
#'pithos_type': 'object-store',
#'pithos_version': 'v1',
#'cyclades_type': 'compute',
#'cyclades_version': 'v2.0',
#'plankton_type': 'image',
#'plankton_version': '',
#'astakos_type': 'identity',
#'astakos_version': 'v2.0'
}
#'default': {
# 'url': '',
# 'token': ''
# 'pithos_type': 'object-store',
# 'pithos_version': 'v1',
# 'cyclades_type': 'compute',
# 'cyclades_version': 'v2.0',
# 'plankton_type': 'image',
# 'plankton_version': '',
# 'astakos_type': 'identity',
# 'astakos_version': 'v2.0'
#}
}
}
......@@ -107,15 +112,15 @@ class Config(RawConfigParser):
self.read(self.path)
for section in self.sections():
r = self._remote_name(section)
r = self._cloud_name(section)
if r:
for k, v in self.items(section):
self.set_remote(r, k, v)
self.set_cloud(r, k, v)
self.remove_section(section)
@staticmethod
def _remote_name(full_section_name):
matcher = match('remote "(\w+)"', full_section_name)
def _cloud_name(full_section_name):
matcher = match('cloud "(\w+)"', full_section_name)
return matcher.groups()[0] if matcher else None
def rescue_old_file(self):
......@@ -139,10 +144,11 @@ class Config(RawConfigParser):
user=dict(serv='astakos', cmd='user'),
)
self.set('global', 'default_cloud', 'default')
for s in self.sections():
if s in ('global'):
# global.url, global.token -->
# remote.default.url, remote.default.token
# cloud.default.url, cloud.default.token
for term in set(self.keys(s)).difference(global_terms):
if term not in ('url', 'token'):
lost_terms.append('%s.%s = %s' % (
......@@ -150,23 +156,23 @@ class Config(RawConfigParser):
self.remove_option(s, term)
continue
gval = self.get(s, term)
cval = self.get_remote('default', term)
cval = self.get_cloud('default', term)
if gval and cval and (
gval.lower().strip('/') != cval.lower().strip('/')):
raise CLISyntaxError(
'Conflicting values for default %s' % term,
importance=2, details=[
' global.%s: %s' % (term, gval),
' remote.default.%s: %s' % (term, cval),
' cloud.default.%s: %s' % (term, cval),
'Please remove one of them manually:',
' /config delete global.%s' % term,
' or'
' /config delete remote.default.%s' % term,
' /config delete cloud.default.%s' % term,
'and try again'])
elif gval:
print('... rescue %s.%s => remote.default.%s' % (
print('... rescue %s.%s => cloud.default.%s' % (
s, term, term))
self.set_remote('default', term, gval)
self.set_cloud('default', term, gval)
self.remove_option(s, term)
# translation for <service> or <command> settings
# <service> or <command group> settings --> translation --> global
......@@ -186,14 +192,12 @@ class Config(RawConfigParser):
if v and k in ('cli',):
print('... rescue %s.%s => global.%s_cli' % (
s, k, trn['cmd']))
self.set('global', 'file_cli', v)
elif (k in ('container', 'uuid')) and (
trn['serv'] in ('pithos',)):
print(
'... rescue %s.%s => remote.default.pithos_%s' % (
self.set('global', '%s_cli' % trn['cmd'], v)
elif k in ('container',) and trn['serv'] in ('pithos',):
print('... rescue %s.%s => cloud.default.pithos_%s' % (
s, k, k))
self.set_remote('default', 'pithos_%s' % k, v)
elif v:
self.set_cloud('default', 'pithos_%s' % k, v)
else:
lost_terms.append('%s.%s = %s' % (s, k, v))
self.remove_section(s)
# self.pretty_print()
......@@ -212,48 +216,51 @@ class Config(RawConfigParser):
print '\t', k, '=>', v
def guess_version(self):
"""
:returns: (float) version of the config file or 0.0 if unrecognized
"""
checker = Config(self.path, with_defaults=False)
sections = checker.sections()
log.warning('Config file heuristic 1: global section ?')
log.warning('Config file heuristic 1: old global section ?')
if 'global' in sections:
if checker.get('global', 'url') or checker.get('global', 'token'):
log.warning('..... config file has an old global section')
return 2.0
log.warning('........ nope')
log.warning('Config file heuristic 2: at least 1 remote section ?')
if 'remote' in sections:
for r in self.keys('remote'):
log.warning('... found remote "%s"' % r)
return 3.0
return 8.0
log.warning('........ nope')
log.warning('Config file heuristic 2: missing all cloud sections ?')
if 'cloud' in sections:
for r in self.keys('cloud'):
log.warning('... found cloud "%s"' % r)
return 9.0
log.warning('........ yep')
log.warning('All heuristics failed, cannot decide')
return 0.0
def get_remote(self, remote, option):
def get_cloud(self, cloud, option):
"""
:param remote: (str) remote cloud alias
:param cloud: (str) cloud alias
:param option: (str) option in remote cloud section
:param option: (str) option in cloud section
:returns: (str) the value assigned on this option
:raises KeyError: if remote or remote's option does not exist
:raises KeyError: if cloud or cloud's option does not exist
"""
r = self.get('remote', remote)
r = self.get('cloud', cloud)
if not r:
raise KeyError('Remote "%s" does not exist' % remote)
raise KeyError('Cloud "%s" does not exist' % cloud)
return r[option]
def get_global(self, option):
return self.get('global', option)
def set_remote(self, remote, option, value):
def set_cloud(self, cloud, option, value):
try:
d = self.get('remote', remote) or dict()
d = self.get('cloud', cloud) or dict()
except KeyError:
d = dict()
d[option] = value
self.set('remote', remote, d)
self.set('cloud', cloud, d)
def set_global(self, option, value):
self.set('global', option, value)
......@@ -279,7 +286,7 @@ class Config(RawConfigParser):
def get(self, section, option):
"""
:param section: (str) HINT: for remotes, use remote.<section>
:param section: (str) HINT: for clouds, use cloud.<section>
:param option: (str)
......@@ -288,8 +295,9 @@ class Config(RawConfigParser):
value = self._overrides.get(section, {}).get(option)
if value is not None:
return value
if section.startswith('remote.'):
return self.get_remote(section[len('remote.'):], option)
prefix = 'cloud.'
if section.startswith(prefix):
return self.get_cloud(section[len(prefix):], option)
try:
return RawConfigParser.get(self, section, option)
except (NoSectionError, NoOptionError):
......@@ -297,14 +305,15 @@ class Config(RawConfigParser):
def set(self, section, option, value):
"""
:param section: (str) HINT: for remotes use remote.<section>
:param section: (str) HINT: for remotes use cloud.<section>
:param option: (str)
:param value: str
"""
if section.startswith('remote.'):
return self.set_remote(section[len('remote.')], option, value)
prefix = 'cloud.'
if section.startswith(prefix):
return self.set_cloud(section[len(prefix)], option, value)
if section not in RawConfigParser.sections(self):
self.add_section(section)
RawConfigParser.set(self, section, option, value)
......@@ -317,8 +326,8 @@ class Config(RawConfigParser):
except NoSectionError:
pass
def remove_from_remote(self, remote, option):
d = self.get('remote', remote)
def remote_from_cloud(self, cloud, option):
d = self.get('cloud', cloud)
if isinstance(d, dict):
d.pop(option)
......@@ -334,10 +343,10 @@ class Config(RawConfigParser):
self._overrides[section][option] = value
def write(self):
for r, d in self.items('remote'):
for r, d in self.items('cloud'):
for k, v in d.items():
self.set('remote "%s"' % r, k, v)
self.remove_section('remote')
self.set('cloud "%s"' % r, k, v)
self.remove_section('cloud')
with open(self.path, 'w') as f:
os.chmod(self.path, 0600)
......
......@@ -78,14 +78,14 @@ class CLIBaseUrlError(CLIError):
'Two options to resolve this:',
'A. (recommended) Let kamaki discover the endpoint URLs for all',
'services by setting a single Authentication URL and token:',
' /config set remote.default.url <AUTH_URL>',
' /config set remote.default.token <t0k3n>',
' /config set cloud.default.url <AUTH_URL>',
' /config set cloud.default.token <t0k3n>',
'B. (advanced users) Explicitly set a valid %s endpoint URL' % (
service.upper()),
'Note: url option has a higher priority, so delete it to',
'make that work',
' /config delete remote.default.url',
' /config set remote.%s.url <%s_URL>' % (
' /config delete cloud.default.url',
' /config set cloud.%s.url <%s_URL>' % (
service, service.upper())]