Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / __init__.py @ 9b3c8fd9

History | View | Annotate | Download (18.7 kB)

1 d486baec Stavros Sachtouris
# Copyright 2012-2013 GRNET S.A. All rights reserved.
2 7493ccb6 Stavros Sachtouris
#
3 7493ccb6 Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 7493ccb6 Stavros Sachtouris
# without modification, are permitted provided that the following
5 7493ccb6 Stavros Sachtouris
# conditions are met:
6 7493ccb6 Stavros Sachtouris
#
7 7493ccb6 Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 fd5db045 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
9 fd5db045 Stavros Sachtouris
#      disclaimer.
10 7493ccb6 Stavros Sachtouris
#
11 7493ccb6 Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 fd5db045 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
13 fd5db045 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 fd5db045 Stavros Sachtouris
#      provided with the distribution.
15 7493ccb6 Stavros Sachtouris
#
16 7493ccb6 Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 7493ccb6 Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 7493ccb6 Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 7493ccb6 Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 7493ccb6 Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 7493ccb6 Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 7493ccb6 Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 7493ccb6 Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 7493ccb6 Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 7493ccb6 Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 7493ccb6 Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 7493ccb6 Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 7493ccb6 Stavros Sachtouris
#
29 7493ccb6 Stavros Sachtouris
# The views and conclusions contained in the software and
30 7493ccb6 Stavros Sachtouris
# documentation are those of the authors and should not be
31 7493ccb6 Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 d486baec Stavros Sachtouris
# or implied, of GRNET S.A.command
33 7493ccb6 Stavros Sachtouris
34 7493ccb6 Stavros Sachtouris
import logging
35 3185cd6d Stavros Sachtouris
from sys import argv, exit, stdout, stderr
36 df0045d8 Stavros Sachtouris
from os.path import basename, exists
37 d486baec Stavros Sachtouris
from inspect import getargspec
38 3dabe5d2 Stavros Sachtouris
39 ee4c47d7 Stavros Sachtouris
from kamaki.cli.argument import ArgumentParseManager
40 fd5db045 Stavros Sachtouris
from kamaki.cli.history import History
41 af569ab9 Stavros Sachtouris
from kamaki.cli.utils import print_dict, red, magenta, yellow
42 7b2e4bf1 Stavros Sachtouris
from kamaki.cli.errors import CLIError, CLICmdSpecError
43 7637d600 Stavros Sachtouris
from kamaki.cli import logger
44 dc897a7e Stavros Sachtouris
from kamaki.clients.astakos import AstakosClient as AuthCachedClient
45 dc897a7e Stavros Sachtouris
from kamaki.clients import ClientError
46 7493ccb6 Stavros Sachtouris
47 d486baec Stavros Sachtouris
_help = False
48 d486baec Stavros Sachtouris
_debug = False
49 d486baec Stavros Sachtouris
_verbose = False
50 d486baec Stavros Sachtouris
_colors = False
51 aa5c0458 Stavros Sachtouris
kloger = None
52 9dc724e5 Stavros Sachtouris
filelog = None
53 fd5db045 Stavros Sachtouris
54 b6a99832 Stavros Sachtouris
#  command auxiliary methods
55 b6a99832 Stavros Sachtouris
56 b6a99832 Stavros Sachtouris
_best_match = []
57 b6a99832 Stavros Sachtouris
58 fd5db045 Stavros Sachtouris
59 24ff0a35 Stavros Sachtouris
def _arg2syntax(arg):
60 24ff0a35 Stavros Sachtouris
    return arg.replace(
61 24ff0a35 Stavros Sachtouris
        '____', '[:').replace(
62 2005b18e Stavros Sachtouris
            '___', ':').replace(
63 2005b18e Stavros Sachtouris
                '__', ']').replace(
64 2005b18e Stavros Sachtouris
                    '_', ' ')
65 24ff0a35 Stavros Sachtouris
66 24ff0a35 Stavros Sachtouris
67 d486baec Stavros Sachtouris
def _construct_command_syntax(cls):
68 fd5db045 Stavros Sachtouris
        spec = getargspec(cls.main.im_func)
69 fd5db045 Stavros Sachtouris
        args = spec.args[1:]
70 fd5db045 Stavros Sachtouris
        n = len(args) - len(spec.defaults or ())
71 24ff0a35 Stavros Sachtouris
        required = ' '.join(['<%s>' % _arg2syntax(x) for x in args[:n]])
72 24ff0a35 Stavros Sachtouris
        optional = ' '.join(['[%s]' % _arg2syntax(x) for x in args[n:]])
73 56d84a4e Stavros Sachtouris
        cls.syntax = ' '.join([required, optional])
74 fd5db045 Stavros Sachtouris
        if spec.varargs:
75 fd5db045 Stavros Sachtouris
            cls.syntax += ' <%s ...>' % spec.varargs
76 fd5db045 Stavros Sachtouris
77 fd5db045 Stavros Sachtouris
78 d486baec Stavros Sachtouris
def _num_of_matching_terms(basic_list, attack_list):
79 d486baec Stavros Sachtouris
    if not attack_list:
80 75c3fc42 Stavros Sachtouris
        return len(basic_list)
81 fd5db045 Stavros Sachtouris
82 d486baec Stavros Sachtouris
    matching_terms = 0
83 d486baec Stavros Sachtouris
    for i, term in enumerate(basic_list):
84 d486baec Stavros Sachtouris
        try:
