Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / commands / __init__.py @ 534e7bbb

History | View | Annotate | Download (12.4 kB)

1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.command
33

    
34
from kamaki.cli.logger import get_logger
35
from kamaki.cli.utils import (
36
    print_list, print_dict, print_json, print_items, ask_user,
37
    filter_dicts_by_dict)
38
from kamaki.cli.argument import FlagArgument, ValueArgument
39
from kamaki.cli.errors import CLIInvalidArgument
40
from sys import stdin, stdout, stderr
41

    
42
log = get_logger(__name__)
43

    
44

    
45
def DontRaiseKeyError(foo):
46
    def wrap(*args, **kwargs):
47
        try:
48
            return foo(*args, **kwargs)
49
        except KeyError:
50
            return None
51
    return wrap
52

    
53

    
54
def addLogSettings(foo):
55
    def wrap(self, *args, **kwargs):
56
        try:
57
            return foo(self, *args, **kwargs)
58
        finally:
59
            self._set_log_params()
60
            self._update_max_threads
61
    return wrap
62

    
63

    
64
class _command_init(object):
65

    
66
    def __init__(
67
            self,
68
            arguments={}, auth_base=None, cloud=None,
69
            _in=None, _out=None, _err=None):
70
        self._in, self._out, self._err = (
71
            _in or stdin, _out or stdout, _err or stderr)
72
        if hasattr(self, 'arguments'):
73
            arguments.update(self.arguments)
74
        if isinstance(self, _optional_output_cmd):
75
            arguments.update(self.oo_arguments)
76
        if isinstance(self, _optional_json):
77
            arguments.update(self.oj_arguments)
78
        if isinstance(self, _name_filter):
79
            arguments.update(self.nf_arguments)
80
        if isinstance(self, _id_filter):
81
            arguments.update(self.if_arguments)
82
        try:
83
            arguments.update(self.wait_arguments)
84
        except AttributeError:
85
            pass
86
        self.arguments = dict(arguments)
87
        try:
88
            self.config = self['config']
89
        except KeyError:
90
            pass
91
        self.auth_base = auth_base or getattr(self, 'auth_base', None)
92
        self.cloud = cloud or getattr(self, 'cloud', None)
93

    
94
    def write(self, s):
95
        self._out.write(u'%s' % s)
96
        self._out.flush()
97

    
98
    def writeln(self, s=''):
99
        self.write(u'%s\n' % s)
100

    
101
    def error(self, s=''):
102
        self._err.write(u'%s\n' % s)
103
        self._err.flush()
104

    
105
    def print_list(self, *args, **kwargs):
106
        kwargs.setdefault('out', self._out)
107
        return print_list(*args, **kwargs)
108

    
109
    def print_dict(self, *args, **kwargs):
110
        kwargs.setdefault('out', self._out)
111
        return print_dict(*args, **kwargs)
112

    
113
    def print_json(self, *args, **kwargs):
114
        kwargs.setdefault('out', self._out)
115
        return print_json(*args, **kwargs)
116

    
117
    def print_items(self, *args, **kwargs):
118
        kwargs.setdefault('out', self._out)
119
        return print_items(*args, **kwargs)
120

    
121
    def ask_user(self, *args, **kwargs):
122
        kwargs.setdefault('user_in', self._in)
123
        kwargs.setdefault('out', self._out)
124
        return ask_user(*args, **kwargs)
125

    
126
    @DontRaiseKeyError
127
    def _custom_url(self, service):
128
        return self.config.get_cloud(self.cloud, '%s_url' % service)
129

    
130
    @DontRaiseKeyError
131
    def _custom_token(self, service):
132
        return self.config.get_cloud(self.cloud, '%s_token' % service)
133

    
134
    @DontRaiseKeyError
135
    def _custom_type(self, service):
136
        return self.config.get_cloud(self.cloud, '%s_type' % service)
137

    
138
    @DontRaiseKeyError
139
    def _custom_version(self, service):
140
        return self.config.get_cloud(self.cloud, '%s_version' % service)
141

    
142
    def _uuids2usernames(self, uuids):
