Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / __init__.py @ 264a13f7

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 f5ff79d9 Stavros Sachtouris
                    fake_cmd._update_max_threads()
316 fa7d08b6 Stavros Sachtouris
                    tmp_base.authenticate(token)
317 fa7d08b6 Stavros Sachtouris
                    auth_base = tmp_base
318 dc897a7e Stavros Sachtouris
            except ClientError as ce:
319 dc897a7e Stavros Sachtouris
                if ce.status in (401, ):
320 db03f805 Stavros Sachtouris
                    logger.warning(
321 db03f805 Stavros Sachtouris
                        'WARNING: Failed to authenticate token %s' % token)
322 fa7d08b6 Stavros Sachtouris
                    failed.append(token)
323 dc897a7e Stavros Sachtouris
                else:
324 dc897a7e Stavros Sachtouris
                    raise
325 fa7d08b6 Stavros Sachtouris
        for token in failed:
326 fa7d08b6 Stavros Sachtouris
            r = raw_input(
327 fa7d08b6 Stavros Sachtouris
                'Token %s failed to authenticate. Remove it? [y/N]: ' % token)
328 fa7d08b6 Stavros Sachtouris
            if r in ('y', 'Y'):
329 fa7d08b6 Stavros Sachtouris
                tokens.remove(token)
330 fa7d08b6 Stavros Sachtouris
        if set(failed).difference(tokens):
331 fa7d08b6 Stavros Sachtouris
            _cnf.set_cloud(cloud, 'token', ' '.join(tokens))
332 fa7d08b6 Stavros Sachtouris
            _cnf.write()
333 fa7d08b6 Stavros Sachtouris
        if tokens:
334 fa7d08b6 Stavros Sachtouris
            return auth_base
335 fa7d08b6 Stavros Sachtouris
        logger.warning('WARNING: cloud.%s.token is now empty' % cloud)
336 8cec3671 Stavros Sachtouris
    except AssertionError as ae:
337 db03f805 Stavros Sachtouris
        logger.warning('WARNING: Failed to load authenticator [%s]' % ae)
338 fa7d08b6 Stavros Sachtouris
    return None
339 d486baec Stavros Sachtouris
340 d486baec Stavros Sachtouris
341 d486baec Stavros Sachtouris
def _load_spec_module(spec, arguments, module):
342 f1d06d74 Stavros Sachtouris
    global kloger
343 f724cd35 Stavros Sachtouris
    if not spec:
344 d486baec Stavros Sachtouris
        return None
345 d486baec Stavros Sachtouris
    pkg = None
346 d486baec Stavros Sachtouris
    for location in cmd_spec_locations:
347 f724cd35 Stavros Sachtouris
        location += spec if location == '' else '.%s' % spec
348 d486baec Stavros Sachtouris
        try:
349 f1d06d74 Stavros Sachtouris
            kloger.debug('Import %s from %s' % ([module], location))
350 d486baec Stavros Sachtouris
            pkg = __import__(location, fromlist=[module])
351 f1d06d74 Stavros Sachtouris
            kloger.debug('\t...OK')
352 d486baec Stavros Sachtouris
            return pkg
353 f724cd35 Stavros Sachtouris
        except ImportError as ie:
354 f1d06d74 Stavros Sachtouris
            kloger.debug('\t...Failed')
355 d486baec Stavros Sachtouris
            continue
356 f724cd35 Stavros Sachtouris
    if not pkg:
357 5506d7aa Stavros Sachtouris
        msg = 'Loading command group %s failed: %s' % (spec, ie)
358 f1d06d74 Stavros Sachtouris
        msg += '\nHINT: use a text editor to remove all global.*_cli'
359 f1d06d74 Stavros Sachtouris
        msg += '\n\tsettings from the configuration file'
360 f1d06d74 Stavros Sachtouris
        kloger.debug(msg)
361 d486baec Stavros Sachtouris
    return pkg
362 d486baec Stavros Sachtouris
363 d486baec Stavros Sachtouris
364 d486baec Stavros Sachtouris
def _groups_help(arguments):
365 d486baec Stavros Sachtouris
    global _debug
366 aa5c0458 Stavros Sachtouris
    global kloger
367 d486baec Stavros Sachtouris
    descriptions = {}
