231 lines
7.4 KiB
Python
231 lines
7.4 KiB
Python
# Copyright 2012 OpenStack Foundation
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from __future__ import print_function
|
|
|
|
import datetime
|
|
import sys
|
|
import textwrap
|
|
import uuid
|
|
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import encodeutils
|
|
from oslo_utils import importutils
|
|
from oslo_utils import timeutils
|
|
import prettytable
|
|
import six
|
|
|
|
from cloudkittyclient.common import cliutils
|
|
from cloudkittyclient import exc
|
|
from cloudkittyclient.i18n import _
|
|
|
|
|
|
def iso2dt(iso_date):
|
|
"""iso8601 format to datetime."""
|
|
iso_dt = timeutils.parse_isotime(iso_date)
|
|
trans_dt = timeutils.normalize_time(iso_dt)
|
|
return trans_dt
|
|
|
|
|
|
def import_versioned_module(version, submodule=None):
|
|
module = 'cloudkittyclient.v%s' % version
|
|
if submodule:
|
|
module = '.'.join((module, submodule))
|
|
return importutils.import_module(module)
|
|
|
|
|
|
# Decorator for cli-args
|
|
def arg(*args, **kwargs):
|
|
def _decorator(func):
|
|
if 'help' in kwargs:
|
|
if 'default' in kwargs:
|
|
kwargs['help'] += " Defaults to %s." % kwargs['default']
|
|
required = kwargs.get('required', False)
|
|
if required:
|
|
kwargs['help'] += " required."
|
|
elif 'default' not in kwargs:
|
|
kwargs['help'] += "."
|
|
|
|
# Because of the sematics of decorator composition if we just append
|
|
# to the options list positional options will appear to be backwards.
|
|
func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs))
|
|
return func
|
|
return _decorator
|
|
|
|
|
|
def pretty_choice_list(l):
|
|
return ', '.join("'%s'" % i for i in l)
|
|
|
|
|
|
def print_list(objs, fields, field_labels, formatters={}, sortby=0):
|
|
|
|
def _make_default_formatter(field):
|
|
return lambda o: getattr(o, field, '')
|
|
|
|
new_formatters = {}
|
|
for field, field_label in six.moves.zip(fields, field_labels):
|
|
if field in formatters:
|
|
new_formatters[field_label] = formatters[field]
|
|
else:
|
|
new_formatters[field_label] = _make_default_formatter(field)
|
|
|
|
cliutils.print_list(objs, field_labels,
|
|
formatters=new_formatters,
|
|
sortby_index=sortby)
|
|
|
|
|
|
def nested_list_of_dict_formatter(field, column_names):
|
|
# (TMaddox) Because the formatting scheme actually drops the whole object
|
|
# into the formatter, rather than just the specified field, we have to
|
|
# extract it and then pass the value.
|
|
return lambda o: format_nested_list_of_dict(getattr(o, field),
|
|
column_names)
|
|
|
|
|
|
def format_nested_list_of_dict(l, column_names):
|
|
pt = prettytable.PrettyTable(caching=False, print_empty=False,
|
|
header=True, hrules=prettytable.FRAME,
|
|
field_names=column_names)
|
|
for d in l:
|
|
pt.add_row(list(map(lambda k: d[k], column_names)))
|
|
return pt.get_string()
|
|
|
|
|
|
def print_dict(d, dict_property="Property", wrap=0):
|
|
pt = prettytable.PrettyTable([dict_property, 'Value'], print_empty=False)
|
|
pt.align = 'l'
|
|
for k, v in sorted(six.iteritems(d)):
|
|
# convert dict to str to check length
|
|
if isinstance(v, dict):
|
|
v = jsonutils.dumps(v)
|
|
# if value has a newline, add in multiple rows
|
|
# e.g. fault with stacktrace
|
|
if v and isinstance(v, six.string_types) and r'\n' in v:
|
|
lines = v.strip().split(r'\n')
|
|
col1 = k
|
|
for line in lines:
|
|
if wrap > 0:
|
|
line = textwrap.fill(str(line), wrap)
|
|
pt.add_row([col1, line])
|
|
col1 = ''
|
|
else:
|
|
if wrap > 0:
|
|
v = textwrap.fill(str(v), wrap)
|
|
pt.add_row([k, v])
|
|
encoded = encodeutils.safe_encode(pt.get_string())
|
|
# FIXME(gordc): https://bugs.launchpad.net/oslo-incubator/+bug/1370710
|
|
if six.PY3:
|
|
encoded = encoded.decode()
|
|
print(encoded)
|
|
|
|
|
|
def find_resource(manager, name_or_id):
|
|
"""Helper for the _find_* methods."""
|
|
# first try to get entity as integer id
|
|
try:
|
|
if isinstance(name_or_id, int) or name_or_id.isdigit():
|
|
return manager.get(int(name_or_id))
|
|
except exc.HTTPNotFound:
|
|
pass
|
|
|
|
# now try to get entity as uuid
|
|
try:
|
|
uuid.UUID(str(name_or_id))
|
|
return manager.get(name_or_id)
|
|
except (ValueError, exc.HTTPNotFound):
|
|
pass
|
|
|
|
# finally try to find entity by name
|
|
try:
|
|
return manager.find(name=name_or_id)
|
|
except exc.HTTPNotFound:
|
|
msg = _("No %(name)s with a name or ID of '%(id)s' exists.") % {
|
|
"name": manager.resource_class.__name__.lower(),
|
|
"id": name_or_id
|
|
}
|
|
raise exc.CommandError(msg)
|
|
|
|
|
|
def args_array_to_dict(kwargs, key_to_convert):
|
|
values_to_convert = kwargs.get(key_to_convert)
|
|
if values_to_convert:
|
|
try:
|
|
kwargs[key_to_convert] = dict(v.split("=", 1)
|
|
for v in values_to_convert)
|
|
except ValueError:
|
|
msg = _("%(key)s must be a list of key=value "
|
|
"not '%(value)s'") % {
|
|
"key": key_to_convert,
|
|
"value": values_to_convert
|
|
}
|
|
raise exc.CommandError(msg)
|
|
return kwargs
|
|
|
|
|
|
def args_array_to_list_of_dicts(kwargs, key_to_convert):
|
|
"""Converts ['a=1;b=2','c=3;d=4'] to [{a:1,b:2},{c:3,d:4}]."""
|
|
values_to_convert = kwargs.get(key_to_convert)
|
|
if values_to_convert:
|
|
try:
|
|
kwargs[key_to_convert] = []
|
|
for lst in values_to_convert:
|
|
pairs = lst.split(";")
|
|
dct = dict()
|
|
for pair in pairs:
|
|
kv = pair.split("=", 1)
|
|
dct[kv[0]] = kv[1].strip(" \"'") # strip spaces and quotes
|
|
kwargs[key_to_convert].append(dct)
|
|
except Exception:
|
|
msg = _("%(key)s must be a list of "
|
|
"key1=value1;key2=value2;... not '%(value)s'") % {
|
|
"key": key_to_convert,
|
|
"value": values_to_convert
|
|
}
|
|
raise exc.CommandError(msg)
|
|
return kwargs
|
|
|
|
|
|
def key_with_slash_to_nested_dict(kwargs):
|
|
nested_kwargs = {}
|
|
for k in list(kwargs):
|
|
keys = k.split('/', 1)
|
|
if len(keys) == 2:
|
|
nested_kwargs.setdefault(keys[0], {})[keys[1]] = kwargs[k]
|
|
del kwargs[k]
|
|
kwargs.update(nested_kwargs)
|
|
return kwargs
|
|
|
|
|
|
def merge_nested_dict(dest, source, depth=0):
|
|
for (key, value) in six.iteritems(source):
|
|
if isinstance(value, dict) and depth:
|
|
merge_nested_dict(dest[key], value,
|
|
depth=(depth - 1))
|
|
else:
|
|
dest[key] = value
|
|
|
|
|
|
def ts2dt(timestamp):
|
|
"""timestamp to datetime format."""
|
|
if not isinstance(timestamp, float):
|
|
timestamp = float(timestamp)
|
|
return datetime.datetime.utcfromtimestamp(timestamp)
|
|
|
|
|
|
def exit(msg=''):
|
|
if msg:
|
|
print(msg, file=sys.stderr)
|
|
sys.exit(1)
|