143
        return self.auth_base.post_user_catalogs(uuids).json['uuid_catalog']
144

    
145
    def _usernames2uuids(self, username):
146
        return self.auth_base.post_user_catalogs(
147
            displaynames=username).json['displayname_catalog']
148

    
149
    def _uuid2username(self, uuid):
150
        return self._uuids2usernames([uuid]).get(uuid, None)
151

    
152
    def _username2uuid(self, username):
153
        return self._usernames2uuids([username]).get(username, None)
154

    
155
    def _set_log_params(self):
156
        try:
157
            self.client.LOG_TOKEN = (
158
                self['config'].get('global', 'log_token').lower() == 'on')
159
        except Exception as e:
160
            log.debug('Failed to read custom log_token setting:'
161
                '%s\n default for log_token is off' % e)
162
        try:
163
            self.client.LOG_DATA = (
164
                self['config'].get('global', 'log_data').lower() == 'on')
165
        except Exception as e:
166
            log.debug('Failed to read custom log_data setting:'
167
                '%s\n default for log_data is off' % e)
168
        try:
169
            self.client.LOG_PID = (
170
                self['config'].get('global', 'log_pid').lower() == 'on')
171
        except Exception as e:
172
            log.debug('Failed to read custom log_pid setting:'
173
                '%s\n default for log_pid is off' % e)
174

    
175
    def _update_max_threads(self):
176
        if getattr(self, 'client', None):
177
            max_threads = int(self['config'].get('global', 'max_threads'))
178
            assert max_threads > 0, 'invalid max_threads config option'
179
            self.client.MAX_THREADS = max_threads
180

    
181
    def _safe_progress_bar(
182
            self, msg, arg='progress_bar', countdown=False, timeout=100):
183
        """Try to get a progress bar, but do not raise errors"""
184
        try:
185
            progress_bar = self.arguments[arg]
186
            progress_bar.file = self._err
187
            gen = progress_bar.get_generator(
188
                msg, countdown=countdown, timeout=timeout)
189
        except Exception:
190
            return (None, None)
191
        return (progress_bar, gen)
192

    
193
    def _safe_progress_bar_finish(self, progress_bar):
194
        try:
195
            progress_bar.finish()
196
        except Exception:
197
            pass
198

    
199
    def __getitem__(self, argterm):
200
        """
201
        :param argterm: (str) the name/label of an argument in self.arguments
202

203
        :returns: the value of the corresponding Argument (not the argument
204
            object)
205

206
        :raises KeyError: if argterm not in self.arguments of this object
207
        """
208
        return self.arguments[argterm].value
209

    
210
    def __setitem__(self, argterm, arg):
211
        """Install an argument as argterm
212
        If argterm points to another argument, the other argument is lost
213

214
        :param argterm: (str)
215

216
        :param arg: (Argument)
217
        """
218
        if not hasattr(self, 'arguments'):
219
            self.arguments = {}
220
        self.arguments[argterm] = arg
221

    
222
    def get_argument_object(self, argterm):
223
        """
224
        :param argterm: (str) the name/label of an argument in self.arguments
225

226
        :returns: the arument object
227

228
        :raises KeyError: if argterm not in self.arguments of this object
229
        """
230
        return self.arguments[argterm]
231

    
232
    def get_argument(self, argterm):
233
        """
234
        :param argterm: (str) the name/label of an argument in self.arguments
235

236
        :returns: the value of the arument object
237

238
        :raises KeyError: if argterm not in self.arguments of this object
239
        """
240
        return self[argterm]
241

    
242

    
243
#  feature classes - inherit them to get special features for your commands
244

    
245

    
246
class OutputFormatArgument(ValueArgument):
247
    """Accepted output formats: json (default)"""
248

    
249
    formats = ('json', )
250

    
251
    def ___init__(self, *args, **kwargs):
252
        super(OutputFormatArgument, self).___init__(*args, **kwargs)
253

    
254
    @property
255
    def value(self):
256
        return self._value
257

    
258
    @value.setter
259
    def value(self, newvalue):
260
        if not newvalue:
261
            self._value = self.default