85 d486baec Stavros Sachtouris
            if term != attack_list[i]:
86 d486baec Stavros Sachtouris
                break
87 d486baec Stavros Sachtouris
        except IndexError:
88 d486baec Stavros Sachtouris
            break
89 d486baec Stavros Sachtouris
        matching_terms += 1
90 d486baec Stavros Sachtouris
    return matching_terms
91 dfee2caf Stavros Sachtouris
92 fd5db045 Stavros Sachtouris
93 d486baec Stavros Sachtouris
def _update_best_match(name_terms, prefix=[]):
94 d486baec Stavros Sachtouris
    if prefix:
95 d486baec Stavros Sachtouris
        pref_list = prefix if isinstance(prefix, list) else prefix.split('_')
96 d486baec Stavros Sachtouris
    else:
97 d486baec Stavros Sachtouris
        pref_list = []
98 dfee2caf Stavros Sachtouris
99 d486baec Stavros Sachtouris
    num_of_matching_terms = _num_of_matching_terms(name_terms, pref_list)
100 d486baec Stavros Sachtouris
    global _best_match
101 e9533b0c Stavros Sachtouris
    if not prefix:
102 e9533b0c Stavros Sachtouris
        _best_match = []
103 fd5db045 Stavros Sachtouris
104 d486baec Stavros Sachtouris
    if num_of_matching_terms and len(_best_match) <= num_of_matching_terms:
105 d486baec Stavros Sachtouris
        if len(_best_match) < num_of_matching_terms:
106 d486baec Stavros Sachtouris
            _best_match = name_terms[:num_of_matching_terms]
107 d486baec Stavros Sachtouris
        return True
108 d486baec Stavros Sachtouris
    return False
109 d486baec Stavros Sachtouris
110 d486baec Stavros Sachtouris
111 d486baec Stavros Sachtouris
def command(cmd_tree, prefix='', descedants_depth=1):
112 d486baec Stavros Sachtouris
    """Load a class as a command
113 16d7b9ff Stavros Sachtouris
        e.g., spec_cmd0_cmd1 will be command spec cmd0
114 451a7992 Stavros Sachtouris

115 451a7992 Stavros Sachtouris
        :param cmd_tree: is initialized in cmd_spec file and is the structure
116 d486baec Stavros Sachtouris
            where commands are loaded. Var name should be _commands
117 451a7992 Stavros Sachtouris
        :param prefix: if given, load only commands prefixed with prefix,
118 451a7992 Stavros Sachtouris
        :param descedants_depth: is the depth of the tree descedants of the
119 d486baec Stavros Sachtouris
            prefix command. It is used ONLY if prefix and if prefix is not
120 d486baec Stavros Sachtouris
            a terminal command
121 451a7992 Stavros Sachtouris

122 451a7992 Stavros Sachtouris
        :returns: the specified class object
123 d486baec Stavros Sachtouris
    """
124 d486baec Stavros Sachtouris
125 d486baec Stavros Sachtouris
    def wrap(cls):
126 451a7992 Stavros Sachtouris
        global kloger
127 d486baec Stavros Sachtouris
        cls_name = cls.__name__
128 d486baec Stavros Sachtouris
129 d486baec Stavros Sachtouris
        if not cmd_tree:
130 d486baec Stavros Sachtouris
            if _debug:
131 451a7992 Stavros Sachtouris
                kloger.warning('command %s found but not loaded' % cls_name)
132 d486baec Stavros Sachtouris
            return cls
133 0b368c8c Stavros Sachtouris
134 d486baec Stavros Sachtouris
        name_terms = cls_name.split('_')
135 d486baec Stavros Sachtouris
        if not _update_best_match(name_terms, prefix):
136 e9533b0c Stavros Sachtouris
            if _debug:
137 451a7992 Stavros Sachtouris
                kloger.warning('%s failed to update_best_match' % cls_name)
138 d486baec Stavros Sachtouris
            return None
139 fd5db045 Stavros Sachtouris
140 d486baec Stavros Sachtouris
        global _best_match
141 d486baec Stavros Sachtouris
        max_len = len(_best_match) + descedants_depth
142 d486baec Stavros Sachtouris
        if len(name_terms) > max_len:
143 d486baec Stavros Sachtouris
            partial = '_'.join(name_terms[:max_len])
144 d486baec Stavros Sachtouris
            if not cmd_tree.has_command(partial):  # add partial path
145 d486baec Stavros Sachtouris
                cmd_tree.add_command(partial)
146 e9533b0c Stavros Sachtouris
            if _debug:
147 451a7992 Stavros Sachtouris
                kloger.warning('%s failed max_len test' % cls_name)
148 d486baec Stavros Sachtouris
            return None
149 fd5db045 Stavros Sachtouris
150 7b2e4bf1 Stavros Sachtouris
        try:
151 7b2e4bf1 Stavros Sachtouris
            (
152 7b2e4bf1 Stavros Sachtouris
                cls.description, sep, cls.long_description
153 7b2e4bf1 Stavros Sachtouris
            ) = cls.__doc__.partition('\n')
154 7b2e4bf1 Stavros Sachtouris
        except AttributeError:
155 7b2e4bf1 Stavros Sachtouris
            raise CLICmdSpecError(
156 7b2e4bf1 Stavros Sachtouris
                'No commend in %s (acts as cmd description)' % cls.__name__)
157 d486baec Stavros Sachtouris
        _construct_command_syntax(cls)
