Compare commits

..

94 Commits

Author SHA1 Message Date
Jenkins
2fe71f729a Merge "Add --all-tenants when get total/summary" 2017-01-26 16:42:43 +00:00
Jenkins
c6b90fc6e6 Merge "Update .gitignore" 2017-01-26 11:50:13 +00:00
Jenkins
d9aa46de34 Merge "Remove white space between print ()" 2017-01-26 11:46:07 +00:00
Aaron-DH
56e6f689bb Add --all-tenants when get total/summary
Use `cloudkitty total-get --all-tenants` to get total
rate of all_tenants. Same with summary-get

Depends-On: 8cf7332162ad30bcdb2c8dfd10a3d348601c2870
Change-Id: I1efcbb8eff77c5f8d358a02178b1f99204b6cba7
2017-01-24 11:05:54 +08:00
Jenkins
75755b2af3 Merge "Add oslo_debug_helper to tox.ini" 2017-01-23 08:03:37 +00:00
Jenkins
40ddf6a470 Merge "Delete unnecessary utf-8 coding" 2017-01-22 15:36:25 +00:00
Jenkins
287dd57185 Merge "Improve User experience" 2017-01-22 15:24:57 +00:00
Jenkins
416a717ebc Merge "Remove white space between print () in cliutils.py" 2017-01-19 09:57:32 +00:00
Maxime Cottret
efb2b0949a Improve User experience
This patch adds access to new REST API for config and service
metadata retrieval.

The following work has been done:

* Create new manager for config retrieval
* Create new Resource and CRUD manager for service info retrieval
* Add managers to client
* Add new CLI command and openstack client entries

Change-Id: I43f572202b1cd3832a820f46f7c7b44a0d998406
Depends-on: https://review.openstack.org/#/c/406180/
2017-01-18 14:48:17 +00:00
Jenkins
a567600b77 Merge "Add client for get summary report" 2017-01-18 14:32:18 +00:00
Jeremy Liu
d04e5ac776 Update .gitignore
These directories are generated by command `python setup.py install`,
better put them into .gitignore.

Change-Id: I63137da72f144556530677b43219aa0e10220b6e
2017-01-18 10:15:57 +08:00
Anh Tran
a1f45a0206 Remove white space between print ()
Change-Id: I464707dd23f006d790c618ef44e37bf6e6de9a22
2017-01-17 10:31:19 +07:00
Aaron-DH
2211d48f72 Add oslo_debug_helper to tox.ini
oslo_debug_helper is used to assist in debugging of python code,
i.e. by adding breakpoints via pdb.

To enable debugging, run tox with the debug environment:
tox -e debug <test_path>

Reference link:
http://docs.openstack.org/developer/oslotest/features.html#debugging-with-oslo-debug-helper

Change-Id: I01d66328a3529e542e503be2cd7bbca69a77c4aa
2017-01-16 19:19:52 +08:00
Aaron-DH
e402ce676c Add client for get summary report
Use commands as follows to get summary:
  -- cloudkitty summary-get
  -- openstack rating summary-get

