Revision 362adf50

b/kamaki/cli/__init__.py
195 195
def _check_config_version(cnf):
196 196
    guess = cnf.guess_version()
197 197
    if guess < 3.0:
198
        print('Configuration file "%s" is not updated to v3.0' % (
198
        print('Config file format version >= 3.0 is required')
199
        print('Configuration file "%s" format is not up to date' % (
199 200
            cnf.path))
200
        print('Calculating changes while preserving information ...')
201
        print('but kamaki can fix this:')
202
        print('Calculating changes while preserving information')
201 203
        lost_terms = cnf.rescue_old_file()
202
        if lost_terms:
203
            print 'The following information will not be preserved:'
204
            print '...', '\n... '.join(lost_terms)
205 204
        print('... DONE')
206
        print('Kamaki is ready to transform config file to version 3.0')
205
        if lost_terms:
206
            print 'The following information will NOT be preserved:'
207
            print '\t', '\n\t'.join(lost_terms)
208
        print('Kamaki is ready to convert the config file to version 3.0')
207 209
        stdout.write('Overwrite file %s ? [Y, y] ' % cnf.path)
208 210
        from sys import stdin
209 211
        reply = stdin.readline()
......
214 216
            print('... ABORTING')
215 217
            raise CLIError(
216 218
                'Invalid format for config file %s' % cnf.path,
217
                importance=3, details=['Please, update config file to v3.0'])
219
                importance=3, details=[
220
                    'Please, update config file to v3.0',
221
                    'For automatic conversion, rerun and say Y'])
218 222

  
219 223

  
220
def _init_session(arguments):
224
def _init_session(arguments, is_non_API=False):
221 225
    global _help
222 226
    _help = arguments['help'].value
223 227
    global _debug
......
228 232
    _verbose = arguments['verbose'].value
229 233
    _cnf = arguments['config']
230 234
    _check_config_version(_cnf.value)
231
    raise CLIError(
232
        'Your file is OK, but i am not ready to proceed',
233
        importance=3, details=['DO NOT PANIC, EXIT THE BUILDING QUIETLY'])
234 235

  
235 236
    global _colors
236
    _colors = _cnf.get('global', 'colors')
237
    _colors = _cnf.value.get_global('colors')
237 238
    if not (stdout.isatty() and _colors == 'on'):
238 239
        from kamaki.cli.utils import remove_colors
239 240
        remove_colors()
240 241
    _silent = arguments['silent'].value
241 242
    _setup_logging(_silent, _debug, _verbose, _include)
242
    picked_cloud = arguments['cloud'].value
243
    if picked_cloud:
244
        global_url = _cnf.get('remotes', picked_cloud)
245
        if not global_url:
246
            raise CLIError(
247
                'No remote cloud "%s" in kamaki configuration' % picked_cloud,
248
                importance=3, details=[
249
                    'To check if this remote cloud alias is declared:',
250
                    '  /config get remotes.%s' % picked_cloud,
251
                    'To set a remote authentication URI aliased as "%s"' % (
252
                        picked_cloud),
253
                    '  /config set remotes.%s <URI>' % picked_cloud
254
                ])
255
    else:
256
        global_url = _cnf.get('global', 'auth_url')
257
    global_token = _cnf.get('global', 'token')
243

  
244
    if _help or is_non_API:
245
        return None
246

  
247
    cloud = arguments['cloud'].value or 'default'
248
    if not cloud in _cnf.value.keys('remote'):
249
        raise CLIError(
250
            'No cloud remote "%s" is configured' % cloud,
251
            importance=3, details=[
252
                'To configure a new cloud remote, find and set the',
253
                'single authentication URL and token:',
254
                '  kamaki config set remote.%s.url <URL>' % cloud,
255
                '  kamaki config set remote.%s.token <t0k3n>' % cloud])
256
    url = _cnf.get_remote(cloud, 'url')
257
    if not url:
258
        kloger.warning(
259
            'WARNING: No remote.%s.url, use service urls instead' % cloud)
260
        return cloud
261
    token = _cnf.get_remote(cloud, 'token')
262
    if not token:
263
        raise CLIError(
264
            'No authentication token provided for %s cloud' % cloud,
265
            importance=3, details=[
266
                'Get and set a token for %s cloud:' % cloud,
267
                '  kamaki config set remote.%s.token <t0k3n>' % cloud])
268

  
258 269
    from kamaki.clients.astakos import AstakosClient as AuthCachedClient
259 270
    try:
260
        return AuthCachedClient(global_url, global_token)
271
        return AuthCachedClient(url, token)
261 272
    except AssertionError as ae:
262
        kloger.warning('WARNING: Failed to load auth_url %s [ %s ]' % (
263
            global_url, ae))
273
        kloger.warning(
274
            'WARNING: Failed to load auth_url %s [ %s ]' % (url, ae))
264 275
        return None
265 276

  
266 277

  
267 278
def _load_spec_module(spec, arguments, module):
268
    #spec_name = arguments['config'].get('cli', spec)
269 279
    if not spec:
270 280
        return None
271 281
    pkg = None
......
304 314

  
305 315
def _load_all_commands(cmd_tree, arguments):
306 316
    _cnf = arguments['config']
307
    #specs = [spec for spec in _cnf.get_groups() if _cnf.get(spec, 'cli')]
308 317
    for cmd_group, spec in _cnf.get_cli_specs():
309 318
        try:
310 319
            spec_module = _load_spec_module(spec, arguments, '_commands')
......
411 420
def run_one_cmd(exe_string, parser, auth_base):
412 421
    global _history
413 422
    _history = History(
414
        parser.arguments['config'].get('history', 'file'))
423
        parser.arguments['config'].get_global('history_file'))