368 a7aacf12 Stavros Sachtouris
    acceptable_groups = arguments['config'].groups
369 a7aacf12 Stavros Sachtouris
    for cmd_group, spec in arguments['config'].cli_specs:
370 d486baec Stavros Sachtouris
        pkg = _load_spec_module(spec, arguments, '_commands')
371 d486baec Stavros Sachtouris
        if pkg:
372 f724cd35 Stavros Sachtouris
            cmds = getattr(pkg, '_commands')
373 d486baec Stavros Sachtouris
            try:
374 d252a7a8 Stavros Sachtouris
                for cmd_tree in cmds:
375 d252a7a8 Stavros Sachtouris
                    if cmd_tree.name in acceptable_groups:
376 d252a7a8 Stavros Sachtouris
                        descriptions[cmd_tree.name] = cmd_tree.description
377 d486baec Stavros Sachtouris
            except TypeError:
378 d486baec Stavros Sachtouris
                if _debug:
379 f724cd35 Stavros Sachtouris
                    kloger.warning(
380 eb46e9a1 Stavros Sachtouris
                        'No cmd description (help) for module %s' % cmd_group)
381 d486baec Stavros Sachtouris
        elif _debug:
382 f724cd35 Stavros Sachtouris
            kloger.warning('Loading of %s cmd spec failed' % cmd_group)
383 d486baec Stavros Sachtouris
    print('\nOptions:\n - - - -')
384 d486baec Stavros Sachtouris
    print_dict(descriptions)
385 d486baec Stavros Sachtouris
386 d486baec Stavros Sachtouris
387 b6a99832 Stavros Sachtouris
def _load_all_commands(cmd_tree, arguments):
388 24ff0a35 Stavros Sachtouris
    _cnf = arguments['config']
389 a7aacf12 Stavros Sachtouris
    for cmd_group, spec in _cnf.cli_specs:
390 b6a99832 Stavros Sachtouris
        try:
391 b6a99832 Stavros Sachtouris
            spec_module = _load_spec_module(spec, arguments, '_commands')
392 b6a99832 Stavros Sachtouris
            spec_commands = getattr(spec_module, '_commands')
393 b6a99832 Stavros Sachtouris
        except AttributeError:
394 b6a99832 Stavros Sachtouris
            if _debug:
395 b6a99832 Stavros Sachtouris
                global kloger
396 f724cd35 Stavros Sachtouris
                kloger.warning('No valid description for %s' % cmd_group)
397 b6a99832 Stavros Sachtouris
            continue
398 b6a99832 Stavros Sachtouris
        for spec_tree in spec_commands:
399 f724cd35 Stavros Sachtouris
            if spec_tree.name == cmd_group:
400 b6a99832 Stavros Sachtouris
                cmd_tree.add_tree(spec_tree)
401 b6a99832 Stavros Sachtouris
                break
402 b6a99832 Stavros Sachtouris
403 b6a99832 Stavros Sachtouris
404 b6a99832 Stavros Sachtouris
#  Methods to be used by CLI implementations
405 b6a99832 Stavros Sachtouris
406 b6a99832 Stavros Sachtouris
407 b6a99832 Stavros Sachtouris
def print_subcommands_help(cmd):
408 d486baec Stavros Sachtouris
    printout = {}
409 eb46e9a1 Stavros Sachtouris
    for subcmd in cmd.subcommands.values():
410 4f6a21f6 Stavros Sachtouris
        spec, sep, print_path = subcmd.path.partition('_')
411 eb46e9a1 Stavros Sachtouris
        printout[print_path.replace('_', ' ')] = subcmd.help
412 d486baec Stavros Sachtouris
    if printout:
413 d486baec Stavros Sachtouris
        print('\nOptions:\n - - - -')
414 d486baec Stavros Sachtouris
        print_dict(printout)
415 d486baec Stavros Sachtouris
416 d486baec Stavros Sachtouris
417 b6a99832 Stavros Sachtouris
def update_parser_help(parser, cmd):
418 d486baec Stavros Sachtouris
    global _best_match
419 7c2247a0 Stavros Sachtouris
    parser.syntax = parser.syntax.split('<')[0]
420 7c2247a0 Stavros Sachtouris
    parser.syntax += ' '.join(_best_match)