158 0b368c8c Stavros Sachtouris
159 38db356b Stavros Sachtouris
        cmd_tree.add_command(
160 38db356b Stavros Sachtouris
            cls_name, cls.description, cls, cls.long_description)
161 d486baec Stavros Sachtouris
        return cls
162 d486baec Stavros Sachtouris
    return wrap
163 fd5db045 Stavros Sachtouris
164 0b368c8c Stavros Sachtouris
165 d486baec Stavros Sachtouris
cmd_spec_locations = [
166 d486baec Stavros Sachtouris
    'kamaki.cli.commands',
167 d486baec Stavros Sachtouris
    'kamaki.commands',
168 d486baec Stavros Sachtouris
    'kamaki.cli',
169 d486baec Stavros Sachtouris
    'kamaki',
170 d486baec Stavros Sachtouris
    '']
171 fd5db045 Stavros Sachtouris
172 0b368c8c Stavros Sachtouris
173 b6a99832 Stavros Sachtouris
#  Generic init auxiliary functions
174 b6a99832 Stavros Sachtouris
175 b6a99832 Stavros Sachtouris
176 f6822a26 Stavros Sachtouris
def _setup_logging(silent=False, debug=False, verbose=False):
177 fd5db045 Stavros Sachtouris
    """handle logging for clients package"""
178 fd5db045 Stavros Sachtouris
179 fd5db045 Stavros Sachtouris
    if silent:
180 9dc724e5 Stavros Sachtouris
        logger.add_stream_logger(__name__, logging.CRITICAL)
181 db8d1766 Stavros Sachtouris
        return
182 db8d1766 Stavros Sachtouris
183 6e1f863b Stavros Sachtouris
    sfmt, rfmt = '> %(message)s', '< %(message)s'
184 db8d1766 Stavros Sachtouris
    if debug:
185 9dc724e5 Stavros Sachtouris
        print('Logging location: %s' % logger.get_log_filename())
186 9dc724e5 Stavros Sachtouris
        logger.add_stream_logger('kamaki.clients.send', logging.DEBUG, sfmt)
187 9dc724e5 Stavros Sachtouris
        logger.add_stream_logger('kamaki.clients.recv', logging.DEBUG, rfmt)
188 9dc724e5 Stavros Sachtouris
        logger.add_stream_logger(__name__, logging.DEBUG)
189 fd5db045 Stavros Sachtouris
    elif verbose:
190 9dc724e5 Stavros Sachtouris
        logger.add_stream_logger('kamaki.clients.send', logging.INFO, sfmt)
191 9dc724e5 Stavros Sachtouris
        logger.add_stream_logger('kamaki.clients.recv', logging.INFO, rfmt)
192 9dc724e5 Stavros Sachtouris
        logger.add_stream_logger(__name__, logging.INFO)
193 9dc724e5 Stavros Sachtouris
    logger.add_stream_logger(__name__, logging.WARNING)
194 aa5c0458 Stavros Sachtouris
    global kloger
195 9dc724e5 Stavros Sachtouris
    kloger = logger.get_logger(__name__)
196 fd5db045 Stavros Sachtouris
197 ce48608f Stavros Sachtouris
198 ce9ccb40 Stavros Sachtouris
def _check_config_version(cnf):
199 ce9ccb40 Stavros Sachtouris
    guess = cnf.guess_version()
200 df0045d8 Stavros Sachtouris
    if exists(cnf.path) and guess < 0.9:
201 c825ddc9 Stavros Sachtouris
        print('Config file format version >= 9.0 is required')
202 df0045d8 Stavros Sachtouris
        print('Configuration file: %s' % cnf.path)
203 fa382f9e Stavros Sachtouris
        print('Attempting to fix this:')
204 362adf50 Stavros Sachtouris
        print('Calculating changes while preserving information')
205 ce9ccb40 Stavros Sachtouris
        lost_terms = cnf.rescue_old_file()
206 ce9ccb40 Stavros Sachtouris
        print('... DONE')
207 362adf50 Stavros Sachtouris
        if lost_terms:
208 362adf50 Stavros Sachtouris
            print 'The following information will NOT be preserved:'
209 362adf50 Stavros Sachtouris
            print '\t', '\n\t'.join(lost_terms)
210 fa382f9e Stavros Sachtouris
        print('Kamaki is ready to convert the config file')
211 904091dd Stavros Sachtouris
        stdout.write('Create (overwrite) file %s ? [y/N] ' % cnf.path)
212 ce9ccb40 Stavros Sachtouris
        from sys import stdin
213 ce9ccb40 Stavros Sachtouris
        reply = stdin.readline()
214 ce9ccb40 Stavros Sachtouris
        if reply in ('Y\n', 'y\n'):
215 ce9ccb40 Stavros Sachtouris
            cnf.write()
216 ce9ccb40 Stavros Sachtouris
            print('... DONE')
217 ce9ccb40 Stavros Sachtouris
        else:
218 ce9ccb40 Stavros Sachtouris
            print('... ABORTING')
219 ce9ccb40 Stavros Sachtouris
            raise CLIError(
220 ce9ccb40 Stavros Sachtouris
                'Invalid format for config file %s' % cnf.path,
221 362adf50 Stavros Sachtouris
                importance=3, details=[
222 fa382f9e Stavros Sachtouris
                    'Please, update config file',
223 362adf50 Stavros Sachtouris
                    'For automatic conversion, rerun and say Y'])