415 424
    _history.add(' '.join([exe_string] + argv[1:]))
416 425
    from kamaki.cli import one_command
417 426
    one_command.run(auth_base, parser, _help)
......
424 433
    shell.run(auth_base, parser)
425 434

  
426 435

  
436
def is_non_API(parser):
437
    nonAPIs = ('history', 'config')
438
    for term in parser.unparsed:
439
        if not term.startswith('-'):
440
            if term in nonAPIs:
441
                return True
442
            return False
443
    return False
444

  
445

  
427 446
def main():
428 447
    try:
429 448
        exe = basename(argv[0])
......
432 451
        if parser.arguments['version'].value:
433 452
            exit(0)
434 453

  
435
        log_file = parser.arguments['config'].get('global', 'log_file')
454
        log_file = parser.arguments['config'].get_global('log_file')
436 455
        if log_file:
437 456
            logger.set_log_filename(log_file)
438 457
        global filelog
439 458
        filelog = logger.add_file_logger(__name__.split('.')[0])
440 459
        filelog.info('* Initial Call *\n%s\n- - -' % ' '.join(argv))
441 460

  
442
        auth_base = _init_session(parser.arguments)
461
        remote_base = _init_session(parser.arguments, is_non_API(parser))
443 462

  
444 463
        from kamaki.cli.utils import suggest_missing
445 464
        suggest_missing()
446 465

  
447 466
        if parser.unparsed:
448
            run_one_cmd(exe, parser, auth_base)
467
            run_one_cmd(exe, parser, remote_base)
449 468
        elif _help:
450 469
            parser.parser.print_help()
451 470
            _groups_help(parser.arguments)
452 471
        else:
453
            run_shell(exe, parser, auth_base)
472
            run_shell(exe, parser, remote_base)
454 473
    except CLIError as err:
455 474
        print_error_message(err)
456 475
        if _debug:
b/kamaki/cli/argument.py
167 167
        return self.value.get(group, term)
168 168

  
169 169
    def get_groups(self):
170
        return self.value.keys('cli')
170
        suffix = '_cli'
171
        slen = len(suffix)
172
        return [term[:-slen] for term in self.value.keys('global') if (
173
            term.endswith(suffix))]
171 174

  
172 175
    def get_cli_specs(self):
173
        return self.value.items('cli')
176
        suffix = '_cli'
177
        slen = len(suffix)
178
        return [(k[:-slen], v) for k, v in self.value.items('global') if (
179
            k.endswith(suffix))]
180

  
181
    def get_global(self, option):
182
        return self.value.get_global(option)
183

  
184
    def get_remote(self, remote, option):
185
        return self.value.get_remote(remote, option)
174 186

  
175 187
_config_arg = ConfigArgument(
176
    1, 'Path to configuration file',
177
    ('-c', '--config'))
