Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / __init__.py @ 1716a15d

History | View | Annotate | Download (17.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 d486baec Stavros Sachtouris
from sys import argv, exit, stdout
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 1a3c18fd Stavros Sachtouris
_include = False
50 d486baec Stavros Sachtouris
_verbose = False
51 d486baec Stavros Sachtouris
_colors = False
52 aa5c0458 Stavros Sachtouris
kloger = None
53 9dc724e5 Stavros Sachtouris
filelog = None
54 fd5db045 Stavros Sachtouris
55 b6a99832 Stavros Sachtouris
#  command auxiliary methods
56 b6a99832 Stavros Sachtouris
57 b6a99832 Stavros Sachtouris
_best_match = []
58 b6a99832 Stavros Sachtouris
59 fd5db045 Stavros Sachtouris
60 24ff0a35 Stavros Sachtouris
def _arg2syntax(arg):
61 24ff0a35 Stavros Sachtouris
    return arg.replace(
62 24ff0a35 Stavros Sachtouris
        '____', '[:').replace(
63 2005b18e Stavros Sachtouris
            '___', ':').replace(
64 2005b18e Stavros Sachtouris
                '__', ']').replace(
65 2005b18e Stavros Sachtouris
                    '_', ' ')
66 24ff0a35 Stavros Sachtouris
67 24ff0a35 Stavros Sachtouris
68 d486baec Stavros Sachtouris
def _construct_command_syntax(cls):
69 fd5db045 Stavros Sachtouris
        spec = getargspec(cls.main.im_func)
70 fd5db045 Stavros Sachtouris
        args = spec.args[1:]
71 fd5db045 Stavros Sachtouris
        n = len(args) - len(spec.defaults or ())
72 24ff0a35 Stavros Sachtouris
        required = ' '.join(['<%s>' % _arg2syntax(x) for x in args[:n]])
73 24ff0a35 Stavros Sachtouris
        optional = ' '.join(['[%s]' % _arg2syntax(x) for x in args[n:]])
74 fd5db045 Stavros Sachtouris
        cls.syntax = ' '.join(x for x in [required, optional] if x)
75 fd5db045 Stavros Sachtouris
        if spec.varargs:
76 fd5db045 Stavros Sachtouris
            cls.syntax += ' <%s ...>' % spec.varargs
77 fd5db045 Stavros Sachtouris
78 fd5db045 Stavros Sachtouris
79 d486baec Stavros Sachtouris
def _num_of_matching_terms(basic_list, attack_list):
80 d486baec Stavros Sachtouris
    if not attack_list:
81 75c3fc42 Stavros Sachtouris
        return len(basic_list)
82 fd5db045 Stavros Sachtouris
83 d486baec Stavros Sachtouris
    matching_terms = 0
84 d486baec Stavros Sachtouris
    for i, term in enumerate(basic_list):
85 d486baec Stavros Sachtouris
        try:
86 d486baec Stavros Sachtouris
            if term != attack_list[i]:
87 d486baec Stavros Sachtouris
                break
88 d486baec Stavros Sachtouris
        except IndexError:
89 d486baec Stavros Sachtouris
            break
90 d486baec Stavros Sachtouris
        matching_terms += 1
91 d486baec Stavros Sachtouris
    return matching_terms
92 dfee2caf Stavros Sachtouris
93 fd5db045 Stavros Sachtouris
94 d486baec Stavros Sachtouris
def _update_best_match(name_terms, prefix=[]):
95 d486baec Stavros Sachtouris
    if prefix:
96 d486baec Stavros Sachtouris
        pref_list = prefix if isinstance(prefix, list) else prefix.split('_')
97 d486baec Stavros Sachtouris
    else:
98 d486baec Stavros Sachtouris
        pref_list = []
99 dfee2caf Stavros Sachtouris
100 d486baec Stavros Sachtouris
    num_of_matching_terms = _num_of_matching_terms(name_terms, pref_list)
101 d486baec Stavros Sachtouris
    global _best_match
102 e9533b0c Stavros Sachtouris
    if not prefix:
103 e9533b0c Stavros Sachtouris
        _best_match = []
104 fd5db045 Stavros Sachtouris
105 d486baec Stavros Sachtouris
    if num_of_matching_terms and len(_best_match) <= num_of_matching_terms:
106 d486baec Stavros Sachtouris
        if len(_best_match) < num_of_matching_terms:
107 d486baec Stavros Sachtouris
            _best_match = name_terms[:num_of_matching_terms]
108 d486baec Stavros Sachtouris
        return True
109 d486baec Stavros Sachtouris
    return False
110 d486baec Stavros Sachtouris
111 d486baec Stavros Sachtouris
112 d486baec Stavros Sachtouris
def command(cmd_tree, prefix='', descedants_depth=1):
113 d486baec Stavros Sachtouris
    """Load a class as a command
114 451a7992 Stavros Sachtouris
        e.g. spec_cmd0_cmd1 will be command spec cmd0
115 451a7992 Stavros Sachtouris

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

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

459 7c2247a0 Stavros Sachtouris
    :param paramters: (list of str) a list of parameters
460 7c2247a0 Stavros Sachtouris
    """
461 d486baec Stavros Sachtouris
    global command
462 d486baec Stavros Sachtouris
    def_params = list(command.func_defaults)
463 7c2247a0 Stavros Sachtouris
    def_params[0] = parameters
464 d486baec Stavros Sachtouris
    command.func_defaults = tuple(def_params)
465 d486baec Stavros Sachtouris
466 d486baec Stavros Sachtouris
467 b6a99832 Stavros Sachtouris
#  CLI Choice:
468 d486baec Stavros Sachtouris
469 844a6bdb Stavros Sachtouris
def run_one_cmd(exe_string, parser, auth_base, cloud):
470 b6a99832 Stavros Sachtouris
    global _history
471 b6a99832 Stavros Sachtouris
    _history = History(
472 362adf50 Stavros Sachtouris
        parser.arguments['config'].get_global('history_file'))
473 b6a99832 Stavros Sachtouris
    _history.add(' '.join([exe_string] + argv[1:]))
474 b6a99832 Stavros Sachtouris
    from kamaki.cli import one_command
475 844a6bdb Stavros Sachtouris
    one_command.run(auth_base, cloud, parser, _help)
476 d53062bd Stavros Sachtouris
477 d53062bd Stavros Sachtouris
478 844a6bdb Stavros Sachtouris
def run_shell(exe_string, parser, auth_base, cloud):
479 d53062bd Stavros Sachtouris
    from command_shell import _init_shell
480 6a2a28bd Stavros Sachtouris
    try:
481 6a2a28bd Stavros Sachtouris
        username, userid = (
482 6a2a28bd Stavros Sachtouris
            auth_base.user_term('name'), auth_base.user_term('id'))
483 6a2a28bd Stavros Sachtouris
    except Exception:
484 6a2a28bd Stavros Sachtouris
        username, userid = '', ''
485 6a2a28bd Stavros Sachtouris
    shell = _init_shell(exe_string, parser, username, userid)
486 074f5027 Stavros Sachtouris
    _load_all_commands(shell.cmd_tree, parser.arguments)
487 844a6bdb Stavros Sachtouris
    shell.run(auth_base, cloud, parser)
488 fd5db045 Stavros Sachtouris
489 1c1fd2fa Stavros Sachtouris
490 362adf50 Stavros Sachtouris
def is_non_API(parser):
491 362adf50 Stavros Sachtouris
    nonAPIs = ('history', 'config')
492 362adf50 Stavros Sachtouris
    for term in parser.unparsed:
493 362adf50 Stavros Sachtouris
        if not term.startswith('-'):
494 362adf50 Stavros Sachtouris
            if term in nonAPIs:
495 362adf50 Stavros Sachtouris
                return True
496 362adf50 Stavros Sachtouris
            return False
497 362adf50 Stavros Sachtouris
    return False
498 362adf50 Stavros Sachtouris
499 362adf50 Stavros Sachtouris
500 1c1fd2fa Stavros Sachtouris
def main():
501 71882bca Stavros Sachtouris
    try:
502 71882bca Stavros Sachtouris
        exe = basename(argv[0])
503 e0da0f90 Stavros Sachtouris
        parser = ArgumentParseManager(exe)
504 7c2247a0 Stavros Sachtouris
505 074f5027 Stavros Sachtouris
        if parser.arguments['version'].value:
506 71882bca Stavros Sachtouris
            exit(0)
507 71882bca Stavros Sachtouris
508 362adf50 Stavros Sachtouris
        log_file = parser.arguments['config'].get_global('log_file')
509 f47417e7 Stavros Sachtouris
        if log_file:
510 7637d600 Stavros Sachtouris
            logger.set_log_filename(log_file)
511 9dc724e5 Stavros Sachtouris
        global filelog
512 9dc724e5 Stavros Sachtouris
        filelog = logger.add_file_logger(__name__.split('.')[0])
513 334338ce Stavros Sachtouris
        filelog.info('* Initial Call *\n%s\n- - -' % ' '.join(argv))
514 9dc724e5 Stavros Sachtouris
515 844a6bdb Stavros Sachtouris
        auth_base, cloud = _init_session(parser.arguments, is_non_API(parser))
516 71882bca Stavros Sachtouris
517 6fb4af77 Stavros Sachtouris
        from kamaki.cli.utils import suggest_missing
518 69691087 Stavros Sachtouris
        global _colors
519 69691087 Stavros Sachtouris
        exclude = ['ansicolors'] if not _colors == 'on' else []
520 69691087 Stavros Sachtouris
        suggest_missing(exclude=exclude)
521 6fb4af77 Stavros Sachtouris
522 b3dd8f4b Stavros Sachtouris
        if parser.unparsed:
523 844a6bdb Stavros Sachtouris
            run_one_cmd(exe, parser, auth_base, cloud)
524 71882bca Stavros Sachtouris
        elif _help:
525 e0da0f90 Stavros Sachtouris
            parser.parser.print_help()
526 074f5027 Stavros Sachtouris
            _groups_help(parser.arguments)
527 71882bca Stavros Sachtouris
        else:
528 844a6bdb Stavros Sachtouris
            run_shell(exe, parser, auth_base, cloud)
529 71882bca Stavros Sachtouris
    except CLIError as err:
530 b6a99832 Stavros Sachtouris
        print_error_message(err)
531 71882bca Stavros Sachtouris
        if _debug:
532 71882bca Stavros Sachtouris
            raise err
533 71882bca Stavros Sachtouris
        exit(1)
534 6069b53b Stavros Sachtouris
    except Exception as er:
535 6069b53b Stavros Sachtouris
        print('Unknown Error: %s' % er)
536 71882bca Stavros Sachtouris
        if _debug:
537 6069b53b Stavros Sachtouris
            raise
538 6069b53b Stavros Sachtouris
        exit(1)