224 ce9ccb40 Stavros Sachtouris
225 ce9ccb40 Stavros Sachtouris
226 362adf50 Stavros Sachtouris
def _init_session(arguments, is_non_API=False):
227 844a6bdb Stavros Sachtouris
    """
228 db03f805 Stavros Sachtouris
    :returns: cloud name
229 844a6bdb Stavros Sachtouris
    """
230 d486baec Stavros Sachtouris
    global _help
231 d486baec Stavros Sachtouris
    _help = arguments['help'].value
232 d486baec Stavros Sachtouris
    global _debug
233 d486baec Stavros Sachtouris
    _debug = arguments['debug'].value
234 d486baec Stavros Sachtouris
    global _verbose
235 d486baec Stavros Sachtouris
    _verbose = arguments['verbose'].value
236 cb4a5d9c Stavros Sachtouris
    _cnf = arguments['config']
237 df0045d8 Stavros Sachtouris
238 f1d06d74 Stavros Sachtouris
    _silent = arguments['silent'].value
239 f6822a26 Stavros Sachtouris
    _setup_logging(_silent, _debug, _verbose)
240 f1d06d74 Stavros Sachtouris
241 df0045d8 Stavros Sachtouris
    if _help or is_non_API:
242 db03f805 Stavros Sachtouris
        return None
243 df0045d8 Stavros Sachtouris
244 ce9ccb40 Stavros Sachtouris
    _check_config_version(_cnf.value)
245 3f0eae61 Stavros Sachtouris
246 d486baec Stavros Sachtouris
    global _colors
247 534e7bbb Stavros Sachtouris
    _colors = _cnf.value.get('global', 'colors')
248 87565d2c Stavros Sachtouris
    if not (stdout.isatty() and _colors == 'on'):
249 4f6a21f6 Stavros Sachtouris
        from kamaki.cli.utils import remove_colors
250 4f6a21f6 Stavros Sachtouris
        remove_colors()
251 362adf50 Stavros Sachtouris
252 c825ddc9 Stavros Sachtouris
    cloud = arguments['cloud'].value or _cnf.value.get(
253 c825ddc9 Stavros Sachtouris
        'global', 'default_cloud')
254 c825ddc9 Stavros Sachtouris
    if not cloud:
255 c825ddc9 Stavros Sachtouris
        num_of_clouds = len(_cnf.value.keys('cloud'))
256 c825ddc9 Stavros Sachtouris
        if num_of_clouds == 1:
257 c825ddc9 Stavros Sachtouris
            cloud = _cnf.value.keys('cloud')[0]
258 df0045d8 Stavros Sachtouris
        elif num_of_clouds > 1:
259 c825ddc9 Stavros Sachtouris
            raise CLIError(
260 44101d8a Stavros Sachtouris
                'Found %s clouds but none of them is set as default' % (
261 44101d8a Stavros Sachtouris
                    num_of_clouds),
262 c825ddc9 Stavros Sachtouris
                importance=2, details=[
263 c825ddc9 Stavros Sachtouris
                    'Please, choose one of the following cloud names:',
264 c825ddc9 Stavros Sachtouris
                    ', '.join(_cnf.value.keys('cloud')),
265 44101d8a Stavros Sachtouris
                    'To see all cloud settings:',
266 44101d8a Stavros Sachtouris
                    '  kamaki config get cloud.<cloud name>',
267 c825ddc9 Stavros Sachtouris
                    'To set a default cloud:',
268 c825ddc9 Stavros Sachtouris
                    '  kamaki config set default_cloud <cloud name>',
269 c825ddc9 Stavros Sachtouris
                    'To pick a cloud for the current session, use --cloud:',
270 c825ddc9 Stavros Sachtouris
                    '  kamaki --cloud=<cloud name> ...'])
271 144b3551 Stavros Sachtouris
    if not cloud in _cnf.value.keys('cloud'):
272 362adf50 Stavros Sachtouris
        raise CLIError(
273 df0045d8 Stavros Sachtouris
            'No cloud%s is configured' % ((' "%s"' % cloud) if cloud else ''),
274 362adf50 Stavros Sachtouris
            importance=3, details=[
275 df0045d8 Stavros Sachtouris
                'To configure a new cloud "%s", find and set the' % (
276 df0045d8 Stavros Sachtouris
                    cloud or '<cloud name>'),
277 362adf50 Stavros Sachtouris
                'single authentication URL and token:',
278 df0045d8 Stavros Sachtouris
                '  kamaki config set cloud.%s.url <URL>' % (
279 df0045d8 Stavros Sachtouris
                    cloud or '<cloud name>'),
280 df0045d8 Stavros Sachtouris
                '  kamaki config set cloud.%s.token <t0k3n>' % (
281 df0045d8 Stavros Sachtouris
                    cloud or '<cloud name>')])
282 844a6bdb Stavros Sachtouris
    auth_args = dict()
283 844a6bdb Stavros Sachtouris
    for term in ('url', 'token'):
284 c825ddc9 Stavros Sachtouris
        try:
285 c825ddc9 Stavros Sachtouris
            auth_args[term] = _cnf.get_cloud(cloud, term)
286 dc897a7e Stavros Sachtouris
        except KeyError or IndexError:
287 c825ddc9 Stavros Sachtouris
            auth_args[term] = ''
288 844a6bdb Stavros Sachtouris
        if not auth_args[term]:
289 844a6bdb Stavros Sachtouris
            raise CLIError(
290 b4ed3a7e Stavros Sachtouris
                'No authentication %s provided for cloud "%s"' % (
291 b4ed3a7e Stavros Sachtouris
                    term.upper(), cloud),
292 844a6bdb Stavros Sachtouris
                importance=3, details=[
293 b4ed3a7e Stavros Sachtouris
                    'Set a %s for cloud %s:' % (term.upper(), cloud),
294 44101d8a Stavros Sachtouris
                    '  kamaki config set cloud.%s.%s <%s>' % (
295 b4ed3a7e Stavros Sachtouris
                        cloud, term, term.upper())])
296 db03f805 Stavros Sachtouris
    return cloud
297 db03f805 Stavros Sachtouris
298 362adf50 Stavros Sachtouris
299 fa7d08b6 Stavros Sachtouris
def init_cached_authenticator(config_argument, cloud, logger):
300 8cec3671 Stavros Sachtouris
    try:
301 fa7d08b6 Stavros Sachtouris
        _cnf = config_argument.value
302 fa7d08b6 Stavros Sachtouris
        url = _cnf.get_cloud(cloud, 'url')
303 fa7d08b6 Stavros Sachtouris
        tokens = _cnf.get_cloud(cloud, 'token').split()
304 fa7d08b6 Stavros Sachtouris
        auth_base, failed = None, []
305 fa7d08b6 Stavros Sachtouris
        for token in tokens:
306 dc897a7e Stavros Sachtouris
            try:
307 dc897a7e Stavros Sachtouris
                if auth_base:
308 dc897a7e Stavros Sachtouris
                    auth_base.authenticate(token)
309 dc897a7e Stavros Sachtouris
                else:
310 fa7d08b6 Stavros Sachtouris
                    tmp_base = AuthCachedClient(url, token)
311 f5ff79d9 Stavros Sachtouris
                    from kamaki.cli.commands import _command_init
312 fa7d08b6 Stavros Sachtouris
                    fake_cmd = _command_init(dict(config=config_argument))
313 f5ff79d9 Stavros Sachtouris
                    fake_cmd.client = auth_base
314 f5ff79d9 Stavros Sachtouris
                    fake_cmd._set_log_params()
315 fa7d08b6 Stavros Sachtouris
                    tmp_base.authenticate(token)
316 fa7d08b6 Stavros Sachtouris
                    auth_base = tmp_base
317 dc897a7e Stavros Sachtouris
            except ClientError as ce:
318 dc897a7e Stavros Sachtouris
                if ce.status in (401, ):
319 db03f805 Stavros Sachtouris
                    logger.warning(
320 db03f805 Stavros Sachtouris
                        'WARNING: Failed to authenticate token %s' % token)
321 fa7d08b6 Stavros Sachtouris
                    failed.append(token)
322 dc897a7e Stavros Sachtouris
                else:
323 dc897a7e Stavros Sachtouris
                    raise
324 fa7d08b6 Stavros Sachtouris
        for token in failed:
325 fa7d08b6 Stavros Sachtouris
            r = raw_input(
326 fa7d08b6 Stavros Sachtouris
                'Token %s failed to authenticate. Remove it? [y/N]: ' % token)
327 fa7d08b6 Stavros Sachtouris
            if r in ('y', 'Y'):
328 fa7d08b6 Stavros Sachtouris
                tokens.remove(token)
329 fa7d08b6 Stavros Sachtouris
        if set(failed).difference(tokens):
330 fa7d08b6 Stavros Sachtouris
            _cnf.set_cloud(cloud, 'token', ' '.join(tokens))
331 fa7d08b6 Stavros Sachtouris
            _cnf.write()
332 fa7d08b6 Stavros Sachtouris
        if tokens:
333 fa7d08b6 Stavros Sachtouris
            return auth_base
334 fa7d08b6 Stavros Sachtouris
        logger.warning('WARNING: cloud.%s.token is now empty' % cloud)
335 8cec3671 Stavros Sachtouris
    except AssertionError as ae:
336 db03f805 Stavros Sachtouris
        logger.warning('WARNING: Failed to load authenticator [%s]' % ae)
337 fa7d08b6 Stavros Sachtouris
    return None
338 d486baec Stavros Sachtouris
339 d486baec Stavros Sachtouris
340 d486baec Stavros Sachtouris
def _load_spec_module(spec, arguments, module):
341 f1d06d74 Stavros Sachtouris
    global kloger
342 f724cd35 Stavros Sachtouris
    if not spec:
343 d486baec Stavros Sachtouris
        return None
344 d486baec Stavros Sachtouris
    pkg = None
345 d486baec Stavros Sachtouris
    for location in cmd_spec_locations:
346 f724cd35 Stavros Sachtouris
        location += spec if location == '' else '.%s' % spec
347 d486baec Stavros Sachtouris
        try:
348 f1d06d74 Stavros Sachtouris
            kloger.debug('Import %s from %s' % ([module], location))
349 d486baec Stavros Sachtouris
            pkg = __import__(location, fromlist=[module])
350 f1d06d74 Stavros Sachtouris
            kloger.debug('\t...OK')
351 d486baec Stavros Sachtouris
            return pkg
352 f724cd35 Stavros Sachtouris
        except ImportError as ie:
353 f1d06d74 Stavros Sachtouris
            kloger.debug('\t...Failed')