262
        elif newvalue.lower() in self.formats:
263
            self._value = newvalue.lower
264
        else:
265
            raise CLIInvalidArgument(
266
                'Invalid value %s for argument %s' % (
267
                    newvalue, '/'.join(self.parsed_name)),
268
                details=['Valid output formats: %s' % ', '.join(self.formats)])
269

    
270

    
271
class _optional_output_cmd(object):
272

    
273
    oo_arguments = dict(
274
        with_output=FlagArgument('show response headers', ('--with-output')),
275
        json_output=FlagArgument(
276
            'show headers in json (DEPRECATED from v0.12,'
277
            ' please use --output-format=json instead)', ('-j', '--json'))
278
    )
279

    
280
    def _optional_output(self, r):
281
        if self['json_output']:
282
            print_json(r, out=self._out)
283
        elif self['with_output']:
284
            print_items([r] if isinstance(r, dict) else r, out=self._out)
285

    
286

    
287
class _optional_json(object):
288

    
289
    oj_arguments = dict(
290
        output_format=OutputFormatArgument(
291
            'Show output in chosen output format (%s)' % ', '.join(
292
                OutputFormatArgument.formats),
293
            '--output-format'),
294
        json_output=FlagArgument(
295
            'show output in json (DEPRECATED from v0.12,'
296
            ' please use --output-format instead)', ('-j', '--json'))
297
    )
298

    
299
    def _print(self, output, print_method=print_items, **print_method_kwargs):
300
        if self['json_output'] or self['output_format']:
301
            print_json(output, out=self._out)
302
        else:
303
            print_method_kwargs.setdefault('out', self._out)
304
            print_method(output, **print_method_kwargs)
305

    
306

    
307
class _name_filter(object):
308

    
309
    nf_arguments = dict(
310
        name=ValueArgument('filter by name', '--name'),
311
        name_pref=ValueArgument(
312
            'filter by name prefix (case insensitive)', '--name-prefix'),
313
        name_suff=ValueArgument(
314
            'filter by name suffix (case insensitive)', '--name-suffix'),
315
        name_like=ValueArgument(
316
            'print only if name contains this (case insensitive)',
317
            '--name-like')
318
    )
319

    
320
    def _non_exact_name_filter(self, items):
321
        np, ns, nl = self['name_pref'], self['name_suff'], self['name_like']
322
        return [item for item in items if (
323
            (not np) or item['name'].lower().startswith(np.lower())) and (
324
            (not ns) or item['name'].lower().endswith(ns.lower())) and (
325
            (not nl) or nl.lower() in item['name'].lower())]
326

    
327
    def _exact_name_filter(self, items):
328
        return filter_dicts_by_dict(items, dict(name=self['name'])) if (
329
            self['name']) else items
330

    
331
    def _filter_by_name(self, items):
332
        return self._non_exact_name_filter(self._exact_name_filter(items))
333

    
334

    
335
class _id_filter(object):
336

    
337
    if_arguments = dict(
338
        id=ValueArgument('filter by id', '--id'),
339
        id_pref=ValueArgument(
340
            'filter by id prefix (case insensitive)', '--id-prefix'),
341
        id_suff=ValueArgument(
342
            'filter by id suffix (case insensitive)', '--id-suffix'),
343
        id_like=ValueArgument(
344
            'print only if id contains this (case insensitive)',
345
            '--id-like')
346
    )
347

    
348
    def _non_exact_id_filter(self, items):
349
        np, ns, nl = self['id_pref'], self['id_suff'], self['id_like']
350
        return [item for item in items if (
351
            (not np) or (
352
                '%s' % item['id']).lower().startswith(np.lower())) and (
353
            (not ns) or ('%s' % item['id']).lower().endswith(ns.lower())) and (
354
            (not nl) or nl.lower() in ('%s' % item['id']).lower())]
355

    
356
    def _exact_id_filter(self, items):
357
        return filter_dicts_by_dict(items, dict(id=self['id'])) if (
358
            self['id']) else items
359

    
360
    def _filter_by_id(self, items):
361
        return self._non_exact_id_filter(self._exact_id_filter(items))