Compare commits
117 Commits
0.4.0
...
queens-eol
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c91e61fc9f | ||
|
|
a4dafc254b | ||
| 65a6bb83cd | |||
| 0353abdd7f | |||
|
|
075cb4993d | ||
|
|
938697cb39 | ||
|
|
7d3730cc77 | ||
|
|
8ba6712b74 | ||
|
|
81bc4e3dce | ||
|
|
64129e43a1 | ||
|
|
bf30936048 | ||
|
|
b07b8c2360 | ||
|
|
87dbe6ea2e | ||
|
|
f3451d8656 | ||
|
|
34aef63639 | ||
|
|
7b5bb67733 | ||
|
|
cf2b4f31c7 | ||
|
|
87dadb689c | ||
|
|
2fe71f729a | ||
|
|
c6b90fc6e6 | ||
|
|
d9aa46de34 | ||
|
|
56e6f689bb | ||
|
|
75755b2af3 | ||
|
|
40ddf6a470 | ||
|
|
287dd57185 | ||
|
|
416a717ebc | ||
|
|
efb2b0949a | ||
|
|
a567600b77 | ||
|
|
d04e5ac776 | ||
|
|
a1f45a0206 | ||
|
|
2211d48f72 | ||
|
|
e402ce676c | ||
|
|
c423ca4470 | ||
|
|
f750d31169 | ||
|
|
cdbcafd142 | ||
|
|
cffa8b8936 | ||
|
|
10f80c7d02 | ||
|
|
87cdbb1340 | ||
|
|
cede66dde6 | ||
|
|
addb3a8b33 | ||
|
|
04bf3504ee | ||
|
|
b6f7a7831f | ||
|
|
70cef21224 | ||
|
|
8fa848fb51 | ||
|
|
d70f9c778c | ||
|
|
ddf93becc1 | ||
|
|
0024760bef | ||
|
|
5d43074cc4 | ||
|
|
d82273fcd4 | ||
|
|
241d90f541 | ||
|
|
708aeff9b7 | ||
|
|
9a9399f284 | ||
|
|
a070f5b16c | ||
|
|
9a1f531155 | ||
|
|
d0f0aaf346 | ||
|
|
a93f8b04b1 | ||
|
|
e6daa3df81 | ||
|
|
7a199820d0 | ||
|
|
b24e261143 | ||
|
|
9acc36b4ca | ||
|
|
a6093c5a36 | ||
|
|
9554c9e440 | ||
|
|
8318891835 | ||
|
|
b2a42f71fe | ||
|
|
d4ae928048 | ||
|
|
1d8378cc6c | ||
|
|
44a2bb0b26 | ||
|
|
0417e30f76 | ||
|
|
a4dd6e1a61 | ||
|
|
d503c98cbe | ||
|
|
0efe6a0606 | ||
|
|
69399b5e5d | ||
|
|
df5087a344 | ||
|
|
70771bf2d7 | ||
|
|
367608ceeb | ||
|
|
92981a7bb4 | ||
|
|
5faa8b0de6 | ||
|
|
55f3a3fa75 | ||
|
|
336f1466e0 | ||
|
|
5b6fd6c529 | ||
|
|
778782c3c9 | ||
|
|
985e3c2304 | ||
|
|
0cad4b0e99 | ||
|
|
1703d5538b | ||
|
|
c6e23ab770 | ||
|
|
450aa61358 | ||
|
|
066c9564fb | ||
|
|
6abecf6348 | ||
|
|
a63b75c555 | ||
|
|
f8c4caba43 | ||
|
|
c6f2cb4643 | ||
|
|
5c188a2306 | ||
|
|
9dd1a6fbea | ||
|
|
0de831021d | ||
|
|
4dc3e65e44 | ||
|
|
8b69ecf237 | ||
|
|
ea21f9761b | ||
|
|
df4e8360e2 | ||
|
|
236bf8b307 | ||
|
|
6dbfc4502e | ||
|
|
def167f77a | ||
|
|
4fe0255682 | ||
|
|
e4623d6663 | ||
|
|
382a2d9565 | ||
|
|
9428ab38aa | ||
|
|
1e095e6da9 | ||
|
|
621c06f8af | ||
|
|
e4df2e2105 | ||
|
|
50f6cb3e54 | ||
|
|
d9d61d7727 | ||
|
|
bf03f8fae6 | ||
|
|
b17283d585 | ||
|
|
0696510d29 | ||
|
|
4ee7dcb3ee | ||
|
|
5efcd30fdd | ||
|
|
52955c5749 | ||
|
|
2ccbed9139 |
@@ -3,10 +3,7 @@ branch = True
|
||||
source = cloudkittyclient
|
||||
omit =
|
||||
cloudkittyclient/tests/*,
|
||||
cloudkittyclient/openstack/*,
|
||||
cloudkittyclient/i18n.py,
|
||||
cloudkittyclient/common/client.py,
|
||||
cloudkittyclient/common/exceptions.py
|
||||
cloudkittyclient/i18n.py
|
||||
|
||||
[report]
|
||||
ignore-errors = True
|
||||
ignore_errors = True
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -11,3 +11,7 @@ cover
|
||||
dist
|
||||
*.egg
|
||||
*.sw?
|
||||
.eggs
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
releasenotes/build
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
host=review.opendev.org
|
||||
port=29418
|
||||
project=stackforge/python-cloudkittyclient.git
|
||||
project=openstack/python-cloudkittyclient.git
|
||||
defaultbranch=stable/queens
|
||||
|
||||
19
.pylintrc
19
.pylintrc
@@ -1,19 +0,0 @@
|
||||
[MASTER]
|
||||
ignore=openstack,test
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
# C0111: Don't require docstrings on every method
|
||||
# W0511: TODOs in code comments are fine.
|
||||
# W0142: *args and **kwargs are fine.
|
||||
# W0622: Redefining id is fine.
|
||||
disable=C0111,W0511,W0142,W0622
|
||||
|
||||
[BASIC]
|
||||
# Don't require docstrings on tests.
|
||||
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
|
||||
|
||||
[Variables]
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
# _ is used by our localization
|
||||
additional-builtins=_
|
||||
8
.zuul.yaml
Normal file
8
.zuul.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
- project:
|
||||
templates:
|
||||
- openstack-python-jobs
|
||||
- openstack-python35-jobs
|
||||
- openstackclient-plugin-jobs
|
||||
post:
|
||||
jobs:
|
||||
- openstack-tox-cover
|
||||
@@ -1,6 +0,0 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
||||
27
README.rst
27
README.rst
@@ -1,7 +1,11 @@
|
||||
Python bindings to the CloudKitty API
|
||||
=====================================
|
||||
================
|
||||
CloudKittyClient
|
||||
================
|
||||
|
||||
:version: 0.2
|
||||
.. image:: http://governance.openstack.org/badges/python-cloudkittyclient.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
:version: 1.1.0
|
||||
:Wiki: `CloudKitty Wiki`_
|
||||
:IRC: #cloudkitty @ freenode
|
||||
|
||||
@@ -9,15 +13,12 @@ Python bindings to the CloudKitty API
|
||||
.. _CloudKitty Wiki: https://wiki.openstack.org/wiki/CloudKitty
|
||||
|
||||
|
||||
python-cloudkittyclient
|
||||
=======================
|
||||
This is a client for CloudKitty_. It provides a Python api (the
|
||||
``cloudkittyclient`` module), a command-line script (``cloudkitty``), and an
|
||||
`OpenStack Client`_ extension (``openstack rating``).
|
||||
|
||||
This is a client library for CloudKitty built on the CloudKitty API. It
|
||||
provides a Python API (the ``cloudkittyclient`` module).
|
||||
|
||||
|
||||
Status
|
||||
======
|
||||
|
||||
This project is **highly** work in progress.
|
||||
The client is available on PyPi_.
|
||||
|
||||
.. _OpenStack Client: https://docs.openstack.org/python-openstackclient/latest/
|
||||
.. _CloudKitty: https://github.com/openstack/cloudkitty
|
||||
.. _PyPi: https://pypi.python.org/pypi/python-cloudkittyclient
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 Objectif Libre
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
||||
@@ -37,7 +37,7 @@ import os
|
||||
import six
|
||||
from stevedore import extension
|
||||
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
|
||||
|
||||
_discovered_plugins = {}
|
||||
@@ -54,7 +54,7 @@ def discover_auth_systems():
|
||||
def add_plugin(ext):
|
||||
_discovered_plugins[ext.name] = ext.plugin
|
||||
|
||||
ep_namespace = "cloudkittyclient.openstack.common.apiclient.auth"
|
||||
ep_namespace = "cloudkittyclient.apiclient.auth"
|
||||
mgr = extension.ExtensionManager(ep_namespace)
|
||||
mgr.map(add_plugin)
|
||||
|
||||
@@ -156,8 +156,7 @@ class BaseAuthPlugin(object):
|
||||
|
||||
@classmethod
|
||||
def add_opts(cls, parser):
|
||||
"""Populate the parser with the options for this plugin.
|
||||
"""
|
||||
"""Populate the parser with the options for this plugin."""
|
||||
for opt in cls.opt_names:
|
||||
# use `BaseAuthPlugin.common_opt_names` since it is never
|
||||
# changed in child classes
|
||||
@@ -166,8 +165,7 @@ class BaseAuthPlugin(object):
|
||||
|
||||
@classmethod
|
||||
def add_common_opts(cls, parser):
|
||||
"""Add options that are common for several plugins.
|
||||
"""
|
||||
"""Add options that are common for several plugins."""
|
||||
for opt in cls.common_opt_names:
|
||||
cls._parser_add_opt(parser, opt)
|
||||
|
||||
@@ -204,8 +202,7 @@ class BaseAuthPlugin(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
def _do_authenticate(self, http_client):
|
||||
"""Protected method for authentication.
|
||||
"""
|
||||
"""Protected method for authentication."""
|
||||
|
||||
def sufficient_options(self):
|
||||
"""Check if all required options are present.
|
||||
@@ -44,8 +44,8 @@ from oslo_utils import strutils
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from cloudkittyclient.openstack.common._i18n import _
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.i18n import _
|
||||
|
||||
|
||||
def getid(obj):
|
||||
@@ -467,8 +467,7 @@ class Resource(object):
|
||||
|
||||
@property
|
||||
def human_id(self):
|
||||
"""Human-readable ID which can be used for bash completion.
|
||||
"""
|
||||
"""Human-readable ID which can be used for bash completion."""
|
||||
if self.HUMAN_ID:
|
||||
name = getattr(self, self.NAME_ATTR, None)
|
||||
if name is not None:
|
||||
@@ -523,6 +522,9 @@ class Resource(object):
|
||||
return self.id == other.id
|
||||
return self._info == other._info
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def is_loaded(self):
|
||||
return self._loaded
|
||||
|
||||
@@ -38,8 +38,9 @@ from oslo_utils import encodeutils
|
||||
from oslo_utils import importutils
|
||||
import requests
|
||||
|
||||
from cloudkittyclient.openstack.common._i18n import _
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.i18n import _
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',)
|
||||
@@ -64,7 +65,7 @@ class HTTPClient(object):
|
||||
into terminal and send the same request with curl.
|
||||
"""
|
||||
|
||||
user_agent = "cloudkittyclient.openstack.common.apiclient"
|
||||
user_agent = "cloudkittyclient.apiclient"
|
||||
|
||||
def __init__(self,
|
||||
auth_plugin,
|
||||
@@ -38,12 +38,11 @@ import sys
|
||||
|
||||
import six
|
||||
|
||||
from cloudkittyclient.openstack.common._i18n import _
|
||||
from cloudkittyclient.i18n import _
|
||||
|
||||
|
||||
class ClientException(Exception):
|
||||
"""The base exception class for all exceptions this library raises.
|
||||
"""
|
||||
"""The base exception class for all exceptions this library raises."""
|
||||
pass
|
||||
|
||||
|
||||
@@ -118,8 +117,7 @@ class AmbiguousEndpoints(EndpointException):
|
||||
|
||||
|
||||
class HttpError(ClientException):
|
||||
"""The base exception class for all HTTP exceptions.
|
||||
"""
|
||||
"""The base exception class for all HTTP exceptions."""
|
||||
http_status = 0
|
||||
message = _("HTTP Error")
|
||||
|
||||
@@ -43,7 +43,7 @@ import requests
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from cloudkittyclient.openstack.common.apiclient import client
|
||||
from cloudkittyclient.apiclient import client
|
||||
|
||||
|
||||
def assert_has_keys(dct, required=None, optional=None):
|
||||
@@ -59,8 +59,7 @@ def assert_has_keys(dct, required=None, optional=None):
|
||||
|
||||
|
||||
class TestResponse(requests.Response):
|
||||
"""Wrap requests.Response and provide a convenient initialization.
|
||||
"""
|
||||
"""Wrap requests.Response and provide a convenient initialization."""
|
||||
|
||||
def __init__(self, data):
|
||||
super(TestResponse, self).__init__()
|
||||
@@ -88,6 +87,9 @@ class TestResponse(requests.Response):
|
||||
self.headers == other.headers and
|
||||
self._content == other._content)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
|
||||
class FakeHTTPClient(client.HTTPClient):
|
||||
|
||||
@@ -99,15 +101,14 @@ class FakeHTTPClient(client.HTTPClient):
|
||||
super(FakeHTTPClient, self).__init__(*args, **kwargs)
|
||||
|
||||
def assert_called(self, method, url, body=None, pos=-1):
|
||||
"""Assert than an API method was just called.
|
||||
"""
|
||||
"""Assert than an API method was just called."""
|
||||
expected = (method, url)
|
||||
called = self.callstack[pos][0:2]
|
||||
assert self.callstack, \
|
||||
"Expected %s %s but no calls were made." % expected
|
||||
msg = "Expected %s %s but no calls were made." % expected
|
||||
assert self.callstack, msg
|
||||
|
||||
assert expected == called, 'Expected %s %s; got %s %s' % \
|
||||
(expected + called)
|
||||
msg = 'Expected %s %s; got %s %s' % (expected + called)
|
||||
assert expected == called, msg
|
||||
|
||||
if body is not None:
|
||||
if self.callstack[pos][3] != body:
|
||||
@@ -115,12 +116,11 @@ class FakeHTTPClient(client.HTTPClient):
|
||||
(self.callstack[pos][3], body))
|
||||
|
||||
def assert_called_anytime(self, method, url, body=None):
|
||||
"""Assert than an API method was called anytime in the test.
|
||||
"""
|
||||
"""Assert than an API method was called anytime in the test."""
|
||||
expected = (method, url)
|
||||
|
||||
assert self.callstack, \
|
||||
"Expected %s %s but no calls were made." % expected
|
||||
msg = "Expected %s %s but no calls were made." % expected
|
||||
assert self.callstack, msg
|
||||
|
||||
found = False
|
||||
entry = None
|
||||
@@ -129,8 +129,8 @@ class FakeHTTPClient(client.HTTPClient):
|
||||
found = True
|
||||
break
|
||||
|
||||
assert found, 'Expected %s %s; got %s' % \
|
||||
(method, url, self.callstack)
|
||||
msg = 'Expected %s %s; got %s' % (method, url, self.callstack)
|
||||
assert found, msg
|
||||
if body is not None:
|
||||
assert entry[3] == body, "%s != %s" % (entry[3], body)
|
||||
|
||||
@@ -28,8 +28,8 @@ from oslo_utils import encodeutils
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
|
||||
from cloudkittyclient.openstack.common._i18n import _
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.i18n import _
|
||||
|
||||
|
||||
def find_resource(manager, name_or_id, **find_args):
|
||||
@@ -84,17 +84,13 @@ def find_resource(manager, name_or_id, **find_args):
|
||||
return manager.find(**kwargs)
|
||||
except exceptions.NotFound:
|
||||
msg = _("No %(name)s with a name or "
|
||||
"ID of '%(name_or_id)s' exists.") % \
|
||||
{
|
||||
"name": manager.resource_class.__name__.lower(),
|
||||
"name_or_id": name_or_id
|
||||
}
|
||||
"ID of '%(name_or_id)s' exists.") % {
|
||||
"name": manager.resource_class.__name__.lower(),
|
||||
"name_or_id": name_or_id}
|
||||
raise exceptions.CommandError(msg)
|
||||
except exceptions.NoUniqueMatch:
|
||||
msg = _("Multiple %(name)s matches found for "
|
||||
"'%(name_or_id)s', use an ID to be more specific.") % \
|
||||
{
|
||||
"name": manager.resource_class.__name__.lower(),
|
||||
"name_or_id": name_or_id
|
||||
}
|
||||
"'%(name_or_id)s', use an ID to be more specific.") % {
|
||||
"name": manager.resource_class.__name__.lower(),
|
||||
"name_or_id": name_or_id}
|
||||
raise exceptions.CommandError(msg)
|
||||
@@ -10,6 +10,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import contextlib
|
||||
import time
|
||||
|
||||
from keystoneclient import adapter
|
||||
from keystoneclient.auth.identity import v2 as v2_auth
|
||||
from keystoneclient.auth.identity import v3 as v3_auth
|
||||
from keystoneclient import discover
|
||||
@@ -18,10 +22,11 @@ from keystoneclient import session
|
||||
from oslo_utils import strutils
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from cloudkittyclient.apiclient import auth
|
||||
from cloudkittyclient.apiclient import client
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.common import utils
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.openstack.common.apiclient import auth
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
|
||||
|
||||
def _discover_auth_versions(session, auth_url):
|
||||
@@ -105,6 +110,8 @@ def _get_keystone_session(**kwargs):
|
||||
user_id=user_id,
|
||||
user_domain_name=user_domain_name,
|
||||
user_domain_id=user_domain_id,
|
||||
project_name=project_name,
|
||||
project_id=project_id,
|
||||
project_domain_name=project_domain_name,
|
||||
project_domain_id=project_domain_id)
|
||||
elif use_v2:
|
||||
@@ -145,7 +152,8 @@ class AuthPlugin(auth.BaseAuthPlugin):
|
||||
'service_type', 'endpoint_type', 'cacert',
|
||||
'auth_url', 'insecure', 'cert_file', 'key_file',
|
||||
'cert', 'key', 'tenant_name', 'project_name',
|
||||
'project_id', 'user_domain_id', 'user_domain_name',
|
||||
'project_id', 'project_domain_id', 'project_domain_name',
|
||||
'user_id', 'user_domain_id', 'user_domain_name',
|
||||
'password', 'username', 'endpoint']
|
||||
|
||||
def __init__(self, auth_system=None, **kwargs):
|
||||
@@ -200,8 +208,13 @@ class AuthPlugin(auth.BaseAuthPlugin):
|
||||
"""
|
||||
has_token = self.opts.get('token') or self.opts.get('auth_token')
|
||||
no_auth = has_token and self.opts.get('endpoint')
|
||||
has_project = (self.opts.get('project_id')
|
||||
or (self.opts.get('project_name')
|
||||
and (self.opts.get('user_domain_name')
|
||||
or self.opts.get('user_domain_id'))))
|
||||
has_tenant = self.opts.get('tenant_id') or self.opts.get('tenant_name')
|
||||
has_credential = (self.opts.get('username') and has_tenant
|
||||
has_credential = (self.opts.get('username')
|
||||
and (has_project or has_tenant)
|
||||
and self.opts.get('password')
|
||||
and self.opts.get('auth_url'))
|
||||
missing = not (no_auth or has_credential)
|
||||
@@ -250,9 +263,28 @@ def get_client(version, **kwargs):
|
||||
:param api_version: the API version to use ('1')
|
||||
:param kwargs: keyword args containing credentials, either:
|
||||
|
||||
* session: a keystoneauth/keystoneclient session object
|
||||
* service_type: The default service_type for URL discovery
|
||||
* service_name: The default service_name for URL discovery
|
||||
* interface: The default interface for URL discovery
|
||||
(Default: public)
|
||||
* region_name: The default region_name for URL discovery
|
||||
* endpoint_override: Always use this endpoint URL for requests
|
||||
for this cloudkittyclient
|
||||
* auth: An auth plugin to use instead of the session one
|
||||
* user_agent: The User-Agent string to set
|
||||
(Default is python-cloudkittyclient)
|
||||
* connect_retries: the maximum number of retries that should be
|
||||
attempted for connection errors
|
||||
* logger: A logging object
|
||||
|
||||
or (DEPRECATED):
|
||||
|
||||
* os_token: pre-existing token to re-use
|
||||
* os_endpoint: Cloudkitty API endpoint
|
||||
or:
|
||||
|
||||
or (DEPRECATED):
|
||||
|
||||
* os_username: name of user
|
||||
* os_password: user's password
|
||||
* os_user_id: user's id
|
||||
@@ -274,8 +306,10 @@ def get_client(version, **kwargs):
|
||||
cli_kwargs = {
|
||||
'username': kwargs.get('os_username'),
|
||||
'password': kwargs.get('os_password'),
|
||||
'tenant_id': kwargs.get('os_tenant_id'),
|
||||
'tenant_name': kwargs.get('os_tenant_name'),
|
||||
'tenant_id': (kwargs.get('os_tenant_id')
|
||||
or kwargs.get('os_project_id')),
|
||||
'tenant_name': (kwargs.get('os_tenant_name')
|
||||
or kwargs.get('os_project_name')),
|
||||
'auth_url': kwargs.get('os_auth_url'),
|
||||
'region_name': kwargs.get('os_region_name'),
|
||||
'service_type': kwargs.get('os_service_type'),
|
||||
@@ -307,10 +341,94 @@ def get_auth_plugin(endpoint, **kwargs):
|
||||
endpoint=endpoint,
|
||||
username=kwargs.get('username'),
|
||||
password=kwargs.get('password'),
|
||||
tenant_name=kwargs.get('tenant_name'),
|
||||
tenant_name=kwargs.get('tenant_name') or kwargs.get('project_name'),
|
||||
user_domain_name=kwargs.get('user_domain_name'),
|
||||
user_domain_id=kwargs.get('user_domain_id'),
|
||||
project_domain_name=kwargs.get('project_domain_name'),
|
||||
project_domain_id=kwargs.get('project_domain_id')
|
||||
)
|
||||
return auth_plugin
|
||||
|
||||
|
||||
LEGACY_OPTS = ('auth_plugin', 'auth_url', 'token', 'insecure', 'cacert',
|
||||
'tenant_id', 'project_id', 'username', 'password',
|
||||
'project_name', 'tenant_name',
|
||||
'user_domain_name', 'user_domain_id',
|
||||
'project_domain_name', 'project_domain_id',
|
||||
'key_file', 'cert_file', 'verify', 'timeout', 'cert')
|
||||
|
||||
|
||||
def construct_http_client(**kwargs):
|
||||
kwargs = kwargs.copy()
|
||||
if kwargs.get('session') is not None:
|
||||
# Drop legacy options
|
||||
for opt in LEGACY_OPTS:
|
||||
kwargs.pop(opt, None)
|
||||
|
||||
return SessionClient(
|
||||
session=kwargs.pop('session'),
|
||||
service_type=kwargs.pop('service_type', 'rating') or 'rating',
|
||||
interface=kwargs.pop('interface', kwargs.pop('endpoint_type',
|
||||
'publicURL')),
|
||||
region_name=kwargs.pop('region_name', None),
|
||||
user_agent=kwargs.pop('user_agent', 'python-cloudkittyclient'),
|
||||
auth=kwargs.get('auth', None),
|
||||
timings=kwargs.pop('timings', None),
|
||||
**kwargs)
|
||||
else:
|
||||
return client.BaseClient(client.HTTPClient(
|
||||
auth_plugin=kwargs.get('auth_plugin'),
|
||||
region_name=kwargs.get('region_name'),
|
||||
endpoint_type=kwargs.get('endpoint_type'),
|
||||
original_ip=kwargs.get('original_ip'),
|
||||
verify=kwargs.get('verify'),
|
||||
cert=kwargs.get('cert'),
|
||||
timeout=kwargs.get('timeout'),
|
||||
timings=kwargs.get('timings'),
|
||||
keyring_saver=kwargs.get('keyring_saver'),
|
||||
debug=kwargs.get('debug'),
|
||||
user_agent=kwargs.get('user_agent'),
|
||||
http=kwargs.get('http')
|
||||
))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def record_time(times, enabled, *args):
|
||||
"""Record the time of a specific action.
|
||||
|
||||
:param times: A list of tuples holds time data.
|
||||
:type times: list
|
||||
:param enabled: Whether timing is enabled.
|
||||
:type enabled: bool
|
||||
:param args: Other data to be stored besides time data, these args
|
||||
will be joined to a string.
|
||||
"""
|
||||
if not enabled:
|
||||
yield
|
||||
else:
|
||||
start = time.time()
|
||||
yield
|
||||
end = time.time()
|
||||
times.append((' '.join(args), start, end))
|
||||
|
||||
|
||||
class SessionClient(adapter.LegacyJsonAdapter):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.times = []
|
||||
self.timings = kwargs.pop('timings', False)
|
||||
super(SessionClient, self).__init__(*args, **kwargs)
|
||||
|
||||
def request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('headers', kwargs.get('headers', {}))
|
||||
# NOTE(sileht): The standard call raises errors from
|
||||
# keystoneauth, where we need to raise the cloudkittyclient errors.
|
||||
raise_exc = kwargs.pop('raise_exc', True)
|
||||
with record_time(self.times, self.timings, method, url):
|
||||
resp, body = super(SessionClient, self).request(url,
|
||||
method,
|
||||
raise_exc=False,
|
||||
**kwargs)
|
||||
|
||||
if raise_exc and resp.status_code >= 400:
|
||||
raise exc.from_response(resp, body)
|
||||
return resp
|
||||
|
||||
@@ -22,8 +22,9 @@ import copy
|
||||
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from cloudkittyclient.apiclient import base
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.openstack.common.apiclient import base
|
||||
from cloudkittyclient.i18n import _
|
||||
|
||||
|
||||
def getid(obj):
|
||||
@@ -138,7 +139,7 @@ class CrudManager(base.CrudManager):
|
||||
'name': self.resource_class.__name__,
|
||||
'args': kwargs
|
||||
}
|
||||
raise exc.NotFound(404, msg)
|
||||
raise exc.HTTPNotFound(msg)
|
||||
return rl
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import prettytable
|
||||
import six
|
||||
from six import moves
|
||||
|
||||
from cloudkittyclient.openstack.common._i18n import _
|
||||
from cloudkittyclient.i18n import _
|
||||
|
||||
|
||||
class MissingArgs(Exception):
|
||||
@@ -267,5 +267,5 @@ def pretty_choice_list(l):
|
||||
|
||||
def exit(msg=''):
|
||||
if msg:
|
||||
print (msg, file=sys.stderr)
|
||||
print(msg, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -23,11 +23,20 @@ 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.openstack.common import cliutils
|
||||
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):
|
||||
@@ -45,7 +54,9 @@ def arg(*args, **kwargs):
|
||||
kwargs['help'] += " Defaults to %s." % kwargs['default']
|
||||
required = kwargs.get('required', False)
|
||||
if required:
|
||||
kwargs['help'] += " 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.
|
||||
@@ -140,8 +151,10 @@ def find_resource(manager, name_or_id):
|
||||
try:
|
||||
return manager.find(name=name_or_id)
|
||||
except exc.HTTPNotFound:
|
||||
msg = ("No %s with a name or ID of '%s' exists." %
|
||||
(manager.resource_class.__name__.lower(), name_or_id))
|
||||
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)
|
||||
|
||||
|
||||
@@ -152,9 +165,12 @@ def args_array_to_dict(kwargs, key_to_convert):
|
||||
kwargs[key_to_convert] = dict(v.split("=", 1)
|
||||
for v in values_to_convert)
|
||||
except ValueError:
|
||||
raise exc.CommandError(
|
||||
'%s must be a list of key=value not "%s"' % (
|
||||
key_to_convert, values_to_convert))
|
||||
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
|
||||
|
||||
|
||||
@@ -172,9 +188,12 @@ def args_array_to_list_of_dicts(kwargs, key_to_convert):
|
||||
dct[kv[0]] = kv[1].strip(" \"'") # strip spaces and quotes
|
||||
kwargs[key_to_convert].append(dct)
|
||||
except Exception:
|
||||
raise exc.CommandError(
|
||||
'%s must be a list of key1=value1;key2=value2;... not "%s"' % (
|
||||
key_to_convert, values_to_convert))
|
||||
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
|
||||
|
||||
|
||||
|
||||
24
cloudkittyclient/i18n.py
Normal file
24
cloudkittyclient/i18n.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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.
|
||||
|
||||
"""oslo.i18n integration module.
|
||||
|
||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html
|
||||
|
||||
"""
|
||||
|
||||
import oslo_i18n as i18n
|
||||
|
||||
_translators = i18n.TranslatorFactory(domain='cloudkittyclient')
|
||||
i18n.enable_lazy()
|
||||
|
||||
_ = _translators.primary
|
||||
@@ -1,45 +0,0 @@
|
||||
# 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.
|
||||
|
||||
"""oslo.i18n integration module.
|
||||
|
||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
import oslo_i18n
|
||||
|
||||
# NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
|
||||
# application name when this module is synced into the separate
|
||||
# repository. It is OK to have more than one translation function
|
||||
# using the same domain, since there will still only be one message
|
||||
# catalog.
|
||||
_translators = oslo_i18n.TranslatorFactory(domain='cloudkittyclient')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# Translators for log levels.
|
||||
#
|
||||
# The abbreviated names are meant to reflect the usual use of a short
|
||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||
# the level.
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
||||
except ImportError:
|
||||
# NOTE(dims): Support for cases where a project wants to use
|
||||
# code from oslo-incubator, but is not ready to be internationalized
|
||||
# (like tempest)
|
||||
_ = _LI = _LW = _LE = _LC = lambda x: x
|
||||
35
cloudkittyclient/osc.py
Normal file
35
cloudkittyclient/osc.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright 2014 OpenStack Foundation
|
||||
#
|
||||
# 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 cloudkittyclient import client as ckclient
|
||||
|
||||
DEFAULT_API_VERSION = '1'
|
||||
API_VERSION_OPTION = 'os_rating_api_version'
|
||||
API_NAME = "rating"
|
||||
API_VERSIONS = {
|
||||
"1": "cloudkittyclient.v1.client.Client",
|
||||
}
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns a rating service client."""
|
||||
version = instance._api_version[API_NAME]
|
||||
version = int(version)
|
||||
auth_config = instance.get_configuration()['auth']
|
||||
return ckclient.get_client(version, **auth_config)
|
||||
|
||||
|
||||
def build_option_parser(parser):
|
||||
"""Hook to add global options."""
|
||||
return parser
|
||||
@@ -28,9 +28,10 @@ from stevedore import extension
|
||||
|
||||
import cloudkittyclient
|
||||
from cloudkittyclient import client as ckclient
|
||||
from cloudkittyclient.common import cliutils
|
||||
from cloudkittyclient.common import utils
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.openstack.common import cliutils
|
||||
from cloudkittyclient.v1.collector import shell as collector_shell
|
||||
from cloudkittyclient.v1.report import shell as report_shell
|
||||
from cloudkittyclient.v1.storage import shell as storage_shell
|
||||
|
||||
@@ -53,6 +54,9 @@ def _positive_non_zero_int(argument_value):
|
||||
|
||||
class CloudkittyShell(object):
|
||||
|
||||
def __init__(self):
|
||||
self.auth_plugin = ckclient.AuthPlugin()
|
||||
|
||||
def get_base_parser(self):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='cloudkitty',
|
||||
@@ -119,6 +123,7 @@ class CloudkittyShell(object):
|
||||
subparsers = parser.add_subparsers(metavar='<subcommand>')
|
||||
submodule = utils.import_versioned_module(version, 'shell')
|
||||
self._find_actions(subparsers, submodule)
|
||||
self._find_actions(subparsers, collector_shell)
|
||||
self._find_actions(subparsers, report_shell)
|
||||
self._find_actions(subparsers, storage_shell)
|
||||
extensions = extension.ExtensionManager(
|
||||
@@ -172,7 +177,6 @@ class CloudkittyShell(object):
|
||||
|
||||
def parse_args(self, argv):
|
||||
# Parse args once to find version
|
||||
self.auth_plugin = ckclient.AuthPlugin()
|
||||
parser = self.get_base_parser()
|
||||
(options, args) = parser.parse_known_args(argv)
|
||||
self.auth_plugin.parse_opts(options)
|
||||
@@ -194,12 +198,11 @@ class CloudkittyShell(object):
|
||||
|
||||
@staticmethod
|
||||
def no_project_and_domain_set(args):
|
||||
if not (args.os_project_id or (args.os_project_name and
|
||||
(args.os_user_domain_name or args.os_user_domain_id)) or
|
||||
(args.os_tenant_id or args.os_tenant_name)):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return not (((args.os_project_id or (args.os_project_name and
|
||||
(args.os_project_domain_name or
|
||||
args.os_project_domain_id)))
|
||||
and (args.os_user_domain_name or args.os_user_domain_id))
|
||||
or (args.os_tenant_id or args.os_tenant_name))
|
||||
|
||||
def main(self, argv):
|
||||
parsed = self.parse_args(argv)
|
||||
@@ -238,20 +241,24 @@ class CloudkittyShell(object):
|
||||
"env[OS_USER_DOMAIN_NAME] or "
|
||||
"a domain_id via either "
|
||||
"--os-user-domain-id or via "
|
||||
"env[OS_USER_DOMAIN_ID]")
|
||||
|
||||
if not (self.auth_plugin.opts['tenant_id']
|
||||
or self.auth_plugin.opts['tenant_name']):
|
||||
raise exc.CommandError("You must provide a tenant_id via "
|
||||
"either --os-tenant-id or via "
|
||||
"env[OS_TENANT_ID]")
|
||||
"env[OS_USER_DOMAIN_ID]\n\n"
|
||||
"As an alternative to project_id, "
|
||||
"you can provide a project_name via "
|
||||
"either --os-project-name or via "
|
||||
"env[OS_PROJECT_NAME] and "
|
||||
"a project_domain_name via either "
|
||||
"--os-project-domain-name or via "
|
||||
"env[OS_PROJECT_DOMAIN_NAME] or "
|
||||
"a project_domain_id via either "
|
||||
"--os-project-domain-id or via "
|
||||
"env[OS_PROJECT_DOMAIN_ID]")
|
||||
|
||||
if not self.auth_plugin.opts['auth_url']:
|
||||
raise exc.CommandError("You must provide an auth url via "
|
||||
"either --os-auth-url or via "
|
||||
"env[OS_AUTH_URL]")
|
||||
|
||||
client_kwargs = vars(args)
|
||||
client_kwargs = {}
|
||||
client_kwargs.update(self.auth_plugin.opts)
|
||||
client_kwargs['auth_plugin'] = self.auth_plugin
|
||||
client = ckclient.get_client(api_version, **client_kwargs)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
|
||||
@@ -45,6 +45,16 @@ class ClientTest(utils.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(ClientTest, self).setUp()
|
||||
|
||||
def test_client_v1_with_session(self):
|
||||
resp = mock.Mock(status_code=200, text=b'')
|
||||
resp.json.return_value = {"modules": []}
|
||||
session = mock.Mock()
|
||||
session.request.return_value = resp
|
||||
c = client.get_client(1, session=session)
|
||||
c.modules.list()
|
||||
self.assertTrue(session.request.called)
|
||||
self.assertTrue(resp.json.called)
|
||||
|
||||
def test_client_version(self):
|
||||
c1 = self.create_client(env=FAKE_ENV, api_version=1)
|
||||
self.assertIsInstance(c1, v1client.Client)
|
||||
@@ -122,7 +132,7 @@ class ClientTest(utils.BaseTestCase):
|
||||
'user_agent': None,
|
||||
'debug': None,
|
||||
}
|
||||
cls = 'cloudkittyclient.openstack.common.apiclient.client.HTTPClient'
|
||||
cls = 'cloudkittyclient.apiclient.client.HTTPClient'
|
||||
with mock.patch(cls) as mocked:
|
||||
self.create_client(env)
|
||||
mocked.assert_called_with(**expected)
|
||||
@@ -137,7 +147,8 @@ class ClientTest(utils.BaseTestCase):
|
||||
env = FAKE_ENV.copy()
|
||||
env['cacert'] = '/path/to/cacert'
|
||||
client = self.create_client(env)
|
||||
self.assertEqual('/path/to/cacert', client.client.verify)
|
||||
self.assertEqual('/path/to/cacert',
|
||||
client.http_client.http_client.verify)
|
||||
|
||||
def test_v1_client_certfile_and_keyfile(self):
|
||||
env = FAKE_ENV.copy()
|
||||
@@ -145,4 +156,4 @@ class ClientTest(utils.BaseTestCase):
|
||||
env['key_file'] = '/path/to/keycert'
|
||||
client = self.create_client(env)
|
||||
self.assertEqual(('/path/to/cert', '/path/to/keycert'),
|
||||
client.client.cert)
|
||||
client.http_client.http_client.cert)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
# 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 cloudkittyclient.openstack.common.apiclient import client
|
||||
from cloudkittyclient.openstack.common.apiclient import fake_client
|
||||
from cloudkittyclient.apiclient import client
|
||||
from cloudkittyclient.apiclient import fake_client
|
||||
from cloudkittyclient.tests import utils
|
||||
import cloudkittyclient.v1.core
|
||||
|
||||
@@ -26,10 +26,12 @@ fixtures = {
|
||||
{
|
||||
'module_id': 'hashmap',
|
||||
'enabled': True,
|
||||
'priority': 1,
|
||||
},
|
||||
{
|
||||
'module_id': 'noop',
|
||||
'enabled': False,
|
||||
'priority': 1,
|
||||
},
|
||||
]},
|
||||
),
|
||||
@@ -40,6 +42,7 @@ fixtures = {
|
||||
{
|
||||
'module_id': 'hashmap',
|
||||
'enabled': True,
|
||||
'priority': 1,
|
||||
}
|
||||
),
|
||||
'PUT': (
|
||||
@@ -47,6 +50,7 @@ fixtures = {
|
||||
{
|
||||
'module_id': 'hashmap',
|
||||
'enabled': False,
|
||||
'priority': 1,
|
||||
}
|
||||
),
|
||||
},
|
||||
@@ -56,6 +60,7 @@ fixtures = {
|
||||
{
|
||||
'module_id': 'noop',
|
||||
'enabled': False,
|
||||
'priority': 1,
|
||||
}
|
||||
),
|
||||
'PUT': (
|
||||
@@ -63,6 +68,7 @@ fixtures = {
|
||||
{
|
||||
'module_id': 'noop',
|
||||
'enabled': True,
|
||||
'priority': 1,
|
||||
}
|
||||
),
|
||||
},
|
||||
@@ -94,9 +100,9 @@ class CloudkittyModuleManagerTest(utils.BaseTestCase):
|
||||
'GET', '/v1/rating/modules'
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
self.assertEqual(len(resources), 2)
|
||||
self.assertEqual(resources[0].module_id, 'hashmap')
|
||||
self.assertEqual(resources[1].module_id, 'noop')
|
||||
self.assertEqual(2, len(resources))
|
||||
self.assertEqual('hashmap', resources[0].module_id)
|
||||
self.assertEqual('noop', resources[1].module_id)
|
||||
|
||||
def test_get_module_status(self):
|
||||
resource = self.mgr.get(module_id='hashmap')
|
||||
@@ -104,8 +110,8 @@ class CloudkittyModuleManagerTest(utils.BaseTestCase):
|
||||
'GET', '/v1/rating/modules/hashmap'
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
self.assertEqual(resource.module_id, 'hashmap')
|
||||
self.assertEqual(resource.enabled, True)
|
||||
self.assertEqual('hashmap', resource.module_id)
|
||||
self.assertTrue(resource.enabled)
|
||||
|
||||
|
||||
class CloudkittyModuleTest(utils.BaseTestCase):
|
||||
@@ -123,7 +129,8 @@ class CloudkittyModuleTest(utils.BaseTestCase):
|
||||
# body : {'enabled': True}
|
||||
expect = [
|
||||
'PUT', '/v1/rating/modules/noop', {'module_id': 'noop',
|
||||
'enabled': True},
|
||||
'enabled': True,
|
||||
'priority': 1},
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
|
||||
@@ -134,6 +141,19 @@ class CloudkittyModuleTest(utils.BaseTestCase):
|
||||
# body : {'enabled': False}
|
||||
expect = [
|
||||
'PUT', '/v1/rating/modules/hashmap', {'module_id': 'hashmap',
|
||||
'enabled': False},
|
||||
'enabled': False,
|
||||
'priority': 1},
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
|
||||
def test_set_priority(self):
|
||||
self.ck_module = self.mgr.get(module_id='hashmap')
|
||||
self.ck_module.set_priority(100)
|
||||
# PUT /v1/rating/modules/hashmap
|
||||
# body : {'priority': 100}
|
||||
expect = [
|
||||
'PUT', '/v1/rating/modules/hashmap', {'module_id': 'hashmap',
|
||||
'enabled': True,
|
||||
'priority': 100},
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
139
cloudkittyclient/tests/v1/test_report.py
Normal file
139
cloudkittyclient/tests/v1/test_report.py
Normal file
@@ -0,0 +1,139 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
# 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 cloudkittyclient.apiclient import client
|
||||
from cloudkittyclient.apiclient import fake_client
|
||||
from cloudkittyclient.tests import utils
|
||||
import cloudkittyclient.v1.report
|
||||
|
||||
|
||||
fixtures = {
|
||||
'/v1/report/summary': {
|
||||
'GET': (
|
||||
{},
|
||||
{'summary': [
|
||||
{
|
||||
'tenant_id': 'ALL',
|
||||
'res_type': 'ALL',
|
||||
'begin': '2017-01-01T00:00:00',
|
||||
'end': '2017-02-01T00:00:00',
|
||||
'rate': '2325.29992'
|
||||
},
|
||||
]},
|
||||
),
|
||||
},
|
||||
'/v1/report/summary?tenant_id=649de47ad78a44bd8562b0aa84389b2b': {
|
||||
'GET': (
|
||||
{},
|
||||
{'summary': [
|
||||
{
|
||||
'tenant_id': '649de47ad78a44bd8562b0aa84389b2b',
|
||||
'res_type': 'ALL',
|
||||
'begin': '2017-01-01T00:00:00',
|
||||
'end': '2017-02-01T00:00:00',
|
||||
'rate': '990.14996'
|
||||
},
|
||||
]},
|
||||
),
|
||||
},
|
||||
'/v1/report/summary?service=compute': {
|
||||
'GET': (
|
||||
{},
|
||||
{'summary': [
|
||||
{
|
||||
'tenant_id': 'ALL',
|
||||
'res_type': 'compute',
|
||||
'begin': '2017-01-01T00:00:00',
|
||||
'end': '2017-02-01T00:00:00',
|
||||
'rate': '690.0'
|
||||
},
|
||||
]},
|
||||
),
|
||||
},
|
||||
'/v1/report/summary?groupby=res_type%2Ctenant_id': {
|
||||
'GET': (
|
||||
{},
|
||||
{'summary': [
|
||||
{
|
||||
'tenant_id': '3747afc360b64702a53bdd64dc1b8976',
|
||||
'res_type': 'compute',
|
||||
'begin': '2017-01-01T00:00:00',
|
||||
'end': '2017-02-01T00:00:00',
|
||||
'rate': '517.5'
|
||||
},
|
||||
{
|
||||
'tenant_id': '3747afc360b64702a53bdd64dc1b8976',
|
||||
'res_type': 'volume',
|
||||
'begin': '2017-01-01T00:00:00',
|
||||
'end': '2017-02-01T00:00:00',
|
||||
'rate': '817.64996'
|
||||
},
|
||||
{
|
||||
'tenant_id': '649de47ad78a44bd8562b0aa84389b2b',
|
||||
'res_type': 'compute',
|
||||
'begin': '2017-01-01T00:00:00',
|
||||
'end': '2017-02-01T00:00:00',
|
||||
'rate': '172.5'
|
||||
},
|
||||
{
|
||||
'tenant_id': '649de47ad78a44bd8562b0aa84389b2b',
|
||||
'res_type': 'volume',
|
||||
'begin': '2017-01-01T00:00:00',
|
||||
'end': '2017-02-01T00:00:00',
|
||||
'rate': '817.64996'
|
||||
},
|
||||
]},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class ReportSummaryManagerTest(utils.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ReportSummaryManagerTest, self).setUp()
|
||||
self.http_client = fake_client.FakeHTTPClient(fixtures=fixtures)
|
||||
self.api = client.BaseClient(self.http_client)
|
||||
self.mgr = cloudkittyclient.v1.report.ReportSummaryManager(self.api)
|
||||
|
||||
def test_get_summary(self):
|
||||
self.mgr.get_summary()
|
||||
expect = [
|
||||
'GET', '/v1/report/summary'
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
|
||||
def test_get_summary_with_tenant(self):
|
||||
self.mgr.get_summary(tenant_id='649de47ad78a44bd8562b0aa84389b2b')
|
||||
expect = [
|
||||
'GET',
|
||||
'/v1/report/summary?tenant_id=649de47ad78a44bd8562b0aa84389b2b'
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
|
||||
def test_get_summary_with_service(self):
|
||||
self.mgr.get_summary(service='compute')
|
||||
expect = [
|
||||
'GET',
|
||||
'/v1/report/summary?service=compute'
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
|
||||
def test_get_summary_with_groupby(self):
|
||||
self.mgr.get_summary(groupby='res_type,tenant_id')
|
||||
expect = [
|
||||
'GET',
|
||||
'/v1/report/summary?groupby=res_type%2Ctenant_id'
|
||||
]
|
||||
self.http_client.assert_called(*expect)
|
||||
@@ -16,7 +16,7 @@
|
||||
from stevedore import extension
|
||||
|
||||
from cloudkittyclient import client as ckclient
|
||||
from cloudkittyclient.openstack.common.apiclient import client
|
||||
from cloudkittyclient.v1 import collector
|
||||
from cloudkittyclient.v1 import core
|
||||
from cloudkittyclient.v1 import report
|
||||
from cloudkittyclient.v1 import storage
|
||||
@@ -27,37 +27,41 @@ SUBMODULES_NAMESPACE = 'cloudkitty.client.modules'
|
||||
class Client(object):
|
||||
"""Client for the Cloudkitty v1 API.
|
||||
|
||||
:param string endpoint: A user-supplied endpoint URL for the cloudkitty
|
||||
service.
|
||||
:param function token: Provides token for authentication.
|
||||
:param integer timeout: Allows customization of the timeout for client
|
||||
http requests. (optional)
|
||||
:param session: a keystoneauth/keystoneclient session object
|
||||
:type session: keystoneclient.session.Session
|
||||
:param str service_type: The default service_type for URL discovery
|
||||
:param str service_name: The default service_name for URL discovery
|
||||
:param str interface: The default interface for URL discovery
|
||||
(Default: public)
|
||||
:param str region_name: The default region_name for URL discovery
|
||||
:param str endpoint_override: Always use this endpoint URL for requests
|
||||
for this cloudkittyclient
|
||||
:param auth: An auth plugin to use instead of the session one
|
||||
:type auth: keystoneclient.auth.base.BaseAuthPlugin
|
||||
:param str user_agent: The User-Agent string to set
|
||||
(Default is python-cloudkittyclient)
|
||||
:param int connect_retries: the maximum number of retries that should be
|
||||
attempted for connection errors
|
||||
:param logger: A logging object
|
||||
:type logger: logging.Logger
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize a new client for the Cloudkitty v1 API."""
|
||||
self.auth_plugin = (kwargs.get('auth_plugin')
|
||||
or ckclient.get_auth_plugin(*args, **kwargs))
|
||||
self.client = client.HTTPClient(
|
||||
auth_plugin=self.auth_plugin,
|
||||
region_name=kwargs.get('region_name'),
|
||||
endpoint_type=kwargs.get('endpoint_type'),
|
||||
original_ip=kwargs.get('original_ip'),
|
||||
verify=kwargs.get('verify'),
|
||||
cert=kwargs.get('cert'),
|
||||
timeout=kwargs.get('timeout'),
|
||||
timings=kwargs.get('timings'),
|
||||
keyring_saver=kwargs.get('keyring_saver'),
|
||||
debug=kwargs.get('debug'),
|
||||
user_agent=kwargs.get('user_agent'),
|
||||
http=kwargs.get('http')
|
||||
)
|
||||
|
||||
self.http_client = client.BaseClient(self.client)
|
||||
if not kwargs.get('auth_plugin'):
|
||||
kwargs['auth_plugin'] = ckclient.get_auth_plugin(*args, **kwargs)
|
||||
self.auth_plugin = kwargs.get('auth_plugin')
|
||||
|
||||
self.http_client = ckclient.construct_http_client(**kwargs)
|
||||
self.modules = core.CloudkittyModuleManager(self.http_client)
|
||||
self.collector = collector.CollectorManager(self.http_client)
|
||||
self.reports = report.ReportManager(self.http_client)
|
||||
self.reportsummary = report.ReportSummaryManager(self.http_client)
|
||||
self.quotations = core.QuotationManager(self.http_client)
|
||||
self.storage = storage.StorageManager(self.http_client)
|
||||
self.config = core.ConfigInfoManager(self.http_client)
|
||||
self.service_info = core.ServiceInfoManager(self.http_client)
|
||||
self._expose_submodules()
|
||||
|
||||
def _expose_submodules(self):
|
||||
|
||||
@@ -12,19 +12,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cloudkittyclient.common import base
|
||||
from cloudkittyclient.v1.collector import mapping
|
||||
from cloudkittyclient.v1.collector import state
|
||||
|
||||
|
||||
class Collector(base.Resource):
|
||||
|
||||
key = 'collector'
|
||||
|
||||
def __repr__(self):
|
||||
return "<Collector %s>" % self._info
|
||||
|
||||
|
||||
class CollectorManager(base.Manager):
|
||||
resource_class = Collector
|
||||
base_url = "/v1/rating"
|
||||
key = "collector"
|
||||
collection_key = "collectors"
|
||||
class CollectorManager(object):
|
||||
def __init__(self, http_client):
|
||||
self.mappings = mapping.MappingManager(http_client)
|
||||
self.states = state.StateManager(http_client)
|
||||
|
||||
30
cloudkittyclient/v1/collector/mapping.py
Normal file
30
cloudkittyclient/v1/collector/mapping.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
#
|
||||
# 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 cloudkittyclient.common import base
|
||||
|
||||
|
||||
class Mapping(base.Resource):
|
||||
|
||||
key = 'mapping'
|
||||
|
||||
def __repr__(self):
|
||||
return "<Mapping %s>" % self._info
|
||||
|
||||
|
||||
class MappingManager(base.CrudManager):
|
||||
resource_class = Mapping
|
||||
base_url = "/v1/collector"
|
||||
key = "mapping"
|
||||
collection_key = "mappings"
|
||||
87
cloudkittyclient/v1/collector/shell.py
Normal file
87
cloudkittyclient/v1/collector/shell.py
Normal file
@@ -0,0 +1,87 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
#
|
||||
# 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 cloudkittyclient.common import utils
|
||||
|
||||
|
||||
@utils.arg('-c', '--collector',
|
||||
help='Collector name to filter on.',
|
||||
required=False,
|
||||
default=None)
|
||||
def do_collector_mapping_list(cc, args):
|
||||
"""List collector mapping."""
|
||||
data = cc.collector.mappings.list(collector=args.collector)
|
||||
fields = ['service', 'collector']
|
||||
fields_labels = ['Service', 'Collector']
|
||||
utils.print_list(data, fields, fields_labels, sortby=0)
|
||||
|
||||
|
||||
@utils.arg('-s', '--service',
|
||||
help='Which service to get the mapping for.',
|
||||
required=True)
|
||||
def do_collector_mapping_get(cc, args):
|
||||
"""Show collector mapping detail."""
|
||||
data = cc.collector.mappings.get(mapping_id=args.service)
|
||||
utils.print_dict(data.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-c', '--collector',
|
||||
help='Map a service to this collector.',
|
||||
required=True)
|
||||
@utils.arg('-s', '--service',
|
||||
help='Map a collector to this service.',
|
||||
required=True)
|
||||
def do_collector_mapping_create(cc, args):
|
||||
"""Create collector mapping."""
|
||||
out = cc.collector.mappings.create(service=args.service,
|
||||
collector=args.collector)
|
||||
utils.print_dict(out.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-s', '--service',
|
||||
help='Filter on this service.',
|
||||
required=True)
|
||||
def do_collector_mapping_delete(cc, args):
|
||||
"""Delete collector mapping."""
|
||||
# TODO(sheeprine): Use a less hacky way to do this
|
||||
cc.collector.mappings.delete(mapping_id=args.service)
|
||||
|
||||
|
||||
@utils.arg('-n', '--name',
|
||||
help='Name of the collector.',
|
||||
required=True)
|
||||
def do_collector_state_get(cc, args):
|
||||
"""Show collector state."""
|
||||
data = cc.collector.states.get(state_id=args.name)
|
||||
utils.print_dict(data.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-n', '--name',
|
||||
help='Name of the collector.',
|
||||
required=True)
|
||||
def do_collector_state_enable(cc, args):
|
||||
"""Enable collector state."""
|
||||
new_state = cc.collector.states.update(name=args.name, enabled=True)
|
||||
utils.print_dict(new_state.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-n', '--name',
|
||||
help='Name of the collector.',
|
||||
required=True)
|
||||
def do_collector_state_disable(cc, args):
|
||||
"""Disable collector state."""
|
||||
new_state = cc.collector.states.update(name=args.name, enabled=False)
|
||||
utils.print_dict(new_state.to_dict())
|
||||
109
cloudkittyclient/v1/collector/shell_cli.py
Normal file
109
cloudkittyclient/v1/collector/shell_cli.py
Normal file
@@ -0,0 +1,109 @@
|
||||
# Copyright 2016 Objectif Libre
|
||||
#
|
||||
# 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 osc_lib.command import command
|
||||
|
||||
from cloudkittyclient.v1.collector import shell
|
||||
|
||||
|
||||
class CliCollectorMappingList(command.Command):
|
||||
"""List collector mappings."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliCollectorMappingList, self).get_parser(prog_name)
|
||||
parser.add_argument('-c', '--collector',
|
||||
help='Collector name to filter on.',
|
||||
required=False,
|
||||
default=None)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_collector_mapping_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliCollectorMappingGet(command.Command):
|
||||
"""Show collector mapping detail."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliCollectorMappingGet, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--service',
|
||||
help='Which service to get the mapping for.',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_collector_mapping_get(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliCollectorMappingCreate(command.Command):
|
||||
"""Create collector mappings."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliCollectorMappingCreate, self).get_parser(prog_name)
|
||||
parser.add_argument('-c', '--collector',
|
||||
help='Map a service to this collector.',
|
||||
required=True)
|
||||
parser.add_argument('-s', '--service',
|
||||
help='Map a collector to this service.',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_collector_mapping_create(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliCollectorMappingDelete(command.Command):
|
||||
"""Delete collector mappings."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliCollectorMappingDelete, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--service',
|
||||
help='Filter on this service',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_collector_mapping_delete(ckclient, parsed_args)
|
||||
|
||||
|
||||
class BaseCliCollectorState(command.Command):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(BaseCliCollectorState, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Name of the collector',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
|
||||
class CliCollectorStateGet(BaseCliCollectorState):
|
||||
"""Show collector state."""
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_collector_state_get(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliCollectorStateEnable(BaseCliCollectorState):
|
||||
"""Enable collector state."""
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_collector_state_enable(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliCollectorStateDisable(BaseCliCollectorState):
|
||||
"""Disable collector state."""
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_collector_state_disable(ckclient, parsed_args)
|
||||
30
cloudkittyclient/v1/collector/state.py
Normal file
30
cloudkittyclient/v1/collector/state.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
#
|
||||
# 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 cloudkittyclient.common import base
|
||||
|
||||
|
||||
class State(base.Resource):
|
||||
|
||||
key = 'state'
|
||||
|
||||
def __repr__(self):
|
||||
return "<State %s>" % self._info
|
||||
|
||||
|
||||
class StateManager(base.CrudManager):
|
||||
resource_class = State
|
||||
base_url = "/v1/collector"
|
||||
key = "state"
|
||||
collection_key = "states"
|
||||
@@ -30,6 +30,10 @@ class CloudkittyModule(base.Resource):
|
||||
self.enabled = False
|
||||
self.update()
|
||||
|
||||
def set_priority(self, value):
|
||||
self.priority = value
|
||||
self.update()
|
||||
|
||||
|
||||
class CloudkittyModuleManager(base.CrudManager):
|
||||
resource_class = CloudkittyModule
|
||||
@@ -60,3 +64,26 @@ class QuotationManager(base.Manager):
|
||||
out = self.api.post(self.base_url,
|
||||
json={'resources': resources}).json()
|
||||
return out
|
||||
|
||||
|
||||
class ServiceInfo(base.Resource):
|
||||
|
||||
key = "service"
|
||||
|
||||
def __repr__(self):
|
||||
return "<Service %s>" % self._info
|
||||
|
||||
|
||||
class ServiceInfoManager(base.CrudManager):
|
||||
resource_class = ServiceInfo
|
||||
base_url = "/v1/info"
|
||||
key = "service"
|
||||
collection_key = "services"
|
||||
|
||||
|
||||
class ConfigInfoManager(base.Manager):
|
||||
base_url = "/v1/info/config"
|
||||
|
||||
def get_config(self):
|
||||
out = self.api.get(self.base_url).json()
|
||||
return out
|
||||
|
||||
@@ -15,23 +15,76 @@
|
||||
from cloudkittyclient.common import base
|
||||
|
||||
|
||||
class Service(base.Resource):
|
||||
key = 'service'
|
||||
class BaseAttributeMixin(object):
|
||||
def _validate_attribute(self, attribute):
|
||||
attr = getattr(self, attribute)
|
||||
if attr:
|
||||
kwargs = {attribute: attr}
|
||||
return kwargs
|
||||
|
||||
def __repr__(self):
|
||||
return "<hashmap.Service %s>" % self._info
|
||||
def _get_resource(self, mgr, attribute):
|
||||
kwargs = self._validate_attribute(attribute)
|
||||
if kwargs:
|
||||
return mgr(client=self.manager.client).get(**kwargs)
|
||||
|
||||
def _get_resources(self, mgr, attribute):
|
||||
kwargs = self._validate_attribute(attribute)
|
||||
if kwargs:
|
||||
try:
|
||||
return mgr(client=self.manager.client).findall(**kwargs)
|
||||
except Exception:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
class ServiceMixin(BaseAttributeMixin):
|
||||
@property
|
||||
def service(self):
|
||||
return self._get_resource(ServiceManager, 'service_id')
|
||||
|
||||
|
||||
class FieldMixin(BaseAttributeMixin):
|
||||
@property
|
||||
def field(self):
|
||||
return self._get_resource(FieldManager, 'field_id')
|
||||
|
||||
|
||||
class GroupMixin(BaseAttributeMixin):
|
||||
@property
|
||||
def group(self):
|
||||
return self._get_resource(GroupManager, 'group_id')
|
||||
|
||||
|
||||
class FieldsMixin(BaseAttributeMixin):
|
||||
attribute = ''
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
return FieldManager(client=self.manager.client).findall(
|
||||
service_id=self.service_id
|
||||
)
|
||||
return self._get_resources(FieldManager, self.attribute)
|
||||
|
||||
|
||||
class MappingsMixin(BaseAttributeMixin):
|
||||
attribute = ''
|
||||
|
||||
@property
|
||||
def mappings(self):
|
||||
return MappingManager(client=self.manager.client).findall(
|
||||
service_id=self.service_id
|
||||
)
|
||||
return self._get_resources(MappingManager, self.attribute)
|
||||
|
||||
|
||||
class ThresholdsMixin(BaseAttributeMixin):
|
||||
attribute = ''
|
||||
|
||||
@property
|
||||
def thresholds(self):
|
||||
return self._get_resources(ThresholdManager, self.attribute)
|
||||
|
||||
|
||||
class Service(base.Resource, FieldsMixin, MappingsMixin, ThresholdsMixin):
|
||||
key = 'service'
|
||||
attribute = 'service_id'
|
||||
|
||||
def __repr__(self):
|
||||
return "<hashmap.Service %s>" % self._info
|
||||
|
||||
|
||||
class ServiceManager(base.CrudManager):
|
||||
@@ -41,18 +94,13 @@ class ServiceManager(base.CrudManager):
|
||||
collection_key = 'services'
|
||||
|
||||
|
||||
class Field(base.Resource):
|
||||
class Field(base.Resource, ServiceMixin, MappingsMixin, ThresholdsMixin):
|
||||
key = 'field'
|
||||
attribute = 'field_id'
|
||||
|
||||
def __repr__(self):
|
||||
return "<hashmap.Field %s>" % self._info
|
||||
|
||||
@property
|
||||
def service(self):
|
||||
return ServiceManager(client=self.manager.client).get(
|
||||
service_id=self.service_id
|
||||
)
|
||||
|
||||
|
||||
class FieldManager(base.CrudManager):
|
||||
resource_class = Field
|
||||
@@ -61,26 +109,12 @@ class FieldManager(base.CrudManager):
|
||||
collection_key = 'fields'
|
||||
|
||||
|
||||
class Mapping(base.Resource):
|
||||
class Mapping(base.Resource, ServiceMixin, FieldMixin, GroupMixin):
|
||||
key = 'mapping'
|
||||
|
||||
def __repr__(self):
|
||||
return "<hashmap.Mapping %s>" % self._info
|
||||
|
||||
@property
|
||||
def service(self):
|
||||
return ServiceManager(client=self.manager.client).get(
|
||||
service_id=self.service_id
|
||||
)
|
||||
|
||||
@property
|
||||
def field(self):
|
||||
if self.field_id is None:
|
||||
return None
|
||||
return FieldManager(client=self.manager.client).get(
|
||||
service_id=self.service_id
|
||||
)
|
||||
|
||||
|
||||
class MappingManager(base.CrudManager):
|
||||
resource_class = Mapping
|
||||
@@ -89,8 +123,9 @@ class MappingManager(base.CrudManager):
|
||||
collection_key = 'mappings'
|
||||
|
||||
|
||||
class Group(base.Resource):
|
||||
class Group(base.Resource, MappingsMixin, ThresholdsMixin):
|
||||
key = 'group'
|
||||
attribute = 'group_id'
|
||||
|
||||
def __repr__(self):
|
||||
return "<hashmap.Group %s>" % self._info
|
||||
@@ -112,7 +147,7 @@ class GroupManager(base.CrudManager):
|
||||
return self._delete(url)
|
||||
|
||||
|
||||
class Threshold(base.Resource):
|
||||
class Threshold(base.Resource, ServiceMixin, FieldMixin, GroupMixin):
|
||||
key = 'threshold'
|
||||
|
||||
def __repr__(self):
|
||||
@@ -124,9 +159,3 @@ class ThresholdManager(base.CrudManager):
|
||||
base_url = '/v1/rating/module_config/hashmap'
|
||||
key = 'threshold'
|
||||
collection_key = 'thresholds'
|
||||
|
||||
def group(self, threshold_id):
|
||||
url = ('%(base_url)s/thresholds/%(threshold_id)s/group' %
|
||||
{'base_url': self.base_url, 'threshold_id': threshold_id})
|
||||
out = self._get(url)
|
||||
return out
|
||||
|
||||
@@ -17,6 +17,7 @@ import functools
|
||||
|
||||
from oslo_utils import strutils
|
||||
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.common import utils
|
||||
from cloudkittyclient import exc
|
||||
|
||||
@@ -44,8 +45,8 @@ def do_hashmap_service_list(cc, args={}):
|
||||
"""List services."""
|
||||
try:
|
||||
services = cc.hashmap.services.list()
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Services not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Services not found.')
|
||||
else:
|
||||
field_labels = ['Name', 'Service id']
|
||||
fields = ['name', 'service_id']
|
||||
@@ -60,8 +61,8 @@ def do_hashmap_service_delete(cc, args={}):
|
||||
"""Delete a service."""
|
||||
try:
|
||||
cc.hashmap.services.delete(service_id=args.service_id)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Service not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Service not found: %s' % args.service_id)
|
||||
|
||||
|
||||
@utils.arg('-n', '--name',
|
||||
@@ -89,11 +90,12 @@ def do_hashmap_field_create(cc, args={}):
|
||||
help='Service id',
|
||||
required=True)
|
||||
def do_hashmap_field_list(cc, args={}):
|
||||
"""Create a field."""
|
||||
"""List fields."""
|
||||
try:
|
||||
created_field = cc.hashmap.fields.list(service_id=args.service_id)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Fields not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Fields not found in service: %s'
|
||||
% args.service_id)
|
||||
else:
|
||||
field_labels = ['Name', 'Field id']
|
||||
fields = ['name', 'field_id']
|
||||
@@ -108,30 +110,43 @@ def do_hashmap_field_delete(cc, args={}):
|
||||
"""Delete a field."""
|
||||
try:
|
||||
cc.hashmap.fields.delete(field_id=args.field_id)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Field not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Field not found: %s' % args.field_id)
|
||||
|
||||
|
||||
def common_hashmap_mapping_arguments(create=False):
|
||||
def _wrapper(func):
|
||||
@utils.arg('-c', '--cost',
|
||||
help='Mapping cost',
|
||||
required=create)
|
||||
@utils.arg('-v', '--value',
|
||||
help='Mapping value',
|
||||
required=False)
|
||||
@utils.arg('-t', '--type',
|
||||
help='Mapping type (flat, rate)',
|
||||
required=False)
|
||||
@utils.arg('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
@utils.arg('-p', '--project-id',
|
||||
help='Project/tenant id',
|
||||
required=False)
|
||||
@functools.wraps(func)
|
||||
def _wrapped(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
return _wrapped
|
||||
return _wrapper
|
||||
|
||||
|
||||
@utils.arg('-c', '--cost',
|
||||
help='Mapping cost',
|
||||
required=True)
|
||||
@utils.arg('-v', '--value',
|
||||
help='Mapping value',
|
||||
required=False)
|
||||
@utils.arg('-t', '--type',
|
||||
help='Mapping type (flat, rate)',
|
||||
required=False)
|
||||
@utils.arg('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=False)
|
||||
@utils.arg('-f', '--field-id',
|
||||
help='Field id',
|
||||
required=False)
|
||||
@utils.arg('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
@common_hashmap_mapping_arguments(create=True)
|
||||
def do_hashmap_mapping_create(cc, args={}):
|
||||
"""Create a ampping."""
|
||||
"""Create a mapping."""
|
||||
arg_to_field_mapping = {
|
||||
'cost': 'cost',
|
||||
'value': 'value',
|
||||
@@ -139,6 +154,7 @@ def do_hashmap_mapping_create(cc, args={}):
|
||||
'service_id': 'service_id',
|
||||
'field_id': 'field_id',
|
||||
'group_id': 'group_id',
|
||||
'project_id': 'tenant_id',
|
||||
}
|
||||
fields = {}
|
||||
for k, v in vars(args).items():
|
||||
@@ -152,18 +168,7 @@ def do_hashmap_mapping_create(cc, args={}):
|
||||
@utils.arg('-m', '--mapping-id',
|
||||
help='Mapping id',
|
||||
required=True)
|
||||
@utils.arg('-c', '--cost',
|
||||
help='Mapping cost',
|
||||
required=False)
|
||||
@utils.arg('-v', '--value',
|
||||
help='Mapping value',
|
||||
required=False)
|
||||
@utils.arg('-t', '--type',
|
||||
help='Mapping type (flat, rate)',
|
||||
required=False)
|
||||
@utils.arg('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
@common_hashmap_mapping_arguments()
|
||||
def do_hashmap_mapping_update(cc, args={}):
|
||||
"""Update a mapping."""
|
||||
arg_to_field_mapping = {
|
||||
@@ -172,11 +177,12 @@ def do_hashmap_mapping_update(cc, args={}):
|
||||
'value': 'value',
|
||||
'type': 'type',
|
||||
'group_id': 'group_id',
|
||||
'project_id': 'tenant_id',
|
||||
}
|
||||
try:
|
||||
mapping = cc.hashmap.mappings.get(mapping_id=args.mapping_id)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Modules not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Mapping not found: %s' % args.mapping_id)
|
||||
for k, v in vars(args).items():
|
||||
if k in arg_to_field_mapping:
|
||||
if v is not None:
|
||||
@@ -193,23 +199,29 @@ def do_hashmap_mapping_update(cc, args={}):
|
||||
@utils.arg('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
@utils.arg('-p', '--project-id',
|
||||
help='Project/tenant id',
|
||||
required=False)
|
||||
def do_hashmap_mapping_list(cc, args={}):
|
||||
"""List mappings."""
|
||||
if args.service_id is None and args.field_id is None:
|
||||
raise exc.CommandError("Provide either service-id or field-id")
|
||||
if (args.group_id is None and
|
||||
args.service_id is None and args.field_id is None):
|
||||
raise exc.CommandError("Provide either group-id, service-id or "
|
||||
"field-id")
|
||||
try:
|
||||
mappings = cc.hashmap.mappings.list(service_id=args.service_id,
|
||||
field_id=args.field_id,
|
||||
group_id=args.group_id)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Mapping not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Mappings not found for field: %s'
|
||||
% args.field_id)
|
||||
else:
|
||||
field_labels = ['Mapping id', 'Value', 'Cost',
|
||||
'Type', 'Field id',
|
||||
'Service id', 'Group id']
|
||||
'Service id', 'Group id', 'Tenant id']
|
||||
fields = ['mapping_id', 'value', 'cost',
|
||||
'type', 'field_id',
|
||||
'service_id', 'group_id']
|
||||
'service_id', 'group_id', 'tenant_id']
|
||||
utils.print_list(mappings, fields, field_labels,
|
||||
sortby=0)
|
||||
|
||||
@@ -221,7 +233,7 @@ def do_hashmap_mapping_delete(cc, args={}):
|
||||
"""Delete a mapping."""
|
||||
try:
|
||||
cc.hashmap.mappings.delete(mapping_id=args.mapping_id)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Mapping not found: %s' % args.mapping_id)
|
||||
|
||||
|
||||
@@ -246,8 +258,8 @@ def do_hashmap_group_list(cc, args={}):
|
||||
"""List groups."""
|
||||
try:
|
||||
groups = cc.hashmap.groups.list()
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Mapping not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Groups not found.')
|
||||
else:
|
||||
field_labels = ['Name',
|
||||
'Group id']
|
||||
@@ -268,37 +280,51 @@ def do_hashmap_group_delete(cc, args={}):
|
||||
try:
|
||||
cc.hashmap.groups.delete(group_id=args.group_id,
|
||||
recursive=args.recursive)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Group not found: %s' % args.group_id)
|
||||
|
||||
|
||||
@utils.arg('-l', '--level',
|
||||
help='Threshold level',
|
||||
required=True)
|
||||
@utils.arg('-c', '--cost',
|
||||
help='Threshold cost',
|
||||
required=True)
|
||||
@utils.arg('-m', '--map-type',
|
||||
help='Threshold type (flat, rate)',
|
||||
required=False)
|
||||
def common_hashmap_threshold_arguments(create=False):
|
||||
def _wrapper(func):
|
||||
@utils.arg('-l', '--level',
|
||||
help='Threshold level',
|
||||
required=create)
|
||||
@utils.arg('-c', '--cost',
|
||||
help='Threshold cost',
|
||||
required=create)
|
||||
@utils.arg('-t', '--type',
|
||||
help='Threshold type (flat, rate)',
|
||||
required=False)
|
||||
@utils.arg('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
@utils.arg('-p', '--project-id',
|
||||
help='Project/tenant id',
|
||||
required=False)
|
||||
@functools.wraps(func)
|
||||
def _wrapped(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
return _wrapped
|
||||
return _wrapper
|
||||
|
||||
|
||||
@utils.arg('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=False)
|
||||
@utils.arg('-f', '--field-id',
|
||||
help='Field id',
|
||||
required=False)
|
||||
@utils.arg('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
@common_hashmap_threshold_arguments(create=True)
|
||||
def do_hashmap_threshold_create(cc, args={}):
|
||||
"""Create a ampping."""
|
||||
"""Create a mapping."""
|
||||
arg_to_field_mapping = {
|
||||
'level': 'level',
|
||||
'cost': 'cost',
|
||||
'map_type': 'map_type',
|
||||
'type': 'type',
|
||||
'service_id': 'service_id',
|
||||
'field_id': 'field_id',
|
||||
'group_id': 'group_id',
|
||||
'project_id': 'tenant_id',
|
||||
}
|
||||
fields = {}
|
||||
for k, v in vars(args).items():
|
||||
@@ -309,34 +335,24 @@ def do_hashmap_threshold_create(cc, args={}):
|
||||
utils.print_dict(out.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-t', '--threshold-id',
|
||||
@utils.arg('-i', '--threshold-id',
|
||||
help='Threshold id',
|
||||
required=True)
|
||||
@utils.arg('-l', '--level',
|
||||
help='Threshold level',
|
||||
required=False)
|
||||
@utils.arg('-c', '--cost',
|
||||
help='Threshold cost',
|
||||
required=False)
|
||||
@utils.arg('-m', '--map-type',
|
||||
help='Threshold type (flat, rate)',
|
||||
required=False)
|
||||
@utils.arg('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
@common_hashmap_threshold_arguments()
|
||||
def do_hashmap_threshold_update(cc, args={}):
|
||||
"""Update a threshold."""
|
||||
arg_to_field_mapping = {
|
||||
'threshold_id': 'threshold_id',
|
||||
'cost': 'cost',
|
||||
'level': 'level',
|
||||
'map_type': 'map_type',
|
||||
'type': 'type',
|
||||
'group_id': 'group_id',
|
||||
'project_id': 'tenant_id',
|
||||
}
|
||||
try:
|
||||
threshold = cc.hashmap.thresholds.get(threshold_id=args.threshold_id)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Modules not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
|
||||
for k, v in vars(args).items():
|
||||
if k in arg_to_field_mapping:
|
||||
if v is not None:
|
||||
@@ -357,6 +373,9 @@ def do_hashmap_threshold_update(cc, args={}):
|
||||
type=_bool_strict, metavar='{True,False}',
|
||||
help='If True, list only orhpaned thresholds',
|
||||
required=False)
|
||||
@utils.arg('-p', '--project-id',
|
||||
help='Project/tenant id',
|
||||
required=False)
|
||||
def do_hashmap_threshold_list(cc, args={}):
|
||||
"""List thresholds."""
|
||||
if (args.group_id is None and
|
||||
@@ -368,48 +387,48 @@ def do_hashmap_threshold_list(cc, args={}):
|
||||
field_id=args.field_id,
|
||||
group_id=args.group_id,
|
||||
no_group=args.no_group)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Threshold not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Thresholds not found')
|
||||
else:
|
||||
field_labels = ['Threshold id', 'Level', 'Cost',
|
||||
'Type', 'Field id',
|
||||
'Service id', 'Group id']
|
||||
'Service id', 'Group id', 'Tenant id']
|
||||
fields = ['threshold_id', 'level', 'cost',
|
||||
'map_type', 'field_id',
|
||||
'service_id', 'group_id']
|
||||
'type', 'field_id',
|
||||
'service_id', 'group_id', 'tenant_id']
|
||||
utils.print_list(thresholds, fields, field_labels, sortby=0)
|
||||
|
||||
|
||||
@utils.arg('-t', '--threshold-id',
|
||||
@utils.arg('-i', '--threshold-id',
|
||||
help='Threshold uuid',
|
||||
required=True)
|
||||
def do_hashmap_threshold_delete(cc, args={}):
|
||||
"""Delete a threshold."""
|
||||
try:
|
||||
cc.hashmap.thresholds.delete(threshold_id=args.threshold_id)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
|
||||
|
||||
|
||||
@utils.arg('-t', '--threshold-id',
|
||||
@utils.arg('-i', '--threshold-id',
|
||||
help='Threshold uuid',
|
||||
required=True)
|
||||
def do_hashmap_threshold_get(cc, args={}):
|
||||
"""Get a threshold."""
|
||||
try:
|
||||
threshold = cc.hashmap.thresholds.get(threshold_id=args.threshold_id)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
|
||||
utils.print_dict(threshold.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-t', '--threshold-id',
|
||||
@utils.arg('-i', '--threshold-id',
|
||||
help='Threshold uuid',
|
||||
required=True)
|
||||
def do_hashmap_threshold_group(cc, args={}):
|
||||
"""Get a threshold group."""
|
||||
try:
|
||||
threshold = cc.hashmap.thresholds.group(threshold_id=args.threshold_id)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
|
||||
utils.print_dict(threshold.to_dict())
|
||||
|
||||
355
cloudkittyclient/v1/rating/hashmap/shell_cli.py
Normal file
355
cloudkittyclient/v1/rating/hashmap/shell_cli.py
Normal file
@@ -0,0 +1,355 @@
|
||||
# Copyright 2016 Objectif Libre
|
||||
# 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.
|
||||
|
||||
import functools
|
||||
|
||||
from osc_lib.command import command
|
||||
from oslo_utils import strutils
|
||||
|
||||
from cloudkittyclient.v1.rating.hashmap import shell
|
||||
|
||||
|
||||
_bool_strict = functools.partial(strutils.bool_from_string, strict=True)
|
||||
|
||||
|
||||
class CliHashmapServiceCreate(command.Command):
|
||||
"""Create a service."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapServiceCreate, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Service name',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_service_create(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapServiceList(command.Command):
|
||||
"""List services."""
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_service_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapServiceDelete(command.Command):
|
||||
"""Delete a service."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapServiceDelete, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_service_delete(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapFieldCreate(command.Command):
|
||||
"""Create a field."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapFieldCreate, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=True)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Field name',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_field_create(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapFieldList(command.Command):
|
||||
"""List fields."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapFieldList, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_field_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapFieldDelete(command.Command):
|
||||
"""Delete a field."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapFieldDelete, self).get_parser(prog_name)
|
||||
parser.add_argument('-f', '--field-id',
|
||||
help='Field id',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_field_delete(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapMappingCommon(command.Command):
|
||||
def get_parser(self, prog_name, cost=False):
|
||||
parser = super(CliHashmapMappingCommon, self).get_parser(prog_name)
|
||||
parser.add_argument('-c', '--cost',
|
||||
help='Mapping Cost',
|
||||
required=cost)
|
||||
parser.add_argument('-v', '--value',
|
||||
help='Mapping Value',
|
||||
required=False)
|
||||
parser.add_argument('-t', '--type',
|
||||
help='Mapping type (flat, rate)',
|
||||
required=False)
|
||||
parser.add_argument('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
parser.add_argument('-p', '--project-id',
|
||||
help='Project/Tenant id',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
|
||||
class CliHashmapMappingCreate(CliHashmapMappingCommon):
|
||||
"""Create a mapping."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapMappingCreate, self).get_parser(prog_name,
|
||||
cost=True)
|
||||
parser.add_argument('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=False)
|
||||
parser.add_argument('-f', '--field-id',
|
||||
help='Service id',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_mapping_create(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapMappingUpdate(CliHashmapMappingCommon):
|
||||
"""Update a mapping."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapMappingUpdate, self).get_parser(prog_name)
|
||||
parser.add_argument('-m', '--mapping-id',
|
||||
help='Mapping id',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_mapping_update(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapMappingList(command.Command):
|
||||
"""List mappings."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapMappingList, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=False)
|
||||
parser.add_argument('-f', '--field-id',
|
||||
help='Field id',
|
||||
required=False)
|
||||
parser.add_argument('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
parser.add_argument('-p', '--project-id',
|
||||
help='Project id',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_mapping_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapMappingDelete(command.Command):
|
||||
"""Delete a mapping."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapMappingDelete, self).get_parser(prog_name)
|
||||
parser.add_argument('-m', '--mapping-id',
|
||||
help='Mapping id',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_mapping_delete(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapGroupCreate(command.Command):
|
||||
"""Create a group."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapGroupCreate, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Group name.',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_group_create(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapGroupList(command.Command):
|
||||
"""List groups."""
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_group_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapGroupDelete(command.Command):
|
||||
"""Delete a group."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapGroupDelete, self).get_parser(prog_name)
|
||||
parser.add_argument('-g', '--group-id',
|
||||
help='Group uuid',
|
||||
required=True)
|
||||
parser.add_argument('-r', '--recursive',
|
||||
help="""Delete the group's mappings.""",
|
||||
required=False,
|
||||
default=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_group_delete(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapThresholdCommon(command.Command):
|
||||
def get_parser(self, prog_name, create=False):
|
||||
parser = super(CliHashmapThresholdCommon, self).get_parser(prog_name)
|
||||
parser.add_argument('-l', '--level',
|
||||
help='Threshold level',
|
||||
required=create)
|
||||
parser.add_argument('-c', '--cost',
|
||||
help='Threshold cost',
|
||||
required=create)
|
||||
parser.add_argument('-t', '--type',
|
||||
help='Threshold type',
|
||||
required=False)
|
||||
parser.add_argument('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
parser.add_argument('-p', '--project-id',
|
||||
help='Project/tenant id',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
|
||||
class CliHashmapThresholdCreate(CliHashmapThresholdCommon):
|
||||
"""Create a threshold."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapThresholdCreate, self).get_parser(prog_name,
|
||||
create=True)
|
||||
parser.add_argument('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=False)
|
||||
parser.add_argument('-f', '--field-id',
|
||||
help='Field id',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_threshold_create(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapThresholdUpdate(CliHashmapThresholdCommon):
|
||||
"""Update a threshold."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapThresholdUpdate, self).get_parser(prog_name)
|
||||
parser.add_argument('-i', '--threshold-id',
|
||||
help='Threshold id',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_threshold_update(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapThresholdList(command.Command):
|
||||
"""List thresholds."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapThresholdList, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--service-id',
|
||||
help='Service id',
|
||||
required=False)
|
||||
parser.add_argument('-f', '--field-id',
|
||||
help='Field id',
|
||||
required=False)
|
||||
parser.add_argument('-g', '--group-id',
|
||||
help='Group id',
|
||||
required=False)
|
||||
parser.add_argument('--no-group',
|
||||
type=_bool_strict, metavar='{True,False}',
|
||||
help='If True, list only orphaned thresholds',
|
||||
required=False)
|
||||
parser.add_argument('-p', '--project-id',
|
||||
help='Project/tenant id',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_threshold_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapThresholdDelete(command.Command):
|
||||
"""Delete a threshold."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapThresholdDelete, self).get_parser(prog_name)
|
||||
parser.add_argument('-i', '--threshold-id',
|
||||
help='Threshold uuid',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_threshold_delete(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapThresholdGet(command.Command):
|
||||
"""Get a threshold."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapThresholdGet, self).get_parser(prog_name)
|
||||
parser.add_argument('-i', '--threshold-id',
|
||||
help='Threshold uuid',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_threshold_get(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliHashmapThresholdGroup(command.Command):
|
||||
"""Get a threshold group."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliHashmapThresholdGroup, self).get_parser(prog_name)
|
||||
parser.add_argument('-i', '--threshold-id',
|
||||
help='Threshold uuid',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_hashmap_threshold_group(ckclient, parsed_args)
|
||||
30
cloudkittyclient/v1/rating/pyscripts/__init__.py
Normal file
30
cloudkittyclient/v1/rating/pyscripts/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
# 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 cloudkittyclient.common import base
|
||||
|
||||
|
||||
class Script(base.Resource):
|
||||
key = 'script'
|
||||
|
||||
def __repr__(self):
|
||||
return "<pyscripts.Script %s>" % self._info
|
||||
|
||||
|
||||
class ScriptManager(base.CrudManager):
|
||||
resource_class = Script
|
||||
base_url = '/v1/rating/module_config/pyscripts'
|
||||
key = 'script'
|
||||
collection_key = 'scripts'
|
||||
28
cloudkittyclient/v1/rating/pyscripts/client.py
Normal file
28
cloudkittyclient/v1/rating/pyscripts/client.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
# 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 cloudkittyclient.v1.rating import pyscripts
|
||||
|
||||
|
||||
class Client(object):
|
||||
"""Client for the PyScripts v1 API.
|
||||
|
||||
:param http_client: A http client.
|
||||
"""
|
||||
|
||||
def __init__(self, http_client):
|
||||
"""Initialize a new client for the PyScripts v1 API."""
|
||||
self.http_client = http_client
|
||||
self.scripts = pyscripts.ScriptManager(self.http_client)
|
||||
31
cloudkittyclient/v1/rating/pyscripts/extension.py
Normal file
31
cloudkittyclient/v1/rating/pyscripts/extension.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
# 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 cloudkittyclient.v1.rating.pyscripts import client
|
||||
from cloudkittyclient.v1.rating.pyscripts import shell
|
||||
|
||||
|
||||
class Extension(object):
|
||||
"""PyScripts extension.
|
||||
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_client(http_client):
|
||||
return client.Client(http_client)
|
||||
|
||||
@staticmethod
|
||||
def get_shell():
|
||||
return shell
|
||||
117
cloudkittyclient/v1/rating/pyscripts/shell.py
Normal file
117
cloudkittyclient/v1/rating/pyscripts/shell.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# Copyright 2015 Objectif Libre
|
||||
# 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.
|
||||
import functools
|
||||
|
||||
from oslo_utils import strutils
|
||||
import six
|
||||
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.common import utils
|
||||
from cloudkittyclient import exc
|
||||
|
||||
_bool_strict = functools.partial(strutils.bool_from_string, strict=True)
|
||||
|
||||
|
||||
@utils.arg('-n', '--name',
|
||||
help='Script name',
|
||||
required=True)
|
||||
@utils.arg('-f', '--file',
|
||||
help='Script file',
|
||||
required=False)
|
||||
def do_pyscripts_script_create(cc, args={}):
|
||||
"""Create a script."""
|
||||
script_args = {'name': args.name}
|
||||
if args.file:
|
||||
with open(args.file) as fp:
|
||||
script_args['data'] = fp.read()
|
||||
out = cc.pyscripts.scripts.create(**script_args)
|
||||
utils.print_dict(out.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-d', '--show-data',
|
||||
help='Show data in the listing',
|
||||
required=False,
|
||||
default=False)
|
||||
def do_pyscripts_script_list(cc, args={}):
|
||||
"""List scripts."""
|
||||
request_args = {}
|
||||
if not args.show_data:
|
||||
request_args['no_data'] = True
|
||||
scripts = cc.pyscripts.scripts.list(**request_args)
|
||||
field_labels = ['Name', 'Script id', 'Data', 'Checksum']
|
||||
fields = ['name', 'script_id', 'data', 'checksum']
|
||||
utils.print_list(scripts,
|
||||
fields,
|
||||
field_labels,
|
||||
sortby=0)
|
||||
|
||||
|
||||
@utils.arg('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
def do_pyscripts_script_get(cc, args={}):
|
||||
"""Get script."""
|
||||
try:
|
||||
script = cc.pyscripts.scripts.get(script_id=args.script_id)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
utils.print_dict(script.to_dict())
|
||||
|
||||
|
||||
@utils.arg('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
def do_pyscripts_script_get_data(cc, args={}):
|
||||
"""Get script data."""
|
||||
try:
|
||||
script = cc.pyscripts.scripts.get(script_id=args.script_id)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
six.print_(script.data)
|
||||
|
||||
|
||||
@utils.arg('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
def do_pyscripts_script_delete(cc, args={}):
|
||||
"""Delete a script."""
|
||||
try:
|
||||
cc.pyscripts.scripts.delete(script_id=args.script_id)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
|
||||
|
||||
@utils.arg('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
@utils.arg('-f', '--file',
|
||||
help='Script file',
|
||||
required=True)
|
||||
def do_pyscripts_script_update(cc, args={}):
|
||||
"""Update a mapping."""
|
||||
excluded_fields = [
|
||||
'checksum',
|
||||
]
|
||||
with open(args.file) as fp:
|
||||
content = fp.read()
|
||||
try:
|
||||
script = cc.pyscripts.scripts.get(script_id=args.script_id)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
script_dict = script.to_dict()
|
||||
for field in excluded_fields:
|
||||
del script_dict[field]
|
||||
script_dict['data'] = content
|
||||
cc.pyscripts.scripts.update(**script_dict)
|
||||
115
cloudkittyclient/v1/rating/pyscripts/shell_cli.py
Normal file
115
cloudkittyclient/v1/rating/pyscripts/shell_cli.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# Copyright 2016 Objectif Libre
|
||||
# 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.
|
||||
|
||||
import functools
|
||||
|
||||
from osc_lib.command import command
|
||||
from oslo_utils import strutils
|
||||
|
||||
from cloudkittyclient.v1.rating.pyscripts import shell
|
||||
|
||||
|
||||
_bool_strict = functools.partial(strutils.bool_from_string, strict=True)
|
||||
|
||||
|
||||
class CliPyScriptCreate(command.Command):
|
||||
"""Create a script."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliPyScriptCreate, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Script name',
|
||||
required=True)
|
||||
parser.add_argument('-f', '--file',
|
||||
help='Script file',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_pyscripts_script_create(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliPyScriptList(command.Command):
|
||||
"""List scripts."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliPyScriptList, self).get_parser(prog_name)
|
||||
parser.add_argument('-d', '--show-data',
|
||||
help='Show data in the listing',
|
||||
required=False,
|
||||
default=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_pyscripts_script_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliPyScriptGet(command.Command):
|
||||
"""Get script."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliPyScriptGet, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_pyscripts_script_get(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliPyScriptGetData(command.Command):
|
||||
"""Get script data."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliPyScriptGetData, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_pyscripts_script_get_data(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliPyScriptDelete(command.Command):
|
||||
"""Get script data."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliPyScriptDelete, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_pyscripts_script_delete(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliPyScriptUpdate(command.Command):
|
||||
"""Update a script."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliPyScriptUpdate, self).get_parser(prog_name)
|
||||
parser.add_argument('-s', '--script-id',
|
||||
help='Script uuid',
|
||||
required=True)
|
||||
parser.add_argument('-f', '--file',
|
||||
help='Script file',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_pyscripts_script_update(ckclient, parsed_args)
|
||||
@@ -15,26 +15,67 @@
|
||||
from cloudkittyclient.common import base
|
||||
|
||||
|
||||
class ReportResult(base.Resource):
|
||||
class ReportSummary(base.Resource):
|
||||
|
||||
key = 'report'
|
||||
key = 'summary'
|
||||
|
||||
def __init(self, tenant_id=None, res_type=None, begin=None,
|
||||
end=None, rate=None):
|
||||
self.tenant_id = tenant_id
|
||||
self.res_type = res_type
|
||||
self.begin = begin
|
||||
self.end = end
|
||||
self.rate = rate
|
||||
|
||||
def __repr__(self):
|
||||
return "<Report %s>" % self._info
|
||||
return "<Summary %s" % self._info
|
||||
|
||||
|
||||
class ReportManager(base.Manager):
|
||||
class ReportManager(base.CrudManager):
|
||||
|
||||
base_url = "/v1/report"
|
||||
|
||||
def list_tenants(self):
|
||||
return self.client.get(self.base_url + "/tenants").json()
|
||||
|
||||
def get_total(self, tenant_id, begin=None, end=None):
|
||||
url = self.base_url + "/total?tenant_id=%s" % tenant_id
|
||||
filter = [url]
|
||||
def get_total(self, tenant_id=None, begin=None, end=None,
|
||||
service=None, all_tenants=False):
|
||||
url = self.base_url + "/total"
|
||||
filters = list()
|
||||
if tenant_id:
|
||||
filters.append("tenant_id=%s" % tenant_id)
|
||||
if begin:
|
||||
filter.append("begin=%s" % begin.isoformat())
|
||||
filters.append("begin=%s" % begin.isoformat())
|
||||
if end:
|
||||
filter.append("end=%s" % end.isoformat())
|
||||
return self.client.get("&".join(filter)).json()
|
||||
filters.append("end=%s" % end.isoformat())
|
||||
if service:
|
||||
filters.append("service=%s" % service)
|
||||
if all_tenants:
|
||||
filters.append("all_tenants=%s" % all_tenants)
|
||||
if filters:
|
||||
url += "?%s" % ('&'.join(filters))
|
||||
return self.client.get(url).json()
|
||||
|
||||
|
||||
class ReportSummaryManager(ReportManager):
|
||||
|
||||
resource_class = ReportSummary
|
||||
key = 'summary'
|
||||
collection_key = "summary"
|
||||
|
||||
def get_summary(self, tenant_id=None, begin=None, end=None,
|
||||
service=None, groupby=None, all_tenants=False):
|
||||
kwargs = {}
|
||||
if tenant_id:
|
||||
kwargs['tenant_id'] = tenant_id
|
||||
if begin:
|
||||
kwargs['begin'] = begin.isoformat()
|
||||
if end:
|
||||
kwargs['end'] = end.isoformat()
|
||||
if service:
|
||||
kwargs['service'] = service
|
||||
if groupby:
|
||||
kwargs['groupby'] = groupby
|
||||
if all_tenants:
|
||||
kwargs['all_tenants'] = all_tenants
|
||||
return super(ReportManager, self).list(**kwargs)
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
# 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
|
||||
|
||||
from cloudkittyclient.common import utils
|
||||
|
||||
|
||||
def do_report_tenant_list(cc, args):
|
||||
"""List tenant report."""
|
||||
tenants = cc.reports.list_tenants()
|
||||
out_table = utils.prettytable.PrettyTable()
|
||||
out_table.add_column("Tenant UUID", tenants)
|
||||
@@ -26,17 +28,69 @@ def do_report_tenant_list(cc, args):
|
||||
|
||||
@utils.arg('-t', '--tenant-id',
|
||||
help='Tenant id',
|
||||
required=False, dest='total_tenant_id')
|
||||
required=False,
|
||||
dest='total_tenant_id')
|
||||
@utils.arg('-b', '--begin',
|
||||
help='Starting date/time (YYYY-MM-DDTHH:MM:SS)',
|
||||
required=False)
|
||||
@utils.arg('-e', '--end',
|
||||
help='Ending date/time (YYYY-MM-DDTHH:MM:SS)',
|
||||
required=False)
|
||||
@utils.arg('-s', '--service',
|
||||
help='Service Type',
|
||||
required=False)
|
||||
@utils.arg('-a', '--all-tenants',
|
||||
default=False,
|
||||
action="store_true",
|
||||
dest='all_tenants',
|
||||
help='Allows to get total from all tenants'
|
||||
' (admin only).')
|
||||
def do_total_get(cc, args):
|
||||
"""Get total reports."""
|
||||
begin = utils.iso2dt(args.begin) if args.begin else None
|
||||
end = utils.iso2dt(args.end) if args.end else None
|
||||
total = cc.reports.get_total(tenant_id=args.total_tenant_id,
|
||||
begin=begin,
|
||||
end=end,
|
||||
service=args.service,
|
||||
all_tenants=args.all_tenants)
|
||||
utils.print_dict({'Total': total or 0.0})
|
||||
|
||||
|
||||
@utils.arg('-t', '--tenant-id',
|
||||
help='Tenant id',
|
||||
required=False,
|
||||
dest='summary_tenant_id')
|
||||
@utils.arg('-b', '--begin',
|
||||
help='Begin timestamp',
|
||||
required=False)
|
||||
@utils.arg('-e', '--end',
|
||||
help='End timestamp',
|
||||
required=False)
|
||||
def do_total_get(cc, args):
|
||||
@utils.arg('-s', '--service',
|
||||
help='Service Type',
|
||||
required=False)
|
||||
@utils.arg('-g', '--groupby',
|
||||
help=('Fields to groupby, separated by commas '
|
||||
'if multiple, now support res_type,tenant_id'),
|
||||
required=False)
|
||||
@utils.arg('-a', '--all-tenants',
|
||||
default=False,
|
||||
action="store_true",
|
||||
dest='all_tenants',
|
||||
help='Allows to get summary from all tenants'
|
||||
' (admin only).')
|
||||
def do_summary_get(cc, args):
|
||||
"""Get summary report."""
|
||||
begin = utils.ts2dt(args.begin) if args.begin else None
|
||||
end = utils.ts2dt(args.end) if args.end else None
|
||||
total = cc.reports.get_total(args.total_tenant_id,
|
||||
begin=begin,
|
||||
end=end)
|
||||
utils.print_dict({'Total': total or 0.0})
|
||||
summarys = cc.reportsummary.get_summary(tenant_id=args.summary_tenant_id,
|
||||
begin=begin,
|
||||
end=end,
|
||||
service=args.service,
|
||||
groupby=args.groupby,
|
||||
all_tenants=args.all_tenants)
|
||||
field_labels = ['Tenant ID', 'Resource Type', 'Rate',
|
||||
'Begin Time', 'End Time']
|
||||
fields = ['tenant_id', 'res_type', 'rate', 'begin', 'end']
|
||||
utils.print_list(summarys, fields, field_labels, sortby=0)
|
||||
|
||||
88
cloudkittyclient/v1/report/shell_cli.py
Normal file
88
cloudkittyclient/v1/report/shell_cli.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# Copyright 2016 Objectif Libre
|
||||
#
|
||||
# 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 osc_lib.command import command
|
||||
|
||||
from cloudkittyclient.v1.report import shell
|
||||
|
||||
|
||||
class CliTotalGet(command.Command):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliTotalGet, self).get_parser(prog_name)
|
||||
parser.add_argument('-t', '--tenant-id',
|
||||
help='Tenant id',
|
||||
required=False,
|
||||
dest='total_tenant_id')
|
||||
parser.add_argument('-b', '--begin',
|
||||
help='Begin timestamp',
|
||||
required=False)
|
||||
parser.add_argument('-e', '--end',
|
||||
help='End timestamp',
|
||||
required=False)
|
||||
parser.add_argument('-s', '--service',
|
||||
help='Service Type',
|
||||
required=False)
|
||||
parser.add_argument('-a', '--all-tenants',
|
||||
default=False,
|
||||
action="store_true",
|
||||
dest='all_tenants',
|
||||
help='Allows to get total from all tenants'
|
||||
' (admin only).')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_total_get(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliReportTenantList(command.Command):
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_report_tenant_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliSummaryGet(command.Command):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliSummaryGet, self).get_parser(prog_name)
|
||||
parser.add_argument('-t', '--tenant-id',
|
||||
help='Tenant id',
|
||||
required=False,
|
||||
dest='summary_tenant_id')
|
||||
parser.add_argument('-b', '--begin',
|
||||
help='Begin timestamp',
|
||||
required=False)
|
||||
parser.add_argument('-e', '--end',
|
||||
help='End timestamp',
|
||||
required=False)
|
||||
parser.add_argument('-s', '--service',
|
||||
help='Service Type',
|
||||
required=False)
|
||||
parser.add_argument('-g', '--groupby',
|
||||
help=('Fields to groupby, separated by '
|
||||
'commas if multiple, now support '
|
||||
'res_type,tenant_id'),
|
||||
required=False)
|
||||
parser.add_argument('-a', '--all-tenants',
|
||||
default=False,
|
||||
action="store_true",
|
||||
dest='all_tenants',
|
||||
help='Allows to get summary from all tenants'
|
||||
' (admin only).')
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_summary_get(ckclient, parsed_args)
|
||||
@@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.common import utils
|
||||
from cloudkittyclient import exc
|
||||
|
||||
@@ -21,11 +22,11 @@ def do_module_list(cc, args):
|
||||
'''List the samples for this meters.'''
|
||||
try:
|
||||
modules = cc.modules.list()
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Modules not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Modules not found')
|
||||
else:
|
||||
field_labels = ['Module', 'Enabled']
|
||||
fields = ['module_id', 'enabled']
|
||||
field_labels = ['Module', 'Enabled', 'Priority']
|
||||
fields = ['module_id', 'enabled', 'priority']
|
||||
utils.print_list(modules, fields, field_labels,
|
||||
sortby=0)
|
||||
|
||||
@@ -38,11 +39,11 @@ def do_module_enable(cc, args):
|
||||
try:
|
||||
module = cc.modules.get(module_id=args.name)
|
||||
module.enable()
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Modules not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Module not found: %s' % args.name)
|
||||
else:
|
||||
field_labels = ['Module', 'Enabled']
|
||||
fields = ['module_id', 'enabled']
|
||||
field_labels = ['Module', 'Enabled', 'Priority']
|
||||
fields = ['module_id', 'enabled', 'priority']
|
||||
modules = [cc.modules.get(module_id=args.name)]
|
||||
utils.print_list(modules, fields, field_labels,
|
||||
sortby=0)
|
||||
@@ -56,11 +57,58 @@ def do_module_disable(cc, args):
|
||||
try:
|
||||
module = cc.modules.get(module_id=args.name)
|
||||
module.disable()
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Modules not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Module not found: %s' % args.name)
|
||||
else:
|
||||
field_labels = ['Module', 'Enabled']
|
||||
fields = ['module_id', 'enabled']
|
||||
field_labels = ['Module', 'Enabled', 'Priority']
|
||||
fields = ['module_id', 'enabled', 'priority']
|
||||
modules = [cc.modules.get(module_id=args.name)]
|
||||
utils.print_list(modules, fields, field_labels,
|
||||
sortby=0)
|
||||
|
||||
|
||||
@utils.arg('-n', '--name',
|
||||
help='Module name',
|
||||
required=True)
|
||||
@utils.arg('-p', '--priority',
|
||||
help='Module priority',
|
||||
required=True)
|
||||
def do_module_set_priority(cc, args):
|
||||
'''Set module priority.'''
|
||||
try:
|
||||
module = cc.modules.get(module_id=args.name)
|
||||
module.set_priority(args.priority)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Module not found: %s' % args.name)
|
||||
else:
|
||||
field_labels = ['Module', 'Enabled', 'Priority']
|
||||
fields = ['module_id', 'enabled', 'priority']
|
||||
modules = [cc.modules.get(module_id=args.name)]
|
||||
utils.print_list(modules, fields, field_labels,
|
||||
sortby=0)
|
||||
|
||||
|
||||
def do_info_config_get(cc, args):
|
||||
'''Get cloudkitty configuration.'''
|
||||
utils.print_dict(cc.config.get_config(), dict_property="Section")
|
||||
|
||||
|
||||
@utils.arg('-n', '--name',
|
||||
help='Service name',
|
||||
required=False)
|
||||
def do_info_service_get(cc, args):
|
||||
'''Get service info.'''
|
||||
if args.name:
|
||||
try:
|
||||
services_info = [cc.service_info.get(service_id=args.name)]
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Service not found: %s' % args.name)
|
||||
else:
|
||||
try:
|
||||
services_info = cc.service_info.list()
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('ServiceInfo not found')
|
||||
|
||||
field_labels = ['Service', 'Metadata', 'Unit']
|
||||
fields = ['service_id', 'metadata', 'unit']
|
||||
utils.print_list(services_info, fields, field_labels, sortby=0)
|
||||
|
||||
91
cloudkittyclient/v1/shell_cli.py
Normal file
91
cloudkittyclient/v1/shell_cli.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# Copyright 2016 Objectif Libre
|
||||
|
||||
# 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 osc_lib.command import command
|
||||
|
||||
from cloudkittyclient.v1 import shell
|
||||
|
||||
|
||||
class CliModuleList(command.Command):
|
||||
"""List modules."""
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_module_list(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliModuleEnable(command.Command):
|
||||
"""Enable a module."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliModuleEnable, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Module name',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_module_enable(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliModuleDisable(command.Command):
|
||||
"""Disable a module."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliModuleDisable, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Module name',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_module_disable(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliModuleSetPriority(command.Command):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliModuleSetPriority, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Module name',
|
||||
required=True)
|
||||
parser.add_argument('-p', '--priority',
|
||||
help='Module priority',
|
||||
required=True)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_module_set_priority(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliInfoGetConfig(command.Command):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliInfoGetConfig, self).get_parser(prog_name)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_info_config_get(ckclient, parsed_args)
|
||||
|
||||
|
||||
class CliInfoGetService(command.Command):
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliInfoGetService, self).get_parser(prog_name)
|
||||
parser.add_argument('-n', '--name',
|
||||
help='Service name',
|
||||
required=False)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_info_service_get(ckclient, parsed_args)
|
||||
@@ -17,21 +17,22 @@
|
||||
from cloudkittyclient.common import utils
|
||||
|
||||
|
||||
@utils.arg('--begin',
|
||||
help='Starting date/time (YYYY-MM-ddThh:mm:ss)',
|
||||
required=True)
|
||||
@utils.arg('--end',
|
||||
help='Ending date/time (YYYY-MM-ddThh:mm:ss)',
|
||||
required=True)
|
||||
@utils.arg('--tenant',
|
||||
@utils.arg('-b', '--begin',
|
||||
help='Starting date/time (YYYY-MM-DDTHH:MM:SS)',
|
||||
required=False)
|
||||
@utils.arg('-e', '--end',
|
||||
help='Ending date/time (YYYY-MM-DDTHH:MM:SS)',
|
||||
required=False)
|
||||
@utils.arg('-t', '--tenant',
|
||||
help='Tenant ID',
|
||||
required=False,
|
||||
default=None)
|
||||
@utils.arg('--resource-type',
|
||||
@utils.arg('-r', '--resource-type',
|
||||
help='Resource type (compute, image, ...)',
|
||||
required=False,
|
||||
default=None)
|
||||
def do_storage_dataframe_list(cc, args):
|
||||
"""List dataframes."""
|
||||
data = cc.storage.dataframes.list(begin=args.begin, end=args.end,
|
||||
tenant_id=args.tenant,
|
||||
resource_type=args.resource_type)
|
||||
|
||||
44
cloudkittyclient/v1/storage/shell_cli.py
Normal file
44
cloudkittyclient/v1/storage/shell_cli.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# Copyright 2016 Objectif Libre
|
||||
#
|
||||
# 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 osc_lib.command import command
|
||||
|
||||
from cloudkittyclient.v1.storage import shell
|
||||
|
||||
|
||||
class CliStorageDataframeList(command.Command):
|
||||
"""List dataframes."""
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CliStorageDataframeList, self).get_parser(prog_name)
|
||||
parser.add_argument('-b', '--begin',
|
||||
help='Starting date/time (YYYY-MM-ddThh:mm:ss)',
|
||||
required=False)
|
||||
parser.add_argument('-e', '--end',
|
||||
help='Ending date/time (YYYY-MM-ddThh:mm:ss)',
|
||||
required=False)
|
||||
parser.add_argument('-t', '--tenant',
|
||||
help='Tenant ID',
|
||||
required=False,
|
||||
default=None)
|
||||
parser.add_argument('-r', '--resource-type',
|
||||
help='Resource type (compute, image...)',
|
||||
required=False,
|
||||
default=None)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
ckclient = self.app.client_manager.rating
|
||||
shell.do_storage_dataframe_list(ckclient, parsed_args)
|
||||
@@ -1,42 +0,0 @@
|
||||
Name: python-cloudkittyclient
|
||||
Version: @VERSION@
|
||||
Release: 1%{?dist}
|
||||
Summary: Client library for CloudKitty
|
||||
License: ASL 2.0
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
|
||||
BuildArch: noarch
|
||||
|
||||
BuildRequires: python-setuptools
|
||||
BuildRequires: python-pbr
|
||||
BuildRequires: python-keystoneclient
|
||||
BuildRequires: python-stevedore
|
||||
BuildRequires: python-babel
|
||||
|
||||
Requires: python-keystoneclient
|
||||
Requires: python-stevedore
|
||||
Requires: python-babel
|
||||
|
||||
%description
|
||||
Client library for CloudKitty
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
%{__python} setup.py build
|
||||
|
||||
%install
|
||||
%{__python} setup.py install -O1 --skip-build --root %{buildroot}
|
||||
|
||||
#export PYTHONPATH="$( pwd ):$PYTHONPATH"
|
||||
#sphinx-build -b html doc/source html
|
||||
|
||||
%files
|
||||
%{_bindir}/*
|
||||
%{python_sitelib}/*
|
||||
#%doc html
|
||||
|
||||
%changelog
|
||||
* Sun Apr 19 2015 Gauvain Pocentek <gauvain.pocentek@objectif-libre.com> @VERSION@-1
|
||||
- Initial release
|
||||
1106
doc/source/cli/index.rst
Normal file
1106
doc/source/cli/index.rst
Normal file
File diff suppressed because it is too large
Load Diff
14
doc/source/conf.py
Executable file → Normal file
14
doc/source/conf.py
Executable file → Normal file
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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
|
||||
@@ -23,7 +22,7 @@ sys.path.insert(0, os.path.abspath('../..'))
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
#'sphinx.ext.intersphinx',
|
||||
'oslosphinx'
|
||||
'openstackdocstheme'
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
@@ -38,7 +37,12 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'python-cloudkittyclient'
|
||||
copyright = u'2013, OpenStack Foundation'
|
||||
copyright = u'2017, OpenStack Foundation'
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/python-cloudkittyclient'
|
||||
bug_project = 'cloudkitty'
|
||||
bug_tag = 'python-cloudkittyclient'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
@@ -57,6 +61,10 @@ pygments_style = 'sphinx'
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = ['static']
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Must set this variable to include year, month, day, hours, and minutes.
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
.. include:: ../../CONTRIBUTING.rst
|
||||
5
doc/source/contributor/index.rst
Normal file
5
doc/source/contributor/index.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
.. include:: ../../../CONTRIBUTING.rst
|
||||
@@ -1,20 +1,19 @@
|
||||
.. python-cloudkittyclient documentation master file, created by
|
||||
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
========================================================
|
||||
Welcome to python-cloudkittyclient's documentation!
|
||||
========================================================
|
||||
|
||||
This is a client library for CloudKitty built on the CloudKitty API. It
|
||||
provides a Python API (the ``cloudkittyclient`` module).
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
readme
|
||||
installation
|
||||
usage
|
||||
contributing
|
||||
install/index
|
||||
contributor/index
|
||||
cli/index
|
||||
user/index
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: ../../README.rst
|
||||
@@ -1,8 +0,0 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The list of modules to copy from oslo-incubator.git
|
||||
module=apiclient
|
||||
module=cliutils
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=cloudkittyclient
|
||||
0
releasenotes/source/_templates/.placeholder
Normal file
0
releasenotes/source/_templates/.placeholder
Normal file
263
releasenotes/source/conf.py
Normal file
263
releasenotes/source/conf.py
Normal file
@@ -0,0 +1,263 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cloudkitty Release Notes documentation build configuration file.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
import pbr.version
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'oslosphinx',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Cloudkitty Client Release Notes'
|
||||
copyright = u'2016, Cloudkitty developers'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
|
||||
cloudkittyclient_version = pbr.version.VersionInfo('python-cloudkittyclient')
|
||||
# The short X.Y version.
|
||||
version = cloudkittyclient_version.canonical_version_string()
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = cloudkittyclient_version.version_string_with_vcs()
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'CloudkittyReleaseNotestdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'PythonCloudkitty.tex', u'Cloudkitty Release Notes Documentation',
|
||||
u'Cloudkitty developers', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'cloudkittyclient',
|
||||
u'Cloudkitty Client Release Notes Documentation',
|
||||
[u'Cloudkitty developers'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'cloudkittyclient',
|
||||
u'Cloudkitty Client Release Notes Documentation',
|
||||
u'Cloudkitty Client developers', 'Cloudkittyclient',
|
||||
'One line description of project.', 'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
|
||||
16
releasenotes/source/index.rst
Normal file
16
releasenotes/source/index.rst
Normal file
@@ -0,0 +1,16 @@
|
||||
Welcome to Cloudkitty Client Release Notes documentation!
|
||||
=========================================================
|
||||
|
||||
Contents
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
unreleased
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
5
releasenotes/source/unreleased.rst
Normal file
5
releasenotes/source/unreleased.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
============================
|
||||
Current Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
@@ -2,10 +2,12 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=0.6,!=0.7,<1.0
|
||||
Babel>=1.3
|
||||
python-keystoneclient
|
||||
stevedore
|
||||
oslo.i18n
|
||||
oslo.serialization
|
||||
oslo.utils
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
Babel!=2.4.0,>=2.3.4 # BSD
|
||||
python-keystoneclient>=3.8.0 # Apache-2.0
|
||||
python-openstackclient!=3.10.0,>=3.3.0 # Apache-2.0
|
||||
stevedore>=1.20.0 # Apache-2.0
|
||||
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
|
||||
oslo.serialization>=1.10.0 # Apache-2.0
|
||||
oslo.utils>=3.20.0 # Apache-2.0
|
||||
PrettyTable<0.8,>=0.7.1 # BSD
|
||||
|
||||
60
setup.cfg
60
setup.cfg
@@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = python-cloudkittyclient
|
||||
summary = Cloudkittyclient is the api client for the cloudkitty rating project.
|
||||
summary = API client of cloudkitty, Rating as a Service project.
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
@@ -16,8 +16,7 @@ classifier =
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.3
|
||||
Programming Language :: Python :: 3.4
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -29,11 +28,66 @@ console_scripts =
|
||||
|
||||
cloudkitty.client.modules =
|
||||
hashmap = cloudkittyclient.v1.rating.hashmap.extension:Extension
|
||||
pyscripts = cloudkittyclient.v1.rating.pyscripts.extension:Extension
|
||||
|
||||
openstack.cli.extension =
|
||||
rating = cloudkittyclient.osc
|
||||
|
||||
openstack.rating.v1 =
|
||||
rating_module-list = cloudkittyclient.v1.shell_cli:CliModuleList
|
||||
rating_module-enable = cloudkittyclient.v1.shell_cli:CliModuleEnable
|
||||
rating_module-disable = cloudkittyclient.v1.shell_cli:CliModuleDisable
|
||||
rating_module-set-priority = cloudkittyclient.v1.shell_cli:CliModuleSetPriority
|
||||
|
||||
rating_info-config-get = cloudkittyclient.v1.shell_cli:CliInfoGetConfig
|
||||
rating_info-service-get = cloudkittyclient.v1.shell_cli:CliInfoGetService
|
||||
|
||||
rating_total-get = cloudkittyclient.v1.report.shell_cli:CliTotalGet
|
||||
rating_summary-get = cloudkittyclient.v1.report.shell_cli:CliSummaryGet
|
||||
rating_report-tenant-list = cloudkittyclient.v1.report.shell_cli:CliReportTenantList
|
||||
|
||||
rating_collector-mapping-list = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingList
|
||||
rating_collector-mapping-get = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingGet
|
||||
rating_collector-mapping-create = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingCreate
|
||||
rating_collector-mapping-delete = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingDelete
|
||||
rating_collector-state-get = cloudkittyclient.v1.collector.shell_cli:CliCollectorStateGet
|
||||
rating_collector-state-enable = cloudkittyclient.v1.collector.shell_cli:CliCollectorStateEnable
|
||||
rating_collector-state-disable = cloudkittyclient.v1.collector.shell_cli:CliCollectorStateDisable
|
||||
|
||||
rating_storage-dataframe-list = cloudkittyclient.v1.storage.shell_cli:CliStorageDataframeList
|
||||
|
||||
rating_hashmap-service-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapServiceCreate
|
||||
rating_hashmap-service-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapServiceList
|
||||
rating_hashmap-service-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapServiceDelete
|
||||
rating_hashmap-field-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapFieldCreate
|
||||
rating_hashmap-field-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapFieldList
|
||||
rating_hashmap-field-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapFieldDelete
|
||||
rating_hashmap-mapping-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingCreate
|
||||
rating_hashmap-mapping-update = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingUpdate
|
||||
rating_hashmap-mapping-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingList
|
||||
rating_hashmap-mapping-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingDelete
|
||||
rating_hashmap-group-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapGroupCreate
|
||||
rating_hashmap-group-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapGroupList
|
||||
rating_hashmap-group-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapGroupDelete
|
||||
rating_hashmap-threshold-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdCreate
|
||||
rating_hashmap-threshold-update = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdUpdate
|
||||
rating_hashmap-threshold-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdList
|
||||
rating_hashmap-threshold-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdDelete
|
||||
rating_hashmap-threshold-get = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdGet
|
||||
rating_hashmap-threshold-group = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdGroup
|
||||
|
||||
rating_pyscripts-script-create = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptCreate
|
||||
rating_pyscripts-script-list = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptList
|
||||
rating_pyscripts-script-get = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptGet
|
||||
rating_pyscripts-script-get-data = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptGetData
|
||||
rating_pyscripts-script-delete = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptDelete
|
||||
rating_pyscripts-script-update = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptUpdate
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
warning-is-error = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
11
setup.py
Executable file → Normal file
11
setup.py
Executable file → Normal file
@@ -1,4 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -17,6 +16,14 @@
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr'],
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
hacking>=0.9.2,<0.10
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
|
||||
coverage>=3.6
|
||||
discover
|
||||
python-subunit
|
||||
sphinx>=1.1.2
|
||||
oslosphinx
|
||||
oslotest>=1.1.0.0a1
|
||||
testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
testtools>=0.9.34
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
sphinx>=1.6.2 # BSD
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
reno>=1.8.0 # Apache2
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Provides methods needed by installation script for OpenStack development
|
||||
virtual environments.
|
||||
|
||||
Since this script is used to bootstrap a virtualenv from the system's Python
|
||||
environment, it should be kept strictly compatible with Python 2.6.
|
||||
|
||||
Synced in from openstack-common
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
class InstallVenv(object):
|
||||
|
||||
def __init__(self, root, venv, requirements,
|
||||
test_requirements, py_version,
|
||||
project):
|
||||
self.root = root
|
||||
self.venv = venv
|
||||
self.requirements = requirements
|
||||
self.test_requirements = test_requirements
|
||||
self.py_version = py_version
|
||||
self.project = project
|
||||
|
||||
def die(self, message, *args):
|
||||
print(message % args, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def check_python_version(self):
|
||||
if sys.version_info < (2, 6):
|
||||
self.die("Need Python Version >= 2.6")
|
||||
|
||||
def run_command_with_code(self, cmd, redirect_output=True,
|
||||
check_exit_code=True):
|
||||
"""Runs a command in an out-of-process shell.
|
||||
|
||||
Returns the output of that command. Working directory is self.root.
|
||||
"""
|
||||
if redirect_output:
|
||||
stdout = subprocess.PIPE
|
||||
else:
|
||||
stdout = None
|
||||
|
||||
proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
|
||||
output = proc.communicate()[0]
|
||||
if check_exit_code and proc.returncode != 0:
|
||||
self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
|
||||
return (output, proc.returncode)
|
||||
|
||||
def run_command(self, cmd, redirect_output=True, check_exit_code=True):
|
||||
return self.run_command_with_code(cmd, redirect_output,
|
||||
check_exit_code)[0]
|
||||
|
||||
def get_distro(self):
|
||||
if (os.path.exists('/etc/fedora-release') or
|
||||
os.path.exists('/etc/redhat-release')):
|
||||
return Fedora(
|
||||
self.root, self.venv, self.requirements,
|
||||
self.test_requirements, self.py_version, self.project)
|
||||
else:
|
||||
return Distro(
|
||||
self.root, self.venv, self.requirements,
|
||||
self.test_requirements, self.py_version, self.project)
|
||||
|
||||
def check_dependencies(self):
|
||||
self.get_distro().install_virtualenv()
|
||||
|
||||
def create_virtualenv(self, no_site_packages=True):
|
||||
"""Creates the virtual environment and installs PIP.
|
||||
|
||||
Creates the virtual environment and installs PIP only into the
|
||||
virtual environment.
|
||||
"""
|
||||
if not os.path.isdir(self.venv):
|
||||
print('Creating venv...', end=' ')
|
||||
if no_site_packages:
|
||||
self.run_command(['virtualenv', '-q', '--no-site-packages',
|
||||
self.venv])
|
||||
else:
|
||||
self.run_command(['virtualenv', '-q', self.venv])
|
||||
print('done.')
|
||||
else:
|
||||
print("venv already exists...")
|
||||
pass
|
||||
|
||||
def pip_install(self, *args):
|
||||
self.run_command(['tools/with_venv.sh',
|
||||
'pip', 'install', '--upgrade'] + list(args),
|
||||
redirect_output=False)
|
||||
|
||||
def install_dependencies(self):
|
||||
print('Installing dependencies with pip (this can take a while)...')
|
||||
|
||||
# First things first, make sure our venv has the latest pip and
|
||||
# setuptools and pbr
|
||||
self.pip_install('pip>=1.4')
|
||||
self.pip_install('setuptools')
|
||||
self.pip_install('pbr')
|
||||
|
||||
self.pip_install('-r', self.requirements, '-r', self.test_requirements)
|
||||
|
||||
def parse_args(self, argv):
|
||||
"""Parses command-line arguments."""
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-n', '--no-site-packages',
|
||||
action='store_true',
|
||||
help="Do not inherit packages from global Python "
|
||||
"install.")
|
||||
return parser.parse_args(argv[1:])[0]
|
||||
|
||||
|
||||
class Distro(InstallVenv):
|
||||
|
||||
def check_cmd(self, cmd):
|
||||
return bool(self.run_command(['which', cmd],
|
||||
check_exit_code=False).strip())
|
||||
|
||||
def install_virtualenv(self):
|
||||
if self.check_cmd('virtualenv'):
|
||||
return
|
||||
|
||||
if self.check_cmd('easy_install'):
|
||||
print('Installing virtualenv via easy_install...', end=' ')
|
||||
if self.run_command(['easy_install', 'virtualenv']):
|
||||
print('Succeeded')
|
||||
return
|
||||
else:
|
||||
print('Failed')
|
||||
|
||||
self.die('ERROR: virtualenv not found.\n\n%s development'
|
||||
' requires virtualenv, please install it using your'
|
||||
' favorite package management tool' % self.project)
|
||||
|
||||
|
||||
class Fedora(Distro):
|
||||
"""This covers all Fedora-based distributions.
|
||||
|
||||
Includes: Fedora, RHEL, CentOS, Scientific Linux
|
||||
"""
|
||||
|
||||
def check_pkg(self, pkg):
|
||||
return self.run_command_with_code(['rpm', '-q', pkg],
|
||||
check_exit_code=False)[1] == 0
|
||||
|
||||
def install_virtualenv(self):
|
||||
if self.check_cmd('virtualenv'):
|
||||
return
|
||||
|
||||
if not self.check_pkg('python-virtualenv'):
|
||||
self.die("Please install 'python-virtualenv'.")
|
||||
|
||||
super(Fedora, self).install_virtualenv()
|
||||
18
tox.ini
18
tox.ini
@@ -1,17 +1,20 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py33,py34,py27,pypy,pep8
|
||||
envlist = py35,py27,pypy,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/queens} -U {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:debug]
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
@@ -25,10 +28,15 @@ commands = python setup.py testr --coverage --testr-args='{posargs}'
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[flake8]
|
||||
# H803 skipped on purpose per list discussion.
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125,H803
|
||||
ignore = E123,E125
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes
|
||||
|
||||
[hacking]
|
||||
import_exceptions = cloudkittyclient.i18n
|
||||
|
||||
[testenv:releasenotes]
|
||||
commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
Reference in New Issue
Block a user