Change-Id: I07da26cb31a03104493ab749efffd73ba8d17d62
Implements: blueprint price-groupby-fields
2017-01-16 18:59:38 +08:00
Jenkins
c423ca4470 Merge "Remove unused pylintrc" 2017-01-09 08:15:38 +00:00
Jenkins
f750d31169 Merge "H803 hacking have been deprecated" 2017-01-09 07:51:21 +00:00
zhangguoqing
cdbcafd142 Remove white space between print () in cliutils.py
There is a white space in line [print (*, then we remove it.

REF. https://review.openstack.org/#/c/387303/

Change-Id: I6762b7d1d9f573d62e7466da134addd51e0b4163
2017-01-04 02:33:38 +00:00
gecong1973
cffa8b8936 Delete unnecessary utf-8 coding
The file was added redundant utf-8 coding by some editor.
we can delete it .

Change-Id: I618e0c8f43717fd13db7b4d5213fa92a7b98a357
2016-12-27 10:08:37 +08:00
xhzhf
10f80c7d02 H803 hacking have been deprecated
H803 hacking have been removed.
https://github.com/openstack-dev/hacking/blob/master/setup.cfg
Closes-Bug: #1650741

Change-Id: Ibeeca8f56dd3fe872e1611bd4b61ec515670ec8d
2016-12-17 20:21:45 +08:00
Jenkins
87cdbb1340 Merge "Change the time args format from timestamp to date/time when total-get" 2016-12-15 12:19:18 +00:00
Jenkins
cede66dde6 Merge "Unify the date format "YYYY-MM-DDTHH:MM:SS"" 2016-12-15 12:18:18 +00:00
Jeremy Liu
addb3a8b33 Set upper-constraints for tox
Change-Id: Ia1dd9b902eae4d3ebdbd46802f7ba8043b3ee2a8
2016-12-14 11:00:05 +08:00
Luka Peschke
04bf3504ee Add support for OpenStack client
Cloudkittyclient now provides a plugin for the Openstack client.
setup.cfg was modified to provide entrypoints for the Openstack client. These
entrypoints can be found in the different shell_cli.py files.
Python-openstackclient was added to the requirements.

Implements: blueprint openstackclient-support
Change-Id: If0bbd919b1552b82cd77a52ded4f4ec32e6e14d8
2016-11-30 15:17:32 +01:00
Maxime Cottret
b6f7a7831f Add module priority management
This patch binds the python client to the module priority REST api.
It also adds a CLI setter command and updates the module list CLI
output to show current priority value.

Change-Id: I06ab6611452cdc6e875b5534cd955a0a3092ed0d
Implements: blueprint module-priority-cli-command
2016-11-29 14:59:35 +00:00
Jenkins
70cef21224 Merge "Use os_project name in get_client" 2016-11-29 14:10:12 +00:00
Luka Peschke
8fa848fb51 Use os_project name in get_client
The cloudkitty Client object cannot authenticate into keystone
if 'os_project_name' is passed as parameter instead of 'os_tenant_name'.
It is the same if 'os_project_id' is passed instead of 'os_tenant_id'.

This patch fixes both of these issues.

Change-Id: Ife248e87e1126d101be5e4550b933e66eccadbb9
2016-11-29 14:32:45 +01:00
zhangguoqing
d70f9c778c Unify the date format "YYYY-MM-DDTHH:MM:SS"
Change-Id: I729cef6f09d1e61f0c99faf87a87955e4bde3c13
2016-11-29 12:29:38 +00:00
Luka Peschke
ddf93becc1 Catches the right exceptions shell.py files
This is a fix for the bug #1645380. The right exceptions are caught and
'counter_name' is not used for the Exception messages anymore.

Change-Id: I242ee04783c5c8b2699ef3efe70f8e397eb794b8
Closes-Bug: 1645380
2016-11-29 12:33:13 +01:00
Jenkins
0024760bef Merge "Show team and repo badges on README" 2016-11-28 14:44:36 +00:00
Jenkins
5d43074cc4 Merge "Make begin and end optional when get dataframes" 2016-11-28 14:15:18 +00:00
Jenkins
d82273fcd4 Merge "Add __ne__ built-in function" 2016-11-26 05:21:25 +00:00
Flavio Percoco
241d90f541 Show team and repo badges on README
This patch adds the team's and repository's badges to the README file.
The motivation behind this is to communicate the project status and
features at first glance.

For more information about this effort, please read this email thread:

http://lists.openstack.org/pipermail/openstack-dev/2016-October/105562.html

To see an example of how this would look like check:

https://gist.github.com/168107e62fde067f3925d71a5a0f2d4d

Change-Id: I8caa8cdaa4efc2ea438b2ace632b62afd5d322e5
2016-11-25 17:25:39 +01:00
zhangguoqing
708aeff9b7 Change the time args format from timestamp to date/time when total-get
The begin and end args format is timestamp in total-get command, which
is less convenient than date/time format(YYYY-MM-DDTHH:MM:SS). So this
patch make it easy to use.

Change-Id: Id83e132e6b8a090d1abfa3002d53de1b678fc9f2
2016-11-24 14:53:06 +00:00
Jenkins
9a9399f284 Merge "py33 is no longer supported by Infra's CI" 2016-11-24 09:16:28 +00:00
gecong1973
a070f5b16c Add __ne__ built-in function
In Python 3 __ne__ by default delegates to __eq__ and inverts the
result, but in Python 2 they urge you to define __ne__ when you
define __eq__ for it to work properly [1].There are no implied
relationships among the comparison operators. The truth of x==y
does not imply that x!=y is false. Accordingly, when defining __eq__(),
one should also define __ne__() so that the operators will behave as
expected.
[1]https://docs.python.org/2/reference/datamodel.html#object.__ne_

Change-Id: Id2d32eedddbbbb91c6da6e36f12515972aa9e5a5
2016-11-23 17:09:15 +08:00
Jeremy Liu
9a1f531155 Remove unused pylintrc
We didn't use it in the gate and it's unmaintained. Clean that.

Change-Id: I5366927f2dd5771c2b8b0f9c68bf0201b8681776
2016-11-22 11:19:35 +08:00
Aaron-DH
d0f0aaf346 Make begin and end optional when get dataframes
when get dataframes with `cloudkitty storage-dataframe-list`,
begin and end is mandatory. make it optional like get total

Change-Id: I0a0f4073888621833b0bb6588a0452a642327797
Closes-Bug: #1640372
2016-11-09 14:51:37 +08:00
Steve Martinelli
a93f8b04b1 move old oslo-incubator code out of openstack/common
As part of the first community-wide goal, teams were asked
to remove the openstack/common package of their projects
if one existed. This was a byproduct of the old oslo-incubator
form of syncing common functionality.

The package, apiclient, was moved to a top level location
and cliutils was moved to the common module. There are no oslo
specific libraries, the recommended solution is to move it
in tree and maintain it there.

Change-Id: I0603d3c1419a5344bee8e43cfbe794c26641960a
2016-10-31 11:15:10 -04:00
Jenkins
e6daa3df81 Merge "Fix the logic of do_hashmap_mapping_list" 2016-09-01 16:25:53 +00:00
Jenkins
7a199820d0 Merge "Add short arg for storage command" 2016-09-01 16:14:36 +00:00
zhangguoqing
b24e261143 Fix the logic of do_hashmap_mapping_list
Only give the group_id should be allowed to do_hashmap_mapping_list,
and add the corresponding test case.

Depends-On: I4fe27a07e369728396d440b6b2f3462ee74d5f4d
Change-Id: Ia4272fff33b70db0dc24f7bf0a6d5971504cee7a
2016-09-01 16:01:11 +00:00
zhangguoqing
9acc36b4ca Add short arg for collector command
Change-Id: I92a9e03c6c8c517a78d03670ca19c3da54a4e3f2
2016-09-01 21:03:30 +08:00
Maxime Cottret
a6093c5a36 Fix CLI threshold command
- Threshold commands now use 'type' API field instead of 'map_type'
- change CLI option from "-m, --map-type" to "-t, --type" (same as mapping commands option)
- change short option for threshold-id to "-i"

Change-Id: I8c0f6b135bdc206ce1fc3ea14debd8d2cafc9ea7
Closes-Bug: #1619150
2016-09-01 11:15:56 +02:00
Jenkins
9554c9e440 Merge "Replaces client_kwargs by empty dict in ckclient/shell.py" 2016-08-29 10:20:59 +00:00
Luka Peschke
8318891835 Replaces client_kwargs by empty dict in ckclient/shell.py
ckclient.get_client() doesn't need the command-line args.

The client_kwargs.update() altered the args, and caused the project_id
field to be overwritten, leading to an invalid http request in
some cases.

Closes-Bug: #1616805
Change-Id: I09fe3bc3c71a399bdcfaaa178543a2516494399b
2016-08-25 15:54:57 +02:00
Maxime Cottret
b2a42f71fe Fix CLI auth user interface
This patch fixes how auth options are checked in CLI.

Use either:
- tenant-id or tenant-name
- project-id and user-domain (id or name)
- project-name and project-domain (id or name) and user-domain (id or name)

For consistency, the same checking is used in the client authentication plugin.

Change-Id: I2210d8bf21bba5d1faf72dfbe38756078d8bc0c1
Closes-Bug: #1616468
2016-08-25 10:11:09 +02:00
zhangguoqing
d4ae928048 Add short arg for storage command
Change-Id: I6c29171d66527fe4284c0ce7b2e9fb2e17a7d49f
2016-08-10 10:45:42 +00:00
Jenkins
1d8378cc6c Merge "Removes MANIFEST.in as it is not needed explicitely by PBR" 2016-08-08 12:48:45 +00:00
Jenkins
44a2bb0b26 Merge "Support getting client with keystone session" 2016-08-08 12:48:40 +00:00
Jenkins
0417e30f76 Merge "Remove discover from test-requirements" 2016-08-08 12:43:23 +00:00
zhangguoqing
a4dd6e1a61 [trivial] fix wrong typo
group_id --> tenant_id

Change-Id: Iaf3d3b4f83ae7007d3bda1f9adade881a298c7ec
2016-08-06 05:15:01 +00:00
Jenkins
d503c98cbe Merge "Fix client V3 unscope bug" 2016-08-02 13:16:16 +00:00
Swapnil Kulkarni (coolsvap)
0efe6a0606 Remove discover from test-requirements
It's only needed for python < 2.7 which is not supported

Change-Id: Ic92b43c09a0f8e4892b4fdd2b91c00e839e7adec
2016-07-22 03:52:26 +00:00
Jenkins
69399b5e5d Merge "Add client support for per tenant hashmap rules" 2016-06-30 16:01:19 +00:00
Jenkins
df5087a344 Merge "[Trivial] Remove executable privilege of doc/source/conf.py" 2016-06-23 15:34:04 +00:00
Stéphane Albert
70771bf2d7 Add client support for per tenant hashmap rules
Change-Id: Id85a0de7115439131cef4d1a98f884c2334fc474
2016-06-15 04:47:12 +00:00
Gauvain Pocentek
367608ceeb Remove spec file since cloudkittyclient is in RDO
Change-Id: I7cfa01daf2a6aa64bbf9afb89e817ff9d3942c71
2016-06-15 06:46:13 +02:00
Gauvain Pocentek
92981a7bb4 Add an explicit dependency on prettytable
Update the requirements for neutron

Change-Id: I0237bc725c6ab1948b40218a01434440dd173e8c
2016-06-14 16:24:59 +02:00
ZhiQiang Fan
5faa8b0de6 [Trivial] Remove executable privilege of doc/source/conf.py
It is a configuration file, rather than a script.

Change-Id: I133c05be7de743d2a89a69ef100ebe5d43422cd9
2016-04-29 20:19:29 +08:00
Stéphane Albert
55f3a3fa75 Updated requirements for mitaka
Preparing mitaka release

Change-Id: Ida7ddf7fcf70f5b9dba91b9c37b4d30581531f32
2016-03-04 15:14:46 +01:00
Jenkins
336f1466e0 Merge "cloudkittyclient with keystone v3 not working" 2016-03-04 14:08:52 +00:00
Jenkins
5b6fd6c529 Merge "Add support for query cost by service" 2016-03-04 11:04:01 +00:00
Jenkins
778782c3c9 Merge "Fix argument order for assertEqual to (expected, observed)" 2016-03-04 10:20:04 +00:00
Jenkins
985e3c2304 Merge "Update requirements" 2016-03-04 10:11:43 +00:00
Pierre-Alexandre Bardina
0cad4b0e99 Update requirements
Update requirements for liberty

Change-Id: I01489c2bc79428174a1005cf45a0803f6dcedfd6
2016-03-04 11:02:42 +01:00
reedip
1703d5538b Fix argument order for assertEqual to (expected, observed)
assertEqual expects that the arguments provided to it should be (expected, observed).
If a particluar order is kept as a convention, then it helps to provide a cleaner
message to the developer if Unit Tests fail.
The following patch fixes this issue

TrivialFix

Change-Id: Id417fb43ecd62563239d492bff3981277565525e
Closes-Bug: #1259292
2016-03-03 22:02:06 +00:00
Aaron-DH
c6e23ab770 Add support for query cost by service
Query cost of each service by using total-get -s servicetype

Change-Id: I7f579c70fe78cbd4031aa6ec20279d7661a2d67c
Closes-Bug: #1549687
2016-02-25 17:24:24 +08:00
Xiangjun Li
450aa61358 cloudkittyclient with keystone v3 not working
cloudkittyclient is failing to pass some domain/project related
information to keystoneclient, which caused "The service catalog
is empty" and "Expecting to find domain in project" error when
executing cloudkittyclient shell.

Change-Id: I386f4ecb38b947a1d8a0c8f1eee72e25ee12771a
Closes-Bug: #1547778
2016-02-20 15:10:44 +08:00
Chaozhe.Chen
066c9564fb Fix client V3 unscope bug
Client V3 will get unscope auth as no project provided. Unscope auth
will make client get empty catalog back from keystone.

This backport from ceilometer-client patch[1] and I verified it in my
devstack.

[1]https://review.openstack.org/#/c/169409/

Change-Id: I1fa5a5b1e9a40501dfbd563bf608d41eb4879bf8
Closes-Bug: #1522728
2016-02-16 17:02:11 +08:00
Jenkins
6abecf6348 Merge "Add helpinfo for collector commands." 2016-01-11 11:04:16 +00:00
Jenkins
a63b75c555 Merge "Drop py33 support" 2016-01-11 10:58:51 +00:00
Adam
f8c4caba43 Add helpinfo for collector commands.
Add some helpinfo for subcommand collector*, report*.

Change-Id: Ica1ad18fcaa4368a5d5a953839ab4499db034def
2016-01-10 14:33:21 +08:00
Jenkins
c6f2cb4643 Merge "Fix name not defined error" 2016-01-04 09:31:39 +00:00
Aaron-DH
5c188a2306 Fix name not defined error
Add the missing import packages and format the log messages
Move i18n to package(cloudkittyclient)

Change-Id: I77e7059e8eb91aef131713f0720f58d23ae7c11f
Closes-Bug: #1524680
2016-01-02 20:55:12 +08:00
Jenkins
9dd1a6fbea Merge "Set AuthPlugin in __init__()" 2015-12-31 12:14:02 +00:00
janonymous
0de831021d py33 is no longer supported by Infra's CI
Python 3.3 support would be dropped by
Infra team from mitaka,CI would no longer be testing it,
so projects should drop it also.

Change-Id: Ic03ded9bba499858a77debbcdc4fc9c9d963dd24
2015-12-26 15:47:45 +05:30
sonu.kumar
4dc3e65e44 Removes MANIFEST.in as it is not needed explicitely by PBR
This patch removes `MANIFEST.in` file as pbr generates a sensible
manifest from git files and some standard files and it removes
the need for an explicit `MANIFEST.in` file.

Change-Id: Iddbd1a4b574223d840707351c5e8f025d56f2046
2015-12-17 14:45:11 +05:30
shu-mutou
8b69ecf237 Drop py33 support
"Python 3.3 support is being dropped since OpenStack Liberty."
written in following URL.
https://wiki.openstack.org/wiki/Python3

And already the infra team and the oslo team are dropping py33
support from their projects.

Since we rely on oslo for a lot of our work, and depend on infra
for our CI, we should drop py33 support too.

Change-Id: I3aa4c969425d885873be222c0ea4e32cb1060341
Closes-Bug: #1526170
2015-12-15 18:52:50 +09:00
Jenkins
ea21f9761b Merge "Fixed bug with report total" 2015-12-10 07:50:17 +00:00
Chaozhe.Chen
df4e8360e2 Support getting client with keystone session
This change will allow cloudkitty client to use keystoneclient/
keystoneauth session object.

Change-Id: Icc4bf5da12fb24d189fc38daf1b5cfb4a43228aa
2015-12-10 01:30:50 +08:00
Chaozhe.Chen
236bf8b307 Set AuthPlugin in __init__()
self.auth_plugin should be set in __init__()

Change-Id: Ib23fd14a697e4a03acd8c62cf1b09670d169a115
2015-12-03 14:47:39 +08:00
Atsushi SAKAI
6dbfc4502e Fix help message
Fix Required to small case(required)
Add period.

This fix is coming from below patch set 1 comment.
https://review.openstack.org/#/c/251331/

Change-Id: I614a8143ed6cba37dc726f3c85606daaf6a767be
2015-12-01 12:24:30 +09:00
Stéphane Albert
def167f77a Fixed bug with report total
The tenant filter was always sent even if not tenant filtering was used
for total retrieving.

Change-Id: I55565a30389b94f559e16d349d6aa3ef56053ea2
Closes-Bug: #1516484
2015-11-25 15:04:41 +01:00
Jenkins
4fe0255682 Merge "Add common arguments" 2015-11-25 12:24:05 +00:00
Chaozhe.Chen
e4623d6663 Fix a typo in command help
Change-Id: Ib0b00cae66907bffddbbd28f6d77ea952ec08508
2015-11-25 17:08:05 +08:00
chenchaozhe1988
382a2d9565 Add common arguments
Merge same arguments in common arguments to make it concise and convenient.

Change-Id: I75e246d36ed7d38858e9dfdedcc77dd19ea587d5
2015-11-18 16:03:05 +08:00
Gauvain Pocentek
9428ab38aa Do no set the version in setup.cfg
Change-Id: I670e61c94f6f58cd5b31caa220e94f6a30bfb66c
2015-10-30 11:33:41 +09:00
Jenkins
1e095e6da9 Merge "Improve HashMap client" 2015-10-22 12:08:17 +00:00
Jenkins
621c06f8af Merge "Add support for PyScripts rating module" 2015-10-22 04:29:02 +00:00
Stéphane Albert
e4df2e2105 Improve HashMap client
Modified tests to handle new functions.
Refactored tests to ease maintenance.

Change-Id: I24d74e0e9983091d4f81a3f72604fbae22476505
2015-10-21 12:20:51 +02:00
Jeremy Stanley
50f6cb3e54 Update .gitreview for new namespace
Change-Id: I1de00621c99bbaa2f785c50f849b635456167539
2015-10-17 22:36:22 +00:00
Stéphane Albert
d9d61d7727 Add support for PyScripts rating module
Change-Id: I06270893460f76fa73616197783b5e2d48702fe9
2015-10-05 17:43:24 +02:00
Monty Taylor
bf03f8fae6 Change ignore-errors to ignore_errors
Needed for coverage 4.0

Change-Id: I5b11e3f02107759d178c2d292a8eb03827924102
2015-09-23 07:55:26 +00:00
Stéphane Albert
b17283d585 Moving to Liberty cycle (0.5)
Change-Id: Ie18aea965350353624e59bfb769059f7af3c9278
2015-09-22 16:04:28 +02:00
57 changed files with 2690 additions and 736 deletions

View File

@@ -3,10 +3,7 @@ branch = True
source = cloudkittyclient
omit =
cloudkittyclient/tests/*,
cloudkittyclient/openstack/*,
cloudkittyclient/i18n.py,
cloudkittyclient/common/client.py,
cloudkittyclient/common/exceptions.py
cloudkittyclient/i18n.py
[report]
ignore-errors = True
ignore_errors = True

3
.gitignore vendored
View File

@@ -11,3 +11,6 @@ cover
dist
*.egg
*.sw?
.eggs
AUTHORS
ChangeLog

View File

@@ -1,4 +1,4 @@
[gerrit]
host=review.openstack.org
port=29418
project=stackforge/python-cloudkittyclient.git
project=openstack/python-cloudkittyclient.git

View File

@@ -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=_

View File

@@ -1,6 +0,0 @@
include AUTHORS
include ChangeLog
exclude .gitignore
exclude .gitreview
global-exclude *.pyc

View File

@@ -1,3 +1,12 @@
========================
Team and repository tags
========================
.. image:: http://governance.openstack.org/badges/python-cloudkittyclient.svg
:target: http://governance.openstack.org/reference/tags/index.html
.. Change things from this point on
Python bindings to the CloudKitty API
=====================================

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2015 Objectif Libre
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@@ -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.

View File

@@ -44,8 +44,8 @@ from oslo_utils import strutils
import six
from six.moves.urllib import parse
from cloudkittyclient.openstack.common._i18n import _
from cloudkittyclient.openstack.common.apiclient import exceptions
from cloudkittyclient.apiclient import exceptions
from cloudkittyclient.i18n import _
def getid(obj):
@@ -467,8 +467,7 @@ class Resource(object):
@property
def human_id(self):
"""Human-readable ID which can be used for bash completion.
"""
"""Human-readable ID which can be used for bash completion."""
if self.HUMAN_ID:
name = getattr(self, self.NAME_ATTR, None)
if name is not None:
@@ -523,6 +522,9 @@ class Resource(object):
return self.id == other.id
return self._info == other._info
def __ne__(self, other):
return not self.__eq__(other)
def is_loaded(self):
return self._loaded

View File

@@ -38,8 +38,9 @@ from oslo_utils import encodeutils
from oslo_utils import importutils
import requests
from cloudkittyclient.openstack.common._i18n import _
from cloudkittyclient.openstack.common.apiclient import exceptions
from cloudkittyclient.apiclient import exceptions
from cloudkittyclient.i18n import _
_logger = logging.getLogger(__name__)
SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',)
@@ -64,7 +65,7 @@ class HTTPClient(object):
into terminal and send the same request with curl.
"""
user_agent = "cloudkittyclient.openstack.common.apiclient"
user_agent = "cloudkittyclient.apiclient"
def __init__(self,
auth_plugin,

View File

@@ -38,12 +38,11 @@ import sys
import six
from cloudkittyclient.openstack.common._i18n import _
from cloudkittyclient.i18n import _
class ClientException(Exception):
"""The base exception class for all exceptions this library raises.
"""
"""The base exception class for all exceptions this library raises."""
pass
@@ -118,8 +117,7 @@ class AmbiguousEndpoints(EndpointException):
class HttpError(ClientException):
"""The base exception class for all HTTP exceptions.
"""
"""The base exception class for all HTTP exceptions."""
http_status = 0
message = _("HTTP Error")

View File

@@ -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)

View File

@@ -28,8 +28,8 @@ from oslo_utils import encodeutils
from oslo_utils import uuidutils
import six
from cloudkittyclient.openstack.common._i18n import _
from cloudkittyclient.openstack.common.apiclient import exceptions
from cloudkittyclient.apiclient import exceptions
from cloudkittyclient.i18n import _
def find_resource(manager, name_or_id, **find_args):
@@ -84,17 +84,13 @@ def find_resource(manager, name_or_id, **find_args):
return manager.find(**kwargs)
except exceptions.NotFound:
msg = _("No %(name)s with a name or "
"ID of '%(name_or_id)s' exists.") % \
{
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id
}
"ID of '%(name_or_id)s' exists.") % {
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id}
raise exceptions.CommandError(msg)
except exceptions.NoUniqueMatch:
msg = _("Multiple %(name)s matches found for "
"'%(name_or_id)s', use an ID to be more specific.") % \
{
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id
}
"'%(name_or_id)s', use an ID to be more specific.") % {
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id}
raise exceptions.CommandError(msg)

View File

@@ -10,6 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import time
from keystoneclient import adapter
from keystoneclient.auth.identity import v2 as v2_auth
from keystoneclient.auth.identity import v3 as v3_auth
from keystoneclient import discover
@@ -18,10 +22,11 @@ from keystoneclient import session
from oslo_utils import strutils
import six.moves.urllib.parse as urlparse
from cloudkittyclient.apiclient import auth
from cloudkittyclient.apiclient import client
from cloudkittyclient.apiclient import exceptions
from cloudkittyclient.common import utils
from cloudkittyclient import exc
from cloudkittyclient.openstack.common.apiclient import auth
from cloudkittyclient.openstack.common.apiclient import exceptions
def _discover_auth_versions(session, auth_url):
@@ -105,6 +110,8 @@ def _get_keystone_session(**kwargs):
user_id=user_id,
user_domain_name=user_domain_name,
user_domain_id=user_domain_id,
project_name=project_name,
project_id=project_id,
project_domain_name=project_domain_name,
project_domain_id=project_domain_id)
elif use_v2:
@@ -145,7 +152,8 @@ class AuthPlugin(auth.BaseAuthPlugin):
'service_type', 'endpoint_type', 'cacert',
'auth_url', 'insecure', 'cert_file', 'key_file',
'cert', 'key', 'tenant_name', 'project_name',
'project_id', 'user_domain_id', 'user_domain_name',
'project_id', 'project_domain_id', 'project_domain_name',
'user_id', 'user_domain_id', 'user_domain_name',
'password', 'username', 'endpoint']
def __init__(self, auth_system=None, **kwargs):
@@ -200,8 +208,13 @@ class AuthPlugin(auth.BaseAuthPlugin):
"""
has_token = self.opts.get('token') or self.opts.get('auth_token')
no_auth = has_token and self.opts.get('endpoint')
has_project = (self.opts.get('project_id')
or (self.opts.get('project_name')
and (self.opts.get('user_domain_name')
or self.opts.get('user_domain_id'))))
has_tenant = self.opts.get('tenant_id') or self.opts.get('tenant_name')
has_credential = (self.opts.get('username') and has_tenant
has_credential = (self.opts.get('username')
and (has_project or has_tenant)
and self.opts.get('password')
and self.opts.get('auth_url'))
missing = not (no_auth or has_credential)
@@ -250,9 +263,28 @@ def get_client(version, **kwargs):
:param api_version: the API version to use ('1')
:param kwargs: keyword args containing credentials, either:
* session: a keystoneauth/keystoneclient session object
* service_type: The default service_type for URL discovery
* service_name: The default service_name for URL discovery
* interface: The default interface for URL discovery
(Default: public)
* region_name: The default region_name for URL discovery
* endpoint_override: Always use this endpoint URL for requests
for this cloudkittyclient
* auth: An auth plugin to use instead of the session one
* user_agent: The User-Agent string to set
(Default is python-cloudkittyclient)
* connect_retries: the maximum number of retries that should be
attempted for connection errors
* logger: A logging object
or (DEPRECATED):
* os_token: pre-existing token to re-use
* os_endpoint: Cloudkitty API endpoint
or:
or (DEPRECATED):
* os_username: name of user
* os_password: user's password
* os_user_id: user's id
@@ -274,8 +306,10 @@ def get_client(version, **kwargs):
cli_kwargs = {
'username': kwargs.get('os_username'),
'password': kwargs.get('os_password'),
'tenant_id': kwargs.get('os_tenant_id'),
'tenant_name': kwargs.get('os_tenant_name'),
'tenant_id': (kwargs.get('os_tenant_id')
or kwargs.get('os_project_id')),
'tenant_name': (kwargs.get('os_tenant_name')
or kwargs.get('os_project_name')),
'auth_url': kwargs.get('os_auth_url'),
'region_name': kwargs.get('os_region_name'),
'service_type': kwargs.get('os_service_type'),
@@ -307,10 +341,94 @@ def get_auth_plugin(endpoint, **kwargs):
endpoint=endpoint,
username=kwargs.get('username'),
password=kwargs.get('password'),
tenant_name=kwargs.get('tenant_name'),
tenant_name=kwargs.get('tenant_name') or kwargs.get('project_name'),
user_domain_name=kwargs.get('user_domain_name'),
user_domain_id=kwargs.get('user_domain_id'),
project_domain_name=kwargs.get('project_domain_name'),
project_domain_id=kwargs.get('project_domain_id')
)
return auth_plugin
LEGACY_OPTS = ('auth_plugin', 'auth_url', 'token', 'insecure', 'cacert',
'tenant_id', 'project_id', 'username', 'password',
'project_name', 'tenant_name',
'user_domain_name', 'user_domain_id',
'project_domain_name', 'project_domain_id',
'key_file', 'cert_file', 'verify', 'timeout', 'cert')
def construct_http_client(**kwargs):
kwargs = kwargs.copy()
if kwargs.get('session') is not None:
# Drop legacy options
for opt in LEGACY_OPTS:
kwargs.pop(opt, None)
return SessionClient(
session=kwargs.pop('session'),
service_type=kwargs.pop('service_type', 'rating') or 'rating',
interface=kwargs.pop('interface', kwargs.pop('endpoint_type',
'publicURL')),
region_name=kwargs.pop('region_name', None),
user_agent=kwargs.pop('user_agent', 'python-cloudkittyclient'),
auth=kwargs.get('auth', None),
timings=kwargs.pop('timings', None),
**kwargs)
else:
return client.BaseClient(client.HTTPClient(
auth_plugin=kwargs.get('auth_plugin'),
region_name=kwargs.get('region_name'),
endpoint_type=kwargs.get('endpoint_type'),
original_ip=kwargs.get('original_ip'),
verify=kwargs.get('verify'),
cert=kwargs.get('cert'),
timeout=kwargs.get('timeout'),
timings=kwargs.get('timings'),
keyring_saver=kwargs.get('keyring_saver'),
debug=kwargs.get('debug'),
user_agent=kwargs.get('user_agent'),
http=kwargs.get('http')
))
@contextlib.contextmanager
def record_time(times, enabled, *args):
"""Record the time of a specific action.
:param times: A list of tuples holds time data.
:type times: list
:param enabled: Whether timing is enabled.
:type enabled: bool
:param args: Other data to be stored besides time data, these args
will be joined to a string.
"""
if not enabled:
yield
else:
start = time.time()
yield
end = time.time()
times.append((' '.join(args), start, end))
class SessionClient(adapter.LegacyJsonAdapter):
def __init__(self, *args, **kwargs):
self.times = []
self.timings = kwargs.pop('timings', False)
super(SessionClient, self).__init__(*args, **kwargs)
def request(self, url, method, **kwargs):
kwargs.setdefault('headers', kwargs.get('headers', {}))
# NOTE(sileht): The standard call raises errors from
# keystoneauth, where we need to raise the cloudkittyclient errors.
raise_exc = kwargs.pop('raise_exc', True)
with record_time(self.times, self.timings, method, url):
resp, body = super(SessionClient, self).request(url,
method,
raise_exc=False,
**kwargs)
if raise_exc and resp.status_code >= 400:
raise exc.from_response(resp, body)
return resp

View File

@@ -22,8 +22,9 @@ import copy
from six.moves.urllib import parse
from cloudkittyclient.apiclient import base
from cloudkittyclient import exc
from cloudkittyclient.openstack.common.apiclient import base
from cloudkittyclient.i18n import _
def getid(obj):
@@ -138,7 +139,7 @@ class CrudManager(base.CrudManager):
'name': self.resource_class.__name__,
'args': kwargs
}
raise exc.NotFound(404, msg)
raise exc.HTTPNotFound(msg)
return rl

View File

@@ -30,7 +30,7 @@ import prettytable
import six
from six import moves
from cloudkittyclient.openstack.common._i18n import _
from cloudkittyclient.i18n import _
class MissingArgs(Exception):
@@ -267,5 +267,5 @@ def pretty_choice_list(l):
def exit(msg=''):
if msg:
print (msg, file=sys.stderr)
print(msg, file=sys.stderr)
sys.exit(1)

View File

@@ -23,11 +23,20 @@ import uuid
from oslo_serialization import jsonutils
from oslo_utils import encodeutils
from oslo_utils import importutils
from oslo_utils import timeutils
import prettytable
import six
from cloudkittyclient.common import cliutils
from cloudkittyclient import exc
from cloudkittyclient.openstack.common import cliutils
from cloudkittyclient.i18n import _
def iso2dt(iso_date):
"""iso8601 format to datetime."""
iso_dt = timeutils.parse_isotime(iso_date)
trans_dt = timeutils.normalize_time(iso_dt)
return trans_dt
def import_versioned_module(version, submodule=None):
@@ -45,7 +54,9 @@ def arg(*args, **kwargs):
kwargs['help'] += " Defaults to %s." % kwargs['default']
required = kwargs.get('required', False)
if required:
kwargs['help'] += " Required."
kwargs['help'] += " required."
elif 'default' not in kwargs:
kwargs['help'] += "."
# Because of the sematics of decorator composition if we just append
# to the options list positional options will appear to be backwards.
@@ -140,8 +151,10 @@ def find_resource(manager, name_or_id):
try:
return manager.find(name=name_or_id)
except exc.HTTPNotFound:
msg = ("No %s with a name or ID of '%s' exists." %
(manager.resource_class.__name__.lower(), name_or_id))
msg = _("No %(name)s with a name or ID of '%(id)s' exists.") % {
"name": manager.resource_class.__name__.lower(),
"id": name_or_id
}
raise exc.CommandError(msg)
@@ -152,9 +165,12 @@ def args_array_to_dict(kwargs, key_to_convert):
kwargs[key_to_convert] = dict(v.split("=", 1)
for v in values_to_convert)
except ValueError:
raise exc.CommandError(
'%s must be a list of key=value not "%s"' % (
key_to_convert, values_to_convert))
msg = _("%(key)s must be a list of key=value "
"not '%(value)s'") % {
"key": key_to_convert,
"value": values_to_convert
}
raise exc.CommandError(msg)
return kwargs
@@ -172,9 +188,12 @@ def args_array_to_list_of_dicts(kwargs, key_to_convert):
dct[kv[0]] = kv[1].strip(" \"'") # strip spaces and quotes
kwargs[key_to_convert].append(dct)
except Exception:
raise exc.CommandError(
'%s must be a list of key1=value1;key2=value2;... not "%s"' % (
key_to_convert, values_to_convert))
msg = _("%(key)s must be a list of "
"key1=value1;key2=value2;... not '%(value)s'") % {
"key": key_to_convert,
"value": values_to_convert
}
raise exc.CommandError(msg)
return kwargs

28
cloudkittyclient/i18n.py Normal file
View File

@@ -0,0 +1,28 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""oslo.i18n integration module.
See http://docs.openstack.org/developer/oslo.i18n/usage.html
"""
import oslo_i18n as i18n
_translators = i18n.TranslatorFactory(domain='cloudkittyclient')
i18n.enable_lazy()
_ = _translators.primary
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical

View File

@@ -1,45 +0,0 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""oslo.i18n integration module.
See http://docs.openstack.org/developer/oslo.i18n/usage.html
"""
try:
import oslo_i18n
# NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
# application name when this module is synced into the separate
# repository. It is OK to have more than one translation function
# using the same domain, since there will still only be one message
# catalog.
_translators = oslo_i18n.TranslatorFactory(domain='cloudkittyclient')
# The primary translation function using the well-known name "_"
_ = _translators.primary
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
except ImportError:
# NOTE(dims): Support for cases where a project wants to use
# code from oslo-incubator, but is not ready to be internationalized
# (like tempest)
_ = _LI = _LW = _LE = _LC = lambda x: x

35
cloudkittyclient/osc.py Normal file
View 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

View File

@@ -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
@@ -54,6 +54,9 @@ def _positive_non_zero_int(argument_value):
class CloudkittyShell(object):
def __init__(self):
self.auth_plugin = ckclient.AuthPlugin()
def get_base_parser(self):
parser = argparse.ArgumentParser(
prog='cloudkitty',
@@ -174,7 +177,6 @@ class CloudkittyShell(object):
def parse_args(self, argv):
# Parse args once to find version
self.auth_plugin = ckclient.AuthPlugin()
parser = self.get_base_parser()
(options, args) = parser.parse_known_args(argv)
self.auth_plugin.parse_opts(options)
@@ -196,12 +198,11 @@ class CloudkittyShell(object):
@staticmethod
def no_project_and_domain_set(args):
if not (args.os_project_id or (args.os_project_name and
(args.os_user_domain_name or args.os_user_domain_id)) or
(args.os_tenant_id or args.os_tenant_name)):
return True
else:
return False
return not (((args.os_project_id or (args.os_project_name and
(args.os_project_domain_name or
args.os_project_domain_id)))
and (args.os_user_domain_name or args.os_user_domain_id))
or (args.os_tenant_id or args.os_tenant_name))
def main(self, argv):
parsed = self.parse_args(argv)
@@ -240,20 +241,24 @@ class CloudkittyShell(object):
"env[OS_USER_DOMAIN_NAME] or "
"a domain_id via either "
"--os-user-domain-id or via "
"env[OS_USER_DOMAIN_ID]")
if not (self.auth_plugin.opts['tenant_id']
or self.auth_plugin.opts['tenant_name']):
raise exc.CommandError("You must provide a tenant_id via "
"either --os-tenant-id or via "
"env[OS_TENANT_ID]")
"env[OS_USER_DOMAIN_ID]\n\n"
"As an alternative to project_id, "
"you can provide a project_name via "
"either --os-project-name or via "
"env[OS_PROJECT_NAME] and "
"a project_domain_name via either "
"--os-project-domain-name or via "
"env[OS_PROJECT_DOMAIN_NAME] or "
"a project_domain_id via either "
"--os-project-domain-id or via "
"env[OS_PROJECT_DOMAIN_ID]")
if not self.auth_plugin.opts['auth_url']:
raise exc.CommandError("You must provide an auth url via "
"either --os-auth-url or via "
"env[OS_AUTH_URL]")
client_kwargs = vars(args)
client_kwargs = {}
client_kwargs.update(self.auth_plugin.opts)
client_kwargs['auth_plugin'] = self.auth_plugin
client = ckclient.get_client(api_version, **client_kwargs)

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#

View File

@@ -45,6 +45,16 @@ class ClientTest(utils.BaseTestCase):
def setUp(self):
super(ClientTest, self).setUp()
def test_client_v1_with_session(self):
resp = mock.Mock(status_code=200, text=b'')
resp.json.return_value = {"modules": []}
session = mock.Mock()
session.request.return_value = resp
c = client.get_client(1, session=session)
c.modules.list()
self.assertTrue(session.request.called)
self.assertTrue(resp.json.called)
def test_client_version(self):
c1 = self.create_client(env=FAKE_ENV, api_version=1)
self.assertIsInstance(c1, v1client.Client)
@@ -122,7 +132,7 @@ class ClientTest(utils.BaseTestCase):
'user_agent': None,
'debug': None,
}
cls = 'cloudkittyclient.openstack.common.apiclient.client.HTTPClient'
cls = 'cloudkittyclient.apiclient.client.HTTPClient'
with mock.patch(cls) as mocked:
self.create_client(env)
mocked.assert_called_with(**expected)
@@ -137,7 +147,8 @@ class ClientTest(utils.BaseTestCase):
env = FAKE_ENV.copy()
env['cacert'] = '/path/to/cacert'
client = self.create_client(env)
self.assertEqual('/path/to/cacert', client.client.verify)
self.assertEqual('/path/to/cacert',
client.http_client.http_client.verify)
def test_v1_client_certfile_and_keyfile(self):
env = FAKE_ENV.copy()
@@ -145,4 +156,4 @@ class ClientTest(utils.BaseTestCase):
env['key_file'] = '/path/to/keycert'
client = self.create_client(env)
self.assertEqual(('/path/to/cert', '/path/to/keycert'),
client.client.cert)
client.http_client.http_client.cert)

View File

@@ -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

View File

@@ -12,8 +12,8 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cloudkittyclient.openstack.common.apiclient import client
from cloudkittyclient.openstack.common.apiclient import fake_client
from cloudkittyclient.apiclient import client
from cloudkittyclient.apiclient import fake_client
from cloudkittyclient.tests import utils
import cloudkittyclient.v1.core
@@ -26,10 +26,12 @@ fixtures = {
{
'module_id': 'hashmap',
'enabled': True,
'priority': 1,
},
{
'module_id': 'noop',
'enabled': False,
'priority': 1,
},
]},
),
@@ -40,6 +42,7 @@ fixtures = {
{
'module_id': 'hashmap',
'enabled': True,
'priority': 1,
}
),
'PUT': (
@@ -47,6 +50,7 @@ fixtures = {
{
'module_id': 'hashmap',
'enabled': False,
'priority': 1,
}
),
},
@@ -56,6 +60,7 @@ fixtures = {
{
'module_id': 'noop',
'enabled': False,
'priority': 1,
}
),
'PUT': (
@@ -63,6 +68,7 @@ fixtures = {
{
'module_id': 'noop',
'enabled': True,
'priority': 1,
}
),
},
@@ -94,9 +100,9 @@ class CloudkittyModuleManagerTest(utils.BaseTestCase):
'GET', '/v1/rating/modules'
]
self.http_client.assert_called(*expect)
self.assertEqual(len(resources), 2)
self.assertEqual(resources[0].module_id, 'hashmap')
self.assertEqual(resources[1].module_id, 'noop')
self.assertEqual(2, len(resources))
self.assertEqual('hashmap', resources[0].module_id)
self.assertEqual('noop', resources[1].module_id)
def test_get_module_status(self):
resource = self.mgr.get(module_id='hashmap')
@@ -104,8 +110,8 @@ class CloudkittyModuleManagerTest(utils.BaseTestCase):
'GET', '/v1/rating/modules/hashmap'
]
self.http_client.assert_called(*expect)
self.assertEqual(resource.module_id, 'hashmap')
self.assertEqual(resource.enabled, True)
self.assertEqual('hashmap', resource.module_id)
self.assertTrue(resource.enabled)
class CloudkittyModuleTest(utils.BaseTestCase):
@@ -123,7 +129,8 @@ class CloudkittyModuleTest(utils.BaseTestCase):
# body : {'enabled': True}
expect = [
'PUT', '/v1/rating/modules/noop', {'module_id': 'noop',
'enabled': True},
'enabled': True,
'priority': 1},
]
self.http_client.assert_called(*expect)
@@ -134,6 +141,19 @@ class CloudkittyModuleTest(utils.BaseTestCase):
# body : {'enabled': False}
expect = [
'PUT', '/v1/rating/modules/hashmap', {'module_id': 'hashmap',
'enabled': False},
'enabled': False,
'priority': 1},
]
self.http_client.assert_called(*expect)
def test_set_priority(self):
self.ck_module = self.mgr.get(module_id='hashmap')
self.ck_module.set_priority(100)
# PUT /v1/rating/modules/hashmap
# body : {'priority': 100}
expect = [
'PUT', '/v1/rating/modules/hashmap', {'module_id': 'hashmap',
'enabled': True,
'priority': 100},
]
self.http_client.assert_called(*expect)

File diff suppressed because it is too large Load Diff

View 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)

View File

@@ -16,7 +16,6 @@
from stevedore import extension
from cloudkittyclient import client as ckclient
from cloudkittyclient.openstack.common.apiclient import client
from cloudkittyclient.v1 import collector
from cloudkittyclient.v1 import core
from cloudkittyclient.v1 import report
@@ -28,38 +27,41 @@ SUBMODULES_NAMESPACE = 'cloudkitty.client.modules'
class Client(object):
"""Client for the Cloudkitty v1 API.
:param string endpoint: A user-supplied endpoint URL for the cloudkitty
service.
:param function token: Provides token for authentication.
:param integer timeout: Allows customization of the timeout for client
http requests. (optional)
:param session: a keystoneauth/keystoneclient session object
:type session: keystoneclient.session.Session
:param str service_type: The default service_type for URL discovery
:param str service_name: The default service_name for URL discovery
:param str interface: The default interface for URL discovery
(Default: public)
:param str region_name: The default region_name for URL discovery
:param str endpoint_override: Always use this endpoint URL for requests
for this cloudkittyclient
:param auth: An auth plugin to use instead of the session one
:type auth: keystoneclient.auth.base.BaseAuthPlugin
:param str user_agent: The User-Agent string to set
(Default is python-cloudkittyclient)
:param int connect_retries: the maximum number of retries that should be
attempted for connection errors
:param logger: A logging object
:type logger: logging.Logger
"""
def __init__(self, *args, **kwargs):
"""Initialize a new client for the Cloudkitty v1 API."""
self.auth_plugin = (kwargs.get('auth_plugin')
or ckclient.get_auth_plugin(*args, **kwargs))
self.client = client.HTTPClient(
auth_plugin=self.auth_plugin,
region_name=kwargs.get('region_name'),
endpoint_type=kwargs.get('endpoint_type'),
original_ip=kwargs.get('original_ip'),
verify=kwargs.get('verify'),
cert=kwargs.get('cert'),
timeout=kwargs.get('timeout'),
timings=kwargs.get('timings'),
keyring_saver=kwargs.get('keyring_saver'),
debug=kwargs.get('debug'),
user_agent=kwargs.get('user_agent'),
http=kwargs.get('http')
)
self.http_client = client.BaseClient(self.client)
if not kwargs.get('auth_plugin'):
kwargs['auth_plugin'] = ckclient.get_auth_plugin(*args, **kwargs)
self.auth_plugin = kwargs.get('auth_plugin')
self.http_client = ckclient.construct_http_client(**kwargs)
self.modules = core.CloudkittyModuleManager(self.http_client)
self.collector = collector.CollectorManager(self.http_client)
self.reports = report.ReportManager(self.http_client)
self.reportsummary = report.ReportSummaryManager(self.http_client)
self.quotations = core.QuotationManager(self.http_client)
self.storage = storage.StorageManager(self.http_client)
self.config = core.ConfigInfoManager(self.http_client)
self.service_info = core.ServiceInfoManager(self.http_client)
self._expose_submodules()
def _expose_submodules(self):

View File

@@ -17,64 +17,71 @@
from cloudkittyclient.common import utils
@utils.arg('--collector',
@utils.arg('-c', '--collector',
help='Collector name to filter on.',
required=False,
default=None)
def do_collector_mapping_list(cc, args):
"""List collector mapping."""
data = cc.collector.mappings.list(collector=args.collector)
fields = ['service', 'collector']
fields_labels = ['Service', 'Collector']
utils.print_list(data, fields, fields_labels, sortby=0)
@utils.arg('--service',
@utils.arg('-s', '--service',
help='Which service to get the mapping for.',
required=True)
def do_collector_mapping_get(cc, args):
"""Show collector mapping detail."""
data = cc.collector.mappings.get(mapping_id=args.service)
utils.print_dict(data.to_dict())
@utils.arg('--collector',
@utils.arg('-c', '--collector',
help='Map a service to this collector.',
required=True)
@utils.arg('--service',
@utils.arg('-s', '--service',
help='Map a collector to this service.',
required=True)
def do_collector_mapping_create(cc, args):
"""Create collector mapping."""
out = cc.collector.mappings.create(service=args.service,
collector=args.collector)
utils.print_dict(out.to_dict())
@utils.arg('--service',
@utils.arg('-s', '--service',
help='Filter on this service.',
required=True)
def do_collector_mapping_delete(cc, args):
"""Delete collector mapping."""
# TODO(sheeprine): Use a less hacky way to do this
cc.collector.mappings.delete(mapping_id=args.service)
@utils.arg('--name',
@utils.arg('-n', '--name',
help='Name of the collector.',
required=True)
def do_collector_state_get(cc, args):
"""Show collector state."""
data = cc.collector.states.get(state_id=args.name)
utils.print_dict(data.to_dict())
@utils.arg('--name',
@utils.arg('-n', '--name',
help='Name of the collector.',
required=True)
def do_collector_state_enable(cc, args):
"""Enable collector state."""
new_state = cc.collector.states.update(name=args.name, enabled=True)
utils.print_dict(new_state.to_dict())
@utils.arg('--name',
@utils.arg('-n', '--name',
help='Name of the collector.',
required=True)
def do_collector_state_disable(cc, args):
"""Disable collector state."""
new_state = cc.collector.states.update(name=args.name, enabled=False)
utils.print_dict(new_state.to_dict())

View 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)

View File

@@ -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

View File

@@ -15,23 +15,76 @@
from cloudkittyclient.common import base
class Service(base.Resource):
key = 'service'
class BaseAttributeMixin(object):
def _validate_attribute(self, attribute):
attr = getattr(self, attribute)
if attr:
kwargs = {attribute: attr}
return kwargs
def __repr__(self):
return "<hashmap.Service %s>" % self._info
def _get_resource(self, mgr, attribute):
kwargs = self._validate_attribute(attribute)
if kwargs:
return mgr(client=self.manager.client).get(**kwargs)
def _get_resources(self, mgr, attribute):
kwargs = self._validate_attribute(attribute)
if kwargs:
try:
return mgr(client=self.manager.client).findall(**kwargs)
except Exception:
pass
return []
class ServiceMixin(BaseAttributeMixin):
@property
def service(self):
return self._get_resource(ServiceManager, 'service_id')
class FieldMixin(BaseAttributeMixin):
@property
def field(self):
return self._get_resource(FieldManager, 'field_id')
class GroupMixin(BaseAttributeMixin):
@property
def group(self):
return self._get_resource(GroupManager, 'group_id')
class FieldsMixin(BaseAttributeMixin):
attribute = ''
@property
def fields(self):
return FieldManager(client=self.manager.client).findall(
service_id=self.service_id
)
return self._get_resources(FieldManager, self.attribute)
class MappingsMixin(BaseAttributeMixin):
attribute = ''
@property
def mappings(self):
return MappingManager(client=self.manager.client).findall(
service_id=self.service_id
)
return self._get_resources(MappingManager, self.attribute)
class ThresholdsMixin(BaseAttributeMixin):
attribute = ''
@property
def thresholds(self):
return self._get_resources(ThresholdManager, self.attribute)
class Service(base.Resource, FieldsMixin, MappingsMixin, ThresholdsMixin):
key = 'service'
attribute = 'service_id'
def __repr__(self):
return "<hashmap.Service %s>" % self._info
class ServiceManager(base.CrudManager):
@@ -41,18 +94,13 @@ class ServiceManager(base.CrudManager):
collection_key = 'services'
class Field(base.Resource):
class Field(base.Resource, ServiceMixin, MappingsMixin, ThresholdsMixin):
key = 'field'
attribute = 'field_id'
def __repr__(self):
return "<hashmap.Field %s>" % self._info
@property
def service(self):
return ServiceManager(client=self.manager.client).get(
service_id=self.service_id
)
class FieldManager(base.CrudManager):
resource_class = Field
@@ -61,26 +109,12 @@ class FieldManager(base.CrudManager):
collection_key = 'fields'
class Mapping(base.Resource):
class Mapping(base.Resource, ServiceMixin, FieldMixin, GroupMixin):
key = 'mapping'
def __repr__(self):
return "<hashmap.Mapping %s>" % self._info
@property
def service(self):
return ServiceManager(client=self.manager.client).get(
service_id=self.service_id
)
@property
def field(self):
if self.field_id is None:
return None
return FieldManager(client=self.manager.client).get(
service_id=self.service_id
)
class MappingManager(base.CrudManager):
resource_class = Mapping
@@ -89,8 +123,9 @@ class MappingManager(base.CrudManager):
collection_key = 'mappings'
class Group(base.Resource):
class Group(base.Resource, MappingsMixin, ThresholdsMixin):
key = 'group'
attribute = 'group_id'
def __repr__(self):
return "<hashmap.Group %s>" % self._info
@@ -112,7 +147,7 @@ class GroupManager(base.CrudManager):
return self._delete(url)
class Threshold(base.Resource):
class Threshold(base.Resource, ServiceMixin, FieldMixin, GroupMixin):
key = 'threshold'
def __repr__(self):
@@ -124,9 +159,3 @@ class ThresholdManager(base.CrudManager):
base_url = '/v1/rating/module_config/hashmap'
key = 'threshold'
collection_key = 'thresholds'
def group(self, threshold_id):
url = ('%(base_url)s/thresholds/%(threshold_id)s/group' %
{'base_url': self.base_url, 'threshold_id': threshold_id})
out = self._get(url)
return out

View File

@@ -17,6 +17,7 @@ import functools
from oslo_utils import strutils
from cloudkittyclient.apiclient import exceptions
from cloudkittyclient.common import utils
from cloudkittyclient import exc
@@ -44,8 +45,8 @@ def do_hashmap_service_list(cc, args={}):
"""List services."""
try:
services = cc.hashmap.services.list()
except exc.HTTPNotFound:
raise exc.CommandError('Services not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Services not found.')
else:
field_labels = ['Name', 'Service id']
fields = ['name', 'service_id']
@@ -60,8 +61,8 @@ def do_hashmap_service_delete(cc, args={}):
"""Delete a service."""
try:
cc.hashmap.services.delete(service_id=args.service_id)
except exc.HTTPNotFound:
raise exc.CommandError('Service not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Service not found: %s' % args.service_id)
@utils.arg('-n', '--name',
@@ -89,11 +90,12 @@ def do_hashmap_field_create(cc, args={}):
help='Service id',
required=True)
def do_hashmap_field_list(cc, args={}):
"""Create a field."""
"""List fields."""
try:
created_field = cc.hashmap.fields.list(service_id=args.service_id)
except exc.HTTPNotFound:
raise exc.CommandError('Fields not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Fields not found in service: %s'
% args.service_id)
else:
field_labels = ['Name', 'Field id']
fields = ['name', 'field_id']
@@ -108,28 +110,41 @@ def do_hashmap_field_delete(cc, args={}):
"""Delete a field."""
try:
cc.hashmap.fields.delete(field_id=args.field_id)
except exc.HTTPNotFound:
raise exc.CommandError('Field not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Field not found: %s' % args.field_id)
def common_hashmap_mapping_arguments(create=False):
def _wrapper(func):
@utils.arg('-c', '--cost',
help='Mapping cost',
required=create)
@utils.arg('-v', '--value',
help='Mapping value',
required=False)
@utils.arg('-t', '--type',
help='Mapping type (flat, rate)',
required=False)
@utils.arg('-g', '--group-id',
help='Group id',
required=False)
@utils.arg('-p', '--project-id',
help='Project/tenant id',
required=False)
@functools.wraps(func)
def _wrapped(*args, **kwargs):
return func(*args, **kwargs)
return _wrapped
return _wrapper
@utils.arg('-c', '--cost',
help='Mapping cost',
required=True)
@utils.arg('-v', '--value',
help='Mapping value',
required=False)
@utils.arg('-t', '--type',
help='Mapping type (flat, rate)',
required=False)
@utils.arg('-s', '--service-id',
help='Service id',
required=False)
@utils.arg('-f', '--field-id',
help='Field id',
required=False)
@utils.arg('-g', '--group-id',
help='Group id',
required=False)
@common_hashmap_mapping_arguments(create=True)
def do_hashmap_mapping_create(cc, args={}):
"""Create a mapping."""
arg_to_field_mapping = {
@@ -139,6 +154,7 @@ def do_hashmap_mapping_create(cc, args={}):
'service_id': 'service_id',
'field_id': 'field_id',
'group_id': 'group_id',
'project_id': 'tenant_id',
}
fields = {}
for k, v in vars(args).items():
@@ -152,18 +168,7 @@ def do_hashmap_mapping_create(cc, args={}):
@utils.arg('-m', '--mapping-id',
help='Mapping id',
required=True)
@utils.arg('-c', '--cost',
help='Mapping cost',
required=False)
@utils.arg('-v', '--value',
help='Mapping value',
required=False)
@utils.arg('-t', '--type',
help='Mapping type (flat, rate)',
required=False)
@utils.arg('-g', '--group-id',
help='Group id',
required=False)
@common_hashmap_mapping_arguments()
def do_hashmap_mapping_update(cc, args={}):
"""Update a mapping."""
arg_to_field_mapping = {
@@ -172,11 +177,12 @@ def do_hashmap_mapping_update(cc, args={}):
'value': 'value',
'type': 'type',
'group_id': 'group_id',
'project_id': 'tenant_id',
}
try:
mapping = cc.hashmap.mappings.get(mapping_id=args.mapping_id)
except exc.HTTPNotFound:
raise exc.CommandError('Modules not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Mapping not found: %s' % args.mapping_id)
for k, v in vars(args).items():
if k in arg_to_field_mapping:
if v is not None:
@@ -193,23 +199,29 @@ def do_hashmap_mapping_update(cc, args={}):
@utils.arg('-g', '--group-id',
help='Group id',
required=False)
@utils.arg('-p', '--project-id',
help='Project/tenant id',
required=False)
def do_hashmap_mapping_list(cc, args={}):
"""List mappings."""
if args.service_id is None and args.field_id is None:
raise exc.CommandError("Provide either service-id or field-id")
if (args.group_id is None and
args.service_id is None and args.field_id is None):
raise exc.CommandError("Provide either group-id, service-id or "
"field-id")
try:
mappings = cc.hashmap.mappings.list(service_id=args.service_id,
field_id=args.field_id,
group_id=args.group_id)
except exc.HTTPNotFound:
raise exc.CommandError('Mapping not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Mappings not found for field: %s'
% args.field_id)
else:
field_labels = ['Mapping id', 'Value', 'Cost',
'Type', 'Field id',
'Service id', 'Group id']
'Service id', 'Group id', 'Tenant id']
fields = ['mapping_id', 'value', 'cost',
'type', 'field_id',
'service_id', 'group_id']
'service_id', 'group_id', 'tenant_id']
utils.print_list(mappings, fields, field_labels,
sortby=0)
@@ -221,7 +233,7 @@ def do_hashmap_mapping_delete(cc, args={}):
"""Delete a mapping."""
try:
cc.hashmap.mappings.delete(mapping_id=args.mapping_id)
except exc.HTTPNotFound:
except exceptions.NotFound:
raise exc.CommandError('Mapping not found: %s' % args.mapping_id)
@@ -246,8 +258,8 @@ def do_hashmap_group_list(cc, args={}):
"""List groups."""
try:
groups = cc.hashmap.groups.list()
except exc.HTTPNotFound:
raise exc.CommandError('Mapping not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Groups not found.')
else:
field_labels = ['Name',
'Group id']
@@ -268,37 +280,51 @@ def do_hashmap_group_delete(cc, args={}):
try:
cc.hashmap.groups.delete(group_id=args.group_id,
recursive=args.recursive)
except exc.HTTPNotFound:
except exceptions.NotFound:
raise exc.CommandError('Group not found: %s' % args.group_id)
@utils.arg('-l', '--level',
help='Threshold level',
required=True)
@utils.arg('-c', '--cost',
help='Threshold cost',
required=True)
@utils.arg('-m', '--map-type',
help='Threshold type (flat, rate)',
required=False)
def common_hashmap_threshold_arguments(create=False):
def _wrapper(func):
@utils.arg('-l', '--level',
help='Threshold level',
required=create)
@utils.arg('-c', '--cost',
help='Threshold cost',
required=create)
@utils.arg('-t', '--type',
help='Threshold type (flat, rate)',
required=False)
@utils.arg('-g', '--group-id',
help='Group id',
required=False)
@utils.arg('-p', '--project-id',
help='Project/tenant id',
required=False)
@functools.wraps(func)
def _wrapped(*args, **kwargs):
return func(*args, **kwargs)
return _wrapped
return _wrapper
@utils.arg('-s', '--service-id',
help='Service id',
required=False)
@utils.arg('-f', '--field-id',
help='Field id',
required=False)
@utils.arg('-g', '--group-id',
help='Group id',
required=False)
@common_hashmap_threshold_arguments(create=True)
def do_hashmap_threshold_create(cc, args={}):
"""Create a mapping."""
arg_to_field_mapping = {
'level': 'level',
'cost': 'cost',
'map_type': 'map_type',
'type': 'type',
'service_id': 'service_id',
'field_id': 'field_id',
'group_id': 'group_id',
'project_id': 'tenant_id',
}
fields = {}
for k, v in vars(args).items():
@@ -309,34 +335,24 @@ def do_hashmap_threshold_create(cc, args={}):
utils.print_dict(out.to_dict())
@utils.arg('-t', '--threshold-id',
@utils.arg('-i', '--threshold-id',
help='Threshold id',
required=True)
@utils.arg('-l', '--level',
help='Threshold level',
required=False)
@utils.arg('-c', '--cost',
help='Threshold cost',
required=False)
@utils.arg('-m', '--map-type',
help='Threshold type (flat, rate)',
required=False)
@utils.arg('-g', '--group-id',
help='Group id',
required=False)
@common_hashmap_threshold_arguments()
def do_hashmap_threshold_update(cc, args={}):
"""Update a threshold."""
arg_to_field_mapping = {
'threshold_id': 'threshold_id',
'cost': 'cost',
'level': 'level',
'map_type': 'map_type',
'type': 'type',
'group_id': 'group_id',
'project_id': 'tenant_id',
}
try:
threshold = cc.hashmap.thresholds.get(threshold_id=args.threshold_id)
except exc.HTTPNotFound:
raise exc.CommandError('Modules not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
for k, v in vars(args).items():
if k in arg_to_field_mapping:
if v is not None:
@@ -357,6 +373,9 @@ def do_hashmap_threshold_update(cc, args={}):
type=_bool_strict, metavar='{True,False}',
help='If True, list only orhpaned thresholds',
required=False)
@utils.arg('-p', '--project-id',
help='Project/tenant id',
required=False)
def do_hashmap_threshold_list(cc, args={}):
"""List thresholds."""
if (args.group_id is None and
@@ -368,48 +387,48 @@ def do_hashmap_threshold_list(cc, args={}):
field_id=args.field_id,
group_id=args.group_id,
no_group=args.no_group)
except exc.HTTPNotFound:
raise exc.CommandError('Threshold not found: %s' % args.counter_name)
except exceptions.NotFound:
raise exc.CommandError('Thresholds not found')
else:
field_labels = ['Threshold id', 'Level', 'Cost',
'Type', 'Field id',
'Service id', 'Group id']
'Service id', 'Group id', 'Tenant id']
fields = ['threshold_id', 'level', 'cost',
'map_type', 'field_id',
'service_id', 'group_id']
'type', 'field_id',
'service_id', 'group_id', 'tenant_id']
utils.print_list(thresholds, fields, field_labels, sortby=0)
@utils.arg('-t', '--threshold-id',
@utils.arg('-i', '--threshold-id',
help='Threshold uuid',
required=True)
def do_hashmap_threshold_delete(cc, args={}):
"""Delete a threshold."""
try:
cc.hashmap.thresholds.delete(threshold_id=args.threshold_id)
except exc.HTTPNotFound:
except exceptions.NotFound:
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
@utils.arg('-t', '--threshold-id',
@utils.arg('-i', '--threshold-id',
help='Threshold uuid',
required=True)
def do_hashmap_threshold_get(cc, args={}):
"""Get a threshold."""
try:
threshold = cc.hashmap.thresholds.get(threshold_id=args.threshold_id)
except exc.HTTPNotFound:
except exceptions.NotFound:
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
utils.print_dict(threshold.to_dict())
@utils.arg('-t', '--threshold-id',
@utils.arg('-i', '--threshold-id',
help='Threshold uuid',
required=True)
def do_hashmap_threshold_group(cc, args={}):
"""Get a threshold group."""
try:
threshold = cc.hashmap.thresholds.group(threshold_id=args.threshold_id)
except exc.HTTPNotFound:
except exceptions.NotFound:
raise exc.CommandError('Threshold not found: %s' % args.threshold_id)
utils.print_dict(threshold.to_dict())

View 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)

View File

@@ -0,0 +1,30 @@
# Copyright 2015 Objectif Libre
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cloudkittyclient.common import base
class Script(base.Resource):
key = 'script'
def __repr__(self):
return "<pyscripts.Script %s>" % self._info
class ScriptManager(base.CrudManager):
resource_class = Script
base_url = '/v1/rating/module_config/pyscripts'
key = 'script'
collection_key = 'scripts'

View File

@@ -0,0 +1,28 @@
# Copyright 2015 Objectif Libre
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cloudkittyclient.v1.rating import pyscripts
class Client(object):
"""Client for the PyScripts v1 API.
:param http_client: A http client.
"""
def __init__(self, http_client):
"""Initialize a new client for the PyScripts v1 API."""
self.http_client = http_client
self.scripts = pyscripts.ScriptManager(self.http_client)

View File

@@ -0,0 +1,31 @@
# Copyright 2015 Objectif Libre
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from cloudkittyclient.v1.rating.pyscripts import client
from cloudkittyclient.v1.rating.pyscripts import shell
class Extension(object):
"""PyScripts extension.
"""
@staticmethod
def get_client(http_client):
return client.Client(http_client)
@staticmethod
def get_shell():
return shell

View File

@@ -0,0 +1,117 @@
# Copyright 2015 Objectif Libre
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import functools
from oslo_utils import strutils
import six
from cloudkittyclient.apiclient import exceptions
from cloudkittyclient.common import utils
from cloudkittyclient import exc
_bool_strict = functools.partial(strutils.bool_from_string, strict=True)
@utils.arg('-n', '--name',
help='Script name',
required=True)
@utils.arg('-f', '--file',
help='Script file',
required=False)
def do_pyscripts_script_create(cc, args={}):
"""Create a script."""
script_args = {'name': args.name}
if args.file:
with open(args.file) as fp:
script_args['data'] = fp.read()
out = cc.pyscripts.scripts.create(**script_args)
utils.print_dict(out.to_dict())
@utils.arg('-d', '--show-data',
help='Show data in the listing',
required=False,
default=False)
def do_pyscripts_script_list(cc, args={}):
"""List scripts."""
request_args = {}
if not args.show_data:
request_args['no_data'] = True
scripts = cc.pyscripts.scripts.list(**request_args)
field_labels = ['Name', 'Script id', 'Data', 'Checksum']
fields = ['name', 'script_id', 'data', 'checksum']
utils.print_list(scripts,
fields,
field_labels,
sortby=0)
@utils.arg('-s', '--script-id',
help='Script uuid',
required=True)
def do_pyscripts_script_get(cc, args={}):
"""Get script."""
try:
script = cc.pyscripts.scripts.get(script_id=args.script_id)
except exceptions.NotFound:
raise exc.CommandError('Script not found: %s' % args.script_id)
utils.print_dict(script.to_dict())
@utils.arg('-s', '--script-id',
help='Script uuid',
required=True)
def do_pyscripts_script_get_data(cc, args={}):
"""Get script data."""
try:
script = cc.pyscripts.scripts.get(script_id=args.script_id)
except exceptions.NotFound:
raise exc.CommandError('Script not found: %s' % args.script_id)
six.print_(script.data)
@utils.arg('-s', '--script-id',
help='Script uuid',
required=True)
def do_pyscripts_script_delete(cc, args={}):
"""Delete a script."""
try:
cc.pyscripts.scripts.delete(script_id=args.script_id)
except exceptions.NotFound:
raise exc.CommandError('Script not found: %s' % args.script_id)
@utils.arg('-s', '--script-id',
help='Script uuid',
required=True)
@utils.arg('-f', '--file',
help='Script file',
required=True)
def do_pyscripts_script_update(cc, args={}):
"""Update a mapping."""
excluded_fields = [
'checksum',
]
with open(args.file) as fp:
content = fp.read()
try:
script = cc.pyscripts.scripts.get(script_id=args.script_id)
except exceptions.NotFound:
raise exc.CommandError('Script not found: %s' % args.script_id)
script_dict = script.to_dict()
for field in excluded_fields:
del script_dict[field]
script_dict['data'] = content
cc.pyscripts.scripts.update(**script_dict)

View 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)

View File

@@ -15,26 +15,67 @@
from cloudkittyclient.common import base
class ReportResult(base.Resource):
class ReportSummary(base.Resource):
key = 'report'
key = 'summary'
def __init(self, tenant_id=None, res_type=None, begin=None,
end=None, rate=None):
self.tenant_id = tenant_id
self.res_type = res_type
self.begin = begin
self.end = end
self.rate = rate
def __repr__(self):
return "<Report %s>" % self._info
return "<Summary %s" % self._info
class ReportManager(base.Manager):
class ReportManager(base.CrudManager):
base_url = "/v1/report"
def list_tenants(self):
return self.client.get(self.base_url + "/tenants").json()
def get_total(self, tenant_id, begin=None, end=None):
url = self.base_url + "/total?tenant_id=%s" % tenant_id
filter = [url]
def get_total(self, tenant_id=None, begin=None, end=None,
service=None, all_tenants=False):
url = self.base_url + "/total"
filters = list()
if tenant_id:
filters.append("tenant_id=%s" % tenant_id)
if begin:
filter.append("begin=%s" % begin.isoformat())
filters.append("begin=%s" % begin.isoformat())
if end:
filter.append("end=%s" % end.isoformat())
return self.client.get("&".join(filter)).json()
filters.append("end=%s" % end.isoformat())
if service:
filters.append("service=%s" % service)
if all_tenants:
filters.append("all_tenants=%s" % all_tenants)
if filters:
url += "?%s" % ('&'.join(filters))
return self.client.get(url).json()
class ReportSummaryManager(ReportManager):
resource_class = ReportSummary
key = 'summary'
collection_key = "summary"
def get_summary(self, tenant_id=None, begin=None, end=None,
service=None, groupby=None, all_tenants=False):
kwargs = {}
if tenant_id:
kwargs['tenant_id'] = tenant_id
if begin:
kwargs['begin'] = begin.isoformat()
if end:
kwargs['end'] = end.isoformat()
if service:
kwargs['service'] = service
if groupby:
kwargs['groupby'] = groupby
if all_tenants:
kwargs['all_tenants'] = all_tenants
return super(ReportManager, self).list(**kwargs)

View File

@@ -13,11 +13,13 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import print_function
from cloudkittyclient.common import utils
def do_report_tenant_list(cc, args):
"""List tenant report."""
tenants = cc.reports.list_tenants()
out_table = utils.prettytable.PrettyTable()
out_table.add_column("Tenant UUID", tenants)
@@ -26,17 +28,69 @@ def do_report_tenant_list(cc, args):
@utils.arg('-t', '--tenant-id',
help='Tenant id',
required=False, dest='total_tenant_id')
required=False,
dest='total_tenant_id')
@utils.arg('-b', '--begin',
help='Starting date/time (YYYY-MM-DDTHH:MM:SS)',
required=False)
@utils.arg('-e', '--end',
help='Ending date/time (YYYY-MM-DDTHH:MM:SS)',
required=False)
@utils.arg('-s', '--service',
help='Service Type',
required=False)
@utils.arg('-a', '--all-tenants',
default=False,
action="store_true",
dest='all_tenants',
help='Allows to get total from all tenants'
' (admin only).')
def do_total_get(cc, args):
"""Get total reports."""
begin = utils.iso2dt(args.begin) if args.begin else None
end = utils.iso2dt(args.end) if args.end else None
total = cc.reports.get_total(tenant_id=args.total_tenant_id,
begin=begin,
end=end,
service=args.service,
all_tenants=args.all_tenants)
utils.print_dict({'Total': total or 0.0})
@utils.arg('-t', '--tenant-id',
help='Tenant id',
required=False,
dest='summary_tenant_id')
@utils.arg('-b', '--begin',
help='Begin timestamp',
required=False)
@utils.arg('-e', '--end',
help='End timestamp',
required=False)
def do_total_get(cc, args):
@utils.arg('-s', '--service',
help='Service Type',
required=False)
@utils.arg('-g', '--groupby',
help=('Fields to groupby, separated by commas '
'if multiple, now support res_type,tenant_id'),
required=False)
@utils.arg('-a', '--all-tenants',
default=False,
action="store_true",
dest='all_tenants',
help='Allows to get summary from all tenants'
' (admin only).')
def do_summary_get(cc, args):
"""Get summary report."""
begin = utils.ts2dt(args.begin) if args.begin else None
end = utils.ts2dt(args.end) if args.end else None
total = cc.reports.get_total(args.total_tenant_id,
begin=begin,
end=end)
utils.print_dict({'Total': total or 0.0})
summarys = cc.reportsummary.get_summary(tenant_id=args.summary_tenant_id,
begin=begin,
end=end,
service=args.service,
groupby=args.groupby,
all_tenants=args.all_tenants)
field_labels = ['Tenant ID', 'Resource Type', 'Rate',
'Begin Time', 'End Time']
fields = ['tenant_id', 'res_type', 'rate', 'begin', 'end']
utils.print_list(summarys, fields, field_labels, sortby=0)

View 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)

View File

@@ -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)

View 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)

View File

@@ -17,21 +17,22 @@
from cloudkittyclient.common import utils
@utils.arg('--begin',
help='Starting date/time (YYYY-MM-ddThh:mm:ss)',
required=True)
@utils.arg('--end',
help='Ending date/time (YYYY-MM-ddThh:mm:ss)',
required=True)
@utils.arg('--tenant',
@utils.arg('-b', '--begin',
help='Starting date/time (YYYY-MM-DDTHH:MM:SS)',
required=False)
@utils.arg('-e', '--end',
help='Ending date/time (YYYY-MM-DDTHH:MM:SS)',
required=False)
@utils.arg('-t', '--tenant',
help='Tenant ID',
required=False,
default=None)
@utils.arg('--resource-type',
@utils.arg('-r', '--resource-type',
help='Resource type (compute, image, ...)',
required=False,
default=None)
def do_storage_dataframe_list(cc, args):
"""List dataframes."""
data = cc.storage.dataframes.list(begin=args.begin, end=args.end,
tenant_id=args.tenant,
resource_type=args.resource_type)

View 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)

View File

@@ -1,42 +0,0 @@
Name: python-cloudkittyclient
Version: @VERSION@
Release: 1%{?dist}
Summary: Client library for CloudKitty
License: ASL 2.0
Source: %{name}-%{version}.tar.gz
BuildArch: noarch
BuildRequires: python-setuptools
BuildRequires: python-pbr
BuildRequires: python-keystoneclient
BuildRequires: python-stevedore
BuildRequires: python-babel
Requires: python-keystoneclient
Requires: python-stevedore
Requires: python-babel
%description
Client library for CloudKitty
%prep
%setup -q
%build
%{__python} setup.py build
%install
%{__python} setup.py install -O1 --skip-build --root %{buildroot}
#export PYTHONPATH="$( pwd ):$PYTHONPATH"
#sphinx-build -b html doc/source html
%files
%{_bindir}/*
%{python_sitelib}/*
#%doc html
%changelog
* Sun Apr 19 2015 Gauvain Pocentek <gauvain.pocentek@objectif-libre.com> @VERSION@-1
- Initial release

1
doc/source/conf.py Executable file → Normal file
View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

View File

@@ -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

View File

@@ -2,10 +2,12 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr!=0.7,<1.0,>=0.6
Babel>=1.3
python-keystoneclient<1.4.0,>=1.2.0
stevedore<1.4.0,>=1.3.0 # Apache-2.0
oslo.i18n<1.6.0,>=1.5.0 # Apache-2.0
oslo.serialization<1.5.0,>=1.4.0 # Apache-2.0
oslo.utils<1.5.0,>=1.4.0 # Apache-2.0
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
python-openstackclient>=3.0.0 # Apache-2.0
stevedore>=1.10.0 # Apache-2.0
oslo.i18n>=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

View File

@@ -1,6 +1,5 @@
[metadata]
name = python-cloudkittyclient
version = 0.4.1
summary = API client of cloudkitty, Rating as a Service project.
description-file =
README.rst
@@ -17,7 +16,6 @@ classifier =
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
[files]
@@ -30,6 +28,60 @@ console_scripts =
cloudkitty.client.modules =
hashmap = cloudkittyclient.v1.rating.hashmap.extension:Extension
pyscripts = cloudkittyclient.v1.rating.pyscripts.extension:Extension
openstack.cli.extension =
rating = cloudkittyclient.osc
openstack.rating.v1 =
rating_module-list = cloudkittyclient.v1.shell_cli:CliModuleList
rating_module-enable = cloudkittyclient.v1.shell_cli:CliModuleEnable
rating_module-disable = cloudkittyclient.v1.shell_cli:CliModuleDisable
rating_module-set-priority = cloudkittyclient.v1.shell_cli:CliModuleSetPriority
rating_info-config-get = cloudkittyclient.v1.shell_cli:CliInfoGetConfig
rating_info-service-get = cloudkittyclient.v1.shell_cli:CliInfoGetService
rating_total-get = cloudkittyclient.v1.report.shell_cli:CliTotalGet
rating_summary-get = cloudkittyclient.v1.report.shell_cli:CliSummaryGet
rating_report-tenant-list = cloudkittyclient.v1.report.shell_cli:CliReportTenantList
rating_collector-mapping-list = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingList
rating_collector-mapping-get = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingGet
rating_collector-mapping-create = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingCreate
rating_collector-mapping-delete = cloudkittyclient.v1.collector.shell_cli:CliCollectorMappingDelete
rating_collector-state-get = cloudkittyclient.v1.collector.shell_cli:CliCollectorStateGet
rating_collector-state-enable = cloudkittyclient.v1.collector.shell_cli:CliCollectorStateEnable
rating_collector-state-disable = cloudkittyclient.v1.collector.shell_cli:CliCollectorStateDisable
rating_storage-dataframe-list = cloudkittyclient.v1.storage.shell_cli:CliStorageDataframeList
rating_hashmap-service-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapServiceCreate
rating_hashmap-service-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapServiceList
rating_hashmap-service-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapServiceDelete
rating_hashmap-field-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapFieldCreate
rating_hashmap-field-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapFieldList
rating_hashmap-field-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapFieldDelete
rating_hashmap-mapping-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingCreate
rating_hashmap-mapping-update = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingUpdate
rating_hashmap-mapping-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingList
rating_hashmap-mapping-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapMappingDelete
rating_hashmap-group-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapGroupCreate
rating_hashmap-group-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapGroupList
rating_hashmap-group-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapGroupDelete
rating_hashmap-threshold-create = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdCreate
rating_hashmap-threshold-update = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdUpdate
rating_hashmap-threshold-list = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdList
rating_hashmap-threshold-delete = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdDelete
rating_hashmap-threshold-get = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdGet
rating_hashmap-threshold-group = cloudkittyclient.v1.rating.hashmap.shell_cli:CliHashmapThresholdGroup
rating_pyscripts-script-create = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptCreate
rating_pyscripts-script-list = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptList
rating_pyscripts-script-get = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptGet
rating_pyscripts-script-get-data = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptGetData
rating_pyscripts-script-delete = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptDelete
rating_pyscripts-script-update = cloudkittyclient.v1.rating.pyscripts.shell_cli:CliPyScriptUpdate
[build_sphinx]
source-dir = doc/source

View File

@@ -25,5 +25,5 @@ except ImportError:
pass
setuptools.setup(
setup_requires=['pbr'],
setup_requires=['pbr>=1.8'],
pbr=True)

View File

@@ -4,12 +4,11 @@
hacking<0.10,>=0.9.2
coverage>=3.6
discover
python-subunit>=0.0.18
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
oslosphinx<2.6.0,>=2.5.0 # Apache-2.0
oslotest<1.6.0,>=1.5.1 # Apache-2.0
testrepository>=0.0.18
testscenarios>=0.4
testtools!=1.2.0,>=0.9.36
coverage>=3.6 # 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
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

15
tox.ini
View File

@@ -1,17 +1,20 @@
[tox]
minversion = 1.6
envlist = py33,py34,py27,pypy,pep8
envlist = py34,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,10 +28,12 @@ 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
[hacking]
import_exceptions = cloudkittyclient.i18n