Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
d4ae928048 | ||
|
|
0de831021d |
@@ -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
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -11,3 +11,7 @@ cover
|
||||
dist
|
||||
*.egg
|
||||
*.sw?
|
||||
.eggs
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
releasenotes/build
|
||||
|
||||
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=_
|
||||
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.apiclient import exceptions
|
||||
from cloudkittyclient.i18n import _
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
|
||||
|
||||
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.apiclient import exceptions
|
||||
from cloudkittyclient.i18n import _
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
|
||||
|
||||
_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,
|
||||
@@ -42,8 +42,7 @@ 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.apiclient import exceptions
|
||||
from cloudkittyclient.i18n import _
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
|
||||
|
||||
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)
|
||||
@@ -22,11 +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 client
|
||||
from cloudkittyclient.openstack.common.apiclient import exceptions
|
||||
|
||||
|
||||
def _discover_auth_versions(session, auth_url):
|
||||
@@ -306,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'),
|
||||
@@ -339,7 +341,7 @@ 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'),
|
||||
|
||||
@@ -22,9 +22,9 @@ import copy
|
||||
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from cloudkittyclient.apiclient import base
|
||||
from cloudkittyclient import exc
|
||||
from cloudkittyclient.i18n import _
|
||||
from cloudkittyclient.openstack.common.apiclient import base
|
||||
|
||||
|
||||
def getid(obj):
|
||||
|
||||
@@ -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,12 +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.i18n import _
|
||||
from cloudkittyclient.openstack.common import cliutils
|
||||
|
||||
|
||||
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):
|
||||
|
||||
@@ -22,7 +22,3 @@ _translators = i18n.TranslatorFactory(domain='cloudkittyclient')
|
||||
i18n.enable_lazy()
|
||||
|
||||
_ = _translators.primary
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
||||
|
||||
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,9 @@ 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
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
|
||||
@@ -132,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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
),
|
||||
},
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
from cloudkittyclient.v1.rating import hashmap
|
||||
|
||||
|
||||
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)
|
||||
@@ -57,8 +57,11 @@ class Client(object):
|
||||
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):
|
||||
|
||||
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,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
|
||||
|
||||
@@ -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',
|
||||
@@ -92,8 +93,9 @@ def do_hashmap_field_list(cc, args={}):
|
||||
"""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,8 +110,8 @@ 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):
|
||||
@@ -179,8 +181,8 @@ def do_hashmap_mapping_update(cc, args={}):
|
||||
}
|
||||
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:
|
||||
@@ -210,8 +212,9 @@ def do_hashmap_mapping_list(cc, args={}):
|
||||
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',
|
||||
@@ -230,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)
|
||||
|
||||
|
||||
@@ -255,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']
|
||||
@@ -277,7 +280,7 @@ 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)
|
||||
|
||||
|
||||
@@ -348,8 +351,8 @@ def do_hashmap_threshold_update(cc, args={}):
|
||||
}
|
||||
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:
|
||||
@@ -384,8 +387,8 @@ 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',
|
||||
@@ -403,7 +406,7 @@ 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)
|
||||
|
||||
|
||||
@@ -414,7 +417,7 @@ 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())
|
||||
|
||||
@@ -426,6 +429,6 @@ 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)
|
||||
@@ -17,6 +17,7 @@ import functools
|
||||
from oslo_utils import strutils
|
||||
import six
|
||||
|
||||
from cloudkittyclient.apiclient import exceptions
|
||||
from cloudkittyclient.common import utils
|
||||
from cloudkittyclient import exc
|
||||
|
||||
@@ -64,7 +65,7 @@ def do_pyscripts_script_get(cc, args={}):
|
||||
"""Get script."""
|
||||
try:
|
||||
script = cc.pyscripts.scripts.get(script_id=args.script_id)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
utils.print_dict(script.to_dict())
|
||||
|
||||
@@ -76,7 +77,7 @@ def do_pyscripts_script_get_data(cc, args={}):
|
||||
"""Get script data."""
|
||||
try:
|
||||
script = cc.pyscripts.scripts.get(script_id=args.script_id)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
six.print_(script.data)
|
||||
|
||||
@@ -88,8 +89,8 @@ def do_pyscripts_script_delete(cc, args={}):
|
||||
"""Delete a script."""
|
||||
try:
|
||||
cc.pyscripts.scripts.delete(script_id=args.script_id)
|
||||
except exc.HTTPNotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.counter_name)
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
|
||||
|
||||
@utils.arg('-s', '--script-id',
|
||||
@@ -107,7 +108,7 @@ def do_pyscripts_script_update(cc, args={}):
|
||||
content = fp.read()
|
||||
try:
|
||||
script = cc.pyscripts.scripts.get(script_id=args.script_id)
|
||||
except exc.HTTPNotFound:
|
||||
except exceptions.NotFound:
|
||||
raise exc.CommandError('Script not found: %s' % args.script_id)
|
||||
script_dict = script.to_dict()
|
||||
for field in excluded_fields:
|
||||
|
||||
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,22 +15,31 @@
|
||||
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=None, begin=None, end=None, service=None):
|
||||
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:
|
||||
@@ -41,6 +50,32 @@ class ReportManager(base.Manager):
|
||||
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)
|
||||
|
||||
@@ -30,6 +30,37 @@ def do_report_tenant_list(cc, args):
|
||||
help='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)
|
||||
@@ -39,12 +70,27 @@ def do_report_tenant_list(cc, args):
|
||||
@utils.arg('-s', '--service',
|
||||
help='Service Type',
|
||||
required=False)
|
||||
def do_total_get(cc, args):
|
||||
"""Get total reports."""
|
||||
@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(tenant_id=args.total_tenant_id,
|
||||
begin=begin,
|
||||
end=end,
|
||||
service=args.service)
|
||||
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,17 +17,17 @@
|
||||
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)
|
||||
|
||||
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)
|
||||
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
@@ -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,11 +2,12 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=1.6 # Apache-2.0
|
||||
Babel>=2.3.4 # BSD
|
||||
python-keystoneclient!=1.8.0,!=2.1.0,>=1.7.0 # Apache-2.0
|
||||
stevedore>=1.10.0 # Apache-2.0
|
||||
oslo.i18n>=2.1.0 # Apache-2.0
|
||||
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.11.0 # Apache-2.0
|
||||
PrettyTable<0.8,>=0.7 # BSD
|
||||
oslo.utils>=3.20.0 # Apache-2.0
|
||||
PrettyTable<0.8,>=0.7.1 # BSD
|
||||
|
||||
56
setup.cfg
56
setup.cfg
@@ -16,7 +16,7 @@ classifier =
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.4
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
@@ -30,10 +30,64 @@ 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
|
||||
|
||||
2
setup.py
2
setup.py
@@ -25,5 +25,5 @@ except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=1.8'],
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
||||
|
||||
@@ -2,13 +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.10,>=0.9.2
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
|
||||
coverage>=3.6 # Apache-2.0
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
|
||||
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
|
||||
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()
|
||||
15
tox.ini
15
tox.ini
@@ -1,17 +1,20 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = 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} -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,13 +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