~ubuntu-branches/ubuntu/utopic/python-ceilometerclient/utopic

« back to all changes in this revision

Viewing changes to ceilometerclient/openstack/common/cliutils.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, James Page, Chuck Short
  • Date: 2014-01-21 09:53:01 UTC
  • mfrom: (1.1.6)
  • Revision ID: package-import@ubuntu.com-20140121095301-cwxsrtdgddkzprjx
Tags: 1.0.8-0ubuntu1
[ James Page ]
* d/control: Add missing BD on python-babel. 

[ Chuck Short ]
* New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2012 Red Hat, Inc.
 
2
#
 
3
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
4
#    not use this file except in compliance with the License. You may obtain
 
5
#    a copy of the License at
 
6
#
 
7
#         http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
9
#    Unless required by applicable law or agreed to in writing, software
 
10
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
11
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
12
#    License for the specific language governing permissions and limitations
 
13
#    under the License.
 
14
 
 
15
# W0603: Using the global statement
 
16
# W0621: Redefining name %s from outer scope
 
17
# pylint: disable=W0603,W0621
 
18
 
 
19
import getpass
 
20
import inspect
 
21
import os
 
22
import sys
 
23
import textwrap
 
24
 
 
25
import prettytable
 
26
import six
 
27
from six import moves
 
28
 
 
29
from ceilometerclient.openstack.common.apiclient import exceptions
 
30
from ceilometerclient.openstack.common import strutils
 
31
 
 
32
 
 
33
def validate_args(fn, *args, **kwargs):
 
34
    """Check that the supplied args are sufficient for calling a function.
 
35
 
 
36
    >>> validate_args(lambda a: None)
 
37
    Traceback (most recent call last):
 
38
        ...
 
39
    MissingArgs: Missing argument(s): a
 
40
    >>> validate_args(lambda a, b, c, d: None, 0, c=1)
 
41
    Traceback (most recent call last):
 
42
        ...
 
43
    MissingArgs: Missing argument(s): b, d
 
44
 
 
45
    :param fn: the function to check
 
46
    :param arg: the positional arguments supplied
 
47
    :param kwargs: the keyword arguments supplied
 
48
    """
 
49
    argspec = inspect.getargspec(fn)
 
50
 
 
51
    num_defaults = len(argspec.defaults or [])
 
52
    required_args = argspec.args[:len(argspec.args) - num_defaults]
 
53
 
 
54
    def isbound(method):
 
55
        return getattr(method, 'im_self', None) is not None
 
56
 
 
57
    if isbound(fn):
 
58
        required_args.pop(0)
 
59
 
 
60
    missing = [arg for arg in required_args if arg not in kwargs]
 
61
    missing = missing[len(args):]
 
62
    if missing:
 
63
        raise exceptions.MissingArgs(missing)
 
64
 
 
65
 
 
66
def arg(*args, **kwargs):
 
67
    """Decorator for CLI args.
 
68
 
 
69
    Example:
 
70
 
 
71
    >>> @arg("name", help="Name of the new entity")
 
72
    ... def entity_create(args):
 
73
    ...     pass
 
74
    """
 
75
    def _decorator(func):
 
76
        add_arg(func, *args, **kwargs)
 
77
        return func
 
78
    return _decorator
 
79
 
 
80
 
 
81
def env(*args, **kwargs):
 
82
    """Returns the first environment variable set.
 
83
 
 
84
    If all are empty, defaults to '' or keyword arg `default`.
 
85
    """
 
86
    for arg in args:
 
87
        value = os.environ.get(arg, None)
 
88
        if value:
 
89
            return value
 
90
    return kwargs.get('default', '')
 
91
 
 
92
 
 
93
def add_arg(func, *args, **kwargs):
 
94
    """Bind CLI arguments to a shell.py `do_foo` function."""
 
95
 
 
96
    if not hasattr(func, 'arguments'):
 
97
        func.arguments = []
 
98
 
 
99
    # NOTE(sirp): avoid dups that can occur when the module is shared across
 
100
    # tests.
 
101
    if (args, kwargs) not in func.arguments:
 
102
        # Because of the semantics of decorator composition if we just append
 
103
        # to the options list positional options will appear to be backwards.
 
104
        func.arguments.insert(0, (args, kwargs))
 