354 d486baec Stavros Sachtouris
            continue
355 f724cd35 Stavros Sachtouris
    if not pkg:
356 5506d7aa Stavros Sachtouris
        msg = 'Loading command group %s failed: %s' % (spec, ie)
357 f1d06d74 Stavros Sachtouris
        msg += '\nHINT: use a text editor to remove all global.*_cli'
358 f1d06d74 Stavros Sachtouris
        msg += '\n\tsettings from the configuration file'
359 f1d06d74 Stavros Sachtouris
        kloger.debug(msg)
360 d486baec Stavros Sachtouris
    return pkg
361 d486baec Stavros Sachtouris
362 d486baec Stavros Sachtouris
363 d486baec Stavros Sachtouris
def _groups_help(arguments):
364 d486baec Stavros Sachtouris
    global _debug
365 aa5c0458 Stavros Sachtouris
    global kloger
366 d486baec Stavros Sachtouris
    descriptions = {}
367 a7aacf12 Stavros Sachtouris
    acceptable_groups = arguments['config'].groups
368 a7aacf12 Stavros Sachtouris
    for cmd_group, spec in arguments['config'].cli_specs:
369 d486baec Stavros Sachtouris
        pkg = _load_spec_module(spec, arguments, '_commands')
370 d486baec Stavros Sachtouris
        if pkg:
371 f724cd35 Stavros Sachtouris
            cmds = getattr(pkg, '_commands')
372 d486baec Stavros Sachtouris
            try:
373 d252a7a8 Stavros Sachtouris
                for cmd_tree in cmds:
374 d252a7a8 Stavros Sachtouris
                    if cmd_tree.name in acceptable_groups:
375 d252a7a8 Stavros Sachtouris
                        descriptions[cmd_tree.name] = cmd_tree.description
376 d486baec Stavros Sachtouris
            except TypeError:
377 d486baec Stavros Sachtouris
                if _debug:
378 f724cd35 Stavros Sachtouris
                    kloger.warning(
379 eb46e9a1 Stavros Sachtouris
                        'No cmd description (help) for module %s' % cmd_group)
380 d486baec Stavros Sachtouris
        elif _debug:
381 f724cd35 Stavros Sachtouris
            kloger.warning('Loading of %s cmd spec failed' % cmd_group)
382 d486baec Stavros Sachtouris
    print('\nOptions:\n - - - -')
383 d486baec Stavros Sachtouris
    print_dict(descriptions)
384 d486baec Stavros Sachtouris
385 d486baec Stavros Sachtouris
386 b6a99832 Stavros Sachtouris
def _load_all_commands(cmd_tree, arguments):
387 24ff0a35 Stavros Sachtouris
    _cnf = arguments['config']
388 a7aacf12 Stavros Sachtouris
    for cmd_group, spec in _cnf.cli_specs:
389 b6a99832 Stavros Sachtouris
        try:
390 b6a99832 Stavros Sachtouris
            spec_module = _load_spec_module(spec, arguments, '_commands')
391 b6a99832 Stavros Sachtouris
            spec_commands = getattr(spec_module, '_commands')
392 b6a99832 Stavros Sachtouris
        except AttributeError:
393 b6a99832 Stavros Sachtouris
            if _debug:
394 b6a99832 Stavros Sachtouris
                global kloger
395 f724cd35 Stavros Sachtouris
                kloger.warning('No valid description for %s' % cmd_group)
396 b6a99832 Stavros Sachtouris
            continue
397 b6a99832 Stavros Sachtouris
        for spec_tree in spec_commands:
398 f724cd35 Stavros Sachtouris
            if spec_tree.name == cmd_group:
399 b6a99832 Stavros Sachtouris
                cmd_tree.add_tree(spec_tree)
400 b6a99832 Stavros Sachtouris
                break
401 b6a99832 Stavros Sachtouris
402 b6a99832 Stavros Sachtouris
403 b6a99832 Stavros Sachtouris
#  Methods to be used by CLI implementations
404 b6a99832 Stavros Sachtouris
405 b6a99832 Stavros Sachtouris
406 b6a99832 Stavros Sachtouris
def print_subcommands_help(cmd):
407 d486baec Stavros Sachtouris
    printout = {}
408 eb46e9a1 Stavros Sachtouris
    for subcmd in cmd.subcommands.values():
409 4f6a21f6 Stavros Sachtouris
        spec, sep, print_path = subcmd.path.partition('_')
410 eb46e9a1 Stavros Sachtouris
        printout[print_path.replace('_', ' ')] = subcmd.help
411 d486baec Stavros Sachtouris
    if printout:
412 d486baec Stavros Sachtouris
        print('\nOptions:\n - - - -')
413 d486baec Stavros Sachtouris
        print_dict(printout)
414 d486baec Stavros Sachtouris
415 d486baec Stavros Sachtouris
416 b6a99832 Stavros Sachtouris
def update_parser_help(parser, cmd):
417 d486baec Stavros Sachtouris
    global _best_match
418 7c2247a0 Stavros Sachtouris
    parser.syntax = parser.syntax.split('<')[0]
419 7c2247a0 Stavros Sachtouris
    parser.syntax += ' '.join(_best_match)
420 d486baec Stavros Sachtouris
421 a71bb904 Stavros Sachtouris
    description = ''
422 d486baec Stavros Sachtouris
    if cmd.is_command:
423 eb46e9a1 Stavros Sachtouris
        cls = cmd.cmd_class
424 7c2247a0 Stavros Sachtouris
        parser.syntax += ' ' + cls.syntax
425 7c2247a0 Stavros Sachtouris
        parser.update_arguments(cls().arguments)
426 eb46e9a1 Stavros Sachtouris
        description = getattr(cls, 'long_description', '').strip()
427 d486baec Stavros Sachtouris
    else:
428 7c2247a0 Stavros Sachtouris
        parser.syntax += ' <...>'
429 eb46e9a1 Stavros Sachtouris
    parser.parser.description = (
430 eb46e9a1 Stavros Sachtouris
        cmd.help + ('\n' if description else '')) if cmd.help else description
431 d486baec Stavros Sachtouris
432 d486baec Stavros Sachtouris
433 3185cd6d Stavros Sachtouris
def print_error_message(cli_err, out=stderr):
434 d486baec Stavros Sachtouris
    errmsg = '%s' % cli_err
435 d486baec Stavros Sachtouris
    if cli_err.importance == 1:
436 d486baec Stavros Sachtouris
        errmsg = magenta(errmsg)
437 d486baec Stavros Sachtouris
    elif cli_err.importance == 2:
438 d486baec Stavros Sachtouris
        errmsg = yellow(errmsg)
439 d486baec Stavros Sachtouris
    elif cli_err.importance > 2:
440 d486baec Stavros Sachtouris
        errmsg = red(errmsg)
441 3185cd6d Stavros Sachtouris
    out.write(errmsg)
442 66f1ff99 Stavros Sachtouris
    for errmsg in cli_err.details:
443 3185cd6d Stavros Sachtouris
        out.write('|  %s\n' % errmsg)
444 3185cd6d Stavros Sachtouris
        out.flush()
445 d486baec Stavros Sachtouris
446 d486baec Stavros Sachtouris
447 b6a99832 Stavros Sachtouris
def exec_cmd(instance, cmd_args, help_method):
448 fd5db045 Stavros Sachtouris
    try:
449 fd5db045 Stavros Sachtouris
        return instance.main(*cmd_args)
450 fd5db045 Stavros Sachtouris
    except TypeError as err:
451 fd5db045 Stavros Sachtouris
        if err.args and err.args[0].startswith('main()'):
452 fd5db045 Stavros Sachtouris
            print(magenta('Syntax error'))
453 d486baec Stavros Sachtouris
            if _debug:
454 d486baec Stavros Sachtouris
                raise err
455 d486baec Stavros Sachtouris
            if _verbose:
456 fd5db045 Stavros Sachtouris
                print(unicode(err))
457 fd5db045 Stavros Sachtouris
            help_method()
458 fd5db045 Stavros Sachtouris
        else:
459 fd5db045 Stavros Sachtouris
            raise
460 fd5db045 Stavros Sachtouris
    return 1
461 fd5db045 Stavros Sachtouris
462 f247bcb4 Stavros Sachtouris
463 b6a99832 Stavros Sachtouris
def get_command_group(unparsed, arguments):
464 a7aacf12 Stavros Sachtouris
    groups = arguments['config'].groups
465 b6a99832 Stavros Sachtouris
    for term in unparsed:
466 b6a99832 Stavros Sachtouris
        if term.startswith('-'):
467 b6a99832 Stavros Sachtouris
            continue
468 b6a99832 Stavros Sachtouris
        if term in groups:
469 b6a99832 Stavros Sachtouris
            unparsed.remove(term)
470 b6a99832 Stavros Sachtouris
            return term
471 b6a99832 Stavros Sachtouris
        return None
472 b6a99832 Stavros Sachtouris
    return None
473 b6a99832 Stavros Sachtouris
474 b6a99832 Stavros Sachtouris
475 7c2247a0 Stavros Sachtouris
def set_command_params(parameters):
476 7c2247a0 Stavros Sachtouris
    """Add a parameters list to a command
477 7c2247a0 Stavros Sachtouris

478 7c2247a0 Stavros Sachtouris
    :param paramters: (list of str) a list of parameters
479 7c2247a0 Stavros Sachtouris
    """
480 d486baec Stavros Sachtouris
    global command
481 d486baec Stavros Sachtouris
    def_params = list(command.func_defaults)
482 7c2247a0 Stavros Sachtouris
    def_params[0] = parameters
483 d486baec Stavros Sachtouris
    command.func_defaults = tuple(def_params)
484 d486baec Stavros Sachtouris
485 d486baec Stavros Sachtouris
486 b6a99832 Stavros Sachtouris
#  CLI Choice:
487 d486baec Stavros Sachtouris
488 db03f805 Stavros Sachtouris
def run_one_cmd(exe_string, parser, cloud):
489 b6a99832 Stavros Sachtouris
    global _history
490 2d1f5058 Stavros Sachtouris
    try:
491 2d1f5058 Stavros Sachtouris
        token = parser.arguments['config'].get_cloud(cloud, 'token').split()[0]
492 2d1f5058 Stavros Sachtouris
    except Exception:
493 2d1f5058 Stavros Sachtouris
        token = None
494 2d1f5058 Stavros Sachtouris
    _history = History(
495 2d1f5058 Stavros Sachtouris
        parser.arguments['config'].get('global', 'history_file'), token=token)
496 b6a99832 Stavros Sachtouris
    _history.add(' '.join([exe_string] + argv[1:]))
