1 # Copyright 2011-2013 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
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.
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.
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
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,
38 from kamaki.cli.argument import FlagArgument, ValueArgument
39 from kamaki.cli.errors import CLIInvalidArgument
40 from sys import stdin, stdout, stderr
42 log = get_logger(__name__)
45 def DontRaiseKeyError(func):
46 def wrap(*args, **kwargs):
48 return func(*args, **kwargs)
54 def addLogSettings(func):
55 def wrap(self, *args, **kwargs):
57 return func(self, *args, **kwargs)
59 self._set_log_params()
63 class _command_init(object):
65 # self.arguments (dict) contains all non-positional arguments
66 # self.required (list or tuple) contains required argument keys
67 # if it is a list, at least one of these arguments is required
68 # if it is a tuple, all arguments are required
69 # Lists and tuples can nest other lists and/or tuples
73 arguments={}, auth_base=None, cloud=None,
74 _in=None, _out=None, _err=None):
75 self._in, self._out, self._err = (
76 _in or stdin, _out or stdout, _err or stderr)
77 self.required = getattr(self, 'required', None)
78 if hasattr(self, 'arguments'):
79 arguments.update(self.arguments)
80 if isinstance(self, _optional_output_cmd):
81 arguments.update(self.oo_arguments)
82 if isinstance(self, _optional_json):
83 arguments.update(self.oj_arguments)
84 if isinstance(self, _name_filter):
85 arguments.update(self.nf_arguments)
86 if isinstance(self, _id_filter):
87 arguments.update(self.if_arguments)
89 arguments.update(self.wait_arguments)
90 except AttributeError:
92 self.arguments = dict(arguments)
94 self.config = self['config']
97 self.auth_base = auth_base or getattr(self, 'auth_base', None)
98 self.cloud = cloud or getattr(self, 'cloud', None)
101 self._out.write('%s' % s)
104 def writeln(self, s=''):
105 self.write('%s\n' % s)
107 def error(self, s=''):
108 self._err.write('%s\n' % s)
111 def print_list(self, *args, **kwargs):
112 kwargs.setdefault('out', self._out)
113 return print_list(*args, **kwargs)
115 def print_dict(self, *args, **kwargs):
116 kwargs.setdefault('out', self._out)
117 return print_dict(*args, **kwargs)
119 def print_json(self, *args, **kwargs):
120 kwargs.setdefault('out', self._out)
121 return print_json(*args, **kwargs)
123 def print_items(self, *args, **kwargs):
124 kwargs.setdefault('out', self._out)
125 return print_items(*args, **kwargs)
127 def ask_user(self, *args, **kwargs):
128 kwargs.setdefault('user_in', self._in)
129 kwargs.setdefault('out', self._out)
130 return ask_user(*args, **kwargs)
133 def _custom_url(self, service):
134 return self.config.get_cloud(self.cloud, '%s_url' % service)
137 def _custom_token(self, service):
138 return self.config.get_cloud(self.cloud, '%s_token' % service)
141 def _custom_type(self, service):
142 return self.config.get_cloud(self.cloud, '%s_type' % service)
145 def _custom_version(self, service):
146 return self.config.get_cloud(self.cloud, '%s_version' % service)
148 def _uuids2usernames(self, uuids):
149 return self.auth_base.post_user_catalogs(uuids)
151 def _usernames2uuids(self, username):
152 return self.auth_base.post_user_catalogs(displaynames=username)
154 def _uuid2username(self, uuid):
155 return self._uuids2usernames([uuid]).get(uuid, None)
157 def _username2uuid(self, username):
158 return self._usernames2uuids([username]).get(username, None)
160 def _set_log_params(self):
162 self.client.LOG_TOKEN = (
163 self['config'].get('global', 'log_token').lower() == 'on')
164 except Exception as e:
165 log.debug('Failed to read custom log_token setting:'
166 '%s\n default for log_token is off' % e)
168 self.client.LOG_DATA = (
169 self['config'].get('global', 'log_data').lower() == 'on')
170 except Exception as e:
171 log.debug('Failed to read custom log_data setting:'
172 '%s\n default for log_data is off' % e)
174 self.client.LOG_PID = (
175 self['config'].get('global', 'log_pid').lower() == 'on')
176 except Exception as e:
177 log.debug('Failed to read custom log_pid setting:'
178 '%s\n default for log_pid is off' % e)
180 def _safe_progress_bar(
181 self, msg, arg='progress_bar', countdown=False, timeout=100):
182 """Try to get a progress bar, but do not raise errors"""
184 progress_bar = self.arguments[arg]
185 progress_bar.file = self._err
186 gen = progress_bar.get_generator(
187 msg, countdown=countdown, timeout=timeout)
190 return (progress_bar, gen)
192 def _safe_progress_bar_finish(self, progress_bar):
194 progress_bar.finish()
198 def __getitem__(self, argterm):
200 :param argterm: (str) the name/label of an argument in self.arguments
202 :returns: the value of the corresponding Argument (not the argument
205 :raises KeyError: if argterm not in self.arguments of this object
207 return self.arguments[argterm].value
209 def __setitem__(self, argterm, arg):
210 """Install an argument as argterm
211 If argterm points to another argument, the other argument is lost
213 :param argterm: (str)
215 :param arg: (Argument)
217 if not hasattr(self, 'arguments'):
219 self.arguments[argterm] = arg
221 def get_argument_object(self, argterm):
223 :param argterm: (str) the name/label of an argument in self.arguments
225 :returns: the arument object
227 :raises KeyError: if argterm not in self.arguments of this object
229 return self.arguments[argterm]
231 def get_argument(self, argterm):
233 :param argterm: (str) the name/label of an argument in self.arguments
235 :returns: the value of the arument object
237 :raises KeyError: if argterm not in self.arguments of this object
242 # feature classes - inherit them to get special features for your commands
245 class OutputFormatArgument(ValueArgument):
246 """Accepted output formats: json (default)"""
250 def ___init__(self, *args, **kwargs):
251 super(OutputFormatArgument, self).___init__(*args, **kwargs)
255 return getattr(self, '_value', None)
258 def value(self, newvalue):
260 self._value = self.default
261 elif newvalue.lower() in self.formats:
262 self._value = newvalue.lower
264 raise CLIInvalidArgument(
265 'Invalid value %s for argument %s' % (
266 newvalue, self.lvalue),
267 details=['Valid output formats: %s' % ', '.join(self.formats)])
270 class _optional_output_cmd(object):
273 with_output=FlagArgument('show response headers', ('--with-output')),
274 json_output=FlagArgument(
275 'show headers in json (DEPRECATED from v0.12,'
276 ' please use --output-format=json instead)', ('-j', '--json'))
279 def _optional_output(self, r):
280 if self['json_output']:
281 print_json(r, out=self._out)
282 elif self['with_output']:
283 print_items([r] if isinstance(r, dict) else r, out=self._out)
286 class _optional_json(object):
289 output_format=OutputFormatArgument(
290 'Show output in chosen output format (%s)' % ', '.join(
291 OutputFormatArgument.formats),
293 json_output=FlagArgument(
294 'show output in json (DEPRECATED from v0.12,'
295 ' please use --output-format instead)', ('-j', '--json'))
298 def _print(self, output, print_method=print_items, **print_method_kwargs):
299 if self['json_output'] or self['output_format']:
300 print_json(output, out=self._out)
302 print_method_kwargs.setdefault('out', self._out)
303 print_method(output, **print_method_kwargs)
306 class _name_filter(object):
309 name=ValueArgument('filter by name', '--name'),
310 name_pref=ValueArgument(
311 'filter by name prefix (case insensitive)', '--name-prefix'),
312 name_suff=ValueArgument(
313 'filter by name suffix (case insensitive)', '--name-suffix'),
314 name_like=ValueArgument(
315 'print only if name contains this (case insensitive)',
319 def _non_exact_name_filter(self, items):
320 np, ns, nl = self['name_pref'], self['name_suff'], self['name_like']
321 return [item for item in items if (
322 (not np) or (item['name'] or '').lower().startswith(
324 (not ns) or (item['name'] or '').lower().endswith(
326 (not nl) or nl.lower() in (item['name'] or '').lower())]
328 def _exact_name_filter(self, items):
329 return filter_dicts_by_dict(items, dict(name=self['name'] or '')) if (
330 self['name']) else items
332 def _filter_by_name(self, items):
333 return self._non_exact_name_filter(self._exact_name_filter(items))
336 class _id_filter(object):
339 id=ValueArgument('filter by id', '--id'),
340 id_pref=ValueArgument(
341 'filter by id prefix (case insensitive)', '--id-prefix'),
342 id_suff=ValueArgument(
343 'filter by id suffix (case insensitive)', '--id-suffix'),
344 id_like=ValueArgument(
345 'print only if id contains this (case insensitive)', '--id-like')
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 (
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())]
356 def _exact_id_filter(self, items):
357 return filter_dicts_by_dict(items, dict(id=self['id'])) if (
358 self['id']) else items
360 def _filter_by_id(self, items):
361 return self._non_exact_id_filter(self._exact_id_filter(items))