188
    1, 'Path to configuration file', ('-c', '--config'))
178 189

  
179 190

  
180 191
class CmdLineConfigArgument(Argument):
b/kamaki/cli/command_shell.py
302 302
        self.auth_base = auth_base
303 303
        self._parser = parser
304 304
        self._history = History(
305
            parser.arguments['config'].get('history', 'file'))
305
            parser.arguments['config'].get_global('history_file'))
306 306
        if path:
307 307
            cmd = self.cmd_tree.get_command(path)
308 308
            intro = cmd.path.replace('_', ' ')
b/kamaki/cli/commands/__init__.py
34 34
from kamaki.cli.logger import get_logger
35 35
from kamaki.cli.utils import print_json, print_items
36 36
from kamaki.cli.argument import FlagArgument
37
from kamaki.cli.errors import CLIError
38
from kamaki.clients import Client
37 39

  
38 40
log = get_logger(__name__)
39 41

  
40 42

  
41 43
class _command_init(object):
42 44

  
43
    def __init__(self, arguments={}, auth_base=None):
45
    def __init__(self, arguments={}, auth_base_or_remote=None):
44 46
        if hasattr(self, 'arguments'):
45 47
            arguments.update(self.arguments)
46 48
        if isinstance(self, _optional_output_cmd):
......
52 54
            self.config = self['config']
53 55
        except KeyError:
54 56
            pass
55
        self.auth_base = auth_base or getattr(self, 'auth_base', None)
57
        if isinstance(auth_base_or_remote, Client):
58
            self.auth_base = auth_base_or_remote
59
        elif not getattr(self, 'auth_base', None):
60
            self.remote = auth_base_or_remote
61
            if not self.remote:
62
                raise CLIError('CRITICAL: No cloud specified', 3)
56 63

  
57 64
    def _set_log_params(self):
58 65
        try:
59 66
            self.client.LOG_TOKEN, self.client.LOG_DATA = (
60
                self['config'].get('global', 'log_token') == 'on',
61
                self['config'].get('global', 'log_data') == 'on')
67
                self['config'].get_global('log_token').lower() == 'on',
68
                self['config'].get_global('log_data').lower() == 'on')
62 69
        except Exception as e:
63 70
            log.warning('Failed to read custom log settings:'
64 71
                '%s\n defaults for token and data logging are off' % e)
65 72

  
66 73
    def _update_max_threads(self):
67 74
        try:
68
            max_threads = int(self['config'].get('global', 'max_threads'))
75
            max_threads = int(self['config'].get_global('max_threads'))
69 76
            assert max_threads > 0
70 77
            self.client.MAX_THREADS = max_threads
71 78
        except Exception as e:
b/kamaki/cli/config.py
79 79
        #  'livetest_cli': 'livetest',
80 80
        #  'astakos_cli': 'snf-astakos'
81 81
    },
82
    'remotes':
82
    'remote':