497 b6a99832 Stavros Sachtouris
    from kamaki.cli import one_command
498 db03f805 Stavros Sachtouris
    one_command.run(cloud, parser, _help)
499 d53062bd Stavros Sachtouris
500 d53062bd Stavros Sachtouris
501 db03f805 Stavros Sachtouris
def run_shell(exe_string, parser, cloud):
502 d53062bd Stavros Sachtouris
    from command_shell import _init_shell
503 db03f805 Stavros Sachtouris
    global kloger
504 db03f805 Stavros Sachtouris
    _cnf = parser.arguments['config']
505 fa7d08b6 Stavros Sachtouris
    auth_base = init_cached_authenticator(_cnf, cloud, kloger)
506 6a2a28bd Stavros Sachtouris
    try:
507 6a2a28bd Stavros Sachtouris
        username, userid = (
508 6a2a28bd Stavros Sachtouris
            auth_base.user_term('name'), auth_base.user_term('id'))
509 6a2a28bd Stavros Sachtouris
    except Exception:
510 6a2a28bd Stavros Sachtouris
        username, userid = '', ''
511 6a2a28bd Stavros Sachtouris
    shell = _init_shell(exe_string, parser, username, userid)
512 074f5027 Stavros Sachtouris
    _load_all_commands(shell.cmd_tree, parser.arguments)
513 844a6bdb Stavros Sachtouris
    shell.run(auth_base, cloud, parser)
514 fd5db045 Stavros Sachtouris
515 1c1fd2fa Stavros Sachtouris
516 362adf50 Stavros Sachtouris
def is_non_API(parser):
517 362adf50 Stavros Sachtouris
    nonAPIs = ('history', 'config')
518 362adf50 Stavros Sachtouris
    for term in parser.unparsed:
519 362adf50 Stavros Sachtouris
        if not term.startswith('-'):
520 362adf50 Stavros Sachtouris
            if term in nonAPIs:
521 362adf50 Stavros Sachtouris
                return True
522 362adf50 Stavros Sachtouris
            return False
523 362adf50 Stavros Sachtouris
    return False
524 362adf50 Stavros Sachtouris
525 362adf50 Stavros Sachtouris
526 1c1fd2fa Stavros Sachtouris
def main():
527 71882bca Stavros Sachtouris
    try:
528 71882bca Stavros Sachtouris
        exe = basename(argv[0])
529 e0da0f90 Stavros Sachtouris
        parser = ArgumentParseManager(exe)
530 7c2247a0 Stavros Sachtouris
531 074f5027 Stavros Sachtouris
        if parser.arguments['version'].value:
532 71882bca Stavros Sachtouris
            exit(0)
533 71882bca Stavros Sachtouris
534 db03f805 Stavros Sachtouris
        _cnf = parser.arguments['config']
535 534e7bbb Stavros Sachtouris
        log_file = _cnf.get('global', 'log_file')
536 f47417e7 Stavros Sachtouris
        if log_file:
537 7637d600 Stavros Sachtouris
            logger.set_log_filename(log_file)
538 9dc724e5 Stavros Sachtouris
        global filelog
539 9dc724e5 Stavros Sachtouris
        filelog = logger.add_file_logger(__name__.split('.')[0])
540 334338ce Stavros Sachtouris
        filelog.info('* Initial Call *\n%s\n- - -' % ' '.join(argv))
541 9dc724e5 Stavros Sachtouris
542 db03f805 Stavros Sachtouris
        cloud = _init_session(parser.arguments, is_non_API(parser))
543 6fb4af77 Stavros Sachtouris
        from kamaki.cli.utils import suggest_missing
544 69691087 Stavros Sachtouris
        global _colors
545 69691087 Stavros Sachtouris
        exclude = ['ansicolors'] if not _colors == 'on' else []
546 69691087 Stavros Sachtouris
        suggest_missing(exclude=exclude)
547 6fb4af77 Stavros Sachtouris
548 b3dd8f4b Stavros Sachtouris
        if parser.unparsed:
549 db03f805 Stavros Sachtouris
            run_one_cmd(exe, parser, cloud)
550 71882bca Stavros Sachtouris
        elif _help:
551 56d84a4e Stavros Sachtouris
            #parser.parser.print_help()
552 56d84a4e Stavros Sachtouris
            parser.print_help()
553 074f5027 Stavros Sachtouris
            _groups_help(parser.arguments)
554 71882bca Stavros Sachtouris
        else:
555 db03f805 Stavros Sachtouris
            run_shell(exe, parser, cloud)
556 71882bca Stavros Sachtouris
    except CLIError as err:
557 b6a99832 Stavros Sachtouris
        print_error_message(err)
558 71882bca Stavros Sachtouris
        if _debug:
559 71882bca Stavros Sachtouris
            raise err
560 71882bca Stavros Sachtouris
        exit(1)
561 ec5d658f Stavros Sachtouris
    except KeyboardInterrupt:
562 ec5d658f Stavros Sachtouris
        print('Canceled by user')
563 ec5d658f Stavros Sachtouris
        exit(1)
564 6069b53b Stavros Sachtouris
    except Exception as er:
565 6069b53b Stavros Sachtouris
        print('Unknown Error: %s' % er)
566 71882bca Stavros Sachtouris
        if _debug:
567 6069b53b Stavros Sachtouris
            raise
568 6069b53b Stavros Sachtouris
        exit(1)