Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / __init__.py @ bb50c4ec

History | View | Annotate | Download (18.9 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 36cb577e Stavros Sachtouris
from kamaki.clients.astakos import CachedAstakosClient
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 e589d3da Stavros Sachtouris
            # if _debug:
137 e589d3da 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 a4986073 Stavros Sachtouris
    if exists(cnf.path) and guess < 0.12:
201 a4986073 Stavros Sachtouris
        print('Config file format version >= 0.12 is required (%s found)' % (
202 a4986073 Stavros Sachtouris
            guess))
203 df0045d8 Stavros Sachtouris
        print('Configuration file: %s' % cnf.path)
204 fa382f9e Stavros Sachtouris
        print('Attempting to fix this:')
205 362adf50 Stavros Sachtouris
        print('Calculating changes while preserving information')
206 ce9ccb40 Stavros Sachtouris
        lost_terms = cnf.rescue_old_file()
207 ce9ccb40 Stavros Sachtouris
        print('... DONE')
208 362adf50 Stavros Sachtouris
        if lost_terms:
209 362adf50 Stavros Sachtouris
            print 'The following information will NOT be preserved:'
210 362adf50 Stavros Sachtouris
            print '\t', '\n\t'.join(lost_terms)
211 fa382f9e Stavros Sachtouris
        print('Kamaki is ready to convert the config file')
212 904091dd Stavros Sachtouris
        stdout.write('Create (overwrite) file %s ? [y/N] ' % cnf.path)
213 ce9ccb40 Stavros Sachtouris
        from sys import stdin
214 ce9ccb40 Stavros Sachtouris
        reply = stdin.readline()
215 ce9ccb40 Stavros Sachtouris
        if reply in ('Y\n', 'y\n'):
216 ce9ccb40 Stavros Sachtouris
            cnf.write()
217 ce9ccb40 Stavros Sachtouris
            print('... DONE')
218 ce9ccb40 Stavros Sachtouris
        else:
219 ce9ccb40 Stavros Sachtouris
            print('... ABORTING')
220 ce9ccb40 Stavros Sachtouris
            raise CLIError(
221 ce9ccb40 Stavros Sachtouris
                'Invalid format for config file %s' % cnf.path,
222 362adf50 Stavros Sachtouris
                importance=3, details=[
223 fa382f9e Stavros Sachtouris
                    'Please, update config file',
224 362adf50 Stavros Sachtouris
                    'For automatic conversion, rerun and say Y'])
225 ce9ccb40 Stavros Sachtouris
226 ce9ccb40 Stavros Sachtouris
227 362adf50 Stavros Sachtouris
def _init_session(arguments, is_non_API=False):
228 844a6bdb Stavros Sachtouris
    """
229 db03f805 Stavros Sachtouris
    :returns: cloud name
230 844a6bdb Stavros Sachtouris
    """
231 d486baec Stavros Sachtouris
    global _help
232 d486baec Stavros Sachtouris
    _help = arguments['help'].value
233 d486baec Stavros Sachtouris
    global _debug
234 d486baec Stavros Sachtouris
    _debug = arguments['debug'].value
235 d486baec Stavros Sachtouris
    global _verbose
236 d486baec Stavros Sachtouris
    _verbose = arguments['verbose'].value
237 cb4a5d9c Stavros Sachtouris
    _cnf = arguments['config']
238 df0045d8 Stavros Sachtouris
239 f1d06d74 Stavros Sachtouris
    _silent = arguments['silent'].value
240 f6822a26 Stavros Sachtouris
    _setup_logging(_silent, _debug, _verbose)
241 f1d06d74 Stavros Sachtouris
242 df0045d8 Stavros Sachtouris
    if _help or is_non_API:
243 db03f805 Stavros Sachtouris
        return None
244 df0045d8 Stavros Sachtouris
245 ce9ccb40 Stavros Sachtouris
    _check_config_version(_cnf.value)
246 3f0eae61 Stavros Sachtouris
247 d486baec Stavros Sachtouris
    global _colors
248 534e7bbb Stavros Sachtouris
    _colors = _cnf.value.get('global', 'colors')
249 87565d2c Stavros Sachtouris
    if not (stdout.isatty() and _colors == 'on'):
250 4f6a21f6 Stavros Sachtouris
        from kamaki.cli.utils import remove_colors
251 4f6a21f6 Stavros Sachtouris
        remove_colors()
252 362adf50 Stavros Sachtouris
253 c825ddc9 Stavros Sachtouris
    cloud = arguments['cloud'].value or _cnf.value.get(
254 c825ddc9 Stavros Sachtouris
        'global', 'default_cloud')
255 c825ddc9 Stavros Sachtouris
    if not cloud:
256 c825ddc9 Stavros Sachtouris
        num_of_clouds = len(_cnf.value.keys('cloud'))
257 c825ddc9 Stavros Sachtouris
        if num_of_clouds == 1:
258 c825ddc9 Stavros Sachtouris
            cloud = _cnf.value.keys('cloud')[0]
259 df0045d8 Stavros Sachtouris
        elif num_of_clouds > 1:
260 c825ddc9 Stavros Sachtouris
            raise CLIError(
261 44101d8a Stavros Sachtouris
                'Found %s clouds but none of them is set as default' % (
262 44101d8a Stavros Sachtouris
                    num_of_clouds),
263 c825ddc9 Stavros Sachtouris
                importance=2, details=[
264 c825ddc9 Stavros Sachtouris
                    'Please, choose one of the following cloud names:',
265 c825ddc9 Stavros Sachtouris
                    ', '.join(_cnf.value.keys('cloud')),
266 44101d8a Stavros Sachtouris
                    'To see all cloud settings:',
267 44101d8a Stavros Sachtouris
                    '  kamaki config get cloud.<cloud name>',
268 c825ddc9 Stavros Sachtouris
                    'To set a default cloud:',
269 c825ddc9 Stavros Sachtouris
                    '  kamaki config set default_cloud <cloud name>',
270 c825ddc9 Stavros Sachtouris
                    'To pick a cloud for the current session, use --cloud:',
271 c825ddc9 Stavros Sachtouris
                    '  kamaki --cloud=<cloud name> ...'])
272 144b3551 Stavros Sachtouris
    if not cloud in _cnf.value.keys('cloud'):
273 362adf50 Stavros Sachtouris
        raise CLIError(
274 df0045d8 Stavros Sachtouris
            'No cloud%s is configured' % ((' "%s"' % cloud) if cloud else ''),
275 362adf50 Stavros Sachtouris
            importance=3, details=[
276 df0045d8 Stavros Sachtouris
                'To configure a new cloud "%s", find and set the' % (
277 df0045d8 Stavros Sachtouris
                    cloud or '<cloud name>'),
278 362adf50 Stavros Sachtouris
                'single authentication URL and token:',
279 df0045d8 Stavros Sachtouris
                '  kamaki config set cloud.%s.url <URL>' % (
280 df0045d8 Stavros Sachtouris
                    cloud or '<cloud name>'),
281 df0045d8 Stavros Sachtouris
                '  kamaki config set cloud.%s.token <t0k3n>' % (
282 df0045d8 Stavros Sachtouris
                    cloud or '<cloud name>')])
283 844a6bdb Stavros Sachtouris
    auth_args = dict()
284 844a6bdb Stavros Sachtouris
    for term in ('url', 'token'):
285 c825ddc9 Stavros Sachtouris
        try:
286 c825ddc9 Stavros Sachtouris
            auth_args[term] = _cnf.get_cloud(cloud, term)
287 dc897a7e Stavros Sachtouris
        except KeyError or IndexError:
288 c825ddc9 Stavros Sachtouris
            auth_args[term] = ''
289 844a6bdb Stavros Sachtouris
        if not auth_args[term]:
290 844a6bdb Stavros Sachtouris
            raise CLIError(
291 b4ed3a7e Stavros Sachtouris
                'No authentication %s provided for cloud "%s"' % (
292 b4ed3a7e Stavros Sachtouris
                    term.upper(), cloud),
293 844a6bdb Stavros Sachtouris
                importance=3, details=[
294 b4ed3a7e Stavros Sachtouris
                    'Set a %s for cloud %s:' % (term.upper(), cloud),
295 44101d8a Stavros Sachtouris
                    '  kamaki config set cloud.%s.%s <%s>' % (
296 b4ed3a7e Stavros Sachtouris
                        cloud, term, term.upper())])
297 db03f805 Stavros Sachtouris
    return cloud
298 db03f805 Stavros Sachtouris
299 362adf50 Stavros Sachtouris
300 fa7d08b6 Stavros Sachtouris
def init_cached_authenticator(config_argument, cloud, logger):
301 8cec3671 Stavros Sachtouris
    try:
302 fa7d08b6 Stavros Sachtouris
        _cnf = config_argument.value
303 fa7d08b6 Stavros Sachtouris
        url = _cnf.get_cloud(cloud, 'url')
304 fa7d08b6 Stavros Sachtouris
        tokens = _cnf.get_cloud(cloud, 'token').split()
305 fa7d08b6 Stavros Sachtouris
        auth_base, failed = None, []
306 fa7d08b6 Stavros Sachtouris
        for token in tokens:
307 dc897a7e Stavros Sachtouris
            try:
308 dc897a7e Stavros Sachtouris
                if auth_base:
309 dc897a7e Stavros Sachtouris
                    auth_base.authenticate(token)
310 dc897a7e Stavros Sachtouris
                else:
311 36cb577e Stavros Sachtouris
                    tmp_base = CachedAstakosClient(url, token)
312 f5ff79d9 Stavros Sachtouris
                    from kamaki.cli.commands import _command_init
313 fa7d08b6 Stavros Sachtouris
                    fake_cmd = _command_init(dict(config=config_argument))
314 f5ff79d9 Stavros Sachtouris
                    fake_cmd.client = auth_base
315 f5ff79d9 Stavros Sachtouris
                    fake_cmd._set_log_params()
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 54b7c1de Stavros Sachtouris
def is_non_API(parser):
490 54b7c1de Stavros Sachtouris
    nonAPIs = ('history', 'config')
491 54b7c1de Stavros Sachtouris
    for term in parser.unparsed:
492 54b7c1de Stavros Sachtouris
        if not term.startswith('-'):
493 54b7c1de Stavros Sachtouris
            if term in nonAPIs:
494 54b7c1de Stavros Sachtouris
                return True
495 54b7c1de Stavros Sachtouris
            return False
496 54b7c1de Stavros Sachtouris
    return False
497 54b7c1de Stavros Sachtouris
498 54b7c1de Stavros Sachtouris
499 392d902d Stavros Sachtouris
def main(func):
500 54b7c1de Stavros Sachtouris
    def wrap():
501 54b7c1de Stavros Sachtouris
        try:
502 54b7c1de Stavros Sachtouris
            exe = basename(argv[0])
503 54b7c1de Stavros Sachtouris
            parser = ArgumentParseManager(exe)
504 54b7c1de Stavros Sachtouris
505 54b7c1de Stavros Sachtouris
            if parser.arguments['version'].value:
506 54b7c1de Stavros Sachtouris
                exit(0)
507 54b7c1de Stavros Sachtouris
508 54b7c1de Stavros Sachtouris
            _cnf = parser.arguments['config']
509 54b7c1de Stavros Sachtouris
            log_file = _cnf.get('global', 'log_file')
510 54b7c1de Stavros Sachtouris
            if log_file:
511 54b7c1de Stavros Sachtouris
                logger.set_log_filename(log_file)
512 54b7c1de Stavros Sachtouris
            global filelog
513 54b7c1de Stavros Sachtouris
            filelog = logger.add_file_logger(__name__.split('.')[0])
514 54b7c1de Stavros Sachtouris
            filelog.info('* Initial Call *\n%s\n- - -' % ' '.join(argv))
515 54b7c1de Stavros Sachtouris
516 54b7c1de Stavros Sachtouris
            from kamaki.cli.utils import suggest_missing
517 54b7c1de Stavros Sachtouris
            global _colors
518 54b7c1de Stavros Sachtouris
            exclude = ['ansicolors'] if not _colors == 'on' else []
519 54b7c1de Stavros Sachtouris
            suggest_missing(exclude=exclude)
520 392d902d Stavros Sachtouris
            func(exe, parser)
521 54b7c1de Stavros Sachtouris
        except CLIError as err:
522 54b7c1de Stavros Sachtouris
            print_error_message(err)
523 54b7c1de Stavros Sachtouris
            if _debug:
524 54b7c1de Stavros Sachtouris
                raise err
525 54b7c1de Stavros Sachtouris
            exit(1)
526 54b7c1de Stavros Sachtouris
        except KeyboardInterrupt:
527 54b7c1de Stavros Sachtouris
            print('Canceled by user')
528 54b7c1de Stavros Sachtouris
            exit(1)
529 54b7c1de Stavros Sachtouris
        except Exception as er:
530 54b7c1de Stavros Sachtouris
            print('Unknown Error: %s' % er)
531 54b7c1de Stavros Sachtouris
            if _debug:
532 54b7c1de Stavros Sachtouris
                raise
533 54b7c1de Stavros Sachtouris
            exit(1)
534 54b7c1de Stavros Sachtouris
    return wrap
535 d53062bd Stavros Sachtouris
536 d53062bd Stavros Sachtouris
537 54b7c1de Stavros Sachtouris
@main
538 c22183b9 Stavros Sachtouris
def run_shell(exe, parser):
539 c22183b9 Stavros Sachtouris
    parser.arguments['help'].value = False
540 c22183b9 Stavros Sachtouris
    cloud = _init_session(parser.arguments)
541 d53062bd Stavros Sachtouris
    from command_shell import _init_shell
542 db03f805 Stavros Sachtouris
    global kloger
543 db03f805 Stavros Sachtouris
    _cnf = parser.arguments['config']
544 fa7d08b6 Stavros Sachtouris
    auth_base = init_cached_authenticator(_cnf, cloud, kloger)
545 6a2a28bd Stavros Sachtouris
    try:
546 6a2a28bd Stavros Sachtouris
        username, userid = (
547 6a2a28bd Stavros Sachtouris
            auth_base.user_term('name'), auth_base.user_term('id'))
548 6a2a28bd Stavros Sachtouris
    except Exception:
549 6a2a28bd Stavros Sachtouris
        username, userid = '', ''
550 54b7c1de Stavros Sachtouris
    shell = _init_shell(exe, parser, username, userid)
551 074f5027 Stavros Sachtouris
    _load_all_commands(shell.cmd_tree, parser.arguments)
552 844a6bdb Stavros Sachtouris
    shell.run(auth_base, cloud, parser)
553 fd5db045 Stavros Sachtouris
554 1c1fd2fa Stavros Sachtouris
555 54b7c1de Stavros Sachtouris
@main
556 c22183b9 Stavros Sachtouris
def run_one_cmd(exe, parser):
557 c22183b9 Stavros Sachtouris
    cloud = _init_session(parser.arguments, is_non_API(parser))
558 54b7c1de Stavros Sachtouris
    if parser.unparsed:
559 54b7c1de Stavros Sachtouris
        global _history
560 50a32c37 Stavros Sachtouris
        cnf = parser.arguments['config']
561 54b7c1de Stavros Sachtouris
        try:
562 50a32c37 Stavros Sachtouris
            token = cnf.get_cloud(cloud, 'token').split()[0]
563 54b7c1de Stavros Sachtouris
        except Exception:
564 54b7c1de Stavros Sachtouris
            token = None
565 50a32c37 Stavros Sachtouris
        _history = History(cnf.get('global', 'history_file'), token=token)
566 50a32c37 Stavros Sachtouris
        _history.limit = cnf.get('global', 'history_limit')
567 54b7c1de Stavros Sachtouris
        _history.add(' '.join([exe] + argv[1:]))
568 54b7c1de Stavros Sachtouris
        from kamaki.cli import one_command
569 54b7c1de Stavros Sachtouris
        one_command.run(cloud, parser, _help)
570 54b7c1de Stavros Sachtouris
    else:
571 54b7c1de Stavros Sachtouris
        parser.print_help()
572 54b7c1de Stavros Sachtouris
        _groups_help(parser.arguments)
573 54b7c1de Stavros Sachtouris
        print('kamaki-shell: An interactive command line shell')