421 d486baec Stavros Sachtouris
422 a71bb904 Stavros Sachtouris
    description = ''
423 d486baec Stavros Sachtouris
    if cmd.is_command:
424 eb46e9a1 Stavros Sachtouris
        cls = cmd.cmd_class
425 7c2247a0 Stavros Sachtouris
        parser.syntax += ' ' + cls.syntax
426 7c2247a0 Stavros Sachtouris
        parser.update_arguments(cls().arguments)
427 eb46e9a1 Stavros Sachtouris
        description = getattr(cls, 'long_description', '').strip()
428 d486baec Stavros Sachtouris
    else:
429 7c2247a0 Stavros Sachtouris
        parser.syntax += ' <...>'
430 eb46e9a1 Stavros Sachtouris
    parser.parser.description = (
431 eb46e9a1 Stavros Sachtouris
        cmd.help + ('\n' if description else '')) if cmd.help else description
432 d486baec Stavros Sachtouris
433 d486baec Stavros Sachtouris
434 3185cd6d Stavros Sachtouris
def print_error_message(cli_err, out=stderr):
435 d486baec Stavros Sachtouris
    errmsg = '%s' % cli_err
436 d486baec Stavros Sachtouris
    if cli_err.importance == 1:
437 d486baec Stavros Sachtouris
        errmsg = magenta(errmsg)
438 d486baec Stavros Sachtouris
    elif cli_err.importance == 2:
439 d486baec Stavros Sachtouris
        errmsg = yellow(errmsg)
440 d486baec Stavros Sachtouris
    elif cli_err.importance > 2:
441 d486baec Stavros Sachtouris
        errmsg = red(errmsg)
442 3185cd6d Stavros Sachtouris
    out.write(errmsg)
443 66f1ff99 Stavros Sachtouris
    for errmsg in cli_err.details:
444 3185cd6d Stavros Sachtouris
        out.write('|  %s\n' % errmsg)
445 3185cd6d Stavros Sachtouris
        out.flush()
446 d486baec Stavros Sachtouris
447 d486baec Stavros Sachtouris
448 b6a99832 Stavros Sachtouris
def exec_cmd(instance, cmd_args, help_method):
449 fd5db045 Stavros Sachtouris
    try:
450 fd5db045 Stavros Sachtouris
        return instance.main(*cmd_args)
451 fd5db045 Stavros Sachtouris
    except TypeError as err:
452 fd5db045 Stavros Sachtouris
        if err.args and err.args[0].startswith('main()'):
453 fd5db045 Stavros Sachtouris
            print(magenta('Syntax error'))
454 d486baec Stavros Sachtouris
            if _debug:
455 d486baec Stavros Sachtouris
                raise err
456 d486baec Stavros Sachtouris
            if _verbose:
457 fd5db045 Stavros Sachtouris
                print(unicode(err))
458 fd5db045 Stavros Sachtouris
            help_method()
459 fd5db045 Stavros Sachtouris
        else:
460 fd5db045 Stavros Sachtouris
            raise
461 fd5db045 Stavros Sachtouris
    return 1
462 fd5db045 Stavros Sachtouris
463 f247bcb4 Stavros Sachtouris
464 b6a99832 Stavros Sachtouris
def get_command_group(unparsed, arguments):
465 a7aacf12 Stavros Sachtouris
    groups = arguments['config'].groups
466 b6a99832 Stavros Sachtouris
    for term in unparsed:
467 b6a99832 Stavros Sachtouris
        if term.startswith('-'):
468 b6a99832 Stavros Sachtouris
            continue
469 b6a99832 Stavros Sachtouris
        if term in groups:
470 b6a99832 Stavros Sachtouris
            unparsed.remove(term)
471 b6a99832 Stavros Sachtouris
            return term
472 b6a99832 Stavros Sachtouris
        return None
473 b6a99832 Stavros Sachtouris
    return None
474 b6a99832 Stavros Sachtouris
475 b6a99832 Stavros Sachtouris
476 7c2247a0 Stavros Sachtouris
def set_command_params(parameters):
477 7c2247a0 Stavros Sachtouris
    """Add a parameters list to a command
478 7c2247a0 Stavros Sachtouris

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