105
 
 
106
 
 
107
def unauthenticated(func):
 
108
    """Adds 'unauthenticated' attribute to decorated function.
 
109
 
 
110
    Usage:
 
111
 
 
112
    >>> @unauthenticated
 
113
    ... def mymethod(f):
 
114
    ...     pass
 
115
    """
 
116
    func.unauthenticated = True
 
117
    return func
 
118
 
 
119
 
 
120
def isunauthenticated(func):
 
121
    """Checks if the function does not require authentication.
 
122
 
 
123
    Mark such functions with the `@unauthenticated` decorator.
 
124
 
 
125
    :returns: bool
 
126
    """
 
127
    return getattr(func, 'unauthenticated', False)
 
128
 
 
129
 
 
130
def print_list(objs, fields, formatters=None, sortby_index=0,
 
131
               mixed_case_fields=None):
 
132
    """Print a list or objects as a table, one row per object.
 
133
 
 
134
    :param objs: iterable of :class:`Resource`
 
135
    :param fields: attributes that correspond to columns, in order
 
136
    :param formatters: `dict` of callables for field formatting
 
137
    :param sortby_index: index of the field for sorting table rows
 
138
    :param mixed_case_fields: fields corresponding to object attributes that
 
139
        have mixed case names (e.g., 'serverId')
 
140
    """
 
141
    formatters = formatters or {}
 
142
    mixed_case_fields = mixed_case_fields or []
 
143
    if sortby_index is None:
 
144
        kwargs = {}
 
145
    else:
 
146
        kwargs = {'sortby': fields[sortby_index]}
 
147
    pt = prettytable.PrettyTable(fields, caching=False)
 
148
    pt.align = 'l'
 
149
 
 
150
    for o in objs:
 
151
        row = []
 
152
        for field in fields:
 
153
            if field in formatters:
 
154
                row.append(formatters[field](o))
 
155
            else:
 
156
                if field in mixed_case_fields:
 
157
                    field_name = field.replace(' ', '_')
 
158
                else:
 
159
                    field_name = field.lower().replace(' ', '_')
 
160
                data = getattr(o, field_name, '')
 
161
                row.append(data)
 
162
        pt.add_row(row)
 
163
 
 
164
    print(strutils.safe_encode(pt.get_string(**kwargs)))
 
165
 
 
166
 
 
167
def print_dict(dct, dict_property="Property", wrap=0):
 
168
    """Print a `dict` as a table of two columns.
 
169
 
 
170
    :param dct: `dict` to print
 
171
    :param dict_property: name of the first column
 
172
    :param wrap: wrapping for the second column
 
173
    """
 
174
    pt = prettytable.PrettyTable([dict_property, 'Value'], caching=False)
 
175
    pt.align = 'l'
 
176
    for k, v in dct.iteritems():
 
177
        # convert dict to str to check length
 
178
        if isinstance(v, dict):
 
179
            v = str(v)
 
180
        if wrap > 0:
 
181
            v = textwrap.fill(str(v), wrap)
 
182
        # if value has a newline, add in multiple rows
 
183
        # e.g. fault with stacktrace
 
184
        if v and isinstance(v, six.string_types) and r'\n' in v:
 
185
            lines = v.strip().split(r'\n')
 
186
            col1 = k
 
187
            for line in lines:
 
188
                pt.add_row([col1, line])
 
189
                col1 = ''
 
190
        else:
 
191
            pt.add_row([k, v])
 
192
    print(strutils.safe_encode(pt.get_string()))
 
193
 
 
194
 
 
195
def get_password(max_password_prompts=3):
 
196
    """Read password from TTY."""
 
197
    verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD"))
 
198
    pw = None
 
199
    if hasattr(sys.stdin, "isatty") and sys.stdin.isatty():
 
200
        # Check for Ctrl-D
 
201
        try:
 
202
            for _ in moves.range(max_password_prompts):
 
203
                pw1 = getpass.getpass("OS Password: ")
 
204
                if verify:
 
205
                    pw2 = getpass.getpass("Please verify: ")
 
206
                else:
 
207
                    pw2 = pw1
 
208
                if pw1 == pw2 and pw1:
 
209
                    pw = pw1
 
210
                    break
 
211
        except EOFError:
 
212
            pass
 
213
    return pw