83 83
    {
84 84
        'default': {
85 85
            'url': '',
......
220 220
                return 2.0
221 221
        log.warning('........ nope')
222 222
        log.warning('Config file heuristic 2: at least 1 remote section ?')
223
        if 'remotes' in sections:
224
            for r in self.keys('remotes'):
223
        if 'remote' in sections:
224
            for r in self.keys('remote'):
225 225
                log.warning('... found remote "%s"' % r)
226 226
                return 3.0
227 227
        log.warning('........ nope')
......
238 238

  
239 239
        :raises KeyError: if remote or remote's option does not exist
240 240
        """
241
        r = self.get('remotes', remote)
241
        r = self.get('remote', remote)
242 242
        if not r:
243 243
            raise KeyError('Remote "%s" does not exist' % remote)
244 244
        return r[option]
245 245

  
246
    def get_global(self, option):
247
        return self.get('global', option)
248

  
246 249
    def set_remote(self, remote, option, value):
247 250
        try:
248
            d = self.get('remotes', remote)
251
            d = self.get('remote', remote)
249 252
        except KeyError:
250 253
            pass
251 254
        d[option] = value
252
        self.set('remotes', remote, d)
255
        self.set('remote', remote, d)
256

  
257
    def set_global(self, option, value):
258
        self.set('global', option, value)
253 259

  
254 260
    def _load_defaults(self):
255 261
        for section, options in DEFAULTS.items():
......
305 311
        self._overrides[section][option] = value
306 312

  
307 313
    def write(self):
308
        for r, d in self.items('remotes'):
314
        for r, d in self.items('remote'):
309 315
            for k, v in d.items():
310 316
                self.set('remote "%s"' % r, k, v)
311
        self.remove_section('remotes')
317
        self.remove_section('remote')
312 318

  
313 319
        with open(self.path, 'w') as f:
314 320
            os.chmod(self.path, 0600)
b/kamaki/cli/errors.py
55 55
            self.importance = 0
56 56

  
57 57

  
58
class CLIUnimplemented(CLIError):
59
    def __init__(
60
            self,
61
            message='I \'M SORRY, DAVE.\nI \'M AFRAID I CAN\'T DO THAT.',
62
            details=[
63
                '      _        |',
64
                '   _-- --_     |',
65
                '  --     --    |',
66
                ' --   .   --   |',
67
                ' -_       _-   |',
68
                '   -_   _-     |',
69
                '      -        |'],
70
            importance=3):
71
        super(CLIUnimplemented, self).__init__(message, details, importance)
72

  
73

  
58 74
class CLIBaseUrlError(CLIError):
59 75
    def __init__(self, message='', details=[], importance=2, service=None):
60 76
        message = message or 'No url for %s' % service.lower()
61 77
        details = details or [
62 78
            'Two options to resolve this:',
63 79
            'A. (recommended) Let kamaki discover the endpoint URLs for all',
64
            'services by setting a single Authentication URL:',
65
            '  /config set auth_url <AUTH_URL>',
80
            'services by setting a single Authentication URL and token:',
81
            '  /config set remote.default.url <AUTH_URL>',
82
            '  /config set remote.default.token <t0k3n>',
66 83
            'B. (advanced users) Explicitly set a valid %s endpoint URL' % (
67 84
                service.upper()),
68
            'Note: auth_url option has a higher priority, so delete it to',
85
            'Note: url option has a higher priority, so delete it to',
69 86
            'make that work',
70
            '  /config delete auth_url',
71
            '  /config set %s.url <%s_URL>' % (service, service.upper())]
87
            '  /config delete remote.default.url',
88
            '  /config set remote.%s.url <%s_URL>' % (
89
                service, service.upper())]
72 90
        super(CLIBaseUrlError, self).__init__(message, details, importance)
73 91

  
74 92

  
b/kamaki/cli/one_command.py
55 55
    return None
56 56

  
57 57

  
58
def run(auth_base, parser, _help):
58
def run(remote_base, parser, _help):
59 59
    group = get_command_group(list(parser.unparsed), parser.arguments)
60 60
    if not group:
61 61
        parser.parser.print_help()
......
68 68
    global _best_match
69 69
    _best_match = []
70 70

  
71
    group_spec = parser.arguments['config'].get('cli', group)
71
    group_spec = parser.arguments['config'].get('global', '%s_cli' % group)
72 72
    spec_module = _load_spec_module(group_spec, parser.arguments, '_commands')
73 73
    if spec_module is None:
74 74
        raise CLIUnknownCommand(
......
96 96
        exit(0)
97 97

  
98 98
    cls = cmd.get_class()
99
    executable = cls(parser.arguments, auth_base)
99
    executable = cls(parser.arguments, remote_base)
100 100
    parser.update_arguments(executable.arguments)
101 101
    #parsed, unparsed = parse_known_args(parser, executable.arguments)
102 102
    for term in _best_match:
b/kamaki/cli/utils.py
39 39

  
40 40
from kamaki.cli.errors import raiseCLIError
41 41

  
42
suggest = dict(
43
    ansicolors=dict(
42
suggest = dict(ansicolors=dict(
44 43
        active=False,
45 44
        url='#install-ansicolors-progress',
46
        description='Add colors to console responses'),
47
    progress=dict(
48
        active=False,
49
        url='#install-ansicolors-progress',
50
        description='Add progress bars to some commands'))
45
        description='Add colors to console responses'))
51 46

  
52 47
try:
53 48
    from colors import magenta, red, yellow, bold

Also available in: Unified diff