python-ceilometerclient-1.0.8/0000775000175300017540000000000012247122150017517 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ChangeLog0000664000175300017540000014523012247122147021304 0ustar jenkinsjenkins00000000000000commit 35e20cbfb27b7df86a88ca6f106ee4d690fb2679 Merge: 1257453 a587581 Author: Jenkins Date: Mon Dec 2 14:57:51 2013 +0000 Merge "Add HTTP proxy support to ceilometer client" commit 125745366085b0072ad99bddc58f978978c2f96d Author: Gordon Chung Date: Fri Nov 29 12:53:45 2013 -0500 sync with oslo-incubator - py3 compatibility fixes - Only pass non-None sortby to PrettyTable.get_string fix synced with commit-id: 0d2546b228f13a42355bc54e82256bf17a2eb478 Change-Id: Ic70eba1a10339ad5407541446cd37c9229836965 commit 7bf11bbef0bdb75c0b63c764c8c09b7000f59d35 Author: Andreas Jaeger Date: Mon Dec 2 09:46:53 2013 +0100 Change OpenStack Metering to OpenStack Telemetry With the project change, let's change the tool as well to use the new name. Change-Id: I90bcf55b54dc765abc377ced005cd7f2aab79c0e commit a587581e02d6f0c77cf1f38d1948d7fdb1424405 Author: Ilya Tyaptin Date: Fri Nov 22 13:53:16 2013 +0400 Add HTTP proxy support to ceilometer client There is a need for HTTP Proxy support in Ceilometer client It's used in the case when Ceilometer API is not directly available Proxy url is taken from environment variables http_proxy or https_proxy Implements: blueprint proxy-support Change-Id: I839d704a7fa16521292205166d7cbea56497842b commit 2950b30089ab7f38b70a9dd6b40cf0823d4ade3a Merge: 181d913 08b476d Author: Jenkins Date: Thu Nov 28 00:20:24 2013 +0000 Merge "Allow alarm-threshold-update to upate generic attributes" commit 181d913ca1415aa1ad28fb1e4ddbdd08b72a155f Merge: 640d6f4 e27ba76 Author: Jenkins Date: Wed Nov 27 20:45:21 2013 +0000 Merge "Allow specifying a timestamp when creating a sample" commit 08b476d2c9edeff7d025038467922f3d1cda6f19 Author: Eoghan Glynn Date: Fri Nov 22 11:18:35 2013 +0000 Allow alarm-threshold-update to upate generic attributes Fixes bug 1253989 Previously, generic (i.e. non-threshold-related) alarm attributes could not be updated with the alarm-threshold-update command. An attempt to do so failed semi-silently when a non-existent dict key was referenced. Now, all alarm attributes can be updated with this command. Change-Id: Iba3f21de879fb853575dcec1730de7873eab8afd commit 640d6f4ff4d38704ea5f43f3196910ffff4e6c51 Author: Lianhao Lu Date: Fri Nov 22 10:57:11 2013 +0800 Enable pep8 E711/E712/E721/H302 checking Change-Id: I229f1c15b46284fae94d4c786765baa18da0055f commit f80b29a6c3cd2326d0e721701dfa7449c07267f8 Author: Lianhao Lu Date: Fri Nov 22 10:48:07 2013 +0800 Enable pep8 E128 checking Change-Id: I31dc17f0faaae1c32e5106873fd13158376367f7 commit 28d1d3a87f5b581e610f3ceb790a70eb98a83ed8 Author: Lianhao Lu Date: Fri Nov 22 10:44:07 2013 +0800 Enable pep8 E121/E122/E123 checking Change-Id: I686f2b7895868e6ebaabe5d274e3efde2dd9c441 commit 936faeb33239106f49187af9424802cffd7a03dc Merge: dd1814c 7a5cbc1 Author: Jenkins Date: Fri Nov 22 02:54:00 2013 +0000 Merge "Avoid reset of repeat_actions attribute on alarm update" commit dd1814c094611045a3bca41d248c50543d9c3207 Merge: 9c645a2 2221249 Author: Jenkins Date: Fri Nov 22 01:18:23 2013 +0000 Merge "Ensure basic logging config is applied" commit e27ba7675800a3f3f30eaf088e8ff66377b0b43d Author: Nejc Saje Date: Thu Nov 21 18:05:51 2013 +0000 Allow specifying a timestamp when creating a sample Change-Id: Ic2947725edb5bf50ba9bfff14d4c0713e9aed3aa commit 7a5cbc14a5ff0679e2797569f9de8ef82068f510 Author: Eoghan Glynn Date: Wed Nov 20 09:59:02 2013 +0000 Avoid reset of repeat_actions attribute on alarm update Fixes bug 1253057 Previously, when an unrelated alarm attribute was updated via the CLI, the repeat_actions attribute was also set to False as an unwanted side-effect. Now, we only set this attribute in the update call if explicitly specified as a CLI arg. Otherwise the value provided in the PUT request body reflects the pre-existing value for this attribute. Change-Id: I94468ff649dd4367848c74e94a3feb08494bb223 commit 9c645a2f486bc8d3a5e3d921cd4a6fd4fc5ad8c9 Merge: 9ec8910 234aaea Author: Jenkins Date: Thu Nov 21 10:21:47 2013 +0000 Merge "Support building wheels (PEP-427)" commit 22212491c6a1acd9b9d9a17b02e5bd0a8f1f0d4f Author: Eoghan Glynn Date: Tue Nov 19 15:42:41 2013 +0000 Ensure basic logging config is applied Fixes bug 1252775 Previously the CLI emitted an obscure message related to logging config when a API call failure was encountered without the --debug flag being set: No handlers could be found for logger "ceilometerclient.common.http" Now we always set the basic logging config with serverity level of warning at least, so as to give the user a more friendly and less confusing error message. Change-Id: I5c6e70e97defca49ca62346f90190935646ff383 commit 234aaea53fef37909e6af2c12ad4e1aafac92370 Author: Sascha Peilicke Date: Tue Nov 19 10:31:47 2013 +0100 Support building wheels (PEP-427) With that, building and uploading wheels to PyPI is only one "python setup.py bdist_wheel" away. Change-Id: I93cee533132d59955b558da34c86ba5d310b2869 commit 9ec8910185d558aacddf6cac27b829df80c9821e Author: chenxiao Date: Wed Nov 13 11:29:42 2013 +0800 Add six to requirements.txt Some modules import six, we can see some .py files contain "import six", so add it to requirements.txt. Change-Id: I74cd752256e581ce38aac75af9a6154fc8c62e73 commit 8bf894f9d907592b11674e081d27dd3f3879d103 Author: OpenStack Jenkins Date: Mon Nov 11 18:49:49 2013 +0000 Updated from global requirements Change-Id: I9dac7eefa4dc427f509219671be990a7c946c44a commit 067b76d9a821c3acb60d93787396d25b729a7421 Author: ChenZheng Date: Tue Oct 29 17:02:28 2013 +0800 Ceilometer UnicodeEncodeError when update or show alarm fix Fix display bug in ceilometer alarm-update ceilometer alarm-show when encounter a alarm with not unicode attributes. Change-Id: I0af0f1d6b8393c7117eb5b56fba3d5e1f4935ff1 Closes-bug: #1245317 commit 03a567f9f44843ac146e058a3c9bfc933b375180 Author: Nejc Saje Date: Thu Oct 24 13:15:01 2013 +0200 Adds the 'limit' parameter to sample list command in V2 API Change-Id: I338590fcd75f39c3419e7e138023f6918f206ae2 Fixes: bug #1244172 commit 1b0f94f20580a55bea52ba468bdeeca3dd4552bd Author: fujioka yuuichi Date: Mon Oct 28 09:05:57 2013 +0900 Fix order of sample list Samples of "ceilometer sample-list" are sorted by Resource ID and Volume. Expected order is Timestamp. This pache fixes this problem. Change-Id: I338014e5868b2176a3afd549e13d0dd6118b3ac1 Closes-Bug: #1227495 commit 46f12ab64b992efa2e0c42e4cefb643abdf2cd74 Author: fujioka yuuichi Date: Mon Oct 28 08:55:39 2013 +0900 add cliutils from oslo-incubator Change-Id: I3122b62ebf87354340e971f7cb58f67045fbcfef Related-Bug: #1227495 commit f9be914ee4f1bec06f9dd604071b3d51b5d3d6e7 Author: fujioka yuuichi Date: Mon Oct 28 08:43:58 2013 +0900 update oslo libraries Change-Id: I7eb0adfbb6bc86157c3e3b969c00099dd8cfe315 Related-Bug: #1227495 commit 4b7a63362345952526006d5cf236afb62abd3430 Merge: 8e341cc e337ae3 Author: Jenkins Date: Fri Oct 25 08:47:45 2013 +0000 Merge "Fix cacert argument to HTTPS connection" commit 8e341cc620352ba07fb80d81a32ba01199e7d4cb Author: OpenStack Jenkins Date: Thu Oct 24 13:17:31 2013 +0000 Updated from global requirements Change-Id: Ie1ce367075e4b32b0bf5acc32aa7152cd3fbff0f commit e337ae3f639d506a40538d6540ca5cdc4da9db61 Author: Kieran Spear Date: Thu Oct 24 18:50:36 2013 +1100 Fix cacert argument to HTTPS connection This fixes a typo that broke the client for HTTPS URLs. Added a basic test to cover this case. Coverage is very low in common/http.py. Change-Id: Ic440f20f463c3b8558a7639f1015096a01496cf8 Closes-bug: 1244091 commit 1d597e12e7aa0a653d2ffae0a96788ebfbdab59a Author: OpenStack Jenkins Date: Wed Oct 23 14:37:23 2013 +0000 Updated from global requirements Change-Id: I59b276d83dd40d82e5713d9e8d1991bd0c41fa56 commit eea025dadbc777b6636f209cbf449a6b5f276b00 Merge: a4e89ba 9c4d491 Author: Jenkins Date: Tue Oct 22 20:25:26 2013 +0000 Merge "Replace mox3 with mock in unit test" commit 9c4d49189fe1150fad8420b8191ae7a240c945af Author: Lianhao Lu Date: Mon Oct 21 10:50:19 2013 +0800 Replace mox3 with mock in unit test Compared to mox3, mock is tested much more in various Python 3 versions. Change-Id: I9e83aa622257c36ff47c79f6b491c1b074d2245d commit a4e89bac8dfd1a4b9d61b9f02235243550fb7d8e Author: OpenStack Jenkins Date: Sun Oct 20 00:00:55 2013 +0000 Updated from global requirements Change-Id: I9c1b02904b83c537ea4f51a54fd7c32b18244c7d commit 73af6d48b1eaa8a02f217c94715bab55c594967f Merge: 847de02 a9f75ec Author: OpenStack Jenkins Date: Fri Oct 18 16:55:56 2013 +0000 Merge "Updated from global requirements" commit 847de02766b2a2fa5d6caa5fcf6c45e84d9b30c6 Author: Dirk Mueller Date: Fri Oct 18 11:29:21 2013 +0200 Fix missed Pep8 error with 1.4.6 Missed E126 continuation line over-indented for hanging indent Change-Id: I17ce2a984367fdb078d59bf6603610a01ad168f4 commit a9f75eca3bfd659500993108db220f8e5377acad Author: OpenStack Jenkins Date: Thu Oct 17 22:23:33 2013 +0000 Updated from global requirements Change-Id: Id45c2a5fee6a57e70749830406310cbe1a9cde5a commit f732428e13de0759f592c5f2298c82d199bb3664 Author: Kui Shi Date: Tue Oct 15 05:10:22 2013 +0800 Replace mox with mox3 Mox3 is an unofficial port of the Google mox framework (http://code.google.com/p/pymox/) to Python 3. https://pypi.python.org/pypi/mox3 Align with global-requirement and update the importing clause. There is still py33 issue induced by keystoneclient. https://review.openstack.org/#/c/51788/ When keystoneclient is upgraded in requirements.txt, all py33 issues should be fixed. Change-Id: If0d00c088779b4a71e606cc87b37b5b1f9701081 Implement: blueprint py33-support commit c58320a4eaa6743e59b31ec90f9f4f38cb9e3b3c Author: Kui Shi Date: Tue Oct 15 00:35:58 2013 +0800 align the order of parameters for urlencode() In Python 3.3, hash randomization is enabled by default. It causes the iteration order of dicts and sets to be unpredictable and differ across Python runs. In the test case, the fixed expecting string will not match the test result, it is relying on the dict order. This change transforms the input dict to a sequence of two-element list, with fixed order, and update the related expecitng string / fixture in test cases. Partial Implement: blueprint py33-support Change-Id: I6dccde9e584be8335a6375f5fbad5c5cbd7b9b6d commit 253569d0700839e277809f3f0ee8c662dedfdbea Author: Kui Shi Date: Mon Oct 14 17:56:11 2013 +0800 replace basetring/xrange basetring --> six.string_types xrange --> six.moves.xrange Partial Implement: blueprint py33-support Change-Id: Iedc2cd66ec65c0e4685e56a3bfbf8e9d38fd0072 commit d0a3dc68ac8d5caa6e33434d78f46b80efdcb7b1 Author: Kui Shi Date: Mon Oct 14 17:48:13 2013 +0800 Replace unicode() with six.u() six.u(text) A fake unicode literal. text should always be a normal string literal. In Python 2, u() returns unicode, and in Python 3, a string. Partial Implement: blueprint py33-support Change-Id: I02849e540c1fbb1f8b02bcbfa4d9be4f60279926 commit 309cacd9c001b176f01268a8db7d16d9f8a44d8e Author: Kui Shi Date: Mon Oct 14 17:06:21 2013 +0800 replace dict.keys() with list(dict) Partial Implement: blueprint py33-support Change-Id: I774e5a25d25ef19ffc217910987f2c9b68d13cd3 commit d70bb3e4e7ecb1464fef7680ed96941b40d0bbb9 Author: Kui Shi Date: Mon Oct 14 17:03:29 2013 +0800 Import urlutils to substitute urllib Use the openstack common urlutils. Partial Implement: blueprint py33-support Change-Id: I45f21d7316fe87952e841004d4c25999138a842b commit 38e1ee4632e955e3b8d3643b9d842675c6d15437 Author: Kui Shi Date: Mon Oct 14 16:57:24 2013 +0800 Use six.iteritems() for dict Fix the error: AttributeError: 'dict' object has no attribute 'iteritems' Partial Implement: blueprint py33-support Change-Id: I5dff17d1df01d6583f882f818434804d65726c51 commit 39328adaff910955407dadaad810cc2d6889f335 Author: Kui Shi Date: Mon Oct 14 16:56:20 2013 +0800 Translate print statement to print function Use "from __future__ import print_function" docs.python.org/3/howto/pyporting.html#from-future-import-print-function Partial Implement: blueprint py33-support Change-Id: I92fc07257851795a9972c8273d3278c8679d6628 commit c6ca94982d30bdb7d8227150578271e9492ad36e Author: Kui Shi Date: Mon Oct 14 16:53:51 2013 +0800 Fix module importing issues for Python 3 Copy py3kcompat from oslo for urlparse Substitute StringIO / httplib with six Partial Implement: blueprint py33-support Change-Id: Ic8da2ca53b4217ef13f15be094437f1f4e643001 commit eaae72edc92940b0e46b7d7b955e2bfbe94542c5 Author: Kui Shi Date: Mon Oct 14 16:52:23 2013 +0800 Import six.StringIO six.StringIO This is an fake file object for textual data. It is an alias for StringIO.StringIO in Python 2 and io.StringIO in Python 3. Partial Implement: blueprint py33-support Change-Id: I76c05041565614241eea7b7595e4503c88211ee8 commit c3283ec1e861f29a53c59c1a09f2a1bc21a713e4 Author: Eoghan Glynn Date: Wed Oct 2 16:18:06 2013 +0000 Add support for new alarm-history command Expose the new alarm history API via the CLI. Change-Id: I5757d309dd7143dfab83985fb51ee6d320478e3b commit 50ecc8f63b9f2f9a482f2211ad0499f2be7c6f5a Merge: 146de67 544e621 Author: Jenkins Date: Thu Oct 3 00:34:12 2013 +0000 Merge "Added support to --os-cacert" commit 146de671496a72ccdc20a3da4beec901a890f9b3 Merge: db2c858 a99a8c6 Author: OpenStack Jenkins Date: Thu Oct 3 00:34:10 2013 +0000 Merge "Updated from global requirements" commit db2c858585aea4426aad9cf6c870b338e1a921ea Merge: 0a6a6b4 fc201b3 Author: Jenkins Date: Thu Oct 3 00:34:09 2013 +0000 Merge "Use standard CLI object-verb ordering for alarm-{g|s}set-state" commit 0a6a6b4d099f627e71ca22dd9d376da2f54d4f90 Merge: 66ef360 7c8a676 Author: Jenkins Date: Thu Oct 3 00:34:08 2013 +0000 Merge "Fix shell.do_alarm_get_state to get as opposed to set" commit fc201b3805dda16fad66d7c0d0e47b1e32612874 Author: Eoghan Glynn Date: Wed Oct 2 09:59:47 2013 +0000 Use standard CLI object-verb ordering for alarm-{g|s}set-state The openstack CLIs all use an object-followed-by-verb ordering convention when construct command names. Follow a similar convention for commands to get and set alarm state. Change-Id: I34e0f450019556c80476df782d4c86dca08bdc9d commit 7c8a676b43e9f82276041afbae15d739dc2c1ab4 Author: Eoghan Glynn Date: Wed Oct 2 09:30:27 2013 +0000 Fix shell.do_alarm_get_state to get as opposed to set Otherwise the CLI fails when attempting to set a state arg that doesn't exist. (Simple copy'n'paste error in the original code). Change-Id: Iab117177805449ddec9d03656a95a0cbbbbd58bb commit a99a8c628faa33768961952b59b8175c1a108780 Author: OpenStack Jenkins Date: Tue Oct 1 16:14:33 2013 +0000 Updated from global requirements Change-Id: I71d6d8aad148b240f8ab1325d3c3ff2996ebf642 commit 66ef360c1480899bcdf6ad7af8f2d581b532c5e6 Author: Mehdi Abaakouk Date: Thu Sep 26 09:50:37 2013 +0200 Allow to update an alarm partially The patch allow to only modify a part of an alarm instead of force to set the full alarm description. This permit to an application that have been written code around alarm with ceilometerclient 1.0.4 and ceilometer pre havana-3. To update alarm without any change with this and ceilometer >= havana-3 (ie: heat). Fixes bug #1231303 Change-Id: I20250131d05d20bfadbca450dfe6b8237f4b7183 commit 544e6217e58a9eaee67e98f27cb9385ee412ba79 Author: Stefano Zilli Date: Fri Sep 13 09:08:19 2013 +0200 Added support to --os-cacert Closes-Bug: #1224343 Change-Id: Ib0549d4496c47900c81cc970b99bcff25cad0040 commit b961738765976e77711d909eec1ecc402fa8a484 Author: Cyril Roelandt Date: Wed Sep 25 12:49:58 2013 +0000 Help messages: specify which options are required Closes-Bug: #1223283 Change-Id: I080fa73bd45a1f9f442dbcdfa65fdc24e30521da commit ce01f564651bee07f91be8dc7f6dfca561216625 Author: Mehdi Abaakouk Date: Tue Sep 24 11:09:05 2013 +0200 Improve the CM shell client alarm visualisation This change aim to get a better alarm representation in shell. In alarm-list: * it add a short sentence to describe the alarm rule * it remove project_id/user_id because it always show the same id for all alarm for end-user In alarm-show, it show alarm rule attributes as alarm properties instead of a unparsable json in the rule property. example of short sentence for column 'Alarm condition': * combinated states (AND) of 8babd6a2-c457-42d0-9eb5-cdfb3cb50203, d0e11a94-8f59-48a9-8f6d-b0d68aaac8d0 * cpu_util >= 50.0 during 1 x 60s Change-Id: If4df2dc08f9f4cb7796fd98308c7d62e311d1138 commit 2610d6c144e0d81cd94a6f18eb899443ce7d64fb Merge: 05e4355 5616563 Author: Jenkins Date: Mon Sep 23 19:21:10 2013 +0000 Merge "Pass region_name argument to keystone client" commit 05e435542b5f42899accad1bbe232139e79cfaff Merge: 3499631 fb1ff39 Author: Jenkins Date: Mon Sep 23 17:02:55 2013 +0000 Merge "Replace OpenStack LLC with OpenStack Foundation" commit 3499631b1a32b2125bd89de2e7ce45d9dd19a7c4 Author: Mehdi Abaakouk Date: Sun Sep 15 22:26:04 2013 +0200 Use the new alarm format The change sync the client API with the new alarm type and keep compatibility with the previous format of the alarm. It allows to create the new type of alarm: combination alarm. Implements blueprint alarming-logical-combination Change-Id: Ie62265023cadc20cf36a0d0b3775f4d984e324cf commit fb1ff39d53826b94c3c04645d972a0a96dc8225a Author: ZhiQiang Fan Date: Fri Sep 20 03:14:11 2013 +0800 Replace OpenStack LLC with OpenStack Foundation Change-Id: Ia007da282714d8ee7337fd3b51f6dcc3b64d7ec3 Fixes-Bug: #1214176 commit 56165637f84681f18f81d1e6afbcd8957c097f22 Author: Bartosz Górski Date: Tue Sep 17 18:48:41 2013 -0700 Pass region_name argument to keystone client Change-Id: If82bd7fe5680faef6e56abb6f76d203b7296beb5 Closes-Bug: #1228063 commit 052f210f56a4b13f19b246f10f18bf071e48e161 Author: Bartosz Górski Date: Thu Sep 12 16:34:03 2013 -0700 Adding missing 'statistic' field to alarm-show Closes-Bug: #1225049 Adding missing 'statistic field to printed table with alarm details Change-Id: I69a743bfa340d4e1193f58c84195db398cbdf02b commit fd9eb2e2375953ef136329ab8a6bf40345097b58 Author: Lianhao Lu Date: Fri Sep 13 11:26:36 2013 +0800 Use global openstack requirements Update global openstack requirements. Change-Id: Idf561ce746c86c775855d00cb3436feaf9f65cba commit b58d4ef76ce1ae85dfe89d35e3144641b92c8e67 Merge: c3f2459 6fdd645 Author: Jenkins Date: Wed Sep 11 22:54:18 2013 +0000 Merge "alarm: rename counter_name to meter_name" commit c3f2459043a8a9ff9a9ed6897228a291a86f43c6 Author: Cyril Roelandt Date: Mon Sep 9 15:31:17 2013 +0000 Fix a typo in "sample-create" help message The word "alarm" is used twice instead of "sample". Change-Id: I9a3ce514f3a3766add14ed1b352e31bf003f1cee commit 3569e6586167f18682acc5843e4d5143355ac2ba Author: Alex Gaynor Date: Thu Sep 5 09:57:46 2013 -0700 Added support for running the tests under PyPy with tox This is a precursor to having them run under check and gate. Change-Id: I42ed34362bb840fe3a6d1d22e28beba8963fdb85 commit 6fdd6454b3044288d334489430b515035c65fa53 Author: Julien Danjou Date: Wed Sep 4 17:45:21 2013 +0200 alarm: rename counter_name to meter_name This is a follow-up wrt the recent fix that handled in Ceilometer for the alarming API. Change-Id: Ide73a2e703bf31652b819c6d5170891c73b77ec9 commit 9e330726ce6409088f7cbf50f4c76622c2f1732f Merge: 9e8a989 e8bf783 Author: Jenkins Date: Fri Aug 9 12:46:45 2013 +0000 Merge "Add support for new alarm repeat_actions attribute" commit e8bf783d6aec35a694db491c8df580db54fa4de0 Author: Eoghan Glynn Date: Thu Aug 8 15:01:21 2013 +0000 Add support for new alarm repeat_actions attribute Continuous notification is generally required by Heat. Change-Id: Ib396f47c68298f883e305c755d821c2679a56a5c commit 9e8a989b2a9e4e7780cc476ac754465c7e8b5a0e Author: Monty Taylor Date: Tue Aug 6 13:30:55 2013 -0300 Updated from global requirements Change-Id: Ibf423f14a5c37aa298b2115bfd4936f660c6f530 commit 091937b9a25aac666dba45ca5c8cc725af765a22 Merge: 423fa26 e4348aa Author: Jenkins Date: Mon Aug 5 13:29:07 2013 +0000 Merge "Handle case where os_auth_token is set to ''" commit 423fa26342ebfe400b2decd81e7f7dfc5053d718 Merge: 194f145 dd4b654 Author: Jenkins Date: Mon Aug 5 13:26:19 2013 +0000 Merge "Fix typo in help text." commit 194f145b3a823a33b0d0bcaea4853d7829c07ed5 Merge: 1772adf 3a780a6 Author: Jenkins Date: Mon Aug 5 13:22:17 2013 +0000 Merge "Enhance ceilometer statistics command with --period" commit e4348aa3d28f26b354326e056dc26c293963ea7f Author: Eoghan Glynn Date: Fri Aug 2 17:07:28 2013 +0100 Handle case where os_auth_token is set to '' Properly handle case (as in CLI) where os_auth_token is set, but to an empty string. Change-Id: I7e96bf22e2a91e0e3ec783b406fd05e4138860c8 commit 1772adf0cce19de9ef151c24c482122f9d18507e Author: Eoghan Glynn Date: Thu Aug 1 17:07:09 2013 +0000 Ensure keystoneclient.auth_token is re-evaluated Fixes bug 1207441 Force the re-evaluation of the keystoneclient.auth_token property so as to avoid missing out on the in-built refresh logic when expiry is imminent. Change-Id: I8319e9e9a71e33e089fbe2fbc812d2b6c15da927 commit dd4b654e5f6d6d739d69226f1898b1d31db07b37 Author: Eoghan Glynn Date: Thu Aug 1 17:41:38 2013 +0000 Fix typo in help text. Minor misspelling in alarm-{create|update} help text. Change-Id: I1ddbaec42dda175a17d32229029c08d46e64151f commit 3a780a6159e53a25e722ec083ab45d915279958a Author: Guangyu Suo Date: Fri Jul 19 10:34:17 2013 +0800 Enhance ceilometer statistics command with --period Currently, if don't specify query parameter in ceilometer statistics command, ceilometer statistics with --period will not work. This change is trying to fix this issue. Change-Id: I8723bad11d5c452c2834e33df9bb01ebdc6ce9ce Fixs: bug #1202658 commit 04cc271da208069e921da252554e839de46442ed Author: Mehdi Abaakouk Date: Tue Jul 16 18:43:35 2013 +0200 Allow to set matching_metadata with the cli This change allows to set the matching_metadata of a alarm like this: ceilometer alarm-create --matching-metadata 'key=value' \ --matching-metadata 'key2=value2' --name 'alarm' ... Fixes bug #1201877 Change-Id: I22bf261b0a9580a06ae107ed45d082171f21fcc4 commit 3010ebcc75ea271c46351ef68d00482f0fbacb85 Author: Angus Salkeld Date: Thu Jul 18 09:41:16 2013 +1000 Add support for creating samples Change-Id: Ib33a5fd162d760efa23a2fc496c1d11d79484491 commit efd67de3fae0be1893c5dbf60e3a29d40e5bdb0d Author: Dirk Mueller Date: Sun Jul 14 21:12:41 2013 +0200 Rename README.md to README.rst It actually is a reStructuredText file and README.rst seems to be more common accross all OpenStack projects. Change-Id: I2f5ef97315ec906fc7f0089536b038b977f8f68c commit 3498d5146dbc5b6021c72b0a802af266b213f646 Author: Gordon Chung Date: Fri Jul 12 14:14:03 2013 -0400 Relax OpenStack upper capping of client versions uncap upper keystoneclient version Change-Id: Id1017f6073be8b7b95ca7ff0c68e09f61b26bb82 Fixes: bug#1200214 commit 5aff59d7f4813daf6e076cb955811a4443c50421 Merge: 12ffe2f 2687315 Author: Jenkins Date: Thu Jul 11 18:03:58 2013 +0000 Merge "Add matching_metadata to the allowed attributes" commit 12ffe2f211cbcb71ad66732f25d13a5e8bfc54f6 Author: Dirk Mueller Date: Wed Jul 10 08:54:59 2013 +0200 Allow Keystoneclient 0.3.x Sync requirements with openstack/requirements file. Change-Id: I344aa47f248071cea8b6cbe31450528cd343bca2 commit 075148bea58a96abd47e428af45035d9df2af801 Author: Monty Taylor Date: Fri Jul 5 22:34:39 2013 -0400 Sync install_venv_common from oslo Change-Id: I74ab14534d80b33f5b9eb1b2200facf82b6d9504 commit 2687315dc8c7dc8936f26c974f731cb3333e45f7 Author: Angus Salkeld Date: Fri Jul 5 12:18:24 2013 +1000 Add matching_metadata to the allowed attributes Change-Id: Icbd68061a43d18dd27baa7ff68f9381a160cd545 commit f1aaf95f0605eeed422a5579710ea93737b1607b Merge: 6f81aa1 f09dfd0 Author: Jenkins Date: Fri Jul 5 01:50:10 2013 +0000 Merge "requirements.txt is not configured properly" commit 6f81aa1a99b4e073855270120d18643ee0a7133e Author: Monty Taylor Date: Sun Jun 30 22:40:55 2013 -0700 Move tests to ceilometerclient. tests.* implies an incorrect global package name. Additionally, consuming code can benefit from being able to choose to consume test code and fixtures. Change-Id: I7ba2b3ba1c2a99410b54fc40b48dfe2fc53eb79a commit 52e3f6ffe188a82a67fdd573bb03ab1d9b0b95b4 Author: Eoghan Glynn Date: Mon Jun 24 18:36:13 2013 +0000 Avoid unnecessary GET of existing alarm for update The existing alarm representation is retrieved prior to update, which is uneccessary as the API merges the new and existing state in any case. This change saves the threshold eval logic one unnecessary API call per state transition. Change-Id: If21c53d6f43164315e1e70e62b0c70520fb5a45c commit ff77aeea56872be38914c851926af1fb504dba5f Author: Eoghan Glynn Date: Fri Jun 21 11:33:33 2013 +0000 Make authenticated client easier to consume. Encapsulate keystone logic in an convenience method. Change-Id: I0a048e02f57d657b8b414a76d759ceb9e0e025df commit aa5330b48cf3165a775e13fc2cc3117a05b31896 Author: Eoghan Glynn Date: Thu Jun 20 20:23:54 2013 +0000 Add support for specifying statistics period. Allow the period to be be specified as an addition parameter when retrieving statistics. Change-Id: Ied3add404fb8b20c46a4de00f07273a6d6c4ffce commit f09dfd05b308b665702e974c2a2dc4b5b0f66c4c Author: Gordon Chung Date: Tue Jun 18 17:33:44 2013 -0400 requirements.txt is not configured properly the leading comment seems to cause an issue with pbr. also adding in d2to1 and pbr versions to requirements. Change-Id: I334d928b8a575522e3879f07d578be39890712b5 Fixes: bug 1192351 commit 819e4580ab17760b8f546d1c8955574571589959 Merge: b5622b2 3efea51 Author: Jenkins Date: Fri Jun 14 01:35:31 2013 +0000 Merge "Use Python 3.x compatible except construct" commit b5622b25a35291ee988abcc1fb2d1516c59cd603 Author: Brian Waldon Date: Thu Jun 13 11:35:00 2013 -0700 Drop unnecessary arg when instantiating HTTP class Fixes bug 1190111 Change-Id: I45e5e5d5af97a4f75833d9ca000b1b555bdda752 commit 6512994a97f3191a7030286b548f21b4b3866cab Author: Monty Taylor Date: Tue Jun 11 11:31:42 2013 -0700 Remove explicit distribute depend. Causes issues with the recent re-merge with setuptools. Advice from upstream is to stop doing explicit depends. Change-Id: Ibcc84afe046cb3e3464423fdb81b7dc5edf9d06b commit d00756acfd95785390132221edbdebe5f8c1599f Author: Dirk Mueller Date: Sun Jun 9 11:34:46 2013 +0200 Start using pyflakes Enable Pyflakes tests. Fix those warnings that occur. Also explicitely list the pep8 warnings that currently fail. Change-Id: Icfd7bf23007a99187cfee66cbd630e0e885bf7d3 commit 3efea51828fed509b31b0ab25ddfe77f258b202e Author: Dirk Mueller Date: Fri Jun 7 15:55:28 2013 +0200 Use Python 3.x compatible except construct Per (proposed) Hacking check, use except x as y: instead of x,y:, which works with any Python version >= 2.6 Change-Id: I6e52cedec5c694bccae10541b1964a8547d4ffd1 commit 4946780d5f3c29d753bd9ba36c89257b0a229703 Author: Eoghan Glynn Date: Wed May 22 16:33:23 2013 +0100 Add client support for creating new alarms. Change-Id: I4e3be2e480803eeaf4ec11079e69e7e6afd5e0d1 commit 8cc09381cb82b197dcdda89cdacbe42c002ea470 Author: Eoghan Glynn Date: Tue May 21 21:25:47 2013 +0000 Add client support for updating alarms. Change-Id: I2a368f536ec440387d32a8076a86d143b94d7c90 commit f9af1f3d78240f3ac957f49fad7c877245dc00cb Merge: c272dc7 a82e531 Author: Jenkins Date: Thu May 23 13:05:18 2013 +0000 Merge "Fix install_venv.py requirements file" commit a82e53121ad27e593d3e9edbe6f1bc0d4aeb366d Author: Angus Salkeld Date: Thu May 23 09:53:39 2013 +1000 Fix install_venv.py requirements file I missed this in https://review.openstack.org/#/c/29876/ (but Gordon noticed) Change-Id: Ie00cae414ae44bbd258910a2628174487c9756e3 commit c272dc73673b1a69aab5dd847186d552783253a4 Author: Angus Salkeld Date: Thu May 23 10:13:01 2013 +1000 Enable more pep8 checks Change-Id: Ib8509391d4c4521e18f0e980003c5e94c7ba2f54 commit bdac572db84e272f3b006023e83b13b77d849d25 Author: Monty Taylor Date: Sat May 18 09:43:36 2013 -0700 Migrate to pbr. Fixes bug 1179007. Change-Id: Ie921a710d9460196ed8023ad63f05dec998b60f0 commit a1a21bd7fd09f31789ac773f37004ba42895894b Merge: 399e422 10e2473 Author: Jenkins Date: Wed May 22 23:36:24 2013 +0000 Merge "Fix pep H306 (import order)" commit 399e422ef83de36c6554e48500c47079f374c3a8 Merge: f987463 ae142c0 Author: Jenkins Date: Wed May 22 23:36:06 2013 +0000 Merge "Fix pep H402 and H401 errors" commit f987463cae866439d7bd1a09190c2a4afa585695 Author: Angus Salkeld Date: Tue May 21 20:29:19 2013 +1000 Rename tools/pip-requires to requirements.txt (and tools/test-requires to test-requirements.txt). Requested by infra: bug 1179008 Change-Id: I7a6375622d095a9eda232ed82d167fc3a198ed06 commit 42e97cf60902f8c39a4d6fa92578a42c082257de Merge: b324ccc d42fbeb Author: Jenkins Date: Mon May 20 04:29:04 2013 +0000 Merge "Add support for deleting alarms." commit 10e247319989da9a70e1a4f37a82dcbd13877365 Author: Angus Salkeld Date: Mon May 20 13:00:29 2013 +1000 Fix pep H306 (import order) Also remove some unused imports This is an effort to get the pep ignores to be closer to nova. Change-Id: I35612de45084fac0ae1c96dad84bb23cfade0e4f commit ae142c0ffe0c10348ef3e6ff38bbffae5cdd6f59 Author: Angus Salkeld Date: Mon May 20 12:52:58 2013 +1000 Fix pep H402 and H401 errors This is an effort to get the pep ignores to be closer to nova. Change-Id: I451df579bbead00a8ff2c301c1a84e7c0a896002 commit b324ccc37bc6c0dfded22ba5b4eccc96642791ba Author: Monty Taylor Date: Sat May 18 09:40:15 2013 -0700 Migrate to flake8. Fixes bug 1172444. Change-Id: Iba46d40a0e3ae0385ce51181f47295a2b520110d commit d42fbebaa817c8b4f1d409b951acb17a72bcfd55 Author: Eoghan Glynn Date: Fri May 17 15:49:35 2013 +0000 Add support for deleting alarms. Change-Id: Ifbf682bc05ce47d04be1940238cd52d0e895588f commit f175a98e9fda8a68b592093040c2f7b8bd584d9b Merge: 27890b2 173cfa5 Author: Jenkins Date: Fri May 17 01:22:12 2013 +0000 Merge "Add support for getting individual alarms." commit 27890b2eb81cfd718b135fa67edf9ba19f4e45e5 Merge: 8bfc457 382e62b Author: Jenkins Date: Fri May 17 01:22:11 2013 +0000 Merge "Add support for listing alarms." commit 173cfa50fd0106cb0ff971bbb6022a52d8f3dfad Author: Eoghan Glynn Date: Wed May 15 16:10:32 2013 +0000 Add support for getting individual alarms. Include the *_actions attributes in the CLI output. Change-Id: I19e2e7b9c60b56c0c84687694365ef3614dd0e44 commit 382e62be8cc4cdf15f8fc71437a69e17c6833cf7 Author: Eoghan Glynn Date: Tue May 14 18:57:33 2013 +0000 Add support for listing alarms. Just getting the ball rolling on client support for the new alarms API. Change-Id: I66380a7bfd650357dd4ec34cbe64807ac6921163 commit 8bfc457b31054b0c3650121a1520958f36b17b43 Author: Eoghan Glynn Date: Wed May 15 15:21:24 2013 +0000 Fix mis-scoped Client class breaking CLI Fixes bug 1180439 The change in importation in this commit: https://github.com/openstack/python-ceilometerclient/commit/453f4d2a caused the ceilometer CLI to fail in all cases with: 'module' object has no attribute 'Client' due to the mis-scoping of ceilometerclient.client.Client as ceilometerclient.Client. Change-Id: I5ec30bf74233f758519810629763ccb70bb7af87 commit 7091db40a9486ebdc8a19699826b171465e40384 Author: Angus Salkeld Date: Fri May 10 21:39:34 2013 +1000 Use testr to run tests This is taken largely from python-novaclient and Heat Change-Id: I8f2eb9185ac073e9ee97bb9c757a4c3c2f8d6d6b commit 44885c83b1548285a80fbff1e3617391c3e0b482 Merge: 37b16a1 99aa8d1 Author: Jenkins Date: Fri May 10 06:22:27 2013 +0000 Merge "Add install_venv_common from oslo" commit 37b16a1032e935ba83484f4c9f6a3b96ea6517a9 Merge: 5b3a92e 453f4d2 Author: Jenkins Date: Fri May 10 03:42:32 2013 +0000 Merge "client does not show version" commit 99aa8d1245a867d3683caee8a8bbc0c8db2b5903 Author: Angus Salkeld Date: Wed May 8 19:59:08 2013 +1000 Add install_venv_common from oslo Change-Id: I8f6e07abd733b1eea6c2d3e397e8e34aef531e4f commit 5b3a92e7ae0b523963654c6f7adb20800d00f708 Author: Angus Salkeld Date: Wed May 8 19:12:14 2013 +1000 Update oslo code and split the module lines Change-Id: I2d9f4b9ac585d91c2d7651db96c9440ff683e0ef commit c557722c609839e3b06fee682288ac044b445d8a Author: Angus Salkeld Date: Wed May 8 17:34:58 2013 +1000 Use the utils.BaseTestCase for all tests Change-Id: I411fa9a60122cef6f9d5f723497a2d9427f5624e commit d1f9a1fa13da33a3b54bf6dba61d5d83a18ab9f6 Author: Angus Salkeld Date: Wed May 8 17:07:52 2013 +1000 Fix pep8 errors in test code Change-Id: Ib38ff406a9edae8a8eea97744f5693c00d76a483 commit 2f6e53a12975dc4e15ba8b85e4df409868ec4df9 Author: Angus Salkeld Date: Wed May 8 16:44:32 2013 +1000 Remove unused test code in test_util.py This doesn't seem to do anything Change-Id: Ieba6b5f7229680146f9b3f2ae2f3f2d2b1354376 commit 765c9ad95c26e0fb53ae98316d592847e3de5a74 Author: Angus Salkeld Date: Wed May 8 16:23:35 2013 +1000 Fix manifest (README.rst -> README.md) and remove HACKING (we don't have one) Change-Id: I12b219f3a2f5d9da580f3a827d40b351e57f6b35 commit 453f4d2a4b7c37fee5f3deee16fb0f8938b6f8d1 Author: Gordon Chung Date: Tue May 7 18:05:12 2013 -0400 client does not show version add support for --version update openstack.common to bring in latest openstack.common.version Change-Id: Ic1e66ea1f1aa65039b79902b893d1b2474ecb63e Fixes:bug1177573 commit f375d940e9c7cf28375d5b21c02aea38533e7186 Merge: 6b76e58 0a07682 Author: Jenkins Date: Thu May 2 15:43:45 2013 +0000 Merge "Sync requirements with openstack-common/requirements" commit 0a076826c0288e3d0dec1177eff12164e251a201 Author: Dirk Mueller Date: Thu Apr 25 17:47:00 2013 +0200 Sync requirements with openstack-common/requirements Allow prettytable 0.7.x specifically. Add testcase. Remove setuptools_git from test-requires, already part of setup_require Change-Id: If0d114cc6ec0f84ea75d798adb5ebc424605b22e commit 6b76e58d6f1f0342b1f3937941a87f83ea799acb Author: Eric Pendergrass Date: Wed May 1 21:07:43 2013 +0000 Fix for Bug #1167521, ceilometer client crashes with missing content type response pep8 compliance Change-Id: I7669c103fe3336aa82b289e04560192b44a186da commit 888d63aaa00641dcb9c38f6c6865fc0e6933be4f Author: Dirk Mueller Date: Mon Apr 29 11:19:13 2013 +0200 Restore compatibility with PrettyTable < 0.7.2 PrettyTable 0.7.2 changed the default to print also tables when the result set is empty. Revert to previous default. Change-Id: I22ab7522227ef70929d31dd2c4aaff93c4c518c2 commit 2bcc72c4855c7256b96ecc23fa45b3c585fd3a39 Author: Angus Salkeld Date: Wed Apr 10 10:37:31 2013 +1000 Change the default API version used by the cli to v2 Since the v1 API is somewhat deprecated lets default to v2. Change-Id: Iece5428b2a47e63995e26f58347d4f7c8f8cb238 commit f3d044291fe75c2b6ce7a4f8e9a6885d4ffd38c0 Author: Lianhao Lu Date: Fri Mar 29 18:40:44 2013 +0800 v2 API: added resource-show. Added resource-show command for v2 API. blueprint more-cli-cmd. Change-Id: I9e0dcff63b2ac6650094d47a947a2deaaea2ba4d commit e30d70074f278f58457d9d2b278815b5ccb39a63 Author: Angus Salkeld Date: Wed Mar 27 18:00:11 2013 +1100 Make it possible to pass -q "metadata.field=value" Currently the regex doesn't account for the "." Change-Id: Ia01bb3e2a79e54fd01b0b6fa5437827694d4fb96 Signed-off-by: Angus Salkeld commit 845a106c0fe44afa32d47befc9d2a67cfe223d3e Merge: e6e357f 3b75a7a Author: Jenkins Date: Tue Mar 19 15:15:33 2013 +0000 Merge "Corrected help strings." commit e6e357f1a00fab9af2c5c803d7ba37773008dbf2 Merge: d84fd99 c813dbd Author: Jenkins Date: Tue Mar 19 15:15:14 2013 +0000 Merge "Don't log unneccessarly on each http request" commit 3b75a7afc608b83b9c77a375eef0b6ef89e24f7d Author: Lianhao Lu Date: Thu Mar 14 17:05:45 2013 +0800 Corrected help strings. Corrected the wrongly mentioning of glance in help strings. Change-Id: Ic8613e8670f96be38d1a63777edc6c113a1894d4 commit d84fd99be8ea223dfadf2a4331d6ad4c9b2f58a3 Merge: 152f764 2b5fcd6 Author: Jenkins Date: Thu Mar 14 08:26:36 2013 +0000 Merge "Catch KeyError exception as early as possible when there is no matching data on the server." commit c813dbd800a475ffb525b376c4497b81a95f1e3e Author: Angus Salkeld Date: Wed Mar 13 13:54:46 2013 +1100 Don't log unneccessarly on each http request Note: logging still works with -d, this just quiets it down for the normal case. bug 1154408 Signed-off-by: Angus Salkeld Change-Id: I0adccc8b2c83d917d2742e86fdb4b6363b398ca6 commit 2b5fcd60c4b5ca37e347657879a28af011729dbd Author: Dan Florea Date: Fri Mar 8 14:15:54 2013 -0800 Catch KeyError exception as early as possible when there is no matching data on the server. When the server does not have any data matching the requested response_key, it can still return successfully. A subsequent lookup in the returned data throws a KeyError exception. The simple fix for this is to catch the KeyError exception and return an empty list. An empty list, rather than None, is required because the calling code expects an iterable. The exception is caught as early as possible after the server returns from the GET request. The end result is that the CLI user sees an empty result when the requested data doesn't exist on the server. Prior to this fix the keyError exception was propagated all the way to the user, causing a confusing message to be printed. Also added associated unit test. Fixes bug #1111972 Change-Id: I88ba658f8be7e7edf255ef9f7d83ba87f36f4efc commit 152f764042bbbb1ad4b1afb6afbcd0283d8283cb Author: yolanda.robla Date: Tue Mar 5 15:06:39 2013 +0100 Properly removing start and ending slashes Fixes: bug #1146690 Change-Id: I4258948f749128edeafb6ef3e64bda4ff35dbb58 Signed-off-by: Doug Hellmann commit e001aaae1c5a88fc82ce7e81511ce0888614894d Author: Angus Salkeld Date: Thu Mar 7 16:00:44 2013 +1100 Remove warlock from pip-requires as it is not used Change-Id: I1f64a774d3888b6c4fc96b62ff282b8c2d636b74 Signed-off-by: Angus Salkeld commit a550dcfa4971df5ef517aa73d2ebc7a6675c72c6 Merge: b81481c 987f1ec Author: Jenkins Date: Mon Mar 4 12:58:16 2013 +0000 Merge "Add resources and meters to the v2 shell" commit b81481c826b49fc9acf3b3e1d66e1563b138a810 Merge: dde86a3 17204a4 Author: Jenkins Date: Mon Mar 4 12:57:01 2013 +0000 Merge "Support --os-auth-token." commit 987f1ec20a429d4ebcccacd2bc17d6f4759c2ad8 Author: Angus Salkeld Date: Wed Feb 27 20:26:56 2013 +1100 Add resources and meters to the v2 shell This also moves the build_url function to a common file. Change-Id: Ia94f9fa37c83fc756a395a918ad254111951f67b Signed-off-by: Angus Salkeld commit dde86a38144a549efa080c4f77c1aa95a5fbface Author: Jason Zhang Date: Tue Feb 26 18:14:34 2013 -0800 Correct the help info of resource-list sub-command. Fixes: bug #1133823 Change-Id: I98691379c9c0adf78ef9ab761c2f2b203cd1ee6c commit a692906b383137a86213149496dff7432bc99c1a Merge: 8b3ef28 a7fba2c Author: Jenkins Date: Mon Feb 25 14:56:00 2013 +0000 Merge "Add shell.py so we can do v2 shell commands" commit a7fba2cb63489d35344b3f8a055aa3fe2fa49193 Author: Angus Salkeld Date: Mon Feb 25 20:59:27 2013 +1100 Add shell.py so we can do v2 shell commands ceilometer --ceilometer-api-version 2 sample-list -m fixes bug #1132633 Signed-off-by: Angus Salkeld Change-Id: I177c2f759b9b07b44dcd6dd20f457cefd036d0b9 commit 17204a4dcd11da3074d16ca9909ad4a956bd1152 Author: Lianhao Lu Date: Wed Feb 20 17:30:58 2013 +0800 Support --os-auth-token. Fixed bug #1130286. Change-Id: Ia0a8884f2738c31c3d91d6679622ebd3ec9b86b5 commit 8b3ef28c752fa96a1019900d3b9b9a80ede3eeec Author: Lianhao Lu Date: Wed Feb 20 15:57:50 2013 +0800 v1-api: Added timestamp support. Added timestamp support for resource/sample api. Fixed bug #1118542. Change-Id: I644c480ca00f57549dc66bd387721c25d3b353c4 commit feb4bcc6148a166676bbff2718590ea2291a2de5 Author: Lianhao Lu Date: Wed Feb 20 13:38:14 2013 +0800 v1-api: Adapted resouce/user api. Added project-id support in resource api and source-id support in user api. Change-Id: I3295a36a4b3a57e9451cc042d542ead1354f8e61 commit d740e3767219e6dd980fc10005ef110752ec406a Author: Julien Danjou Date: Mon Feb 4 19:11:50 2013 +0100 Add support for v2 API Change-Id: I861e53db5446d2e3dc49935ea1cdd1607cff0a2a Signed-off-by: Julien Danjou commit 26b92cc64fb9308cae05c91132d37f02d280a964 Author: Monty Taylor Date: Mon Feb 4 11:46:14 2013 +1100 Update to latest oslo-version Gets things set for tag-based versions. Change-Id: I8cc498562dde8831145583caf85de2ed46ccd206 commit 1a76566511f18523a0dc00f66484edaaebfa945c Author: Angus Salkeld Date: Fri Jan 18 17:48:05 2013 +1100 Add tests for samples Change-Id: I8725fedc6f728adaef91ffe9bb80df1c04debf85 commit a8605b57e5dbcf9b7cf69e6a8e898a0c46940623 Author: Angus Salkeld Date: Fri Jan 18 17:44:54 2013 +1100 Add a test for list by source Change-Id: If63134c37784157d19848f35f6546c98402de92d Signed-off-by: Angus Salkeld commit 22eb07ac5055fe8b1a1dbda54e980bda645df1b7 Author: Angus Salkeld Date: Fri Dec 7 14:47:48 2012 +1100 fix the fields in v1 do_meter_list Change-Id: I2ac7f9af13a38d1de0d61585f75e5955330284b7 commit bcf63bc548d423683a420d3a9aab3404244c8883 Merge: 027f574 47e1dab Author: Jenkins Date: Tue Jan 8 15:37:36 2013 +0000 Merge "Pin pep8 to 1.3.3" commit 027f574bce74db82ee4bdc6762edcd3c803abd45 Author: Chuck Short Date: Mon Jan 7 12:08:12 2013 -0600 Add missing dependencies Add httplib2 to make tests build again. Change-Id: If581019cc7ecbe4fd7c97570c2984e215cedd43d Signed-off-by: Chuck Short commit 47e1dab59796094a019801a3d485c583d205786f Author: Chuck Short Date: Mon Jan 7 11:58:17 2013 -0600 Pin pep8 to 1.3.3 pep8 1.3.3 is pretty much standard across the openstack projects. Pin pep8 1.3.3 and fix associated warnings/errors. Change-Id: I7230857889d261320a0dab2c261c9f85dc0ee602 Signed-off-by: Chuck Short commit 904f2077ec1333770f27d22d78dd1766680ca105 Author: Angus Salkeld Date: Wed Dec 12 16:51:31 2012 +1100 Add support for metadata query Change-Id: Iade319df3ab503e392f4216b2531b20642a20c31 commit f66ca807efe8d827da49e96f0cb385445eb84069 Merge: 39e98f8 5180619 Author: Jenkins Date: Tue Dec 11 10:38:35 2012 +0000 Merge "Move repository to openstack org." commit 39e98f8a3990bc3a55e635489a9074a4903e3fe8 Author: Julien Danjou Date: Thu Dec 6 13:37:12 2012 +0100 Fix tests Change-Id: I8c6a1c4bc4dec052fbb5f2544f519253bc8be143 Signed-off-by: Julien Danjou commit 5180619b831eac1be5579cff9b3f9b81e0b3a6a9 Author: James E. Blair Date: Wed Dec 5 14:01:25 2012 -0800 Move repository to openstack org. The upstream repo is now: https://github.com/openstack/python-ceilometerclient Update the README and .gitreview to reflect that. Change-Id: I573584457a626174b5115a999181ed82dd2abf9a commit 956259fbf8eb127e7548f04eb1ff18964c08444f Author: Angus Salkeld Date: Fri Nov 30 15:10:42 2012 +1100 Revert "Remove the event class and use Meter instead." Just rename Event to Sample This reverts commit 46cedbcf1e0f7a329b8a895e1f97ca8b817dd319. commit 46cedbcf1e0f7a329b8a895e1f97ca8b817dd319 Author: Angus Salkeld Date: Fri Nov 30 13:31:47 2012 +1100 Remove the event class and use Meter instead. Signed-off-by: Angus Salkeld commit 2ae6238fe974cfe983be68e69fd3d8846af063a2 Author: Angus Salkeld Date: Fri Nov 30 13:20:43 2012 +1100 Make sure the version is prepended Signed-off-by: Angus Salkeld commit 16357a15abb38a70c94f829fdb3db8693b9c54ca Author: Angus Salkeld Date: Fri Nov 30 13:20:21 2012 +1100 Fix the default service_type Signed-off-by: Angus Salkeld commit 635bc921dbf0fe0c65d5ec84a6cd327e83550e40 Author: Angus Salkeld Date: Fri Nov 9 23:31:27 2012 +1100 Add basic functionality Signed-off-by: Angus Salkeld commit 098f944eda406a2741c8d0f3546ea318421340bf Author: Angus Salkeld Date: Fri Nov 9 12:52:21 2012 +1100 Initial Commit Signed-off-by: Angus Salkeld python-ceilometerclient-1.0.8/.testr.conf0000664000175300017540000000030012247122076021605 0ustar jenkinsjenkins00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ./ $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list python-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/0000775000175300017540000000000012247122150026141 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/dependency_links.txt0000664000175300017540000000000112247122147032215 0ustar jenkinsjenkins00000000000000 python-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/SOURCES.txt0000664000175300017540000000522712247122150030033 0ustar jenkinsjenkins00000000000000.testr.conf AUTHORS ChangeLog LICENSE MANIFEST.in README.rst openstack-common.conf requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt tox.ini ceilometerclient/__init__.py ceilometerclient/client.py ceilometerclient/exc.py ceilometerclient/shell.py ceilometerclient/common/__init__.py ceilometerclient/common/base.py ceilometerclient/common/http.py ceilometerclient/common/utils.py ceilometerclient/openstack/__init__.py ceilometerclient/openstack/common/__init__.py ceilometerclient/openstack/common/cliutils.py ceilometerclient/openstack/common/gettextutils.py ceilometerclient/openstack/common/importutils.py ceilometerclient/openstack/common/strutils.py ceilometerclient/openstack/common/apiclient/__init__.py ceilometerclient/openstack/common/apiclient/auth.py ceilometerclient/openstack/common/apiclient/base.py ceilometerclient/openstack/common/apiclient/client.py ceilometerclient/openstack/common/apiclient/exceptions.py ceilometerclient/openstack/common/apiclient/fake_client.py ceilometerclient/openstack/common/py3kcompat/__init__.py ceilometerclient/openstack/common/py3kcompat/urlutils.py ceilometerclient/tests/__init__.py ceilometerclient/tests/fakes.py ceilometerclient/tests/test_http.py ceilometerclient/tests/test_shell.py ceilometerclient/tests/test_utils.py ceilometerclient/tests/utils.py ceilometerclient/tests/v1/__init__.py ceilometerclient/tests/v1/test_meters.py ceilometerclient/tests/v1/test_projects.py ceilometerclient/tests/v1/test_resources.py ceilometerclient/tests/v1/test_samples.py ceilometerclient/tests/v1/test_users.py ceilometerclient/tests/v2/__init__.py ceilometerclient/tests/v2/test_alarms.py ceilometerclient/tests/v2/test_options.py ceilometerclient/tests/v2/test_resources.py ceilometerclient/tests/v2/test_samples.py ceilometerclient/tests/v2/test_shell.py ceilometerclient/tests/v2/test_statistics.py ceilometerclient/v1/__init__.py ceilometerclient/v1/client.py ceilometerclient/v1/meters.py ceilometerclient/v1/shell.py ceilometerclient/v2/__init__.py ceilometerclient/v2/alarms.py ceilometerclient/v2/client.py ceilometerclient/v2/meters.py ceilometerclient/v2/options.py ceilometerclient/v2/resources.py ceilometerclient/v2/samples.py ceilometerclient/v2/shell.py ceilometerclient/v2/statistics.py doc/source/conf.py doc/source/index.rst python_ceilometerclient.egg-info/PKG-INFO python_ceilometerclient.egg-info/SOURCES.txt python_ceilometerclient.egg-info/dependency_links.txt python_ceilometerclient.egg-info/entry_points.txt python_ceilometerclient.egg-info/not-zip-safe python_ceilometerclient.egg-info/requires.txt python_ceilometerclient.egg-info/top_level.txt tools/install_venv.py tools/install_venv_common.py tools/with_venv.shpython-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/top_level.txt0000664000175300017540000000002112247122147030672 0ustar jenkinsjenkins00000000000000ceilometerclient python-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/not-zip-safe0000664000175300017540000000000112247122100030362 0ustar jenkinsjenkins00000000000000 python-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/PKG-INFO0000664000175300017540000000257112247122147027251 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-ceilometerclient Version: 1.0.8 Summary: OpenStack Telemetry API Client Library Home-page: http://www.openstack.org/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: Python bindings to the Ceilometer API ===================================== This is a client library for Ceilometer built on the Ceilometer API. It provides a Python API (the ``ceilometerclient`` module) and a command-line tool (``ceilometer``). Development takes place via the usual OpenStack processes as outlined in the `OpenStack wiki `_. The master repository is on `GitHub `_. See release notes and more at ``_. Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2.6 python-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/entry_points.txt0000664000175300017540000000007412247122147031446 0ustar jenkinsjenkins00000000000000[console_scripts] ceilometer = ceilometerclient.shell:main python-ceilometerclient-1.0.8/python_ceilometerclient.egg-info/requires.txt0000664000175300017540000000014612247122147030550 0ustar jenkinsjenkins00000000000000pbr>=0.5.21,<1.0 httplib2 iso8601>=0.1.8 PrettyTable>=0.6,<0.8 python-keystoneclient>=0.4.1 six>=1.4.1python-ceilometerclient-1.0.8/test-requirements.txt0000664000175300017540000000037212247122076023771 0ustar jenkinsjenkins00000000000000# Install bounded pep8/pyflakes first, then let flake8 install pep8==1.4.5 pyflakes>=0.7.2,<0.7.4 flake8==2.0 hacking>=0.8.0,<0.9 coverage>=3.6 discover fixtures>=0.3.14 mock>=1.0 python-subunit sphinx>=1.1.2 testrepository>=0.0.17 testtools>=0.9.32 python-ceilometerclient-1.0.8/setup.py0000664000175300017540000000141512247122076021241 0ustar jenkinsjenkins00000000000000#!/usr/bin/env python # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools setuptools.setup( setup_requires=['pbr'], pbr=True) python-ceilometerclient-1.0.8/run_tests.sh0000775000175300017540000001057012247122076022116 0ustar jenkinsjenkins00000000000000#!/bin/bash set -eu function usage { echo "Usage: $0 [OPTION]..." echo "Run python-ceilometerclient test suite" echo "" echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" echo " -s, --no-site-packages Isolate the virtualenv from the global Python environment" echo " -x, --stop Stop running tests after the first error or failure." echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." echo " -p, --pep8 Just run pep8" echo " -P, --no-pep8 Don't run pep8" echo " -c, --coverage Generate coverage report" echo " -h, --help Print this usage message" echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list" echo "" echo "Note: with no options specified, the script will try to run the tests in a virtual environment," echo " If no virtualenv is found, the script will ask if you would like to create one. If you " echo " prefer to run tests NOT in a virtual environment, simply pass the -N option." exit } function process_option { case "$1" in -h|--help) usage;; -V|--virtual-env) always_venv=1; never_venv=0;; -N|--no-virtual-env) always_venv=0; never_venv=1;; -s|--no-site-packages) no_site_packages=1;; -f|--force) force=1;; -p|--pep8) just_pep8=1;; -P|--no-pep8) no_pep8=1;; -c|--coverage) coverage=1;; -*) testropts="$testropts $1";; *) testrargs="$testrargs $1" esac } venv=.venv with_venv=tools/with_venv.sh always_venv=0 never_venv=0 force=0 no_site_packages=0 installvenvopts= testrargs= testropts= wrapper="" just_pep8=0 no_pep8=0 coverage=0 LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C for arg in "$@"; do process_option $arg done if [ $no_site_packages -eq 1 ]; then installvenvopts="--no-site-packages" fi function init_testr { if [ ! -d .testrepository ]; then ${wrapper} testr init fi } function run_tests { # Cleanup *pyc ${wrapper} find . -type f -name "*.pyc" -delete if [ $coverage -eq 1 ]; then # Do not test test_coverage_ext when gathering coverage. if [ "x$testrargs" = "x" ]; then testrargs="^(?!.*test_coverage_ext).*$" fi export PYTHON="${wrapper} coverage run --source novaclient --parallel-mode" fi # Just run the test suites in current environment set +e TESTRTESTS="$TESTRTESTS $testrargs" echo "Running \`${wrapper} $TESTRTESTS\`" ${wrapper} $TESTRTESTS RESULT=$? set -e copy_subunit_log return $RESULT } function copy_subunit_log { LOGNAME=`cat .testrepository/next-stream` LOGNAME=$(($LOGNAME - 1)) LOGNAME=".testrepository/${LOGNAME}" cp $LOGNAME subunit.log } function run_pep8 { echo "Running flake8 ..." ${wrapper} flake8 } TESTRTESTS="testr run --parallel $testropts" if [ $never_venv -eq 0 ] then # Remove the virtual environment if --force used if [ $force -eq 1 ]; then echo "Cleaning virtualenv..." rm -rf ${venv} fi if [ -e ${venv} ]; then wrapper="${with_venv}" else if [ $always_venv -eq 1 ]; then # Automatically install the virtualenv python tools/install_venv.py $installvenvopts wrapper="${with_venv}" else echo -e "No virtual environment found...create one? (Y/n) \c" read use_ve if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then # Install the virtualenv and run the test suite in it python tools/install_venv.py $installvenvopts wrapper=${with_venv} fi fi fi fi # Delete old coverage data from previous runs if [ $coverage -eq 1 ]; then ${wrapper} coverage erase fi if [ $just_pep8 -eq 1 ]; then run_pep8 exit fi init_testr run_tests # NOTE(sirp): we only want to run pep8 when we're running the full-test suite, # not when we're running tests individually. To handle this, we need to # distinguish between options (noseopts), which begin with a '-', and # arguments (testrargs). if [ -z "$testrargs" ]; then if [ $no_pep8 -eq 0 ]; then run_pep8 fi fi if [ $coverage -eq 1 ]; then echo "Generating coverage report in covhtml/" ${wrapper} coverage combine ${wrapper} coverage html --include='novaclient/*' --omit='novaclient/openstack/common/*' -d covhtml -i fi python-ceilometerclient-1.0.8/README.rst0000664000175300017540000000110412247122076021211 0ustar jenkinsjenkins00000000000000Python bindings to the Ceilometer API ===================================== This is a client library for Ceilometer built on the Ceilometer API. It provides a Python API (the ``ceilometerclient`` module) and a command-line tool (``ceilometer``). Development takes place via the usual OpenStack processes as outlined in the `OpenStack wiki `_. The master repository is on `GitHub `_. See release notes and more at ``_. python-ceilometerclient-1.0.8/requirements.txt0000664000175300017540000000016012247122076023007 0ustar jenkinsjenkins00000000000000pbr>=0.5.21,<1.0 argparse httplib2 iso8601>=0.1.8 PrettyTable>=0.6,<0.8 python-keystoneclient>=0.4.1 six>=1.4.1 python-ceilometerclient-1.0.8/tox.ini0000664000175300017540000000115012247122076021036 0ustar jenkinsjenkins00000000000000[tox] envlist = py26,py27,pypy,pep8 [testenv] setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --testr-args='{posargs}' [tox:jenkins] downloadcache = ~/cache/pip [testenv:pep8] commands = flake8 [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:venv] commands = {posargs} [flake8] ignore = H233 show-source = True exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools python-ceilometerclient-1.0.8/PKG-INFO0000664000175300017540000000257112247122150020621 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-ceilometerclient Version: 1.0.8 Summary: OpenStack Telemetry API Client Library Home-page: http://www.openstack.org/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: Python bindings to the Ceilometer API ===================================== This is a client library for Ceilometer built on the Ceilometer API. It provides a Python API (the ``ceilometerclient`` module) and a command-line tool (``ceilometer``). Development takes place via the usual OpenStack processes as outlined in the `OpenStack wiki `_. The master repository is on `GitHub `_. See release notes and more at ``_. Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 2.6 python-ceilometerclient-1.0.8/doc/0000775000175300017540000000000012247122150020264 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/doc/source/0000775000175300017540000000000012247122150021564 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/doc/source/conf.py0000664000175300017540000000360512247122076023076 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # import os import sys project = 'python-ceilometerclient' # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. copyright = u'OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'nature' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ( 'index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual' ), ] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} python-ceilometerclient-1.0.8/doc/source/index.rst0000664000175300017540000000257612247122076023446 0ustar jenkinsjenkins00000000000000Python API ========== In order to use the python api directly, you must first obtain an auth token and identify which endpoint you wish to speak to. Once you have done so, you can use the API like so:: >>> from ceilometerclient import Client >>> ceilometer = Client('1', endpoint=OS_IMAGE_ENDPOINT, token=OS_AUTH_TOKEN) ... Command-line Tool ================= In order to use the CLI, you must provide your OpenStack username, password, tenant, and auth endpoint. Use the corresponding configuration options (``--os-username``, ``--os-password``, ``--os-tenant-id``, and ``--os-auth-url``) or set them in environment variables:: export OS_USERNAME=user export OS_PASSWORD=pass export OS_TENANT_ID=b363706f891f48019483f8bd6503c54b export OS_AUTH_URL=http://auth.example.com:5000/v2.0 The command line tool will attempt to reauthenticate using your provided credentials for every request. You can override this behavior by manually supplying an auth token using ``--os-image-url`` and ``--os-auth-token``. You can alternatively set these environment variables:: export OS_IMAGE_URL=http://ceilometer.example.org:8004/ export OS_AUTH_TOKEN=3bcc3d3a03f44e3d8377f9247b0ad155 Once you've configured your authentication parameters, you can run ``ceilometer help`` to see a complete listing of available commands. Release Notes ============= 0.1.0 ----- * Initial release python-ceilometerclient-1.0.8/MANIFEST.in0000664000175300017540000000024012247122076021260 0ustar jenkinsjenkins00000000000000include AUTHORS include LICENSE include README.md include ChangeLog include tox.ini recursive-include doc * recursive-include tests * recursive-include tools * python-ceilometerclient-1.0.8/LICENSE0000664000175300017540000002363612247122076020545 0ustar jenkinsjenkins00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. python-ceilometerclient-1.0.8/setup.cfg0000664000175300017540000000170012247122150021336 0ustar jenkinsjenkins00000000000000[metadata] name = python-ceilometerclient summary = OpenStack Telemetry API Client Library description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = http://www.openstack.org/ classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 2.6 [files] packages = ceilometerclient [global] setup-hooks = pbr.hooks.setup_hook [entry_points] console_scripts = ceilometer = ceilometerclient.shell:main [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 [upload_sphinx] upload-dir = doc/build/html [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 python-ceilometerclient-1.0.8/tools/0000775000175300017540000000000012247122150020657 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/tools/install_venv_common.py0000664000175300017540000001350612247122076025321 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Provides methods needed by installation script for OpenStack development virtual environments. Since this script is used to bootstrap a virtualenv from the system's Python environment, it should be kept strictly compatible with Python 2.6. Synced in from openstack-common """ from __future__ import print_function import optparse import os import subprocess import sys class InstallVenv(object): def __init__(self, root, venv, requirements, test_requirements, py_version, project): self.root = root self.venv = venv self.requirements = requirements self.test_requirements = test_requirements self.py_version = py_version self.project = project def die(self, message, *args): print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): if sys.version_info < (2, 6): self.die("Need Python Version >= 2.6") def run_command_with_code(self, cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. Returns the output of that command. Working directory is self.root. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(self, cmd, redirect_output=True, check_exit_code=True): return self.run_command_with_code(cmd, redirect_output, check_exit_code)[0] def get_distro(self): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): return Fedora( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) else: return Distro( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) def check_dependencies(self): self.get_distro().install_virtualenv() def create_virtualenv(self, no_site_packages=True): """Creates the virtual environment and installs PIP. Creates the virtual environment and installs PIP only into the virtual environment. """ if not os.path.isdir(self.venv): print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) print('done.') else: print("venv already exists...") pass def pip_install(self, *args): self.run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(self): print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and # setuptools and pbr self.pip_install('pip>=1.4') self.pip_install('setuptools') self.pip_install('pbr') self.pip_install('-r', self.requirements, '-r', self.test_requirements) def parse_args(self, argv): """Parses command-line arguments.""" parser = optparse.OptionParser() parser.add_option('-n', '--no-site-packages', action='store_true', help="Do not inherit packages from global Python " "install") return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): def check_cmd(self, cmd): return bool(self.run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): print('Succeeded') return else: print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your' ' favorite package management tool' % self.project) class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux """ def check_pkg(self, pkg): return self.run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.die("Please install 'python-virtualenv'.") super(Fedora, self).install_virtualenv() python-ceilometerclient-1.0.8/tools/with_venv.sh0000775000175300017540000000012412247122076023233 0ustar jenkinsjenkins00000000000000#!/bin/bash TOOLS=`dirname $0` VENV=$TOOLS/../.venv source $VENV/bin/activate && $@ python-ceilometerclient-1.0.8/tools/install_venv.py0000664000175300017540000000467412247122076023757 0ustar jenkinsjenkins00000000000000# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys import install_venv_common as install_venv def print_help(venv, root): help = """ Ceilometerclient development environment setup is complete. Ceilometerclient development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the Ceilometerclient virtualenv for the extent of your current shell session you can run: $ source %s/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ %s/tools/with_venv.sh Also, make test will automatically use the virtualenv. """ print help % (venv, root) def main(argv): root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) if os.environ.get('tools_path'): root = os.environ['tools_path'] venv = os.path.join(root, '.venv') if os.environ.get('venv'): venv = os.environ['venv'] pip_requires = os.path.join(root, 'requirements.txt') test_requires = os.path.join(root, 'test-requirements.txt') py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) project = 'Ceilometerclient' install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, py_version, project) options = install.parse_args(argv) install.check_python_version() install.check_dependencies() install.create_virtualenv(no_site_packages=options.no_site_packages) install.install_dependencies() install.post_process() print_help(venv, root) if __name__ == '__main__': main(sys.argv) python-ceilometerclient-1.0.8/ceilometerclient/0000775000175300017540000000000012247122150023046 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/client.py0000664000175300017540000001035112247122076024705 0ustar jenkinsjenkins00000000000000# 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 ceilometerclient.common import utils from keystoneclient.v2_0 import client as ksclient def _get_ksclient(**kwargs): """Get an endpoint and auth token from Keystone. :param kwargs: keyword args containing credentials: * username: name of user * password: user's password * auth_url: endpoint to authenticate against * cacert: path of CA TLS certificate * insecure: allow insecure SSL (no cert verification) * tenant_{name|id}: name or ID of tenant """ return ksclient.Client(username=kwargs.get('username'), password=kwargs.get('password'), tenant_id=kwargs.get('tenant_id'), tenant_name=kwargs.get('tenant_name'), auth_url=kwargs.get('auth_url'), region_name=kwargs.get('region_name'), cacert=kwargs.get('cacert'), insecure=kwargs.get('insecure')) def _get_endpoint(client, **kwargs): """Get an endpoint using the provided keystone client.""" return client.service_catalog.url_for( service_type=kwargs.get('service_type') or 'metering', endpoint_type=kwargs.get('endpoint_type') or 'publicURL') def get_client(api_version, **kwargs): """Get an authtenticated client, based on the credentials in the keyword args. :param api_version: the API version to use ('1' or '2') :param kwargs: keyword args containing credentials, either: * os_auth_token: pre-existing token to re-use * ceilometer_url: ceilometer API endpoint or: * os_username: name of user * os_password: user's password * os_auth_url: endpoint to authenticate against * os_cacert: path of CA TLS certificate * insecure: allow insecure SSL (no cert verification) * os_tenant_{name|id}: name or ID of tenant """ if kwargs.get('os_auth_token') and kwargs.get('ceilometer_url'): token = kwargs.get('os_auth_token') endpoint = kwargs.get('ceilometer_url') elif (kwargs.get('os_username') and kwargs.get('os_password') and kwargs.get('os_auth_url') and (kwargs.get('os_tenant_id') or kwargs.get('os_tenant_name'))): ks_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'), 'auth_url': kwargs.get('os_auth_url'), 'region_name': kwargs.get('os_region_name'), 'service_type': kwargs.get('os_service_type'), 'endpoint_type': kwargs.get('os_endpoint_type'), 'cacert': kwargs.get('os_cacert'), 'insecure': kwargs.get('insecure'), } _ksclient = _get_ksclient(**ks_kwargs) token = ((lambda: kwargs.get('os_auth_token')) if kwargs.get('os_auth_token') else (lambda: _ksclient.auth_token)) endpoint = kwargs.get('ceilometer_url') or \ _get_endpoint(_ksclient, **ks_kwargs) cli_kwargs = { 'token': token, 'insecure': kwargs.get('insecure'), 'timeout': kwargs.get('timeout'), 'cacert': kwargs.get('cacert'), 'cert_file': kwargs.get('cert_file'), 'key_file': kwargs.get('key_file'), } return Client(api_version, endpoint, **cli_kwargs) def Client(version, *args, **kwargs): module = utils.import_versioned_module(version, 'client') client_class = getattr(module, 'Client') return client_class(*args, **kwargs) python-ceilometerclient-1.0.8/ceilometerclient/__init__.py0000664000175300017540000000140712247122076025170 0ustar jenkinsjenkins00000000000000# 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. __all__ = ['__version__'] import pbr.version version_info = pbr.version.VersionInfo('python-ceilometerclient') try: __version__ = version_info.version_string() except AttributeError: __version__ = None python-ceilometerclient-1.0.8/ceilometerclient/common/0000775000175300017540000000000012247122150024336 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/common/__init__.py0000664000175300017540000000000012247122076026444 0ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/common/utils.py0000664000175300017540000001314512247122076026063 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import print_function import itertools import os import six import sys import textwrap import uuid import prettytable from ceilometerclient import exc from ceilometerclient.openstack.common import cliutils from ceilometerclient.openstack.common import importutils from ceilometerclient.openstack.common import strutils # Decorator for cli-args def arg(*args, **kwargs): def _decorator(func): if 'help' in kwargs: if 'default' in kwargs: kwargs['help'] += " Defaults to %s." % kwargs['default'] required = kwargs.get('required', False) if required: kwargs['help'] += " Required." # Because of the sematics of decorator composition if we just append # to the options list positional options will appear to be backwards. func.__dict__.setdefault('arguments', []).insert(0, (args, kwargs)) return func return _decorator def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) def print_list(objs, fields, field_labels, formatters={}, sortby=0): def _make_default_formatter(field): return lambda o: getattr(o, field, '') new_formatters = {} for field, field_label in itertools.izip(fields, field_labels): if field in formatters: new_formatters[field_label] = formatters[field] else: new_formatters[field_label] = _make_default_formatter(field) cliutils.print_list(objs, field_labels, formatters=new_formatters, sortby_index=sortby) def print_dict(d, dict_property="Property", wrap=0): pt = prettytable.PrettyTable([dict_property, 'Value'], caching=False, print_empty=False) pt.align = 'l' for k, v in sorted(six.iteritems(d)): # convert dict to str to check length if isinstance(v, dict): v = str(v) if isinstance(v, six.string_types): v = strutils.safe_encode(v) # if value has a newline, add in multiple rows # e.g. fault with stacktrace if v and isinstance(v, six.string_types) and r'\n' in v: lines = v.strip().split(r'\n') col1 = k for line in lines: if wrap > 0: line = textwrap.fill(str(line), wrap) pt.add_row([col1, line]) col1 = '' else: if wrap > 0: v = textwrap.fill(str(v), wrap) pt.add_row([k, v]) print(pt.get_string()) def find_resource(manager, name_or_id): """Helper for the _find_* methods.""" # first try to get entity as integer id try: if isinstance(name_or_id, int) or name_or_id.isdigit(): return manager.get(int(name_or_id)) except exc.NotFound: pass # now try to get entity as uuid try: uuid.UUID(str(name_or_id)) return manager.get(name_or_id) except (ValueError, exc.NotFound): pass # finally try to find entity by name try: return manager.find(name=name_or_id) except exc.NotFound: msg = "No %s with a name or ID of '%s' exists." % \ (manager.resource_class.__name__.lower(), name_or_id) raise exc.CommandError(msg) def string_to_bool(arg): return arg.strip().lower() in ('t', 'true', 'yes', '1') def env(*vars, **kwargs): """Search for the first defined of possibly many env vars Returns the first environment variable defined in vars, or returns the default defined in kwargs. """ for v in vars: value = os.environ.get(v, None) if value: return value return kwargs.get('default', '') def import_versioned_module(version, submodule=None): module = 'ceilometerclient.v%s' % version if submodule: module = '.'.join((module, submodule)) return importutils.import_module(module) def args_array_to_dict(kwargs, key_to_convert): values_to_convert = kwargs.get(key_to_convert) if values_to_convert: try: kwargs[key_to_convert] = dict(v.split("=", 1) for v in values_to_convert) except ValueError: raise exc.CommandError( '%s must be a list of key=value not "%s"' % ( key_to_convert, values_to_convert)) return kwargs def key_with_slash_to_nested_dict(kwargs): nested_kwargs = {} for k in list(kwargs): keys = k.split('/', 1) if len(keys) == 2: nested_kwargs.setdefault(keys[0], {})[keys[1]] = kwargs[k] del kwargs[k] kwargs.update(nested_kwargs) return kwargs def merge_nested_dict(dest, source, depth=0): for (key, value) in six.iteritems(source): if isinstance(value, dict) and depth: merge_nested_dict(dest[key], value, depth=(depth - 1)) else: dest[key] = value def exit(msg=''): if msg: print(msg, file=sys.stderr) sys.exit(1) python-ceilometerclient-1.0.8/ceilometerclient/common/base.py0000664000175300017540000001030512247122076025630 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ import copy import six # Python 2.4 compat try: all except NameError: def all(iterable): return True not in (not x for x in iterable) def getid(obj): """Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: return obj.id except AttributeError: return obj class Manager(object): """Managers interact with a particular type of API (samples, meters, alarms, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, api): self.api = api def _create(self, url, body): resp, body = self.api.json_request('POST', url, body=body) if body: return self.resource_class(self, body) def _list(self, url, response_key=None, obj_class=None, body=None, expect_single=False): resp, body = self.api.json_request('GET', url) if obj_class is None: obj_class = self.resource_class if response_key: try: data = body[response_key] except KeyError: return [] else: data = body if expect_single: data = [data] return [obj_class(self, res, loaded=True) for res in data if res] def _update(self, url, body, response_key=None): resp, body = self.api.json_request('PUT', url, body=body) # PUT requests may not return a body if body: return self.resource_class(self, body) def _delete(self, url): self.api.raw_request('DELETE', url) class Resource(object): """A resource represents a particular instance of an object (tenant, user, etc). This is pretty much just a bag for attributes. :param manager: Manager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ def __init__(self, manager, info, loaded=False): self.manager = manager self._info = info self._add_details(info) self._loaded = loaded def _add_details(self, info): for (k, v) in six.iteritems(info): setattr(self, k, v) def __getattr__(self, k): if k not in self.__dict__: # NOTE(bcwaldon): disallow lazy-loading if already loaded once if not self.is_loaded(): self.get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) def get(self): # set_loaded() first ... so if we have to bail, we know we tried. self.set_loaded(True) if not hasattr(self.manager, 'get'): return new = self.manager.get(self.id) if new: self._add_details(new._info) def __eq__(self, other): if not isinstance(other, self.__class__): return False if hasattr(self, 'id') and hasattr(other, 'id'): return self.id == other.id return self._info == other._info def is_loaded(self): return self._loaded def set_loaded(self, val): self._loaded = val def to_dict(self): return copy.deepcopy(self._info) python-ceilometerclient-1.0.8/ceilometerclient/common/http.py0000664000175300017540000002455612247122076025712 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import logging import os import socket try: import ssl except ImportError: #TODO(bcwaldon): Handle this failure more gracefully pass try: import json except ImportError: import simplejson as json import six from six.moves import http_client as httplib # noqa from ceilometerclient import exc from ceilometerclient.openstack.common.py3kcompat import urlutils LOG = logging.getLogger(__name__) USER_AGENT = 'python-ceilometerclient' CHUNKSIZE = 1024 * 64 # 64kB class HTTPClient(object): def __init__(self, endpoint, **kwargs): self.endpoint = endpoint self.auth_token = kwargs.get('token') self.connection_params = self.get_connection_params(endpoint, **kwargs) self.proxy_url = self.get_proxy_url() @staticmethod def get_connection_params(endpoint, **kwargs): parts = urlutils.urlparse(endpoint) _args = (parts.hostname, parts.port, parts.path) _kwargs = {'timeout': (float(kwargs.get('timeout')) if kwargs.get('timeout') else 600)} if parts.scheme == 'https': _class = VerifiedHTTPSConnection _kwargs['cacert'] = kwargs.get('cacert', None) _kwargs['cert_file'] = kwargs.get('cert_file', None) _kwargs['key_file'] = kwargs.get('key_file', None) _kwargs['insecure'] = kwargs.get('insecure', False) elif parts.scheme == 'http': _class = httplib.HTTPConnection else: msg = 'Unsupported scheme: %s' % parts.scheme raise exc.InvalidEndpoint(msg) return (_class, _args, _kwargs) def get_connection(self): _class = self.connection_params[0] try: if self.proxy_url: proxy_parts = urlutils.urlparse(self.proxy_url) return _class(proxy_parts.hostname, proxy_parts.port, **self.connection_params[2]) else: return _class(*self.connection_params[1][0:2], **self.connection_params[2]) except httplib.InvalidURL: raise exc.InvalidEndpoint() def log_curl_request(self, method, url, kwargs): curl = ['curl -i -X %s' % method] for (key, value) in kwargs['headers'].items(): header = '-H \'%s: %s\'' % (key, value) curl.append(header) conn_params_fmt = [ ('key_file', '--key %s'), ('cert_file', '--cert %s'), ('cacert', '--cacert %s'), ] for (key, fmt) in conn_params_fmt: value = self.connection_params[2].get(key) if value: curl.append(fmt % value) if self.connection_params[2].get('insecure'): curl.append('-k') if 'body' in kwargs: curl.append('-d \'%s\'' % kwargs['body']) curl.append('%s%s' % (self.endpoint, url)) LOG.debug(' '.join(curl)) @staticmethod def log_http_response(resp, body=None): status = (resp.version / 10.0, resp.status, resp.reason) dump = ['\nHTTP/%.1f %s %s' % status] dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()]) dump.append('') if body: dump.extend([body, '']) LOG.debug('\n'.join(dump)) def _make_connection_url(self, url): (_class, _args, _kwargs) = self.connection_params base_url = _args[2] return '%s/%s' % (base_url.rstrip('/'), url.lstrip('/')) def _http_request(self, url, method, **kwargs): """Send an http request with the specified characteristics. Wrapper around httplib.HTTP(S)Connection.request to handle tasks such as setting headers and error handling. """ # Copy the kwargs so we can reuse the original in case of redirects kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'].setdefault('User-Agent', USER_AGENT) auth_token = self.auth_token() if auth_token: kwargs['headers'].setdefault('X-Auth-Token', auth_token) self.log_curl_request(method, url, kwargs) conn = self.get_connection() try: if self.proxy_url: conn_url = self.endpoint + self._make_connection_url(url) else: conn_url = self._make_connection_url(url) conn.request(method, conn_url, **kwargs) resp = conn.getresponse() except socket.gaierror as e: message = ("Error finding address for %(url)s: %(e)s" % dict(url=url, e=e)) raise exc.InvalidEndpoint(message=message) except (socket.error, socket.timeout) as e: endpoint = self.endpoint message = ("Error communicating with %(endpoint)s %(e)s" % dict(endpoint=endpoint, e=e)) raise exc.CommunicationError(message=message) body_iter = ResponseBodyIterator(resp) # Read body into string if it isn't obviously image data if resp.getheader('content-type', None) != 'application/octet-stream': body_str = ''.join([chunk for chunk in body_iter]) self.log_http_response(resp, body_str) body_iter = six.StringIO(body_str) else: self.log_http_response(resp) if 400 <= resp.status < 600: LOG.warn("Request returned failure status.") raise exc.from_response(resp) elif resp.status in (301, 302, 305): # Redirected. Reissue the request to the new location. return self._http_request(resp['location'], method, **kwargs) elif resp.status == 300: raise exc.from_response(resp) return resp, body_iter def json_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/json') kwargs['headers'].setdefault('Accept', 'application/json') if 'body' in kwargs: kwargs['body'] = json.dumps(kwargs['body']) resp, body_iter = self._http_request(url, method, **kwargs) content_type = resp.getheader('content-type', None) if resp.status == 204 or resp.status == 205 or content_type is None: return resp, list() if 'application/json' in content_type: body = ''.join([chunk for chunk in body_iter]) try: body = json.loads(body) except ValueError: LOG.error('Could not decode response body as JSON') else: body = None return resp, body def raw_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/octet-stream') return self._http_request(url, method, **kwargs) def get_proxy_url(self): scheme = urlutils.urlparse(self.endpoint).scheme if scheme == 'https': return os.environ.get('https_proxy') elif scheme == 'http': return os.environ.get('http_proxy') msg = 'Unsupported scheme: %s' % scheme raise exc.InvalidEndpoint(msg) class VerifiedHTTPSConnection(httplib.HTTPSConnection): """httplib-compatibile connection using client-side SSL authentication :see http://code.activestate.com/recipes/ 577548-https-httplib-client-connection-with-certificate-v/ """ def __init__(self, host, port, key_file=None, cert_file=None, cacert=None, timeout=None, insecure=False): httplib.HTTPSConnection.__init__(self, host, port, key_file=key_file, cert_file=cert_file) self.key_file = key_file self.cert_file = cert_file if cacert is not None: self.cacert = cacert else: self.cacert = self.get_system_ca_file() self.timeout = timeout self.insecure = insecure def connect(self): """Connect to a host on a given (SSL) port. If cacert is pointing somewhere, use it to check Server Certificate. Redefined/copied and extended from httplib.py:1105 (Python 2.6.x). This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to ssl.wrap_socket(), which forces SSL to check server certificate against our client certificate. """ sock = socket.create_connection((self.host, self.port), self.timeout) if self._tunnel_host: self.sock = sock self._tunnel() if self.insecure is True: kwargs = {'cert_reqs': ssl.CERT_NONE} else: kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.cacert} if self.cert_file: kwargs['certfile'] = self.cert_file if self.key_file: kwargs['keyfile'] = self.key_file self.sock = ssl.wrap_socket(sock, **kwargs) @staticmethod def get_system_ca_file(): """Return path to system default CA file.""" # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora, # Suse, FreeBSD/OpenBSD ca_path = ['/etc/ssl/certs/ca-certificates.crt', '/etc/pki/tls/certs/ca-bundle.crt', '/etc/ssl/ca-bundle.pem', '/etc/ssl/cert.pem'] for ca in ca_path: if os.path.exists(ca): return ca return None class ResponseBodyIterator(object): """A class that acts as an iterator over an HTTP response.""" def __init__(self, resp): self.resp = resp def __iter__(self): while True: yield self.next() def next(self): chunk = self.resp.read(CHUNKSIZE) if chunk: return chunk else: raise StopIteration() python-ceilometerclient-1.0.8/ceilometerclient/openstack/0000775000175300017540000000000012247122150025035 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/openstack/__init__.py0000664000175300017540000000000012247122076027143 0ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/0000775000175300017540000000000012247122150026325 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/apiclient/0000775000175300017540000000000012247122150030275 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/apiclient/exceptions.py0000664000175300017540000002705312247122076033046 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Exception definitions. """ import inspect import sys import six class ClientException(Exception): """The base exception class for all exceptions this library raises. """ pass class MissingArgs(ClientException): """Supplied arguments are not sufficient for calling a function.""" def __init__(self, missing): self.missing = missing msg = "Missing argument(s): %s" % ", ".join(missing) super(MissingArgs, self).__init__(msg) class ValidationError(ClientException): """Error in validation on API client side.""" pass class UnsupportedVersion(ClientException): """User is trying to use an unsupported version of the API.""" pass class CommandError(ClientException): """Error in CLI tool.""" pass class AuthorizationFailure(ClientException): """Cannot authorize API client.""" pass class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( "Authentication failed. Missing options: %s" % ", ".join(opt_names)) self.opt_names = opt_names class AuthSystemNotFound(AuthorizationFailure): """User has specified a AuthSystem that is not installed.""" def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__( "AuthSystemNotFound: %s" % repr(auth_system)) self.auth_system = auth_system class NoUniqueMatch(ClientException): """Multiple entities found instead of one.""" pass class EndpointException(ClientException): """Something is rotten in Service Catalog.""" pass class EndpointNotFound(EndpointException): """Could not find requested endpoint in Service Catalog.""" pass class AmbiguousEndpoints(EndpointException): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__( "AmbiguousEndpoints: %s" % repr(endpoints)) self.endpoints = endpoints class HttpError(ClientException): """The base exception class for all HTTP exceptions. """ http_status = 0 message = "HTTP Error" def __init__(self, message=None, details=None, response=None, request_id=None, url=None, method=None, http_status=None): self.http_status = http_status or self.http_status self.message = message or self.message self.details = details self.request_id = request_id self.response = response self.url = url self.method = method formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) if request_id: formatted_string += " (Request-ID: %s)" % request_id super(HttpError, self).__init__(formatted_string) class HTTPClientError(HttpError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = "HTTP Client Error" class HttpServerError(HttpError): """Server-side HTTP error. Exception for cases in which the server is aware that it has erred or is incapable of performing the request. """ message = "HTTP Server Error" class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. The request cannot be fulfilled due to bad syntax. """ http_status = 400 message = "Bad Request" class Unauthorized(HTTPClientError): """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. """ http_status = 401 message = "Unauthorized" class PaymentRequired(HTTPClientError): """HTTP 402 - Payment Required. Reserved for future use. """ http_status = 402 message = "Payment Required" class Forbidden(HTTPClientError): """HTTP 403 - Forbidden. The request was a valid request, but the server is refusing to respond to it. """ http_status = 403 message = "Forbidden" class NotFound(HTTPClientError): """HTTP 404 - Not Found. The requested resource could not be found but may be available again in the future. """ http_status = 404 message = "Not Found" class MethodNotAllowed(HTTPClientError): """HTTP 405 - Method Not Allowed. A request was made of a resource using a request method not supported by that resource. """ http_status = 405 message = "Method Not Allowed" class NotAcceptable(HTTPClientError): """HTTP 406 - Not Acceptable. The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. """ http_status = 406 message = "Not Acceptable" class ProxyAuthenticationRequired(HTTPClientError): """HTTP 407 - Proxy Authentication Required. The client must first authenticate itself with the proxy. """ http_status = 407 message = "Proxy Authentication Required" class RequestTimeout(HTTPClientError): """HTTP 408 - Request Timeout. The server timed out waiting for the request. """ http_status = 408 message = "Request Timeout" class Conflict(HTTPClientError): """HTTP 409 - Conflict. Indicates that the request could not be processed because of conflict in the request, such as an edit conflict. """ http_status = 409 message = "Conflict" class Gone(HTTPClientError): """HTTP 410 - Gone. Indicates that the resource requested is no longer available and will not be available again. """ http_status = 410 message = "Gone" class LengthRequired(HTTPClientError): """HTTP 411 - Length Required. The request did not specify the length of its content, which is required by the requested resource. """ http_status = 411 message = "Length Required" class PreconditionFailed(HTTPClientError): """HTTP 412 - Precondition Failed. The server does not meet one of the preconditions that the requester put on the request. """ http_status = 412 message = "Precondition Failed" class RequestEntityTooLarge(HTTPClientError): """HTTP 413 - Request Entity Too Large. The request is larger than the server is willing or able to process. """ http_status = 413 message = "Request Entity Too Large" def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RequestEntityTooLarge, self).__init__(*args, **kwargs) class RequestUriTooLong(HTTPClientError): """HTTP 414 - Request-URI Too Long. The URI provided was too long for the server to process. """ http_status = 414 message = "Request-URI Too Long" class UnsupportedMediaType(HTTPClientError): """HTTP 415 - Unsupported Media Type. The request entity has a media type which the server or resource does not support. """ http_status = 415 message = "Unsupported Media Type" class RequestedRangeNotSatisfiable(HTTPClientError): """HTTP 416 - Requested Range Not Satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion. """ http_status = 416 message = "Requested Range Not Satisfiable" class ExpectationFailed(HTTPClientError): """HTTP 417 - Expectation Failed. The server cannot meet the requirements of the Expect request-header field. """ http_status = 417 message = "Expectation Failed" class UnprocessableEntity(HTTPClientError): """HTTP 422 - Unprocessable Entity. The request was well-formed but was unable to be followed due to semantic errors. """ http_status = 422 message = "Unprocessable Entity" class InternalServerError(HttpServerError): """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. """ http_status = 500 message = "Internal Server Error" # NotImplemented is a python keyword. class HttpNotImplemented(HttpServerError): """HTTP 501 - Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request. """ http_status = 501 message = "Not Implemented" class BadGateway(HttpServerError): """HTTP 502 - Bad Gateway. The server was acting as a gateway or proxy and received an invalid response from the upstream server. """ http_status = 502 message = "Bad Gateway" class ServiceUnavailable(HttpServerError): """HTTP 503 - Service Unavailable. The server is currently unavailable. """ http_status = 503 message = "Service Unavailable" class GatewayTimeout(HttpServerError): """HTTP 504 - Gateway Timeout. The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. """ http_status = 504 message = "Gateway Timeout" class HttpVersionNotSupported(HttpServerError): """HTTP 505 - HttpVersion Not Supported. The server does not support the HTTP protocol version used in the request. """ http_status = 505 message = "HTTP Version Not Supported" # _code_map contains all the classes that have http_status attribute. _code_map = dict( (getattr(obj, 'http_status', None), obj) for name, obj in six.iteritems(vars(sys.modules[__name__])) if inspect.isclass(obj) and getattr(obj, 'http_status', False) ) def from_response(response, method, url): """Returns an instance of :class:`HttpError` or subclass based on response. :param response: instance of `requests.Response` class :param method: HTTP method used for request :param url: URL used for request """ kwargs = { "http_status": response.status_code, "response": response, "method": method, "url": url, "request_id": response.headers.get("x-compute-request-id"), } if "retry-after" in response.headers: kwargs["retry_after"] = response.headers["retry-after"] content_type = response.headers.get("Content-Type", "") if content_type.startswith("application/json"): try: body = response.json() except ValueError: pass else: if hasattr(body, "keys"): error = body[body.keys()[0]] kwargs["message"] = error.get("message", None) kwargs["details"] = error.get("details", None) elif content_type.startswith("text/"): kwargs["details"] = response.text try: cls = _code_map[response.status_code] except KeyError: if 500 <= response.status_code < 600: cls = HttpServerError elif 400 <= response.status_code < 500: cls = HTTPClientError else: cls = HttpError return cls(**kwargs) python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/apiclient/client.py0000664000175300017540000003071012247122076032135 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 Grid Dynamics # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ OpenStack Client interface. Handles the REST calls and responses. """ # E0202: An attribute inherited from %s hide this method # pylint: disable=E0202 import logging import time try: import simplejson as json except ImportError: import json import requests from ceilometerclient.openstack.common.apiclient import exceptions from ceilometerclient.openstack.common import importutils _logger = logging.getLogger(__name__) class HTTPClient(object): """This client handles sending HTTP requests to OpenStack servers. Features: - share authentication information between several clients to different services (e.g., for compute and image clients); - reissue authentication request for expired tokens; - encode/decode JSON bodies; - raise exceptions on HTTP errors; - pluggable authentication; - store authentication information in a keyring; - store time spent for requests; - register clients for particular services, so one can use `http_client.identity` or `http_client.compute`; - log requests and responses in a format that is easy to copy-and-paste into terminal and send the same request with curl. """ user_agent = "ceilometerclient.openstack.common.apiclient" def __init__(self, auth_plugin, region_name=None, endpoint_type="publicURL", original_ip=None, verify=True, cert=None, timeout=None, timings=False, keyring_saver=None, debug=False, user_agent=None, http=None): self.auth_plugin = auth_plugin self.endpoint_type = endpoint_type self.region_name = region_name self.original_ip = original_ip self.timeout = timeout self.verify = verify self.cert = cert self.keyring_saver = keyring_saver self.debug = debug self.user_agent = user_agent or self.user_agent self.times = [] # [("item", starttime, endtime), ...] self.timings = timings # requests within the same session can reuse TCP connections from pool self.http = http or requests.Session() self.cached_token = None def _http_log_req(self, method, url, kwargs): if not self.debug: return string_parts = [ "curl -i", "-X '%s'" % method, "'%s'" % url, ] for element in kwargs['headers']: header = "-H '%s: %s'" % (element, kwargs['headers'][element]) string_parts.append(header) _logger.debug("REQ: %s" % " ".join(string_parts)) if 'data' in kwargs: _logger.debug("REQ BODY: %s\n" % (kwargs['data'])) def _http_log_resp(self, resp): if not self.debug: return _logger.debug( "RESP: [%s] %s\n", resp.status_code, resp.headers) if resp._content_consumed: _logger.debug( "RESP BODY: %s\n", resp.text) def serialize(self, kwargs): if kwargs.get('json') is not None: kwargs['headers']['Content-Type'] = 'application/json' kwargs['data'] = json.dumps(kwargs['json']) try: del kwargs['json'] except KeyError: pass def get_timings(self): return self.times def reset_timings(self): self.times = [] def request(self, method, url, **kwargs): """Send an http request with the specified characteristics. Wrapper around `requests.Session.request` to handle tasks such as setting headers, JSON encoding/decoding, and error handling. :param method: method of HTTP request :param url: URL of HTTP request :param kwargs: any other parameter that can be passed to ' requests.Session.request (such as `headers`) or `json` that will be encoded as JSON and used as `data` argument """ kwargs.setdefault("headers", kwargs.get("headers", {})) kwargs["headers"]["User-Agent"] = self.user_agent if self.original_ip: kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % ( self.original_ip, self.user_agent) if self.timeout is not None: kwargs.setdefault("timeout", self.timeout) kwargs.setdefault("verify", self.verify) if self.cert is not None: kwargs.setdefault("cert", self.cert) self.serialize(kwargs) self._http_log_req(method, url, kwargs) if self.timings: start_time = time.time() resp = self.http.request(method, url, **kwargs) if self.timings: self.times.append(("%s %s" % (method, url), start_time, time.time())) self._http_log_resp(resp) if resp.status_code >= 400: _logger.debug( "Request returned failure status: %s", resp.status_code) raise exceptions.from_response(resp, method, url) return resp @staticmethod def concat_url(endpoint, url): """Concatenate endpoint and final URL. E.g., "http://keystone/v2.0/" and "/tokens" are concatenated to "http://keystone/v2.0/tokens". :param endpoint: the base URL :param url: the final URL """ return "%s/%s" % (endpoint.rstrip("/"), url.strip("/")) def client_request(self, client, method, url, **kwargs): """Send an http request using `client`'s endpoint and specified `url`. If request was rejected as unauthorized (possibly because the token is expired), issue one authorization attempt and send the request once again. :param client: instance of BaseClient descendant :param method: method of HTTP request :param url: URL of HTTP request :param kwargs: any other parameter that can be passed to ' `HTTPClient.request` """ filter_args = { "endpoint_type": client.endpoint_type or self.endpoint_type, "service_type": client.service_type, } token, endpoint = (self.cached_token, client.cached_endpoint) just_authenticated = False if not (token and endpoint): try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: pass if not (token and endpoint): self.authenticate() just_authenticated = True token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) if not (token and endpoint): raise exceptions.AuthorizationFailure( "Cannot find endpoint or token for request") old_token_endpoint = (token, endpoint) kwargs.setdefault("headers", {})["X-Auth-Token"] = token self.cached_token = token client.cached_endpoint = endpoint # Perform the request once. If we get Unauthorized, then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: return self.request( method, self.concat_url(endpoint, url), **kwargs) except exceptions.Unauthorized as unauth_ex: if just_authenticated: raise self.cached_token = None client.cached_endpoint = None self.authenticate() try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: raise unauth_ex if (not (token and endpoint) or old_token_endpoint == (token, endpoint)): raise unauth_ex self.cached_token = token client.cached_endpoint = endpoint kwargs["headers"]["X-Auth-Token"] = token return self.request( method, self.concat_url(endpoint, url), **kwargs) def add_client(self, base_client_instance): """Add a new instance of :class:`BaseClient` descendant. `self` will store a reference to `base_client_instance`. Example: >>> def test_clients(): ... from keystoneclient.auth import keystone ... from openstack.common.apiclient import client ... auth = keystone.KeystoneAuthPlugin( ... username="user", password="pass", tenant_name="tenant", ... auth_url="http://auth:5000/v2.0") ... openstack_client = client.HTTPClient(auth) ... # create nova client ... from novaclient.v1_1 import client ... client.Client(openstack_client) ... # create keystone client ... from keystoneclient.v2_0 import client ... client.Client(openstack_client) ... # use them ... openstack_client.identity.tenants.list() ... openstack_client.compute.servers.list() """ service_type = base_client_instance.service_type if service_type and not hasattr(self, service_type): setattr(self, service_type, base_client_instance) def authenticate(self): self.auth_plugin.authenticate(self) # Store the authentication results in the keyring for later requests if self.keyring_saver: self.keyring_saver.save(self) class BaseClient(object): """Top-level object to access the OpenStack API. This client uses :class:`HTTPClient` to send requests. :class:`HTTPClient` will handle a bunch of issues such as authentication. """ service_type = None endpoint_type = None # "publicURL" will be used cached_endpoint = None def __init__(self, http_client, extensions=None): self.http_client = http_client http_client.add_client(self) # Add in any extensions... if extensions: for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) def client_request(self, method, url, **kwargs): return self.http_client.client_request( self, method, url, **kwargs) def head(self, url, **kwargs): return self.client_request("HEAD", url, **kwargs) def get(self, url, **kwargs): return self.client_request("GET", url, **kwargs) def post(self, url, **kwargs): return self.client_request("POST", url, **kwargs) def put(self, url, **kwargs): return self.client_request("PUT", url, **kwargs) def delete(self, url, **kwargs): return self.client_request("DELETE", url, **kwargs) def patch(self, url, **kwargs): return self.client_request("PATCH", url, **kwargs) @staticmethod def get_class(api_name, version, version_map): """Returns the client class for the requested API version :param api_name: the name of the API, e.g. 'compute', 'image', etc :param version: the requested API version :param version_map: a dict of client classes keyed by version :rtype: a client class for the requested API version """ try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = "Invalid %s client version '%s'. must be one of: %s" % ( (api_name, version, ', '.join(version_map.keys()))) raise exceptions.UnsupportedVersion(msg) return importutils.import_class(client_path) python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/apiclient/__init__.py0000664000175300017540000000117312247122076032417 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/apiclient/fake_client.py0000664000175300017540000001315212247122076033124 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ A fake server that "responds" to API methods with pre-canned responses. All of these responses come from the spec, so if for some reason the spec's wrong the tests might raise AssertionError. I've indicated in comments the places where actual behavior differs from the spec. """ # W0102: Dangerous default value %s as argument # pylint: disable=W0102 import json import requests from ceilometerclient.openstack.common.apiclient import client from ceilometerclient.openstack.common.py3kcompat import urlutils def assert_has_keys(dct, required=[], optional=[]): for k in required: try: assert k in dct except AssertionError: extra_keys = set(dct.keys()).difference(set(required + optional)) raise AssertionError("found unexpected keys: %s" % list(extra_keys)) class TestResponse(requests.Response): """Wrap requests.Response and provide a convenient initialization. """ def __init__(self, data): super(TestResponse, self).__init__() self._content_consumed = True if isinstance(data, dict): self.status_code = data.get('status_code', 200) # Fake the text attribute to streamline Response creation text = data.get('text', "") if isinstance(text, (dict, list)): self._content = json.dumps(text) default_headers = { "Content-Type": "application/json", } else: self._content = text default_headers = {} self.headers = data.get('headers') or default_headers else: self.status_code = data def __eq__(self, other): return (self.status_code == other.status_code and self.headers == other.headers and self._content == other._content) class FakeHTTPClient(client.HTTPClient): def __init__(self, *args, **kwargs): self.callstack = [] self.fixtures = kwargs.pop("fixtures", None) or {} if not args and not "auth_plugin" in kwargs: args = (None, ) super(FakeHTTPClient, self).__init__(*args, **kwargs) def assert_called(self, method, url, body=None, pos=-1): """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 assert expected == called, 'Expected %s %s; got %s %s' % \ (expected + called) if body is not None: if self.callstack[pos][3] != body: raise AssertionError('%r != %r' % (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. """ expected = (method, url) assert self.callstack, \ "Expected %s %s but no calls were made." % expected found = False entry = None for entry in self.callstack: if expected == entry[0:2]: found = True break assert found, 'Expected %s %s; got %s' % \ (method, url, self.callstack) if body is not None: assert entry[3] == body, "%s != %s" % (entry[3], body) self.callstack = [] def clear_callstack(self): self.callstack = [] def authenticate(self): pass def client_request(self, client, method, url, **kwargs): # Check that certain things are called correctly if method in ["GET", "DELETE"]: assert "json" not in kwargs # Note the call self.callstack.append( (method, url, kwargs.get("headers") or {}, kwargs.get("json") or kwargs.get("data"))) try: fixture = self.fixtures[url][method] except KeyError: pass else: return TestResponse({"headers": fixture[0], "text": fixture[1]}) # Call the method args = urlutils.parse_qsl(urlutils.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') munged_url = munged_url.replace('-', '_') callback = "%s_%s" % (method.lower(), munged_url) if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) resp = getattr(self, callback)(**kwargs) if len(resp) == 3: status, headers, body = resp else: status, body = resp headers = {} return TestResponse({ "status_code": status, "text": body, "headers": headers, }) python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/apiclient/base.py0000664000175300017540000003704712247122076031603 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 Grid Dynamics # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ # E1102: %s is not callable # pylint: disable=E1102 import abc import urllib import six from ceilometerclient.openstack.common.apiclient import exceptions from ceilometerclient.openstack.common import strutils def getid(obj): """Return id if argument is a Resource. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: if obj.uuid: return obj.uuid except AttributeError: pass try: return obj.id except AttributeError: return obj # TODO(aababilov): call run_hooks() in HookableMixin's child classes class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): """Add a new hook of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param hook_func: hook function """ if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): """Run all hooks of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param **args: args to be passed to every hook function :param **kwargs: kwargs to be passed to every hook function """ hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) class BaseManager(HookableMixin): """Basic manager type providing common operations. Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, client): """Initializes BaseManager with `client`. :param client: instance of BaseClient descendant for HTTP requests """ super(BaseManager, self).__init__() self.client = client def _list(self, url, response_key, obj_class=None, json=None): """List the collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param obj_class: class for constructing the returned objects (self.resource_class will be used by default) :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) """ if json: body = self.client.post(url, json=json).json() else: body = self.client.get(url).json() if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... try: data = data['values'] except (KeyError, TypeError): pass return [obj_class(self, res, loaded=True) for res in data if res] def _get(self, url, response_key): """Get an object from collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'server' """ body = self.client.get(url).json() return self.resource_class(self, body[response_key], loaded=True) def _head(self, url): """Retrieve request headers for an object. :param url: a partial URL, e.g., '/servers' """ resp = self.client.head(url) return resp.status_code == 204 def _post(self, url, json, response_key, return_raw=False): """Create an object. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param return_raw: flag to force returning raw JSON instead of Python object of self.resource_class """ body = self.client.post(url, json=json).json() if return_raw: return body[response_key] return self.resource_class(self, body[response_key]) def _put(self, url, json=None, response_key=None): """Update an object with PUT method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ resp = self.client.put(url, json=json) # PUT requests may not return a body if resp.content: body = resp.json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _patch(self, url, json=None, response_key=None): """Update an object with PATCH method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ body = self.client.patch(url, json=json).json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _delete(self, url): """Delete an object. :param url: a partial URL, e.g., '/servers/my-server' """ return self.client.delete(url) @six.add_metaclass(abc.ABCMeta) class ManagerWithFind(BaseManager): """Manager with additional `find()`/`findall()` methods.""" @abc.abstractmethod def list(self): pass def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(msg) elif num_matches > 1: raise exceptions.NoUniqueMatch() else: return matches[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = kwargs.items() for obj in self.list(): try: if all(getattr(obj, attr) == value for (attr, value) in searches): found.append(obj) except AttributeError: continue return found class CrudManager(BaseManager): """Base manager class for manipulating entities. Children of this class are expected to define a `collection_key` and `key`. - `collection_key`: Usually a plural noun by convention (e.g. `entities`); used to refer collections in both URL's (e.g. `/v3/entities`) and JSON objects containing a list of member resources (e.g. `{'entities': [{}, {}, {}]}`). - `key`: Usually a singular noun by convention (e.g. `entity`); used to refer to an individual member of the collection. """ collection_key = None key = None def build_url(self, base_url=None, **kwargs): """Builds a resource URL for the given kwargs. Given an example collection where `collection_key = 'entities'` and `key = 'entity'`, the following URL's could be generated. By default, the URL will represent a collection of entities, e.g.:: /entities If kwargs contains an `entity_id`, then the URL will represent a specific member, e.g.:: /entities/{entity_id} :param base_url: if provided, the generated URL will be appended to it """ url = base_url if base_url is not None else '' url += '/%s' % self.collection_key # do we have a specific entity? entity_id = kwargs.get('%s_id' % self.key) if entity_id is not None: url += '/%s' % entity_id return url def _filter_kwargs(self, kwargs): """Drop null values and handle ids.""" for key, ref in kwargs.copy().iteritems(): if ref is None: kwargs.pop(key) else: if isinstance(ref, Resource): kwargs.pop(key) kwargs['%s_id' % key] = getid(ref) return kwargs def create(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._post( self.build_url(**kwargs), {self.key: kwargs}, self.key) def get(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._get( self.build_url(**kwargs), self.key) def head(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._head(self.build_url(**kwargs)) def list(self, base_url=None, **kwargs): """List the collection. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '', }, self.collection_key) def put(self, base_url=None, **kwargs): """Update an element. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._put(self.build_url(base_url=base_url, **kwargs)) def update(self, **kwargs): kwargs = self._filter_kwargs(kwargs) params = kwargs.copy() params.pop('%s_id' % self.key) return self._patch( self.build_url(**kwargs), {self.key: params}, self.key) def delete(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._delete( self.build_url(**kwargs)) def find(self, base_url=None, **kwargs): """Find a single item with attributes matching ``**kwargs``. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) rl = self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '', }, self.collection_key) num = len(rl) if num == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num > 1: raise exceptions.NoUniqueMatch else: return rl[0] class Extension(HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') manager_class = None def __init__(self, name, module): super(Extension, self).__init__() self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in self.module.__dict__.items(): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) else: try: if issubclass(attr_value, BaseManager): self.manager_class = attr_value except TypeError: pass def __repr__(self): return "" % self.name class Resource(object): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ HUMAN_ID = False NAME_ATTR = 'name' def __init__(self, manager, info, loaded=False): """Populate and bind to a manager. :param manager: BaseManager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ self.manager = manager self._info = info self._add_details(info) self._loaded = loaded def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) @property def human_id(self): """Human-readable ID which can be used for bash completion. """ if self.NAME_ATTR in self.__dict__ and self.HUMAN_ID: return strutils.to_slug(getattr(self, self.NAME_ATTR)) return None def _add_details(self, info): for (k, v) in info.iteritems(): try: setattr(self, k, v) self._info[k] = v except AttributeError: # In this case we already defined the attribute on the class pass def __getattr__(self, k): if k not in self.__dict__: #NOTE(bcwaldon): disallow lazy-loading if already loaded once if not self.is_loaded(): self.get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] def get(self): # set_loaded() first ... so if we have to bail, we know we tried. self.set_loaded(True) if not hasattr(self.manager, 'get'): return new = self.manager.get(self.id) if new: self._add_details(new._info) def __eq__(self, other): if not isinstance(other, Resource): return NotImplemented # two resources of different types are not equal if not isinstance(other, self.__class__): return False if hasattr(self, 'id') and hasattr(other, 'id'): return self.id == other.id return self._info == other._info def is_loaded(self): return self._loaded def set_loaded(self, val): self._loaded = val python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/apiclient/auth.py0000664000175300017540000001567112247122076031631 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 Spanish National Research Council. # 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. # E0202: An attribute inherited from %s hide this method # pylint: disable=E0202 import abc import argparse import logging import os import six from stevedore import extension from ceilometerclient.openstack.common.apiclient import exceptions logger = logging.getLogger(__name__) _discovered_plugins = {} def discover_auth_systems(): """Discover the available auth-systems. This won't take into account the old style auth-systems. """ global _discovered_plugins _discovered_plugins = {} def add_plugin(ext): _discovered_plugins[ext.name] = ext.plugin ep_namespace = "ceilometerclient.openstack.common.apiclient.auth" mgr = extension.ExtensionManager(ep_namespace) mgr.map(add_plugin) def load_auth_system_opts(parser): """Load options needed by the available auth-systems into a parser. This function will try to populate the parser with options from the available plugins. """ group = parser.add_argument_group("Common auth options") BaseAuthPlugin.add_common_opts(group) for name, auth_plugin in _discovered_plugins.iteritems(): group = parser.add_argument_group( "Auth-system '%s' options" % name, conflict_handler="resolve") auth_plugin.add_opts(group) def load_plugin(auth_system): try: plugin_class = _discovered_plugins[auth_system] except KeyError: raise exceptions.AuthSystemNotFound(auth_system) return plugin_class(auth_system=auth_system) def load_plugin_from_args(args): """Load required plugin and populate it with options. Try to guess auth system if it is not specified. Systems are tried in alphabetical order. :type args: argparse.Namespace :raises: AuthorizationFailure """ auth_system = args.os_auth_system if auth_system: plugin = load_plugin(auth_system) plugin.parse_opts(args) plugin.sufficient_options() return plugin for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)): plugin_class = _discovered_plugins[plugin_auth_system] plugin = plugin_class() plugin.parse_opts(args) try: plugin.sufficient_options() except exceptions.AuthPluginOptionsMissing: continue return plugin raise exceptions.AuthPluginOptionsMissing(["auth_system"]) @six.add_metaclass(abc.ABCMeta) class BaseAuthPlugin(object): """Base class for authentication plugins. An authentication plugin needs to override at least the authenticate method to be a valid plugin. """ auth_system = None opt_names = [] common_opt_names = [ "auth_system", "username", "password", "tenant_name", "token", "auth_url", ] def __init__(self, auth_system=None, **kwargs): self.auth_system = auth_system or self.auth_system self.opts = dict((name, kwargs.get(name)) for name in self.opt_names) @staticmethod def _parser_add_opt(parser, opt): """Add an option to parser in two variants. :param opt: option name (with underscores) """ dashed_opt = opt.replace("_", "-") env_var = "OS_%s" % opt.upper() arg_default = os.environ.get(env_var, "") arg_help = "Defaults to env[%s]." % env_var parser.add_argument( "--os-%s" % dashed_opt, metavar="<%s>" % dashed_opt, default=arg_default, help=arg_help) parser.add_argument( "--os_%s" % opt, metavar="<%s>" % dashed_opt, help=argparse.SUPPRESS) @classmethod def add_opts(cls, parser): """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 if opt not in BaseAuthPlugin.common_opt_names: cls._parser_add_opt(parser, opt) @classmethod def add_common_opts(cls, parser): """Add options that are common for several plugins. """ for opt in cls.common_opt_names: cls._parser_add_opt(parser, opt) @staticmethod def get_opt(opt_name, args): """Return option name and value. :param opt_name: name of the option, e.g., "username" :param args: parsed arguments """ return (opt_name, getattr(args, "os_%s" % opt_name, None)) def parse_opts(self, args): """Parse the actual auth-system options if any. This method is expected to populate the attribute `self.opts` with a dict containing the options and values needed to make authentication. """ self.opts.update(dict(self.get_opt(opt_name, args) for opt_name in self.opt_names)) def authenticate(self, http_client): """Authenticate using plugin defined method. The method usually analyses `self.opts` and performs a request to authentication server. :param http_client: client object that needs authentication :type http_client: HTTPClient :raises: AuthorizationFailure """ self.sufficient_options() self._do_authenticate(http_client) @abc.abstractmethod def _do_authenticate(self, http_client): """Protected method for authentication. """ def sufficient_options(self): """Check if all required options are present. :raises: AuthPluginOptionsMissing """ missing = [opt for opt in self.opt_names if not self.opts.get(opt)] if missing: raise exceptions.AuthPluginOptionsMissing(missing) @abc.abstractmethod def token_and_endpoint(self, endpoint_type, service_type): """Return token and endpoint. :param service_type: Service type of the endpoint :type service_type: string :param endpoint_type: Type of endpoint. Possible values: public or publicURL, internal or internalURL, admin or adminURL :type endpoint_type: string :returns: tuple of token and endpoint strings :raises: EndpointException """ python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/importutils.py0000664000175300017540000000421512247122076031303 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Import related utilities and helper functions. """ import sys import traceback def import_class(import_str): """Returns a class from a string including module and class.""" mod_str, _sep, class_str = import_str.rpartition('.') try: __import__(mod_str) return getattr(sys.modules[mod_str], class_str) except (ValueError, AttributeError): raise ImportError('Class %s cannot be found (%s)' % (class_str, traceback.format_exception(*sys.exc_info()))) def import_object(import_str, *args, **kwargs): """Import a class and return an instance of it.""" return import_class(import_str)(*args, **kwargs) def import_object_ns(name_space, import_str, *args, **kwargs): """Tries to import object from default namespace. Imports a class and return an instance of it, first by trying to find the class in a default namespace, then failing back to a full path if not found in the default namespace. """ import_value = "%s.%s" % (name_space, import_str) try: return import_class(import_value)(*args, **kwargs) except ImportError: return import_class(import_str)(*args, **kwargs) def import_module(import_str): """Import a module.""" __import__(import_str) return sys.modules[import_str] def try_import(import_str, default=None): """Try to import a module and if it fails return default.""" try: return import_module(import_str) except ImportError: return default python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/cliutils.py0000664000175300017540000001447612247122076030552 0ustar jenkinsjenkins00000000000000# Copyright 2012 Red Hat, Inc. # # 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. # W0603: Using the global statement # W0621: Redefining name %s from outer scope # pylint: disable=W0603,W0621 import getpass import inspect import os import sys import textwrap import prettytable import six from six import moves from ceilometerclient.openstack.common.apiclient import exceptions from ceilometerclient.openstack.common import strutils def validate_args(fn, *args, **kwargs): """Check that the supplied args are sufficient for calling a function. >>> validate_args(lambda a: None) Traceback (most recent call last): ... MissingArgs: Missing argument(s): a >>> validate_args(lambda a, b, c, d: None, 0, c=1) Traceback (most recent call last): ... MissingArgs: Missing argument(s): b, d :param fn: the function to check :param arg: the positional arguments supplied :param kwargs: the keyword arguments supplied """ argspec = inspect.getargspec(fn) num_defaults = len(argspec.defaults or []) required_args = argspec.args[:len(argspec.args) - num_defaults] def isbound(method): return getattr(method, 'im_self', None) is not None if isbound(fn): required_args.pop(0) missing = [arg for arg in required_args if arg not in kwargs] missing = missing[len(args):] if missing: raise exceptions.MissingArgs(missing) def arg(*args, **kwargs): """Decorator for CLI args. Example: >>> @arg("name", help="Name of the new entity") ... def entity_create(args): ... pass """ def _decorator(func): add_arg(func, *args, **kwargs) return func return _decorator def env(*args, **kwargs): """Returns the first environment variable set. If all are empty, defaults to '' or keyword arg `default`. """ for arg in args: value = os.environ.get(arg, None) if value: return value return kwargs.get('default', '') def add_arg(func, *args, **kwargs): """Bind CLI arguments to a shell.py `do_foo` function.""" if not hasattr(func, 'arguments'): func.arguments = [] # NOTE(sirp): avoid dups that can occur when the module is shared across # tests. if (args, kwargs) not in func.arguments: # Because of the semantics of decorator composition if we just append # to the options list positional options will appear to be backwards. func.arguments.insert(0, (args, kwargs)) def unauthenticated(func): """Adds 'unauthenticated' attribute to decorated function. Usage: >>> @unauthenticated ... def mymethod(f): ... pass """ func.unauthenticated = True return func def isunauthenticated(func): """Checks if the function does not require authentication. Mark such functions with the `@unauthenticated` decorator. :returns: bool """ return getattr(func, 'unauthenticated', False) def print_list(objs, fields, formatters=None, sortby_index=0, mixed_case_fields=None): """Print a list or objects as a table, one row per object. :param objs: iterable of :class:`Resource` :param fields: attributes that correspond to columns, in order :param formatters: `dict` of callables for field formatting :param sortby_index: index of the field for sorting table rows :param mixed_case_fields: fields corresponding to object attributes that have mixed case names (e.g., 'serverId') """ formatters = formatters or {} mixed_case_fields = mixed_case_fields or [] if sortby_index is None: kwargs = {} else: kwargs = {'sortby': fields[sortby_index]} pt = prettytable.PrettyTable(fields, caching=False) pt.align = 'l' for o in objs: row = [] for field in fields: if field in formatters: row.append(formatters[field](o)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') row.append(data) pt.add_row(row) print(strutils.safe_encode(pt.get_string(**kwargs))) def print_dict(dct, dict_property="Property", wrap=0): """Print a `dict` as a table of two columns. :param dct: `dict` to print :param dict_property: name of the first column :param wrap: wrapping for the second column """ pt = prettytable.PrettyTable([dict_property, 'Value'], caching=False) pt.align = 'l' for k, v in dct.iteritems(): # convert dict to str to check length if isinstance(v, dict): v = str(v) if wrap > 0: v = textwrap.fill(str(v), wrap) # if value has a newline, add in multiple rows # e.g. fault with stacktrace if v and isinstance(v, six.string_types) and r'\n' in v: lines = v.strip().split(r'\n') col1 = k for line in lines: pt.add_row([col1, line]) col1 = '' else: pt.add_row([k, v]) print(strutils.safe_encode(pt.get_string())) def get_password(max_password_prompts=3): """Read password from TTY.""" verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD")) pw = None if hasattr(sys.stdin, "isatty") and sys.stdin.isatty(): # Check for Ctrl-D try: for _ in moves.range(max_password_prompts): pw1 = getpass.getpass("OS Password: ") if verify: pw2 = getpass.getpass("Please verify: ") else: pw2 = pw1 if pw1 == pw2 and pw1: pw = pw1 break except EOFError: pass return pw python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/__init__.py0000664000175300017540000000000012247122076030433 0ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/gettextutils.py0000664000175300017540000003154512247122076031463 0ustar jenkinsjenkins00000000000000# Copyright 2012 Red Hat, Inc. # Copyright 2013 IBM Corp. # 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. """ gettext for openstack-common modules. Usual usage in an openstack.common module: from ceilometerclient.openstack.common.gettextutils import _ """ import copy import gettext import logging import os import re try: import UserString as _userString except ImportError: import collections as _userString from babel import localedata import six _localedir = os.environ.get('ceilometerclient'.upper() + '_LOCALEDIR') _t = gettext.translation('ceilometerclient', localedir=_localedir, fallback=True) _AVAILABLE_LANGUAGES = {} USE_LAZY = False def enable_lazy(): """Convenience function for configuring _() to use lazy gettext Call this at the start of execution to enable the gettextutils._ function to use lazy gettext functionality. This is useful if your project is importing _ directly instead of using the gettextutils.install() way of importing the _ function. """ global USE_LAZY USE_LAZY = True def _(msg): if USE_LAZY: return Message(msg, 'ceilometerclient') else: if six.PY3: return _t.gettext(msg) return _t.ugettext(msg) def install(domain, lazy=False): """Install a _() function using the given translation domain. Given a translation domain, install a _() function using gettext's install() function. The main difference from gettext.install() is that we allow overriding the default localedir (e.g. /usr/share/locale) using a translation-domain-specific environment variable (e.g. NOVA_LOCALEDIR). :param domain: the translation domain :param lazy: indicates whether or not to install the lazy _() function. The lazy _() introduces a way to do deferred translation of messages by installing a _ that builds Message objects, instead of strings, which can then be lazily translated into any available locale. """ if lazy: # NOTE(mrodden): Lazy gettext functionality. # # The following introduces a deferred way to do translations on # messages in OpenStack. We override the standard _() function # and % (format string) operation to build Message objects that can # later be translated when we have more information. # # Also included below is an example LocaleHandler that translates # Messages to an associated locale, effectively allowing many logs, # each with their own locale. def _lazy_gettext(msg): """Create and return a Message object. Lazy gettext function for a given domain, it is a factory method for a project/module to get a lazy gettext function for its own translation domain (i.e. nova, glance, cinder, etc.) Message encapsulates a string so that we can translate it later when needed. """ return Message(msg, domain) from six import moves moves.builtins.__dict__['_'] = _lazy_gettext else: localedir = '%s_LOCALEDIR' % domain.upper() if six.PY3: gettext.install(domain, localedir=os.environ.get(localedir)) else: gettext.install(domain, localedir=os.environ.get(localedir), unicode=True) class Message(_userString.UserString, object): """Class used to encapsulate translatable messages.""" def __init__(self, msg, domain): # _msg is the gettext msgid and should never change self._msg = msg self._left_extra_msg = '' self._right_extra_msg = '' self._locale = None self.params = None self.domain = domain @property def data(self): # NOTE(mrodden): this should always resolve to a unicode string # that best represents the state of the message currently localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR') if self.locale: lang = gettext.translation(self.domain, localedir=localedir, languages=[self.locale], fallback=True) else: # use system locale for translations lang = gettext.translation(self.domain, localedir=localedir, fallback=True) if six.PY3: ugettext = lang.gettext else: ugettext = lang.ugettext full_msg = (self._left_extra_msg + ugettext(self._msg) + self._right_extra_msg) if self.params is not None: full_msg = full_msg % self.params return six.text_type(full_msg) @property def locale(self): return self._locale @locale.setter def locale(self, value): self._locale = value if not self.params: return # This Message object may have been constructed with one or more # Message objects as substitution parameters, given as a single # Message, or a tuple or Map containing some, so when setting the # locale for this Message we need to set it for those Messages too. if isinstance(self.params, Message): self.params.locale = value return if isinstance(self.params, tuple): for param in self.params: if isinstance(param, Message): param.locale = value return if isinstance(self.params, dict): for param in self.params.values(): if isinstance(param, Message): param.locale = value def _save_dictionary_parameter(self, dict_param): full_msg = self.data # look for %(blah) fields in string; # ignore %% and deal with the # case where % is first character on the line keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', full_msg) # if we don't find any %(blah) blocks but have a %s if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg): # apparently the full dictionary is the parameter params = copy.deepcopy(dict_param) else: params = {} for key in keys: try: params[key] = copy.deepcopy(dict_param[key]) except TypeError: # cast uncopyable thing to unicode string params[key] = six.text_type(dict_param[key]) return params def _save_parameters(self, other): # we check for None later to see if # we actually have parameters to inject, # so encapsulate if our parameter is actually None if other is None: self.params = (other, ) elif isinstance(other, dict): self.params = self._save_dictionary_parameter(other) else: # fallback to casting to unicode, # this will handle the problematic python code-like # objects that cannot be deep-copied try: self.params = copy.deepcopy(other) except TypeError: self.params = six.text_type(other) return self # overrides to be more string-like def __unicode__(self): return self.data def __str__(self): if six.PY3: return self.__unicode__() return self.data.encode('utf-8') def __getstate__(self): to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg', 'domain', 'params', '_locale'] new_dict = self.__dict__.fromkeys(to_copy) for attr in to_copy: new_dict[attr] = copy.deepcopy(self.__dict__[attr]) return new_dict def __setstate__(self, state): for (k, v) in state.items(): setattr(self, k, v) # operator overloads def __add__(self, other): copied = copy.deepcopy(self) copied._right_extra_msg += other.__str__() return copied def __radd__(self, other): copied = copy.deepcopy(self) copied._left_extra_msg += other.__str__() return copied def __mod__(self, other): # do a format string to catch and raise # any possible KeyErrors from missing parameters self.data % other copied = copy.deepcopy(self) return copied._save_parameters(other) def __mul__(self, other): return self.data * other def __rmul__(self, other): return other * self.data def __getitem__(self, key): return self.data[key] def __getslice__(self, start, end): return self.data.__getslice__(start, end) def __getattribute__(self, name): # NOTE(mrodden): handle lossy operations that we can't deal with yet # These override the UserString implementation, since UserString # uses our __class__ attribute to try and build a new message # after running the inner data string through the operation. # At that point, we have lost the gettext message id and can just # safely resolve to a string instead. ops = ['capitalize', 'center', 'decode', 'encode', 'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] if name in ops: return getattr(self.data, name) else: return _userString.UserString.__getattribute__(self, name) def get_available_languages(domain): """Lists the available languages for the given translation domain. :param domain: the domain to get languages for """ if domain in _AVAILABLE_LANGUAGES: return copy.copy(_AVAILABLE_LANGUAGES[domain]) localedir = '%s_LOCALEDIR' % domain.upper() find = lambda x: gettext.find(domain, localedir=os.environ.get(localedir), languages=[x]) # NOTE(mrodden): en_US should always be available (and first in case # order matters) since our in-line message strings are en_US language_list = ['en_US'] # NOTE(luisg): Babel <1.0 used a function called list(), which was # renamed to locale_identifiers() in >=1.0, the requirements master list # requires >=0.9.6, uncapped, so defensively work with both. We can remove # this check when the master list updates to >=1.0, and update all projects list_identifiers = (getattr(localedata, 'list', None) or getattr(localedata, 'locale_identifiers')) locale_identifiers = list_identifiers() for i in locale_identifiers: if find(i) is not None: language_list.append(i) _AVAILABLE_LANGUAGES[domain] = language_list return copy.copy(language_list) def get_localized_message(message, user_locale): """Gets a localized version of the given message in the given locale. If the message is not a Message object the message is returned as-is. If the locale is None the message is translated to the default locale. :returns: the translated message in unicode, or the original message if it could not be translated """ translated = message if isinstance(message, Message): original_locale = message.locale message.locale = user_locale translated = six.text_type(message) message.locale = original_locale return translated class LocaleHandler(logging.Handler): """Handler that can have a locale associated to translate Messages. A quick example of how to utilize the Message class above. LocaleHandler takes a locale and a target logging.Handler object to forward LogRecord objects to after translating the internal Message. """ def __init__(self, locale, target): """Initialize a LocaleHandler :param locale: locale to use for translating messages :param target: logging.Handler object to forward LogRecord objects to after translation """ logging.Handler.__init__(self) self.locale = locale self.target = target def emit(self, record): if isinstance(record.msg, Message): # set the locale and resolve to a string record.msg.locale = self.locale self.target.emit(record) python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/strutils.py0000664000175300017540000001622612247122076030606 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ System-level utilities and helper functions. """ import re import sys import unicodedata import six from ceilometerclient.openstack.common.gettextutils import _ # noqa # Used for looking up extensions of text # to their 'multiplied' byte amount BYTE_MULTIPLIERS = { '': 1, 't': 1024 ** 4, 'g': 1024 ** 3, 'm': 1024 ** 2, 'k': 1024, } BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)') TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") def int_from_bool_as_string(subject): """Interpret a string as a boolean and return either 1 or 0. Any string value in: ('True', 'true', 'On', 'on', '1') is interpreted as a boolean True. Useful for JSON-decoded stuff and config file parsing """ return bool_from_string(subject) and 1 or 0 def bool_from_string(subject, strict=False): """Interpret a string as a boolean. A case-insensitive match is performed such that strings matching 't', 'true', 'on', 'y', 'yes', or '1' are considered True and, when `strict=False`, anything else is considered False. Useful for JSON-decoded stuff and config file parsing. If `strict=True`, unrecognized values, including None, will raise a ValueError which is useful when parsing values passed in from an API call. Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. """ if not isinstance(subject, six.string_types): subject = str(subject) lowered = subject.strip().lower() if lowered in TRUE_STRINGS: return True elif lowered in FALSE_STRINGS: return False elif strict: acceptable = ', '.join( "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) msg = _("Unrecognized value '%(val)s', acceptable values are:" " %(acceptable)s") % {'val': subject, 'acceptable': acceptable} raise ValueError(msg) else: return False def safe_decode(text, incoming=None, errors='strict'): """Decodes incoming str using `incoming` if they're not already unicode. :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a unicode `incoming` encoded representation of it. :raises TypeError: If text is not an instance of str """ if not isinstance(text, six.string_types): raise TypeError("%s can't be decoded" % type(text)) if isinstance(text, six.text_type): return text if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) try: return text.decode(incoming, errors) except UnicodeDecodeError: # Note(flaper87) If we get here, it means that # sys.stdin.encoding / sys.getdefaultencoding # didn't return a suitable encoding to decode # text. This happens mostly when global LANG # var is not set correctly and there's no # default encoding. In this case, most likely # python will use ASCII or ANSI encoders as # default encodings but they won't be capable # of decoding non-ASCII characters. # # Also, UTF-8 is being used since it's an ASCII # extension. return text.decode('utf-8', errors) def safe_encode(text, incoming=None, encoding='utf-8', errors='strict'): """Encodes incoming str/unicode using `encoding`. If incoming is not specified, text is expected to be encoded with current python's default encoding. (`sys.getdefaultencoding`) :param incoming: Text's current encoding :param encoding: Expected encoding for text (Default UTF-8) :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a bytestring `encoding` encoded representation of it. :raises TypeError: If text is not an instance of str """ if not isinstance(text, six.string_types): raise TypeError("%s can't be encoded" % type(text)) if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) if isinstance(text, six.text_type): return text.encode(encoding, errors) elif text and encoding != incoming: # Decode text before encoding it with `encoding` text = safe_decode(text, incoming, errors) return text.encode(encoding, errors) return text def to_bytes(text, default=0): """Converts a string into an integer of bytes. Looks at the last characters of the text to determine what conversion is needed to turn the input text into a byte number. Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive) :param text: String input for bytes size conversion. :param default: Default return value when text is blank. """ match = BYTE_REGEX.search(text) if match: magnitude = int(match.group(1)) mult_key_org = match.group(2) if not mult_key_org: return magnitude elif text: msg = _('Invalid string format: %s') % text raise TypeError(msg) else: return default mult_key = mult_key_org.lower().replace('b', '', 1) multiplier = BYTE_MULTIPLIERS.get(mult_key) if multiplier is None: msg = _('Unknown byte multiplier: %s') % mult_key_org raise TypeError(msg) return magnitude * multiplier def to_slug(value, incoming=None, errors="strict"): """Normalize string. Convert to lowercase, remove non-word characters, and convert spaces to hyphens. Inspired by Django's `slugify` filter. :param value: Text to slugify :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: slugified unicode representation of `value` :raises TypeError: If text is not an instance of str """ value = safe_decode(value, incoming, errors) # NOTE(aababilov): no need to use safe_(encode|decode) here: # encodings are always "ascii", error handling is always "ignore" # and types are always known (first: unicode; second: str) value = unicodedata.normalize("NFKD", value).encode( "ascii", "ignore").decode("ascii") value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() return SLUGIFY_HYPHENATE_RE.sub("-", value) python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/py3kcompat/0000775000175300017540000000000012247122150030417 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/py3kcompat/urlutils.py0000664000175300017540000000332012247122076032661 0ustar jenkinsjenkins00000000000000# # Copyright 2013 Canonical Ltd. # 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. # """ Python2/Python3 compatibility layer for OpenStack """ import six if six.PY3: # python3 import urllib.error import urllib.parse import urllib.request urlencode = urllib.parse.urlencode urljoin = urllib.parse.urljoin quote = urllib.parse.quote parse_qsl = urllib.parse.parse_qsl unquote = urllib.parse.unquote urlparse = urllib.parse.urlparse urlsplit = urllib.parse.urlsplit urlunsplit = urllib.parse.urlunsplit SplitResult = urllib.parse.SplitResult urlopen = urllib.request.urlopen URLError = urllib.error.URLError pathname2url = urllib.request.pathname2url else: # python2 import urllib import urllib2 import urlparse urlencode = urllib.urlencode quote = urllib.quote unquote = urllib.unquote parse = urlparse parse_qsl = parse.parse_qsl urljoin = parse.urljoin urlparse = parse.urlparse urlsplit = parse.urlsplit urlunsplit = parse.urlunsplit SplitResult = parse.SplitResult urlopen = urllib2.urlopen URLError = urllib2.URLError pathname2url = urllib.pathname2url python-ceilometerclient-1.0.8/ceilometerclient/openstack/common/py3kcompat/__init__.py0000664000175300017540000000117112247122076032537 0ustar jenkinsjenkins00000000000000# # Copyright 2013 Canonical Ltd. # 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. # python-ceilometerclient-1.0.8/ceilometerclient/v1/0000775000175300017540000000000012247122150023374 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/v1/client.py0000664000175300017540000000275212247122076025241 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.common import http from ceilometerclient.v1 import meters class Client(http.HTTPClient): """Client for the Ceilometer v1 API. :param string endpoint: A user-supplied endpoint URL for the ceilometer service. :param function token: Provides token for authentication. :param integer timeout: Allows customization of the timeout for client http requests. (optional) """ def __init__(self, *args, **kwargs): """Initialize a new client for the Ceilometer v1 API.""" super(Client, self).__init__(*args, **kwargs) self.meters = meters.MeterManager(self) self.samples = meters.SampleManager(self) self.users = meters.UserManager(self) self.resources = meters.ResourceManager(self) self.projects = meters.ProjectManager(self) python-ceilometerclient-1.0.8/ceilometerclient/v1/__init__.py0000664000175300017540000000126412247122076025517 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.v1.client import Client # noqa python-ceilometerclient-1.0.8/ceilometerclient/v1/meters.py0000664000175300017540000001240412247122076025255 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenMeter LLC. # 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 six from ceilometerclient.common import base def _get_opt_path(simple_params=[], **kwargs): l = [] #get simple paramters for key in simple_params: val = kwargs.get(key) if val: l.append(key + '=' + val) #get metadata query paramters metaquery = kwargs.get('metaquery') if metaquery: l.extend(metaquery.split(':')) return '&'.join(l) class User(base.Resource): def __init__(self, manager, info, loaded=False): _d = {six.u('user_id'): info} super(User, self).__init__(manager, _d, loaded) def __repr__(self): return "" % self._info def data(self, **kwargs): return self.manager.data(self, **kwargs) class UserManager(base.Manager): resource_class = User def list(self, **kwargs): s = kwargs.get('source') if s: path = '/sources/%s/users' % (s) else: path = '/users' return self._list('/v1%s' % path, 'users') class Project(base.Resource): def __init__(self, manager, info, loaded=False): _d = {six.u('project_id'): info} super(Project, self).__init__(manager, _d, loaded) def __repr__(self): return "" % self._info def data(self, **kwargs): return self.manager.data(self, **kwargs) class ProjectManager(base.Manager): resource_class = Project def list(self, **kwargs): s = kwargs.get('source') if s: path = '/sources/%s/projects' % (kwargs['source']) else: path = '/projects' return self._list('/v1%s' % path, 'projects') class Resource(base.Resource): def __repr__(self): return "" % self._info def data(self, **kwargs): return self.manager.data(self, **kwargs) class ResourceManager(base.Manager): resource_class = Resource def list(self, **kwargs): u = kwargs.get('user_id') s = kwargs.get('source') p = kwargs.get('project_id') opts_path = _get_opt_path(['start_timestamp', 'end_timestamp'], **kwargs) if u: path = '/users/%s/resources' % (u) elif s: path = '/sources/%s/resources' % (s) elif p: path = '/projects/%s/resources' % (p) else: path = '/resources' if opts_path: path = '/v1%s?%s' % (path, opts_path) else: path = '/v1%s' % (path) return self._list(path, 'resources') class Sample(base.Resource): def __init__(self, manager, info, loaded=False): smaller = dict((k, v) for (k, v) in six.iteritems(info) if k not in ('metadata', 'message_signature')) super(Sample, self).__init__(manager, smaller, loaded) def __repr__(self): return "" % self._info def data(self, **kwargs): return self.manager.data(self, **kwargs) class SampleManager(base.Manager): resource_class = Sample def list(self, **kwargs): c = kwargs['counter_name'] r = kwargs.get('resource_id') u = kwargs.get('user_id') p = kwargs.get('project_id') s = kwargs.get('source') opts_path = _get_opt_path(['start_timestamp', 'end_timestamp'], **kwargs) if r: path = '/resources/%s/meters/%s' % (r, c) elif u: path = '/users/%s/meters/%s' % (u, c) elif p: path = '/projects/%s/meters/%s' % (p, c) elif s: path = '/sources/%s/meters/%s' % (s, c) else: path = '/meters' if opts_path: path = '/v1%s?%s' % (path, opts_path) else: path = '/v1%s' % (path) return self._list(path, 'events') class Meter(base.Resource): def __repr__(self): return "" % self._info def data(self, **kwargs): return self.manager.data(self, **kwargs) class MeterManager(base.Manager): resource_class = Meter def list(self, **kwargs): r = kwargs.get('resource_id') u = kwargs.get('user_id') p = kwargs.get('project_id') s = kwargs.get('source') opts_path = _get_opt_path(**kwargs) if u: path = '/users/%s/meters' % u elif r: path = '/resources/%s/meters' % r elif p: path = '/projects/%s/meters' % p elif s: path = '/sources/%s/meters' % s else: path = '/meters' if opts_path: path = '/v1%s?%s' % (path, opts_path) else: path = '/v1%s' % (path) return self._list(path, 'meters') python-ceilometerclient-1.0.8/ceilometerclient/v1/shell.py0000664000175300017540000001315512247122076025071 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.common import utils import ceilometerclient.exc as exc @utils.arg('-m', '--metaquery', metavar='', help='Query into the metadata metadata.key=value:..') @utils.arg('-s', '--source', metavar='', help='ID of the resource to show samples for.') @utils.arg('-r', '--resource_id', metavar='', help='ID of the resource to show samples for.') @utils.arg('-u', '--user_id', metavar='', help='ID of the user to show samples for.') @utils.arg('-p', '--project_id', metavar='', help='ID of the project to show samples for.') @utils.arg('-c', '--counter_name', metavar='', help='Name of meter to show samples for.') @utils.arg('--start', metavar='', help='ISO date in UTC which limits events by ' 'timestamp >= this value') @utils.arg('--end', metavar='', help='ISO date in UTC which limits events by ' 'timestamp <= this value') def do_sample_list(cc, args): '''List the samples for this meters.''' fields = {'counter_name': args.counter_name, 'resource_id': args.resource_id, 'user_id': args.user_id, 'project_id': args.project_id, 'source': args.source, 'start_timestamp': args.start, 'end_timestamp': args.end, 'metaquery': args.metaquery} try: samples = cc.samples.list(**fields) except exc.HTTPNotFound: raise exc.CommandError('Samples not found: %s' % args.counter_name) else: field_labels = ['Resource ID', 'Name', 'Type', 'Volume', 'Timestamp'] fields = ['resource_id', 'counter_name', 'counter_type', 'counter_volume', 'timestamp'] utils.print_list(samples, fields, field_labels, sortby=0) @utils.arg('-m', '--metaquery', metavar='', help='Query into the metadata metadata.key=value:..') @utils.arg('-s', '--source', metavar='', help='ID of the resource to show samples for.') @utils.arg('-r', '--resource_id', metavar='', help='ID of the resource to show samples for.') @utils.arg('-u', '--user_id', metavar='', help='ID of the user to show samples for.') @utils.arg('-p', '--project_id', metavar='', help='ID of the project to show samples for.') def do_meter_list(cc, args={}): '''List the user's meter''' fields = {'resource_id': args.resource_id, 'user_id': args.user_id, 'project_id': args.project_id, 'source': args.source} meters = cc.meters.list(**fields) field_labels = ['Name', 'Type', 'Resource ID', 'User ID', 'Project ID'] fields = ['name', 'type', 'resource_id', 'user_id', 'project_id'] utils.print_list(meters, fields, field_labels, sortby=0) @utils.arg('-s', '--source', metavar='', help='ID of the resource to show projects for.') def do_user_list(cc, args={}): '''List the users.''' kwargs = {'source': args.source} users = cc.users.list(**kwargs) field_labels = ['User ID'] fields = ['user_id'] utils.print_list(users, fields, field_labels, sortby=0) @utils.arg('-s', '--source', metavar='', help='ID of the resource to show for.') @utils.arg('-u', '--user_id', metavar='', help='ID of the user to show resources for.') @utils.arg('-p', '--project_id', metavar='', help='ID of the project to show samples for.') @utils.arg('-m', '--metaquery', metavar='', help='Query into the metadata metadata.key=value:..') @utils.arg('--start', metavar='', help='ISO date in UTC which limits resouces by ' 'last update time >= this value') @utils.arg('--end', metavar='', help='ISO date in UTC which limits resouces by ' 'last update time <= this value') def do_resource_list(cc, args={}): '''List the resources.''' kwargs = {'source': args.source, 'user_id': args.user_id, 'project_id': args.project_id, 'start_timestamp': args.start, 'end_timestamp': args.end, 'metaquery': args.metaquery} resources = cc.resources.list(**kwargs) field_labels = ['Resource ID', 'Source', 'User ID', 'Project ID'] fields = ['resource_id', 'source', 'user_id', 'project_id'] utils.print_list(resources, fields, field_labels, sortby=1) @utils.arg('-s', '--source', metavar='', help='ID of the resource to show projects for.') def do_project_list(cc, args={}): '''List the projects.''' kwargs = {'source': args.source} projects = cc.projects.list(**kwargs) field_labels = ['Project ID'] fields = ['project_id'] utils.print_list(projects, fields, field_labels, sortby=0) python-ceilometerclient-1.0.8/ceilometerclient/exc.py0000664000175300017540000000642312247122076024213 0ustar jenkinsjenkins00000000000000# 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 sys class BaseException(Exception): """An error occurred.""" def __init__(self, message=None): self.message = message def __str__(self): return self.message or self.__class__.__doc__ class CommandError(BaseException): """Invalid usage of CLI.""" class InvalidEndpoint(BaseException): """The provided endpoint is invalid.""" class CommunicationError(BaseException): """Unable to communicate with server.""" class ClientException(Exception): """DEPRECATED.""" class HTTPException(ClientException): """Base exception for all HTTP-derived exceptions.""" code = 'N/A' def __init__(self, details=None): self.details = details def __str__(self): return "%s (HTTP %s)" % (self.__class__.__name__, self.code) class HTTPMultipleChoices(HTTPException): code = 300 def __str__(self): self.details = ("Requested version of OpenStack Images API is not" "available.") return "%s (HTTP %s) %s" % (self.__class__.__name__, self.code, self.details) class BadRequest(HTTPException): """DEPRECATED.""" code = 400 class HTTPBadRequest(BadRequest): pass class Unauthorized(HTTPException): """DEPRECATED.""" code = 401 class HTTPUnauthorized(Unauthorized): pass class Forbidden(HTTPException): """DEPRECATED.""" code = 403 class HTTPForbidden(Forbidden): pass class NotFound(HTTPException): """DEPRECATED.""" code = 404 class HTTPNotFound(NotFound): pass class HTTPMethodNotAllowed(HTTPException): code = 405 class Conflict(HTTPException): """DEPRECATED.""" code = 409 class HTTPConflict(Conflict): pass class OverLimit(HTTPException): """DEPRECATED.""" code = 413 class HTTPOverLimit(OverLimit): pass class HTTPInternalServerError(HTTPException): code = 500 class HTTPNotImplemented(HTTPException): code = 501 class HTTPBadGateway(HTTPException): code = 502 class ServiceUnavailable(HTTPException): """DEPRECATED.""" code = 503 class HTTPServiceUnavailable(ServiceUnavailable): pass #NOTE(bcwaldon): Build a mapping of HTTP codes to corresponding exception # classes _code_map = {} for obj_name in dir(sys.modules[__name__]): if obj_name.startswith('HTTP'): obj = getattr(sys.modules[__name__], obj_name) _code_map[obj.code] = obj def from_response(response): """Return an instance of an HTTPException based on httplib response.""" cls = _code_map.get(response.status, HTTPException) return cls() class NoTokenLookupException(Exception): """DEPRECATED.""" pass class EndpointNotFound(Exception): """DEPRECATED.""" pass python-ceilometerclient-1.0.8/ceilometerclient/tests/0000775000175300017540000000000012247122150024210 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/tests/test_http.py0000664000175300017540000000400512247122076026606 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import print_function from ceilometerclient.common import http from ceilometerclient.tests import utils class HttpClientTest(utils.BaseTestCase): url = 'http://localhost' def test_url_generation_trailing_slash_in_base(self): client = http.HTTPClient("%s/" % self.url) url = client._make_connection_url('/v1/resources') print(client.connection_params) self.assertEqual(url, '/v1/resources') def test_url_generation_without_trailing_slash_in_base(self): client = http.HTTPClient(self.url) url = client._make_connection_url('/v1/resources') print(client.connection_params) self.assertEqual(url, '/v1/resources') def test_url_generation_prefix_slash_in_path(self): client = http.HTTPClient("%s/" % self.url) url = client._make_connection_url('/v1/resources') print(client.connection_params) self.assertEqual(url, '/v1/resources') def test_url_generation_without_prefix_slash_in_path(self): client = http.HTTPClient(self.url) url = client._make_connection_url('v1/resources') print(client.connection_params) self.assertEqual(url, '/v1/resources') def test_get_connection(self): client = http.HTTPClient(self.url) self.assertIsNotNone(client.get_connection()) class HttpsClientTest(HttpClientTest): url = 'https://localhost' python-ceilometerclient-1.0.8/ceilometerclient/tests/__init__.py0000664000175300017540000000000012247122076026316 0ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/tests/test_shell.py0000664000175300017540000000651112247122076026742 0ustar jenkinsjenkins00000000000000# 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 httplib2 import re import six import sys import fixtures import mock from testtools import matchers from keystoneclient.v2_0 import client as ksclient from ceilometerclient import exc from ceilometerclient import shell as ceilometer_shell from ceilometerclient.tests import utils from ceilometerclient.v1 import client as v1client FAKE_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', 'OS_AUTH_URL': 'http://no.where'} class ShellTest(utils.BaseTestCase): re_options = re.DOTALL | re.MULTILINE # Patch os.environ to avoid required auth info. def make_env(self, exclude=None): env = dict((k, v) for k, v in FAKE_ENV.items() if k != exclude) self.useFixture(fixtures.MonkeyPatch('os.environ', env)) def setUp(self): super(ShellTest, self).setUp() @mock.patch.object(ksclient, 'Client') @mock.patch.object(v1client.Client, 'json_request') @mock.patch.object(v1client.Client, 'raw_request') def shell(self, argstr, mock_ksclient, mock_json, mock_raw): orig = sys.stdout try: sys.stdout = six.StringIO() _shell = ceilometer_shell.CeilometerShell() _shell.main(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertEqual(exc_value.code, 0) finally: out = sys.stdout.getvalue() sys.stdout.close() sys.stdout = orig return out def test_help_unknown_command(self): self.assertRaises(exc.CommandError, self.shell, 'help foofoo') def test_debug(self): httplib2.debuglevel = 0 self.shell('--debug help') self.assertEqual(httplib2.debuglevel, 1) def test_help(self): required = [ '.*?^usage: ceilometer', '.*?^See "ceilometer help COMMAND" ' 'for help on a specific command', ] for argstr in ['--help', 'help']: help_text = self.shell(argstr) for r in required: self.assertThat(help_text, matchers.MatchesRegex(r, self.re_options)) def test_help_on_subcommand(self): required = [ '.*?^usage: ceilometer meter-list', ".*?^List the user's meter", ] argstrings = [ 'help meter-list', ] for argstr in argstrings: help_text = self.shell(argstr) for r in required: self.assertThat(help_text, matchers.MatchesRegex(r, self.re_options)) def test_auth_param(self): self.make_env(exclude='OS_USERNAME') self.test_help() python-ceilometerclient-1.0.8/ceilometerclient/tests/utils.py0000664000175300017540000000377012247122076025740 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import fixtures import six import testtools from ceilometerclient.common import http class BaseTestCase(testtools.TestCase): def setUp(self): super(BaseTestCase, self).setUp() self.useFixture(fixtures.FakeLogger()) class FakeAPI(object): def __init__(self, fixtures): self.fixtures = fixtures self.calls = [] def _request(self, method, url, headers=None, body=None): call = (method, url, headers or {}, body) self.calls.append(call) return self.fixtures[url][method] def raw_request(self, *args, **kwargs): fixture = self._request(*args, **kwargs) body_iter = http.ResponseBodyIterator(six.StringIO(fixture[1])) return FakeResponse(fixture[0]), body_iter def json_request(self, *args, **kwargs): fixture = self._request(*args, **kwargs) return FakeResponse(fixture[0]), fixture[1] class FakeResponse(object): def __init__(self, headers, body=None, version=None): """:param headers: dict representing HTTP response headers :param body: file-like object """ self.headers = headers self.body = body def getheaders(self): return copy.deepcopy(self.headers).items() def getheader(self, key, default): return self.headers.get(key, default) def read(self, amt): return self.body.read(amt) python-ceilometerclient-1.0.8/ceilometerclient/tests/v1/0000775000175300017540000000000012247122150024536 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/tests/v1/test_samples.py0000664000175300017540000001471212247122076027627 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.tests import utils import ceilometerclient.v1.meters fixtures = { '/v1/users/freddy/meters/balls': { 'GET': ( {}, {'events': [ { 'resource_id': 'inst-0045', 'project_id': 'melbourne_open', 'user_id': 'freddy', 'name': 'tennis', 'type': 'counter', 'unit': 'balls', 'volume': 3, 'timestamp': None, 'resource_metadata': None, }, ]}, ), }, '/v1/sources/openstack/meters/this': { 'GET': ( {}, {'events': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'this', 'type': 'counter', 'unit': 'b', 'volume': 45, 'timestamp': None, 'resource_metadata': None, }, ]}, ), }, '/v1/projects/dig_the_ditch/meters/meters': { 'GET': ( {}, {'events': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'meters', 'type': 'counter', 'unit': 'meters', 'volume': 345, 'timestamp': None, 'resource_metadata': None, }, ]}, ), }, '/v1/meters?metadata.zxc_id=foo': { 'GET': ( {}, {'events': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'this', 'type': 'counter', 'unit': 'meters', 'volume': 98, 'timestamp': None, 'resource_metadata': {'zxc_id': 'foo'}, }, ]}, ), }, '/v1/users/freddy/meters/balls?start_timestamp=now&end_timestamp=now': { 'GET': ( {}, {'events': [ { 'resource_id': 'inst-0045', 'project_id': 'melbourne_open', 'user_id': 'freddy', 'name': 'tennis', 'type': 'counter', 'unit': 'balls', 'volume': 3, 'timestamp': 'now', 'resource_metadata': None, }, ]}, ), }, '/v1/meters': { 'GET': ( {}, {'meters': []}, ), }, } class SampleManagerTest(utils.BaseTestCase): def setUp(self): super(SampleManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v1.meters.SampleManager(self.api) def test_list_all(self): samples = list(self.mgr.list(counter_name=None)) expect = [ ('GET', '/v1/meters', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 0) def test_list_by_source(self): samples = list(self.mgr.list(source='openstack', counter_name='this')) expect = [ ('GET', '/v1/sources/openstack/meters/this', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].resource_id, 'b') def test_list_by_user(self): samples = list(self.mgr.list(user_id='freddy', counter_name='balls')) expect = [ ('GET', '/v1/users/freddy/meters/balls', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].project_id, 'melbourne_open') self.assertEqual(samples[0].user_id, 'freddy') self.assertEqual(samples[0].volume, 3) def test_list_by_project(self): samples = list(self.mgr.list(project_id='dig_the_ditch', counter_name='meters')) expect = [ ('GET', '/v1/projects/dig_the_ditch/meters/meters', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].project_id, 'dig_the_ditch') self.assertEqual(samples[0].volume, 345) self.assertEqual(samples[0].unit, 'meters') def test_list_by_metaquery(self): samples = list(self.mgr.list(metaquery='metadata.zxc_id=foo', counter_name='this')) expect = [ ('GET', '/v1/meters?metadata.zxc_id=foo', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].resource_metadata['zxc_id'], 'foo') def test_list_by_timestamp(self): samples = list(self.mgr.list(user_id='freddy', counter_name='balls', start_timestamp='now', end_timestamp='now')) expect = [ ('GET', '/v1/users/freddy/meters/balls?' + 'start_timestamp=now&end_timestamp=now', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].project_id, 'melbourne_open') self.assertEqual(samples[0].user_id, 'freddy') self.assertEqual(samples[0].volume, 3) python-ceilometerclient-1.0.8/ceilometerclient/tests/v1/test_users.py0000664000175300017540000000351012247122076027316 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.tests import utils import ceilometerclient.v1.meters fixtures = { '/v1/users': { 'GET': ( {}, {'users': [ 'a', 'b', ]}, ), }, '/v1/sources/source_b/users': { 'GET': ( {}, {'users': ['b']}, ), }, } class UserManagerTest(utils.BaseTestCase): def setUp(self): super(UserManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v1.meters.UserManager(self.api) def test_list_all(self): users = list(self.mgr.list()) expect = [ ('GET', '/v1/users', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(users), 2) self.assertEqual(users[0].user_id, 'a') self.assertEqual(users[1].user_id, 'b') def test_list_by_source(self): users = list(self.mgr.list(source='source_b')) expect = [ ('GET', '/v1/sources/source_b/users', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(users), 1) self.assertEqual(users[0].user_id, 'b') python-ceilometerclient-1.0.8/ceilometerclient/tests/v1/test_meters.py0000664000175300017540000001170112247122076027455 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.tests import utils import ceilometerclient.v1.meters fixtures = { '/v1/meters': { 'GET': ( {}, {'meters': [ { 'resource_id': 'a', 'project_id': 'dig_the_ditch', 'user_id': 'freddy', 'name': 'this', 'type': 'counter', }, { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'this', 'type': 'counter', }, ]}, ), }, '/v1/users/joey/meters': { 'GET': ( {}, {'meters': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'this', 'type': 'counter', }, ]}, ), }, '/v1/projects/dig_the_ditch/meters': { 'GET': ( {}, {'meters': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'this', 'type': 'counter', }, ]}, ), }, '/v1/sources/openstack/meters': { 'GET': ( {}, {'meters': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'this', 'type': 'counter', }, { 'resource_id': 'q', 'project_id': 'dig_the_trench', 'user_id': 'joey', 'name': 'this', 'type': 'counter', }, ]}, ), }, '/v1/meters?metadata.zxc_id=foo': { 'GET': ( {}, {'meters': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'name': 'this', 'type': 'counter', }, ]}, ), }, } class MeterManagerTest(utils.BaseTestCase): def setUp(self): super(MeterManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v1.meters.MeterManager(self.api) def test_list_all(self): resources = list(self.mgr.list()) expect = [ ('GET', '/v1/meters', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'a') self.assertEqual(resources[1].resource_id, 'b') def test_list_by_source(self): resources = list(self.mgr.list(source='openstack')) expect = [ ('GET', '/v1/sources/openstack/meters', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'b') self.assertEqual(resources[1].resource_id, 'q') def test_list_by_user(self): resources = list(self.mgr.list(user_id='joey')) expect = [ ('GET', '/v1/users/joey/meters', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_project(self): resources = list(self.mgr.list(project_id='dig_the_ditch')) expect = [ ('GET', '/v1/projects/dig_the_ditch/meters', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_metaquery(self): resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo')) expect = [ ('GET', '/v1/meters?metadata.zxc_id=foo', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') python-ceilometerclient-1.0.8/ceilometerclient/tests/v1/__init__.py0000664000175300017540000000000012247122076026644 0ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/tests/v1/test_projects.py0000664000175300017540000000360112247122076030007 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.tests import utils import ceilometerclient.v1.meters fixtures = { '/v1/projects': { 'GET': ( {}, {'projects': [ 'a', 'b', ]}, ), }, '/v1/sources/source_b/projects': { 'GET': ( {}, {'projects': ['b']}, ), }, } class ProjectManagerTest(utils.BaseTestCase): def setUp(self): super(ProjectManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v1.meters.ProjectManager(self.api) def test_list_all(self): projects = list(self.mgr.list()) expect = [ ('GET', '/v1/projects', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(projects), 2) self.assertEqual(projects[0].project_id, 'a') self.assertEqual(projects[1].project_id, 'b') def test_list_by_source(self): projects = list(self.mgr.list(source='source_b')) expect = [ ('GET', '/v1/sources/source_b/projects', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(projects), 1) self.assertEqual(projects[0].project_id, 'b') python-ceilometerclient-1.0.8/ceilometerclient/tests/v1/test_resources.py0000664000175300017540000001226512247122076030176 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.tests import utils import ceilometerclient.v1.meters fixtures = { '/v1/resources': { 'GET': ( {}, {'resources': [ { 'resource_id': 'a', 'project_id': 'project_bla', 'user_id': 'freddy', 'timestamp': 'now', 'meter': ['this', 'that'], 'metadata': {'zxc_id': 'bla'}, }, { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'timestamp': 'now', 'meter': ['this', 'that'], 'metadata': {'zxc_id': 'foo'}, }, ]}, ), }, '/v1/users/joey/resources': { 'GET': ( {}, {'resources': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'timestamp': 'now', 'meter': ['this', 'that'], 'metadata': {'zxc_id': 'foo'}, }, ]}, ), }, '/v1/resources?metadata.zxc_id=foo': { 'GET': ( {}, {'resources': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'timestamp': 'now', 'meter': ['this', 'that'], 'metadata': {'zxc_id': 'foo'}, }, ]}, ), }, '/v1/projects/project_bla/resources': { 'GET': ( {}, {'resources': [ { 'resource_id': 'a', 'project_id': 'project_bla', 'user_id': 'freddy', 'timestamp': 'now', 'meter': ['this', 'that'], 'metadata': {'zxc_id': 'bla'}, }, ]}, ), }, '/v1/resources?start_timestamp=now&end_timestamp=now': { 'GET': ( {}, {'resources': [ { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'timestamp': 'now', 'meter': ['this', 'that'], 'metadata': {'zxc_id': 'foo'}, }, ]}, ), }, } class ResourceManagerTest(utils.BaseTestCase): def setUp(self): super(ResourceManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v1.meters.ResourceManager(self.api) def test_list_all(self): resources = list(self.mgr.list()) expect = [ ('GET', '/v1/resources', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'a') self.assertEqual(resources[1].resource_id, 'b') def test_list_by_user(self): resources = list(self.mgr.list(user_id='joey')) expect = [ ('GET', '/v1/users/joey/resources', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_metaquery(self): resources = list(self.mgr.list(metaquery='metadata.zxc_id=foo')) expect = [ ('GET', '/v1/resources?metadata.zxc_id=foo', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') def test_list_by_project(self): resources = list(self.mgr.list(project_id='project_bla')) expect = [ ('GET', '/v1/projects/project_bla/resources', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'a') def test_list_by_timestamp(self): resources = list(self.mgr.list(start_timestamp='now', end_timestamp='now')) expect = [ ('GET', '/v1/resources?start_timestamp=now&end_timestamp=now', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'b') python-ceilometerclient-1.0.8/ceilometerclient/tests/fakes.py0000664000175300017540000000352712247122076025671 0ustar jenkinsjenkins00000000000000# 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 keystoneclient.v2_0 import client as ksclient def script_keystone_client(): ksclient.Client(auth_url='http://no.where', insecure=False, password='password', tenant_id='', tenant_name='tenant_name', username='username').AndReturn(FakeKeystone('abcd1234')) def fake_headers(): return {'X-Auth-Token': 'abcd1234', 'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-ceilometerclient'} class FakeServiceCatalog(): def url_for(self, endpoint_type, service_type): return 'http://192.168.1.5:8004/v1/f14b41234' class FakeKeystone(): service_catalog = FakeServiceCatalog() def __init__(self, auth_token): self.auth_token = auth_token class FakeHTTPResponse(): version = 1.1 def __init__(self, status, reason, headers, body): self.headers = headers self.body = body self.status = status self.reason = reason def getheader(self, name, default=None): return self.headers.get(name, default) def getheaders(self): return self.headers.items() def read(self, amt=None): b = self.body self.body = None return b python-ceilometerclient-1.0.8/ceilometerclient/tests/test_utils.py0000664000175300017540000001415012247122076026771 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six import sys from ceilometerclient.common import utils from ceilometerclient.tests import utils as test_utils class UtilsTest(test_utils.BaseTestCase): def test_prettytable(self): class Struct: def __init__(self, **entries): self.__dict__.update(entries) # test that the prettytable output is wellformatted (left-aligned) saved_stdout = sys.stdout try: sys.stdout = output_dict = six.StringIO() utils.print_dict({'K': 'k', 'Key': 'Value'}) finally: sys.stdout = saved_stdout self.assertEqual(output_dict.getvalue(), '''\ +----------+-------+ | Property | Value | +----------+-------+ | K | k | | Key | Value | +----------+-------+ ''') def test_print_list(self): class Foo: def __init__(self, one, two, three): self.one = one self.two = two self.three = three foo_list = [ Foo(10, 'a', 'B'), Foo(8, 'c', 'c'), Foo(12, '0', 'Z')] def do_print_list(sortby): saved_stdout = sys.stdout try: sys.stdout = output = six.StringIO() utils.print_list(foo_list, ['one', 'two', 'three'], ['1st', '2nd', '3rd'], {'one': lambda o: o.one * 10}, sortby) finally: sys.stdout = saved_stdout return output.getvalue() printed = do_print_list(None) self.assertEqual(printed, '''\ +-----+-----+-----+ | 1st | 2nd | 3rd | +-----+-----+-----+ | 100 | a | B | | 80 | c | c | | 120 | 0 | Z | +-----+-----+-----+ ''') printed = do_print_list(0) self.assertEqual(printed, '''\ +-----+-----+-----+ | 1st | 2nd | 3rd | +-----+-----+-----+ | 80 | c | c | | 100 | a | B | | 120 | 0 | Z | +-----+-----+-----+ ''') printed = do_print_list(1) self.assertEqual(printed, '''\ +-----+-----+-----+ | 1st | 2nd | 3rd | +-----+-----+-----+ | 120 | 0 | Z | | 100 | a | B | | 80 | c | c | +-----+-----+-----+ ''') def test_args_array_to_dict(self): my_args = { 'matching_metadata': ['metadata.key=metadata_value'], 'other': 'value' } cleaned_dict = utils.args_array_to_dict(my_args, "matching_metadata") self.assertEqual(cleaned_dict, { 'matching_metadata': {'metadata.key': 'metadata_value'}, 'other': 'value' }) def test_key_with_slash_to_nested_dict(self): my_args = { 'combination_rule/alarm_ids': ['id1', 'id2'], 'combination_rule/operator': 'and', 'threshold_rule/threshold': 400, 'threshold_rule/statictic': 'avg', 'threshold_rule/comparison_operator': 'or', } nested_dict = utils.key_with_slash_to_nested_dict(my_args) self.assertEqual(nested_dict, { 'combination_rule': {'alarm_ids': ['id1', 'id2'], 'operator': 'and'}, 'threshold_rule': {'threshold': 400, 'statictic': 'avg', 'comparison_operator': 'or'}, }) def test_arg(self): @utils.arg(help="not_required_no_default.") def not_required_no_default(): pass _, args = not_required_no_default.__dict__['arguments'][0] self.assertEqual(args['help'], "not_required_no_default.") @utils.arg(required=True, help="required_no_default.") def required_no_default(): pass _, args = required_no_default.__dict__['arguments'][0] self.assertEqual(args['help'], "required_no_default. Required.") @utils.arg(default=42, help="not_required_default.") def not_required_default(): pass _, args = not_required_default.__dict__['arguments'][0] self.assertEqual(args['help'], "not_required_default. Defaults to 42.") def test_merge_nested_dict(self): dest = {'key': 'value', 'nested': {'key2': 'value2', 'key3': 'value3', 'nested2': {'key': 'value', 'some': 'thing'}}} source = {'key': 'modified', 'nested': {'key3': 'modified3', 'nested2': {'key5': 'value5'}}} utils.merge_nested_dict(dest, source, depth=1) self.assertEqual(dest, {'key': 'modified', 'nested': {'key2': 'value2', 'key3': 'modified3', 'nested2': {'key5': 'value5'}}}) def test_merge_nested_dict_no_depth(self): dest = {'key': 'value', 'nested': {'key2': 'value2', 'key3': 'value3', 'nested2': {'key': 'value', 'some': 'thing'}}} source = {'key': 'modified', 'nested': {'key3': 'modified3', 'nested2': {'key5': 'value5'}}} utils.merge_nested_dict(dest, source) self.assertEqual(dest, {'key': 'modified', 'nested': {'key3': 'modified3', 'nested2': {'key5': 'value5'}}}) python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/0000775000175300017540000000000012247122150024537 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/test_samples.py0000664000175300017540000000705712247122076027634 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from ceilometerclient.tests import utils import ceilometerclient.v2.samples GET_SAMPLE = {u'counter_name': u'instance', u'user_id': u'user-id', u'resource_id': u'resource-id', u'timestamp': u'2012-07-02T10:40:00', u'source': u'test_source', u'message_id': u'54558a1c-6ef3-11e2-9875-5453ed1bbb5f', u'counter_unit': u'', u'counter_volume': 1.0, u'project_id': u'project1', u'resource_metadata': {u'tag': u'self.counter', u'display_name': u'test-server'}, u'counter_type': u'cumulative'} CREATE_SAMPLE = copy.deepcopy(GET_SAMPLE) del CREATE_SAMPLE['message_id'] del CREATE_SAMPLE['source'] base_url = '/v2/meters/instance' args = 'q.field=resource_id&q.field=source&q.op=&q.op=&q.value=foo&q.value=bar' args_limit = 'limit=1' fixtures = { base_url: { 'GET': ( {}, [GET_SAMPLE] ), 'POST': ( {}, [CREATE_SAMPLE], ), }, '%s?%s' % (base_url, args): { 'GET': ( {}, [], ), }, '%s?%s' % (base_url, args_limit): { 'GET': ( {}, [GET_SAMPLE] ), } } class SampleManagerTest(utils.BaseTestCase): def setUp(self): super(SampleManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v2.samples.SampleManager(self.api) def test_list_by_meter_name(self): samples = list(self.mgr.list(meter_name='instance')) expect = [ ('GET', '/v2/meters/instance', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 1) self.assertEqual(samples[0].resource_id, 'resource-id') def test_list_by_meter_name_extended(self): samples = list(self.mgr.list(meter_name='instance', q=[ {"field": "resource_id", "value": "foo"}, {"field": "source", "value": "bar"}, ])) expect = [('GET', '%s?%s' % (base_url, args), {}, None)] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 0) def test_create(self): sample = self.mgr.create(**CREATE_SAMPLE) expect = [ ('POST', '/v2/meters/instance', {}, [CREATE_SAMPLE]), ] self.assertEqual(self.api.calls, expect) self.assertTrue(sample) def test_limit(self): samples = list(self.mgr.list(meter_name='instance', limit=1)) expect = [('GET', '/v2/meters/instance?limit=1', {}, None)] self.assertEqual(self.api.calls, expect) self.assertEqual(len(samples), 1) python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/__init__.py0000664000175300017540000000000012247122076026645 0ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/test_options.py0000664000175300017540000000633312247122076027657 0ustar jenkinsjenkins00000000000000# # 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 ceilometerclient.tests import utils from ceilometerclient.v2 import options class BuildUrlTest(utils.BaseTestCase): def test_one(self): url = options.build_url('/', [{'field': 'this', 'op': 'gt', 'value': 43}]) self.assertEqual(url, '/?q.field=this&q.op=gt&q.value=43') def test_two(self): url = options.build_url('/', [{'field': 'this', 'op': 'gt', 'value': 43}, {'field': 'that', 'op': 'lt', 'value': 88}]) ops = 'q.op=gt&q.op=lt' vals = 'q.value=43&q.value=88' fields = 'q.field=this&q.field=that' self.assertEqual(url, '/?%s&%s&%s' % (fields, ops, vals)) def test_default_op(self): url = options.build_url('/', [{'field': 'this', 'value': 43}]) self.assertEqual(url, '/?q.field=this&q.op=&q.value=43') def test_one_param(self): url = options.build_url('/', None, ['period=60']) self.assertEqual(url, '/?period=60') def test_two_params(self): url = options.build_url('/', None, ['period=60', 'others=value']) self.assertEqual(url, '/?period=60&others=value') class CliTest(utils.BaseTestCase): def test_one(self): ar = options.cli_to_array('this<=34') self.assertEqual(ar, [{'field': 'this', 'op': 'le', 'value': '34'}]) def test_two(self): ar = options.cli_to_array('this<=34;that!=foo') self.assertEqual(ar, [{'field': 'this', 'op': 'le', 'value': '34'}, {'field': 'that', 'op': 'ne', 'value': 'foo'}]) def test_negative(self): ar = options.cli_to_array('this>=-783') self.assertEqual(ar, [{'field': 'this', 'op': 'ge', 'value': '-783'}]) def test_float(self): ar = options.cli_to_array('this<=283.347') self.assertEqual(ar, [{'field': 'this', 'op': 'le', 'value': '283.347'}]) def test_invalid_seperator(self): self.assertRaises(ValueError, options.cli_to_array, 'this=2.4,fooo=doof') def test_invalid_operator(self): self.assertRaises(ValueError, options.cli_to_array, 'this=2.4;fooo-doof') def test_with_dot(self): ar = options.cli_to_array('metadata.this<=34') self.assertEqual(ar, [{'field': 'metadata.this', 'op': 'le', 'value': '34'}]) python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/test_statistics.py0000664000175300017540000000645512247122076030363 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.tests import utils import ceilometerclient.v2.statistics base_url = '/v2/meters/instance/statistics' qry = 'q.field=resource_id&q.field=source&q.op=&q.op=&q.value=foo&q.value=bar' period = '&period=60' samples = [ {u'count': 135, u'duration_start': u'2013-02-04T10:51:42', u'min': 1.0, u'max': 1.0, u'duration_end': u'2013-02-05T15:46:09', u'duration': 1734.0, u'avg': 1.0, u'sum': 135.0}, ] fixtures = { base_url: { 'GET': ( {}, samples ), }, '%s?%s' % (base_url, qry): { 'GET': ( {}, samples ), }, '%s?%s%s' % (base_url, qry, period): { 'GET': ( {}, samples ), }, } class StatisticsManagerTest(utils.BaseTestCase): def setUp(self): super(StatisticsManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v2.statistics.StatisticsManager(self.api) def test_list_by_meter_name(self): stats = list(self.mgr.list(meter_name='instance')) expect = [ ('GET', '/v2/meters/instance/statistics', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(stats), 1) self.assertEqual(stats[0].count, 135) def test_list_by_meter_name_extended(self): stats = list(self.mgr.list(meter_name='instance', q=[ {"field": "resource_id", "value": "foo"}, {"field": "source", "value": "bar"}, ])) expect = [ ('GET', '%s?%s' % (base_url, qry), {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(stats), 1) self.assertEqual(stats[0].count, 135) def test_list_by_meter_name_with_period(self): stats = list(self.mgr.list(meter_name='instance', q=[ {"field": "resource_id", "value": "foo"}, {"field": "source", "value": "bar"}, ], period=60)) expect = [ ('GET', '%s?%s%s' % (base_url, qry, period), {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(stats), 1) self.assertEqual(stats[0].count, 135) python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/test_shell.py0000664000175300017540000003160012247122076027266 0ustar jenkinsjenkins00000000000000# 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 mock import re import six import sys from testtools import matchers from ceilometerclient.tests import utils from ceilometerclient.v2 import alarms from ceilometerclient.v2 import samples from ceilometerclient.v2 import shell as ceilometer_shell class ShellAlarmStateCommandsTest(utils.BaseTestCase): ALARM_ID = 'foobar' def setUp(self): super(ShellAlarmStateCommandsTest, self).setUp() self.cc = mock.Mock() self.cc.alarms = mock.Mock() self.args = mock.Mock() self.args.alarm_id = self.ALARM_ID def test_alarm_state_get(self): ceilometer_shell.do_alarm_state_get(self.cc, self.args) self.cc.alarms.get_state.assert_called_once_with(self.ALARM_ID) self.assertFalse(self.cc.alarms.set_state.called) def test_alarm_state_set(self): self.args.state = 'ok' ceilometer_shell.do_alarm_state_set(self.cc, self.args) self.cc.alarms.set_state.assert_called_once_with(self.ALARM_ID, 'ok') self.assertFalse(self.cc.alarms.get_state.called) class ShellAlarmHistoryCommandTest(utils.BaseTestCase): ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc' FULL_DETAIL = ('{"alarm_actions": [], ' '"user_id": "8185aa72421a4fd396d4122cba50e1b5", ' '"name": "scombo", ' '"timestamp": "2013-10-03T08:58:33.647912", ' '"enabled": true, ' '"state_timestamp": "2013-10-03T08:58:33.647912", ' '"rule": {"operator": "or", "alarm_ids": ' '["062cc907-3a9f-4867-ab3b-fa83212b39f7"]}, ' '"alarm_id": "768ff714-8cfb-4db9-9753-d484cb33a1cc", ' '"state": "insufficient data", ' '"insufficient_data_actions": [], ' '"repeat_actions": false, ' '"ok_actions": [], ' '"project_id": "57d04f24d0824b78b1ea9bcecedbda8f", ' '"type": "combination", ' '"description": "Combined state of alarms ' '062cc907-3a9f-4867-ab3b-fa83212b39f7"}') ALARM_HISTORY = [{'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', 'timestamp': '2013-10-03T08:59:28.326000', 'detail': '{"state": "alarm"}', 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 'type': 'state transition'}, {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', 'timestamp': '2013-10-03T08:59:28.326000', 'detail': '{"description": "combination of one"}', 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 'type': 'rule change'}, {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 'event_id': '4fd7df9e-190d-4471-8884-dc5a33d5d4bb', 'timestamp': '2013-10-03T08:58:33.647000', 'detail': FULL_DETAIL, 'alarm_id': '768ff714-8cfb-4db9-9753-d484cb33a1cc', 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 'type': 'creation'}] TIMESTAMP_RE = (' +\| (\d{4})-(\d{2})-(\d{2})T' '(\d{2})\:(\d{2})\:(\d{2})\.(\d{6}) \| +') def setUp(self): super(ShellAlarmHistoryCommandTest, self).setUp() self.cc = mock.Mock() self.cc.alarms = mock.Mock() self.args = mock.Mock() self.args.alarm_id = self.ALARM_ID def _do_test_alarm_history(self, raw_query=None, parsed_query=None): self.args.query = raw_query orig = sys.stdout sys.stdout = six.StringIO() history = [alarms.AlarmChange(mock.Mock(), change) for change in self.ALARM_HISTORY] self.cc.alarms.get_history.return_value = history try: ceilometer_shell.do_alarm_history(self.cc, self.args) self.cc.alarms.get_history.assert_called_once_with( q=parsed_query, alarm_id=self.ALARM_ID ) out = sys.stdout.getvalue() required = [ '.*creation%sname: scombo.*' % self.TIMESTAMP_RE, '.*rule change%sdescription: combination of one.*' % self.TIMESTAMP_RE, '.*state transition%sstate: alarm.*' % self.TIMESTAMP_RE, ] for r in required: self.assertThat(out, matchers.MatchesRegex(r, re.DOTALL)) finally: sys.stdout.close() sys.stdout = orig def test_alarm_all_history(self): self._do_test_alarm_history() def test_alarm_constrained_history(self): parsed_query = [dict(field='timestamp', value='2013-10-03T08:59:28', op='gt')] self._do_test_alarm_history(raw_query='timestamp>2013-10-03T08:59:28', parsed_query=parsed_query) class ShellAlarmCommandTest(utils.BaseTestCase): ALARM_ID = '768ff714-8cfb-4db9-9753-d484cb33a1cc' ALARM = {"alarm_actions": ["log://"], "ok_actions": [], "description": "instance running hot", "timestamp": "2013-11-20T10:38:42.206952", "enabled": True, "state_timestamp": "2013-11-19T17:20:44", "threshold_rule": {"meter_name": "cpu_util", "evaluation_periods": 3, "period": 600, "statistic": "avg", "threshold": 99.0, "query": [{"field": "resource_id", "value": "INSTANCE_ID", "op": "eq"}], "comparison_operator": "gt"}, "alarm_id": ALARM_ID, "state": "insufficient data", "insufficient_data_actions": [], "repeat_actions": True, "user_id": "528d9b68fa774689834b5c04b4564f8a", "project_id": "ed9d4e2be2a748bc80108053cf4598f5", "type": "threshold", "name": "cpu_high"} def setUp(self): super(ShellAlarmCommandTest, self).setUp() self.cc = mock.Mock() self.cc.alarms = mock.Mock() self.args = mock.Mock() self.args.alarm_id = self.ALARM_ID def _do_test_alarm_update_repeat_actions(self, method, repeat_actions): self.args.threshold = 42.0 if repeat_actions is not None: self.args.repeat_actions = repeat_actions orig = sys.stdout sys.stdout = six.StringIO() alarm = [alarms.Alarm(mock.Mock(), self.ALARM)] self.cc.alarms.get.return_value = alarm self.cc.alarms.update.return_value = alarm[0] try: method(self.cc, self.args) args, kwargs = self.cc.alarms.update.call_args self.assertEqual(self.ALARM_ID, args[0]) self.assertEqual(42.0, kwargs.get('threshold')) if repeat_actions is not None: self.assertEqual(repeat_actions, kwargs.get('repeat_actions')) else: self.assertFalse('repeat_actions' in kwargs) finally: sys.stdout.close() sys.stdout = orig def test_alarm_update_repeat_actions_untouched(self): method = ceilometer_shell.do_alarm_update self._do_test_alarm_update_repeat_actions(method, None) def test_alarm_update_repeat_actions_set(self): method = ceilometer_shell.do_alarm_update self._do_test_alarm_update_repeat_actions(method, True) def test_alarm_update_repeat_actions_clear(self): method = ceilometer_shell.do_alarm_update self._do_test_alarm_update_repeat_actions(method, False) def test_alarm_combination_update_repeat_actions_untouched(self): method = ceilometer_shell.do_alarm_combination_update self._do_test_alarm_update_repeat_actions(method, None) def test_alarm_combination_update_repeat_actions_set(self): method = ceilometer_shell.do_alarm_combination_update self._do_test_alarm_update_repeat_actions(method, True) def test_alarm_combination_update_repeat_actions_clear(self): method = ceilometer_shell.do_alarm_combination_update self._do_test_alarm_update_repeat_actions(method, False) def test_alarm_threshold_update_repeat_actions_untouched(self): method = ceilometer_shell.do_alarm_threshold_update self._do_test_alarm_update_repeat_actions(method, None) def test_alarm_threshold_update_repeat_actions_set(self): method = ceilometer_shell.do_alarm_threshold_update self._do_test_alarm_update_repeat_actions(method, True) def test_alarm_threshold_update_repeat_actions_clear(self): method = ceilometer_shell.do_alarm_threshold_update self._do_test_alarm_update_repeat_actions(method, False) class ShellSampleListCommandTest(utils.BaseTestCase): METER = 'cpu_util' SAMPLES = [{"counter_name": "cpu_util", "resource_id": "5dcf5537-3161-4e25-9235-407e1385bd35", "timestamp": "2013-10-15T05:50:30", "counter_unit": "%", "counter_volume": 0.261666666666667, "counter_type": "gauge"}, {"counter_name": "cpu_util", "resource_id": "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f", "timestamp": "2013-10-15T05:50:29", "counter_unit": "%", "counter_volume": 0.261666666666667, "counter_type": "gauge"}, {"counter_name": "cpu_util", "resource_id": "5dcf5537-3161-4e25-9235-407e1385bd35", "timestamp": "2013-10-15T05:40:30", "counter_unit": "%", "counter_volume": 0.251247920133111, "counter_type": "gauge"}, {"counter_name": "cpu_util", "resource_id": "87d197e9-9cf6-4c25-bc66-1b1f4cedb52f", "timestamp": "2013-10-15T05:40:29", "counter_unit": "%", "counter_volume": 0.26, "counter_type": "gauge"}] def setUp(self): super(ShellSampleListCommandTest, self).setUp() self.cc = mock.Mock() self.cc.alarms = mock.Mock() self.args = mock.Mock() self.args.meter = self.METER self.args.query = None self.args.limit = None def test_sample_list(self): sample_list = [samples.Sample(mock.Mock(), sample) for sample in self.SAMPLES] self.cc.samples.list.return_value = sample_list org_stdout = sys.stdout try: sys.stdout = output = six.StringIO() ceilometer_shell.do_sample_list(self.cc, self.args) self.cc.samples.list.assert_called_once_with( meter_name=self.METER, q=None, limit=None) finally: sys.stdout = org_stdout self.assertEqual(output.getvalue(), '''\ +--------------------------------------+----------+-------+----------------\ +------+---------------------+ | Resource ID | Name | Type | Volume \ | Unit | Timestamp | +--------------------------------------+----------+-------+----------------\ +------+---------------------+ | 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.261666666667 \ | % | 2013-10-15T05:50:30 | | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.261666666667 \ | % | 2013-10-15T05:50:29 | | 5dcf5537-3161-4e25-9235-407e1385bd35 | cpu_util | gauge | 0.251247920133 \ | % | 2013-10-15T05:40:30 | | 87d197e9-9cf6-4c25-bc66-1b1f4cedb52f | cpu_util | gauge | 0.26 \ | % | 2013-10-15T05:40:29 | +--------------------------------------+----------+-------+----------------\ +------+---------------------+ ''') python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/test_alarms.py0000664000175300017540000003402612247122076027443 0ustar jenkinsjenkins00000000000000# -*- encoding: utf-8 -*- # # Copyright © 2013 Red Hat, Inc # # Author: Eoghan Glynn # # 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 copy import six from six.moves import xrange # noqa import testtools from ceilometerclient.tests import utils from ceilometerclient.v2 import alarms AN_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'], u'ok_actions': [u'http://site:8000/ok'], u'description': u'An alarm', u'type': u'threshold', u'threshold_rule': { u'meter_name': u'storage.objects', u'query': [{u'field': u'key_name', u'op': u'eq', u'value': u'key_value'}], u'evaluation_periods': 2, u'period': 240.0, u'statistic': u'avg', u'threshold': 200.0, u'comparison_operator': 'gt', }, u'timestamp': u'2013-05-09T13:41:23.085000', u'enabled': True, u'alarm_id': u'alarm-id', u'state': u'ok', u'insufficient_data_actions': [u'http://site:8000/nodata'], u'user_id': u'user-id', u'project_id': u'project-id', u'state_timestamp': u'2013-05-09T13:41:23.085000', u'repeat_actions': False, u'name': 'SwiftObjectAlarm'} CREATE_ALARM = copy.deepcopy(AN_ALARM) del CREATE_ALARM['timestamp'] del CREATE_ALARM['state_timestamp'] del CREATE_ALARM['alarm_id'] DELTA_ALARM = {u'alarm_actions': ['url1', 'url2']} DELTA_ALARM_RULE = {u'comparison_operator': u'lt', u'threshold': 42.1, u'meter_name': u'foobar', u'query': [{u'field': u'key_name', u'op': u'eq', u'value': u'key_value'}]} UPDATED_ALARM = copy.deepcopy(AN_ALARM) UPDATED_ALARM.update(DELTA_ALARM) UPDATED_ALARM['threshold_rule'].update(DELTA_ALARM_RULE) DELTA_ALARM['threshold_rule'] = DELTA_ALARM_RULE UPDATE_ALARM = copy.deepcopy(UPDATED_ALARM) del UPDATE_ALARM['user_id'] del UPDATE_ALARM['project_id'] del UPDATE_ALARM['name'] del UPDATE_ALARM['alarm_id'] del UPDATE_ALARM['timestamp'] del UPDATE_ALARM['state_timestamp'] AN_LEGACY_ALARM = {u'alarm_actions': [u'http://site:8000/alarm'], u'ok_actions': [u'http://site:8000/ok'], u'description': u'An alarm', u'matching_metadata': {u'key_name': u'key_value'}, u'evaluation_periods': 2, u'timestamp': u'2013-05-09T13:41:23.085000', u'enabled': True, u'meter_name': u'storage.objects', u'period': 240.0, u'alarm_id': u'alarm-id', u'state': u'ok', u'insufficient_data_actions': [u'http://site:8000/nodata'], u'statistic': u'avg', u'threshold': 200.0, u'user_id': u'user-id', u'project_id': u'project-id', u'state_timestamp': u'2013-05-09T13:41:23.085000', u'comparison_operator': 'gt', u'repeat_actions': False, u'name': 'SwiftObjectAlarm'} CREATE_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM) del CREATE_LEGACY_ALARM['timestamp'] del CREATE_LEGACY_ALARM['state_timestamp'] del CREATE_LEGACY_ALARM['alarm_id'] DELTA_LEGACY_ALARM = {u'alarm_actions': ['url1', 'url2'], u'comparison_operator': u'lt', u'meter_name': u'foobar', u'threshold': 42.1} UPDATED_LEGACY_ALARM = copy.deepcopy(AN_LEGACY_ALARM) UPDATED_LEGACY_ALARM.update(DELTA_LEGACY_ALARM) UPDATE_LEGACY_ALARM = copy.deepcopy(UPDATED_LEGACY_ALARM) del UPDATE_LEGACY_ALARM['user_id'] del UPDATE_LEGACY_ALARM['project_id'] del UPDATE_LEGACY_ALARM['name'] del UPDATE_LEGACY_ALARM['alarm_id'] del UPDATE_LEGACY_ALARM['timestamp'] del UPDATE_LEGACY_ALARM['state_timestamp'] FULL_DETAIL = ('{"alarm_actions": [], ' '"user_id": "8185aa72421a4fd396d4122cba50e1b5", ' '"name": "scombo", ' '"timestamp": "2013-10-03T08:58:33.647912", ' '"enabled": true, ' '"state_timestamp": "2013-10-03T08:58:33.647912", ' '"rule": {"operator": "or", "alarm_ids": ' '["062cc907-3a9f-4867-ab3b-fa83212b39f7"]}, ' '"alarm_id": "alarm-id, ' '"state": "insufficient data", ' '"insufficient_data_actions": [], ' '"repeat_actions": false, ' '"ok_actions": [], ' '"project_id": "57d04f24d0824b78b1ea9bcecedbda8f", ' '"type": "combination", ' '"description": "Combined state of alarms ' '062cc907-3a9f-4867-ab3b-fa83212b39f7"}') ALARM_HISTORY = [{'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', 'timestamp': '2013-10-03T08:59:28.326000', 'detail': '{"state": "alarm"}', 'alarm_id': 'alarm-id', 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 'type': 'state transition'}, {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 'event_id': 'c74a8611-6553-4764-a860-c15a6aabb5d0', 'timestamp': '2013-10-03T08:59:28.326000', 'detail': '{"description": "combination of one"}', 'alarm_id': 'alarm-id', 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 'type': 'rule change'}, {'on_behalf_of': '57d04f24d0824b78b1ea9bcecedbda8f', 'user_id': '8185aa72421a4fd396d4122cba50e1b5', 'event_id': '4fd7df9e-190d-4471-8884-dc5a33d5d4bb', 'timestamp': '2013-10-03T08:58:33.647000', 'detail': FULL_DETAIL, 'alarm_id': 'alarm-id', 'project_id': '57d04f24d0824b78b1ea9bcecedbda8f', 'type': 'creation'}] fixtures = { '/v2/alarms': { 'GET': ( {}, [AN_ALARM], ), 'POST': ( {}, CREATE_ALARM, ), }, '/v2/alarms/alarm-id': { 'GET': ( {}, AN_ALARM, ), 'PUT': ( {}, UPDATED_ALARM, ), }, '/v2/alarms/alarm-id/state': { 'PUT': ( {}, 'alarm' ), 'GET': ( {}, 'alarm' ), }, '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op=' '&q.value=project-id&q.value=SwiftObjectAlarm': { 'GET': ( {}, [AN_ALARM], ), }, '/v2/alarms/victim-id': { 'DELETE': ( {}, None, ), }, '/v2/alarms/alarm-id/history': { 'GET': ( {}, ALARM_HISTORY, ), }, '/v2/alarms/alarm-id/history?q.field=timestamp&q.op=&q.value=NOW': { 'GET': ( {}, ALARM_HISTORY, ), }, } class AlarmManagerTest(testtools.TestCase): def setUp(self): super(AlarmManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = alarms.AlarmManager(self.api) def test_list_all(self): alarms = list(self.mgr.list()) expect = [ ('GET', '/v2/alarms', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(alarms), 1) self.assertEqual(alarms[0].alarm_id, 'alarm-id') def test_list_with_query(self): alarms = list(self.mgr.list(q=[{"field": "project_id", "value": "project-id"}, {"field": "name", "value": "SwiftObjectAlarm"}])) expect = [ ('GET', '/v2/alarms?q.field=project_id&q.field=name&q.op=&q.op=' '&q.value=project-id&q.value=SwiftObjectAlarm', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(alarms), 1) self.assertEqual(alarms[0].alarm_id, 'alarm-id') def test_get(self): alarm = self.mgr.get(alarm_id='alarm-id') expect = [ ('GET', '/v2/alarms/alarm-id', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') self.assertEqual(alarm.rule, alarm.threshold_rule) def test_create(self): alarm = self.mgr.create(**CREATE_ALARM) expect = [ ('POST', '/v2/alarms', {}, CREATE_ALARM), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) def test_update(self): alarm = self.mgr.update(alarm_id='alarm-id', **UPDATE_ALARM) expect = [ ('GET', '/v2/alarms/alarm-id', {}, None), ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) def test_update_delta(self): alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_ALARM) expect = [ ('GET', '/v2/alarms/alarm-id', {}, None), ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) def test_set_state(self): state = self.mgr.set_state(alarm_id='alarm-id', state='alarm') expect = [ ('PUT', '/v2/alarms/alarm-id/state', {}, 'alarm'), ] self.assertEqual(self.api.calls, expect) self.assertEqual(state, 'alarm') def test_get_state(self): state = self.mgr.get_state(alarm_id='alarm-id') expect = [ ('GET', '/v2/alarms/alarm-id/state', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(state, 'alarm') def test_delete(self): deleted = self.mgr.delete(alarm_id='victim-id') expect = [ ('DELETE', '/v2/alarms/victim-id', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertTrue(deleted is None) def _do_test_get_history(self, q, url): history = self.mgr.get_history(q=q, alarm_id='alarm-id') expect = [('GET', url, {}, None)] self.assertEqual(self.api.calls, expect) for i in xrange(len(history)): change = history[i] self.assertTrue(isinstance(change, alarms.AlarmChange)) for k, v in six.iteritems(ALARM_HISTORY[i]): self.assertEqual(getattr(change, k), v) def test_get_all_history(self): url = '/v2/alarms/alarm-id/history' self._do_test_get_history(None, url) def test_get_constrained_history(self): q = [dict(field='timestamp', value='NOW')] url = ('/v2/alarms/alarm-id/history?q.field=timestamp' '&q.op=&q.value=NOW') self._do_test_get_history(q, url) class AlarmLegacyManagerTest(testtools.TestCase): def setUp(self): super(AlarmLegacyManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = alarms.AlarmManager(self.api) def test_create(self): alarm = self.mgr.create(**CREATE_LEGACY_ALARM) expect = [ ('POST', '/v2/alarms', {}, CREATE_ALARM), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) def test_create_counter_name(self): create = {} create.update(CREATE_LEGACY_ALARM) create['counter_name'] = CREATE_LEGACY_ALARM['meter_name'] del create['meter_name'] alarm = self.mgr.create(**create) expect = [ ('POST', '/v2/alarms', {}, CREATE_ALARM), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) def test_update(self): alarm = self.mgr.update(alarm_id='alarm-id', **DELTA_LEGACY_ALARM) expect = [ ('GET', '/v2/alarms/alarm-id', {}, None), ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) def test_update_counter_name(self): updated = {} updated.update(UPDATE_LEGACY_ALARM) updated['counter_name'] = UPDATED_LEGACY_ALARM['meter_name'] del updated['meter_name'] alarm = self.mgr.update(alarm_id='alarm-id', **updated) expect = [ ('GET', '/v2/alarms/alarm-id', {}, None), ('PUT', '/v2/alarms/alarm-id', {}, UPDATED_ALARM), ] self.assertEqual(self.api.calls, expect) self.assertTrue(alarm) self.assertEqual(alarm.alarm_id, 'alarm-id') for (key, value) in six.iteritems(UPDATED_ALARM): self.assertEqual(getattr(alarm, key), value) python-ceilometerclient-1.0.8/ceilometerclient/tests/v2/test_resources.py0000664000175300017540000000630512247122076030175 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.tests import utils import ceilometerclient.v2.resources fixtures = { '/v2/resources': { 'GET': ( {}, [ { 'resource_id': 'a', 'project_id': 'project_bla', 'user_id': 'freddy', 'metadata': {'zxc_id': 'bla'}, }, { 'resource_id': 'b', 'project_id': 'dig_the_ditch', 'user_id': 'joey', 'metadata': {'zxc_id': 'foo'}, }, ] ), }, '/v2/resources?q.field=resource_id&q.op=&q.value=a': { 'GET': ( {}, [ { 'resource_id': 'a', 'project_id': 'project_bla', 'user_id': 'freddy', 'metadata': {'zxc_id': 'bla'}, }, ] ), }, '/v2/resources/a': { 'GET': ( {}, { 'resource_id': 'a', 'project_id': 'project_bla', 'user_id': 'freddy', 'metadata': {'zxc_id': 'bla'}, }, ), }, } class ResourceManagerTest(utils.BaseTestCase): def setUp(self): super(ResourceManagerTest, self).setUp() self.api = utils.FakeAPI(fixtures) self.mgr = ceilometerclient.v2.resources.ResourceManager(self.api) def test_list_all(self): resources = list(self.mgr.list()) expect = [ ('GET', '/v2/resources', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 2) self.assertEqual(resources[0].resource_id, 'a') self.assertEqual(resources[1].resource_id, 'b') def test_list_one(self): resource = self.mgr.get(resource_id='a') expect = [ ('GET', '/v2/resources/a', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertTrue(resource) self.assertEqual(resource.resource_id, 'a') def test_list_by_query(self): resources = list(self.mgr.list(q=[{"field": "resource_id", "value": "a"}, ])) expect = [ ('GET', '/v2/resources?q.field=resource_id&q.op=&q.value=a', {}, None), ] self.assertEqual(self.api.calls, expect) self.assertEqual(len(resources), 1) self.assertEqual(resources[0].resource_id, 'a') python-ceilometerclient-1.0.8/ceilometerclient/shell.py0000664000175300017540000002727212247122076024550 0ustar jenkinsjenkins00000000000000# 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. """ Command-line interface to the OpenStack Telemetry API. """ import argparse import httplib2 import logging import sys import ceilometerclient from ceilometerclient import client as ceiloclient from ceilometerclient.common import utils from ceilometerclient import exc class CeilometerShell(object): def get_base_parser(self): parser = argparse.ArgumentParser( prog='ceilometer', description=__doc__.strip(), epilog='See "ceilometer help COMMAND" ' 'for help on a specific command.', add_help=False, formatter_class=HelpFormatter, ) # Global arguments parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS, ) parser.add_argument('--version', action='version', version=ceilometerclient.__version__) parser.add_argument('-d', '--debug', default=bool(utils.env('CEILOMETERCLIENT_DEBUG')), action='store_true', help='Defaults to env[CEILOMETERCLIENT_DEBUG]') parser.add_argument('-v', '--verbose', default=False, action="store_true", help="Print more verbose output") parser.add_argument('-k', '--insecure', default=False, action='store_true', help="Explicitly allow ceilometerclient to " "perform \"insecure\" SSL (https) requests. " "The server's certificate will " "not be verified against any certificate " "authorities. This option should be used with " "caution.") parser.add_argument('--cert-file', help='Path of certificate file to use in SSL ' 'connection. This file can optionally be prepended' ' with the private key.') parser.add_argument('--key-file', help='Path of client key to use in SSL connection.' ' This option is not necessary if your key is ' 'prepended to your cert file.') parser.add_argument('--os-cacert', metavar='', dest='os_cacert', default=utils.env('OS_CACERT'), help='Path of CA TLS certificate(s) used to verify' 'the remote server\'s certificate. Without this ' 'option ceilometer looks for the default system ' 'CA certificates.') parser.add_argument('--ca-file', dest='os_cacert', help='DEPRECATED! Use --os-cacert.') parser.add_argument('--timeout', default=600, help='Number of seconds to wait for a response') parser.add_argument('--os-username', default=utils.env('OS_USERNAME'), help='Defaults to env[OS_USERNAME]') parser.add_argument('--os_username', help=argparse.SUPPRESS) parser.add_argument('--os-password', default=utils.env('OS_PASSWORD'), help='Defaults to env[OS_PASSWORD]') parser.add_argument('--os_password', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-id', default=utils.env('OS_TENANT_ID'), help='Defaults to env[OS_TENANT_ID]') parser.add_argument('--os_tenant_id', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-name', default=utils.env('OS_TENANT_NAME'), help='Defaults to env[OS_TENANT_NAME]') parser.add_argument('--os_tenant_name', help=argparse.SUPPRESS) parser.add_argument('--os-auth-url', default=utils.env('OS_AUTH_URL'), help='Defaults to env[OS_AUTH_URL]') parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) parser.add_argument('--os-region-name', default=utils.env('OS_REGION_NAME'), help='Defaults to env[OS_REGION_NAME]') parser.add_argument('--os_region_name', help=argparse.SUPPRESS) parser.add_argument('--os-auth-token', default=utils.env('OS_AUTH_TOKEN'), help='Defaults to env[OS_AUTH_TOKEN]') parser.add_argument('--os_auth_token', help=argparse.SUPPRESS) parser.add_argument('--ceilometer-url', default=utils.env('CEILOMETER_URL'), help='Defaults to env[CEILOMETER_URL]') parser.add_argument('--ceilometer_url', help=argparse.SUPPRESS) parser.add_argument('--ceilometer-api-version', default=utils.env( 'CEILOMETER_API_VERSION', default='2'), help='Defaults to env[CEILOMETER_API_VERSION] ' 'or 2') parser.add_argument('--ceilometer_api_version', help=argparse.SUPPRESS) parser.add_argument('--os-service-type', default=utils.env('OS_SERVICE_TYPE'), help='Defaults to env[OS_SERVICE_TYPE]') parser.add_argument('--os_service_type', help=argparse.SUPPRESS) parser.add_argument('--os-endpoint-type', default=utils.env('OS_ENDPOINT_TYPE'), help='Defaults to env[OS_ENDPOINT_TYPE]') parser.add_argument('--os_endpoint_type', help=argparse.SUPPRESS) return parser def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='') submodule = utils.import_versioned_module(version, 'shell') self._find_actions(subparsers, submodule) self._find_actions(subparsers, self) return parser def _find_actions(self, subparsers, actions_module): for attr in (a for a in dir(actions_module) if a.startswith('do_')): # I prefer to be hypen-separated instead of underscores. command = attr[3:].replace('_', '-') callback = getattr(actions_module, attr) desc = callback.__doc__ or '' help = desc.strip().split('\n')[0] arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser(command, help=help, description=desc, add_help=False, formatter_class=HelpFormatter) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS) self.subcommands[command] = subparser for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) def _setup_logging(self, debug): format = '%(levelname)s (%(module)s:%(lineno)d) %(message)s' if debug: logging.basicConfig(format=format, level=logging.DEBUG) httplib2.debuglevel = 1 else: logging.basicConfig(format=format, level=logging.WARN) def main(self, argv): # Parse args once to find version parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self._setup_logging(options.debug) # build available subcommands based on version api_version = options.ceilometer_api_version subcommand_parser = self.get_subcommand_parser(api_version) self.parser = subcommand_parser # Handle top-level --help/-h before attempting to parse # a command off the command line if options.help or not argv: self.do_help(options) return 0 # Parse args again and call whatever callback was selected args = subcommand_parser.parse_args(argv) # Short-circuit and deal with help command right away. if args.func == self.do_help: self.do_help(args) return 0 if not (args.os_auth_token and args.ceilometer_url): if not args.os_username: raise exc.CommandError("You must provide a username via " "either --os-username or via " "env[OS_USERNAME]") if not args.os_password: raise exc.CommandError("You must provide a password via " "either --os-password or via " "env[OS_PASSWORD]") if not (args.os_tenant_id or args.os_tenant_name): raise exc.CommandError("You must provide a tenant_id via " "either --os-tenant-id or via " "env[OS_TENANT_ID]") if not args.os_auth_url: raise exc.CommandError("You must provide an auth url via " "either --os-auth-url or via " "env[OS_AUTH_URL]") client = ceiloclient.get_client(api_version, **(args.__dict__)) try: args.func(client, args) except exc.Unauthorized: raise exc.CommandError("Invalid OpenStack Identity credentials.") @utils.arg('command', metavar='', nargs='?', help='Display help for ') def do_help(self, args): """Display help about this program or one of its subcommands.""" if getattr(args, 'command', None): if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError("'%s' is not a valid subcommand" % args.command) else: self.parser.print_help() class HelpFormatter(argparse.HelpFormatter): def start_section(self, heading): # Title-case the headings heading = '%s%s' % (heading[0].upper(), heading[1:]) super(HelpFormatter, self).start_section(heading) def main(): try: CeilometerShell().main(sys.argv[1:]) except Exception as e: print >> sys.stderr, e sys.exit(1) if __name__ == "__main__": main() python-ceilometerclient-1.0.8/ceilometerclient/v2/0000775000175300017540000000000012247122150023375 5ustar jenkinsjenkins00000000000000python-ceilometerclient-1.0.8/ceilometerclient/v2/samples.py0000664000175300017540000000371712247122076025432 0ustar jenkinsjenkins00000000000000# # 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 ceilometerclient.common import base from ceilometerclient.v2 import options CREATION_ATTRIBUTES = ('source', 'counter_name', 'counter_type', 'counter_unit', 'counter_volume', 'user_id', 'project_id', 'resource_id', 'timestamp', 'resource_metadata') class Sample(base.Resource): def __repr__(self): return "" % self._info class SampleManager(base.Manager): resource_class = Sample @staticmethod def _path(counter_name=None): return '/v2/meters/%s' % counter_name if counter_name else '/v2/meters' def list(self, meter_name=None, q=None, limit=None): path = self._path(counter_name=meter_name) params = ['limit=%s' % str(limit)] if limit else None return self._list(options.build_url(path, q, params)) def create(self, **kwargs): new = dict((key, value) for (key, value) in kwargs.items() if key in CREATION_ATTRIBUTES) url = self._path(counter_name=kwargs['counter_name']) resp, body = self.api.json_request('POST', url, body=[new]) if body: return [Sample(self, b) for b in body] python-ceilometerclient-1.0.8/ceilometerclient/v2/alarms.py0000664000175300017540000001157112247122076025242 0ustar jenkinsjenkins00000000000000# -*- encoding: utf-8 -*- # # Copyright © 2013 Red Hat, Inc # # Author: Eoghan Glynn # # 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 warnings from ceilometerclient.common import base from ceilometerclient.common import utils from ceilometerclient.v2 import options UPDATABLE_ATTRIBUTES = [ 'name', 'description', 'type', 'state', 'enabled', 'alarm_actions', 'ok_actions', 'insufficient_data_actions', 'repeat_actions', 'threshold_rule', 'combination_rule', ] CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['project_id', 'user_id'] class Alarm(base.Resource): def __repr__(self): return "" % self._info def __getattr__(self, k): # Alias to have the Alarm client object # that look like the Alarm storage object if k == 'rule': k = '%s_rule' % self.type return super(Alarm, self).__getattr__(k) class AlarmChange(base.Resource): def __repr__(self): return "" % self._info def __getattr__(self, k): return super(AlarmChange, self).__getattr__(k) class AlarmManager(base.Manager): resource_class = Alarm @staticmethod def _path(id=None): return '/v2/alarms/%s' % id if id else '/v2/alarms' def list(self, q=None): return self._list(options.build_url(self._path(), q)) def get(self, alarm_id): try: return self._list(self._path(alarm_id), expect_single=True)[0] except IndexError: return None @classmethod def _compat_legacy_alarm_kwargs(cls, kwargs, create=False): cls._compat_counter_rename_kwargs(kwargs, create) cls._compat_alarm_before_rule_type_kwargs(kwargs, create) @staticmethod def _compat_counter_rename_kwargs(kwargs, create=False): # NOTE(jd) Compatibility with Havana-2 API if 'counter_name' in kwargs: warnings.warn("counter_name has been renamed to meter_name", DeprecationWarning) kwargs['meter_name'] = kwargs['counter_name'] @staticmethod def _compat_alarm_before_rule_type_kwargs(kwargs, create=False): # NOTE(sileht) Compatibility with Havana-3 API if create and 'type' not in kwargs: warnings.warn("alarm without type set is deprecated", DeprecationWarning) kwargs['type'] = 'threshold' for field in ['period', 'evaluation_periods', 'threshold', 'statistic', 'comparison_operator', 'meter_name']: if field in kwargs: kwargs.setdefault('threshold_rule', {})[field] = kwargs[field] del kwargs[field] if 'matching_metadata' in kwargs: query = [] for key in kwargs['matching_metadata']: query.append({'field': key, 'op': 'eq', 'value': kwargs['matching_metadata'][key]}) del kwargs['matching_metadata'] kwargs['threshold_rule']['query'] = query def create(self, **kwargs): self._compat_legacy_alarm_kwargs(kwargs, create=True) new = dict((key, value) for (key, value) in kwargs.items() if key in CREATION_ATTRIBUTES) return self._create(self._path(), new) def update(self, alarm_id, **kwargs): self._compat_legacy_alarm_kwargs(kwargs) updated = self.get(alarm_id).to_dict() kwargs = dict((k, v) for k, v in kwargs.items() if k in updated and k in UPDATABLE_ATTRIBUTES) utils.merge_nested_dict(updated, kwargs, depth=1) return self._update(self._path(alarm_id), updated) def delete(self, alarm_id): return self._delete(self._path(alarm_id)) def set_state(self, alarm_id, state): resp, body = self.api.json_request('PUT', "%s/state" % self._path(alarm_id), body=state) return body def get_state(self, alarm_id): resp, body = self.api.json_request('GET', "%s/state" % self._path(alarm_id)) return body def get_history(self, alarm_id, q=None): path = '%s/history' % self._path(alarm_id) url = options.build_url(path, q) return self._list(url, obj_class=AlarmChange) python-ceilometerclient-1.0.8/ceilometerclient/v2/resources.py0000664000175300017540000000226512247122076025775 0ustar jenkinsjenkins00000000000000# -*- encoding: utf-8 -*- # # Copyright © 2013 Red Hat, Inc # # 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 ceilometerclient.common import base from ceilometerclient.v2 import options class Resource(base.Resource): def __repr__(self): return "" % self._info class ResourceManager(base.Manager): resource_class = Resource def list(self, q=None): path = '/v2/resources' return self._list(options.build_url(path, q)) def get(self, resource_id): path = '/v2/resources/%s' % resource_id try: return self._list(path, expect_single=True)[0] except IndexError: return None python-ceilometerclient-1.0.8/ceilometerclient/v2/options.py0000664000175300017540000000607312247122076025457 0ustar jenkinsjenkins00000000000000# # 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 ceilometerclient.openstack.common.py3kcompat import urlutils import re def build_url(path, q, params=None): '''This converts from a list of dicts and a list of params to what the rest api needs, so from: "[{field=this,op=le,value=34},{field=that,op=eq,value=foo}], ['foo=bar','sna=fu']" to: "?q.field=this&q.op=le&q.value=34& q.field=that&q.op=eq&q.value=foo& foo=bar&sna=fu" ''' if q: query_params = {'q.field': [], 'q.value': [], 'q.op': []} for query in q: for name in ['field', 'op', 'value']: query_params['q.%s' % name].append(query.get(name, '')) # Transform the dict to a sequence of two-element tuples in fixed # order, then the encoded string will be consistent in Python 2&3. new_qparams = sorted(query_params.items(), key=lambda x: x[0]) path += "?" + urlutils.urlencode(new_qparams, doseq=True) if params: for p in params: path += '&%s' % p elif params: path += '?%s' % params[0] for p in params[1:]: path += '&%s' % p return path def cli_to_array(cli_query): '''This converts from the cli list of queries to what is required by the python api. so from: "this<=34;that=foo" to "[{field=this,op=le,value=34},{field=that,op=eq,value=foo}]" ''' if cli_query is None: return None op_lookup = {'!=': 'ne', '>=': 'ge', '<=': 'le', '>': 'gt', '<': 'lt', '=': 'eq'} def split_by_op(string): # two character split (<=,!=) frags = re.findall(r'([[a-zA-Z0-9_.]+)([><=])([^ -,\t\n\r\f\v]+)', string) return frags opts = [] queries = cli_query.split(';') for q in queries: frag = split_by_op(q) if len(frag) > 1: raise ValueError('incorrect seperator %s in query "%s"' % ('(should be ";")', q)) if len(frag) == 0: raise ValueError('invalid query %s' % q) query = frag[0] opt = {} opt['field'] = query[0] opt['op'] = op_lookup[query[1]] opt['value'] = query[2] opts.append(opt) return opts python-ceilometerclient-1.0.8/ceilometerclient/v2/client.py0000664000175300017540000000323512247122076025237 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.common import http from ceilometerclient.v2 import alarms from ceilometerclient.v2 import meters from ceilometerclient.v2 import resources from ceilometerclient.v2 import samples from ceilometerclient.v2 import statistics class Client(http.HTTPClient): """Client for the Ceilometer v2 API. :param string endpoint: A user-supplied endpoint URL for the ceilometer service. :param function token: Provides token for authentication. :param integer timeout: Allows customization of the timeout for client http requests. (optional) """ def __init__(self, *args, **kwargs): """Initialize a new client for the Ceilometer v1 API.""" super(Client, self).__init__(*args, **kwargs) self.meters = meters.MeterManager(self) self.samples = samples.SampleManager(self) self.statistics = statistics.StatisticsManager(self) self.resources = resources.ResourceManager(self) self.alarms = alarms.AlarmManager(self) python-ceilometerclient-1.0.8/ceilometerclient/v2/__init__.py0000664000175300017540000000126412247122076025520 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from ceilometerclient.v2.client import Client # noqa python-ceilometerclient-1.0.8/ceilometerclient/v2/meters.py0000664000175300017540000000173212247122076025260 0ustar jenkinsjenkins00000000000000# -*- encoding: utf-8 -*- # # Copyright © 2013 Red Hat, Inc # # 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 ceilometerclient.common import base from ceilometerclient.v2 import options class Meter(base.Resource): def __repr__(self): return "" % self._info class MeterManager(base.Manager): resource_class = Meter def list(self, q=None): path = '/v2/meters' return self._list(options.build_url(path, q)) python-ceilometerclient-1.0.8/ceilometerclient/v2/statistics.py0000664000175300017540000000204712247122076026153 0ustar jenkinsjenkins00000000000000# # 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 ceilometerclient.common import base from ceilometerclient.v2 import options class Statistics(base.Resource): def __repr__(self): return "" % self._info class StatisticsManager(base.Manager): resource_class = Statistics def list(self, meter_name, q=None, period=None): p = ['period=%s' % period] if period else None return self._list(options.build_url( '/v2/meters/' + meter_name + '/statistics', q, p)) python-ceilometerclient-1.0.8/ceilometerclient/v2/shell.py0000664000175300017540000005704612247122076025101 0ustar jenkinsjenkins00000000000000# -*- encoding: utf-8 -*- # # Copyright © 2013 Red Hat, Inc # # Author: Angus Salkeld # # 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 import json import six from ceilometerclient.common import utils from ceilometerclient import exc from ceilometerclient.v2 import options ALARM_STATES = ['ok', 'alarm', 'insufficient_data'] ALARM_OPERATORS = ['lt', 'le', 'eq', 'ne', 'ge', 'gt'] ALARM_COMBINATION_OPERATORS = ['and', 'or'] STATISTICS = ['max', 'min', 'avg', 'sum', 'count'] OPERATORS_STRING = dict(gt='>', ge='>=', lt='<', le="<=", eq='==', ne='!=') @utils.arg('-q', '--query', metavar='', help='key[op]value; list.') @utils.arg('-m', '--meter', metavar='', required=True, help='Name of meter to show samples for.') @utils.arg('-p', '--period', metavar='', help='Period in seconds over which to group samples.') def do_statistics(cc, args): '''List the statistics for this meter.''' fields = {'meter_name': args.meter, 'q': options.cli_to_array(args.query), 'period': args.period} try: statistics = cc.statistics.list(**fields) except exc.HTTPNotFound: raise exc.CommandError('Samples not found: %s' % args.meter) else: field_labels = ['Period', 'Period Start', 'Period End', 'Count', 'Min', 'Max', 'Sum', 'Avg', 'Duration', 'Duration Start', 'Duration End'] fields = ['period', 'period_start', 'period_end', 'count', 'min', 'max', 'sum', 'avg', 'duration', 'duration_start', 'duration_end'] utils.print_list(statistics, fields, field_labels) @utils.arg('-q', '--query', metavar='', help='key[op]value; list.') @utils.arg('-m', '--meter', metavar='', required=True, help='Name of meter to show samples for.') @utils.arg('-l', '--limit', metavar='', help='Maximum number of samples to return.') def do_sample_list(cc, args): '''List the samples for this meters.''' fields = {'meter_name': args.meter, 'q': options.cli_to_array(args.query), 'limit': args.limit} try: samples = cc.samples.list(**fields) except exc.HTTPNotFound: raise exc.CommandError('Samples not found: %s' % args.meter) else: field_labels = ['Resource ID', 'Name', 'Type', 'Volume', 'Unit', 'Timestamp'] fields = ['resource_id', 'counter_name', 'counter_type', 'counter_volume', 'counter_unit', 'timestamp'] utils.print_list(samples, fields, field_labels, sortby=None) @utils.arg('--project-id', metavar='', help='Tenant to associate with sample ' '(only settable by admin users)') @utils.arg('--user-id', metavar='', help='User to associate with sample ' '(only settable by admin users)') @utils.arg('-r', '--resource-id', metavar='', required=True, help='ID of the resource.') @utils.arg('-m', '--meter-name', metavar='', help='the meter name') @utils.arg('--meter-type', metavar='', required=True, help='the meter type') @utils.arg('--meter-unit', metavar='', required=True, help='the meter unit') @utils.arg('--sample-volume', metavar='', required=True, help='The sample volume') @utils.arg('--resource-metadata', metavar='', help='resource metadata') @utils.arg('--timestamp', metavar='', help='the sample timestamp') def do_sample_create(cc, args={}): '''Create a sample.''' arg_to_field_mapping = {'meter_name': 'counter_name', 'meter_unit': 'counter_unit', 'meter_type': 'counter_type', 'sample_volume': 'counter_volume'} fields = {} for var in vars(args).items(): k, v = var[0], var[1] if v is not None: if k == 'resource_metadata': fields[k] = json.loads(v) else: fields[arg_to_field_mapping.get(k, k)] = v cc.samples.create(**fields) @utils.arg('-q', '--query', metavar='', help='key[op]value; list.') def do_meter_list(cc, args={}): '''List the user's meters.''' meters = cc.meters.list(q=options.cli_to_array(args.query)) field_labels = ['Name', 'Type', 'Unit', 'Resource ID', 'User ID', 'Project ID'] fields = ['name', 'type', 'unit', 'resource_id', 'user_id', 'project_id'] utils.print_list(meters, fields, field_labels, sortby=0) def _display_rule(type, rule): if type == 'threshold': return ('%(meter_name)s %(comparison_operator)s ' '%(threshold)s during %(evaluation_periods)s x %(period)ss' % { 'meter_name': rule['meter_name'], 'threshold': rule['threshold'], 'evaluation_periods': rule['evaluation_periods'], 'period': rule['period'], 'comparison_operator': OPERATORS_STRING.get( rule['comparison_operator']) }) elif type == 'combination': return ('combinated states (%(operator)s) of %(alarms)s' % { 'operator': rule['operator'].upper(), 'alarms': ", ".join(rule['alarm_ids'])}) else: # just dump all return "\n".join(["%s: %s" % (f, v) for f, v in rule.iteritems()]) def alarm_rule_formatter(alarm): return _display_rule(alarm.type, alarm.rule) def _infer_type(detail): if 'type' in detail: return detail['type'] elif 'meter_name' in detail['rule']: return 'threshold' elif 'alarms' in detail['rule']: return 'combination' else: return 'unknown' def alarm_change_detail_formatter(change): detail = json.loads(change.detail) fields = [] if change.type == 'state transition': fields.append('state: %s' % detail['state']) elif change.type == 'creation' or change.type == 'deletion': for k in ['name', 'description', 'type', 'rule']: if k == 'rule': fields.append('rule: %s' % _display_rule(detail['type'], detail[k])) else: fields.append('%s: %s' % (k, detail[k])) elif change.type == 'rule change': for k, v in six.iteritems(detail): if k == 'rule': fields.append('rule: %s' % _display_rule(_infer_type(detail), v)) else: fields.append('%s: %s' % (k, v)) return '\n'.join(fields) @utils.arg('-q', '--query', metavar='', help='key[op]value; list.') def do_alarm_list(cc, args={}): '''List the user's alarms.''' alarms = cc.alarms.list(q=options.cli_to_array(args.query)) # omit action initially to keep output width sane # (can switch over to vertical formatting when available from CLIFF) field_labels = ['Alarm ID', 'Name', 'State', 'Enabled', 'Continuous', 'Alarm condition'] fields = ['alarm_id', 'name', 'state', 'enabled', 'repeat_actions', 'rule'] utils.print_list(alarms, fields, field_labels, formatters={'rule': alarm_rule_formatter}, sortby=0) def alarm_query_formater(alarm): qs = [] for q in alarm.rule['query']: qs.append('%s %s %s' % ( q['field'], OPERATORS_STRING.get(q['op']), q['value'])) return r' AND\n'.join(qs) def _display_alarm(alarm): fields = ['name', 'description', 'type', 'state', 'enabled', 'alarm_id', 'user_id', 'project_id', 'alarm_actions', 'ok_actions', 'insufficient_data_actions', 'repeat_actions'] data = dict([(f, getattr(alarm, f, '')) for f in fields]) data.update(alarm.rule) if alarm.type == 'threshold': data['query'] = alarm_query_formater(alarm) utils.print_dict(data, wrap=72) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm to show.') def do_alarm_show(cc, args={}): '''Show an alarm.''' try: alarm = cc.alarms.get(args.alarm_id) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) else: _display_alarm(alarm) def common_alarm_arguments(create=False): def _wrapper(func): @utils.arg('--name', metavar='', required=create, help='Name of the alarm (must be unique per tenant)') @utils.arg('--project-id', metavar='', help='Tenant to associate with alarm ' '(only settable by admin users)') @utils.arg('--user-id', metavar='', help='User to associate with alarm ' '(only settable by admin users)') @utils.arg('--description', metavar='', help='Free text description of the alarm') @utils.arg('--state', metavar='', help='State of the alarm, one of: ' + str(ALARM_STATES)) @utils.arg('--enabled', type=utils.string_to_bool, metavar='{True|False}', help='True if alarm evaluation/actioning is enabled') @utils.arg('--alarm-action', dest='alarm_actions', metavar='', action='append', default=None, help=('URL to invoke when state transitions to alarm. ' 'May be used multiple times.')) @utils.arg('--ok-action', dest='ok_actions', metavar='', action='append', default=None, help=('URL to invoke when state transitions to OK. ' 'May be used multiple times.')) @utils.arg('--insufficient-data-action', dest='insufficient_data_actions', metavar='', action='append', default=None, help=('URL to invoke when state transitions to unkown. ' 'May be used multiple times.')) @functools.wraps(func) def _wrapped(*args, **kwargs): return func(*args, **kwargs) return _wrapped return _wrapper @common_alarm_arguments(create=True) @utils.arg('--period', type=int, metavar='', help='Length of each period (seconds) to evaluate over') @utils.arg('--evaluation-periods', type=int, metavar='', help='Number of periods to evaluate over') @utils.arg('--meter-name', metavar='', required=True, help='Metric to evaluate against') @utils.arg('--statistic', metavar='', help='Statistic to evaluate, one of: ' + str(STATISTICS)) @utils.arg('--comparison-operator', metavar='', help='Operator to compare with, one of: ' + str(ALARM_OPERATORS)) @utils.arg('--threshold', type=float, metavar='', required=True, help='Threshold to evaluate against') @utils.arg('--matching-metadata', dest='matching_metadata', metavar='', action='append', default=None, help=('A meter should match this resource metadata (key=value) ' 'additionally to the meter_name')) @utils.arg('--repeat-actions', dest='repeat_actions', metavar='{True|False}', type=utils.string_to_bool, default=False, help=('True if actions should be repeatedly notified ' 'while alarm remains in target state')) def do_alarm_create(cc, args={}): '''Create a new alarm (Deprecated).''' fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) fields = utils.args_array_to_dict(fields, "matching_metadata") alarm = cc.alarms.create(**fields) _display_alarm(alarm) @common_alarm_arguments(create=True) @utils.arg('--meter-name', metavar='', required=True, dest='threshold_rule/meter_name', help='Metric to evaluate against') @utils.arg('--period', type=int, metavar='', dest='threshold_rule/period', help='Length of each period (seconds) to evaluate over') @utils.arg('--evaluation-periods', type=int, metavar='', dest='threshold_rule/evaluation_periods', help='Number of periods to evaluate over') @utils.arg('--statistic', metavar='', dest='threshold_rule/statistic', help='Statistic to evaluate, one of: ' + str(STATISTICS)) @utils.arg('--comparison-operator', metavar='', dest='threshold_rule/comparison_operator', help='Operator to compare with, one of: ' + str(ALARM_OPERATORS)) @utils.arg('--threshold', type=float, metavar='', required=True, dest='threshold_rule/threshold', help='Threshold to evaluate against') @utils.arg('-q', '--query', metavar='', dest='threshold_rule/query', help='The query to find the data for computing statistics ' '(key[op]value; list.)') @utils.arg('--repeat-actions', dest='repeat_actions', metavar='{True|False}', type=utils.string_to_bool, default=False, help=('True if actions should be repeatedly notified ' 'while alarm remains in target state')) def do_alarm_threshold_create(cc, args={}): '''Create a new alarm based on computed statistics.''' fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) fields = utils.key_with_slash_to_nested_dict(fields) fields['type'] = 'threshold' if 'query' in fields['threshold_rule']: fields['threshold_rule']['query'] = options.cli_to_array( fields['threshold_rule']['query']) alarm = cc.alarms.create(**fields) _display_alarm(alarm) @common_alarm_arguments(create=True) @utils.arg('--alarm_ids', action='append', metavar='', required=True, dest='combination_rule/alarm_ids', help='List of alarm id') @utils.arg('--operator', metavar='', dest='combination_rule/operator', help='Operator to compare with, one of: ' + str( ALARM_COMBINATION_OPERATORS)) @utils.arg('--repeat-actions', dest='repeat_actions', metavar='{True|False}', type=utils.string_to_bool, default=False, help=('True if actions should be repeatedly notified ' 'while alarm remains in target state')) def do_alarm_combination_create(cc, args={}): '''Create a new alarm based on state of other alarms.''' fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) fields = utils.key_with_slash_to_nested_dict(fields) fields['type'] = 'combination' alarm = cc.alarms.create(**fields) _display_alarm(alarm) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm to update.') @common_alarm_arguments() @utils.arg('--period', type=int, metavar='', help='Length of each period (seconds) to evaluate over') @utils.arg('--evaluation-periods', type=int, metavar='', help='Number of periods to evaluate over') @utils.arg('--meter-name', metavar='', help='Metric to evaluate against') @utils.arg('--statistic', metavar='', help='Statistic to evaluate, one of: ' + str(STATISTICS)) @utils.arg('--comparison-operator', metavar='', help='Operator to compare with, one of: ' + str(ALARM_OPERATORS)) @utils.arg('--threshold', type=float, metavar='', help='Threshold to evaluate against') @utils.arg('--matching-metadata', dest='matching_metadata', metavar='', action='append', default=None, help=('A meter should match this resource metadata (key=value) ' 'additionally to the meter_name')) @utils.arg('--repeat-actions', dest='repeat_actions', metavar='{True|False}', type=utils.string_to_bool, help=('True if actions should be repeatedly notified ' 'while alarm remains in target state')) def do_alarm_update(cc, args={}): '''Update an existing alarm.''' fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) fields = utils.args_array_to_dict(fields, "matching_metadata") fields.pop('alarm_id') try: alarm = cc.alarms.update(args.alarm_id, **fields) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) _display_alarm(alarm) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm to update.') @common_alarm_arguments() @utils.arg('--meter-name', metavar='', dest='threshold_rule/meter_name', help='Metric to evaluate against') @utils.arg('--period', type=int, metavar='', dest='threshold_rule/period', help='Length of each period (seconds) to evaluate over') @utils.arg('--evaluation-periods', type=int, metavar='', dest='threshold_rule/evaluation_periods', help='Number of periods to evaluate over') @utils.arg('--statistic', metavar='', dest='threshold_rule/statistic', help='Statistic to evaluate, one of: ' + str(STATISTICS)) @utils.arg('--comparison-operator', metavar='', dest='threshold_rule/comparison_operator', help='Operator to compare with, one of: ' + str(ALARM_OPERATORS)) @utils.arg('--threshold', type=float, metavar='', dest='threshold_rule/threshold', help='Threshold to evaluate against') @utils.arg('-q', '--query', metavar='', dest='threshold_rule/query', help='The query to find the data for computing statistics ' '(key[op]value; list.)') @utils.arg('--repeat-actions', dest='repeat_actions', metavar='{True|False}', type=utils.string_to_bool, help=('True if actions should be repeatedly notified ' 'while alarm remains in target state')) def do_alarm_threshold_update(cc, args={}): '''Update an existing alarm based on computed statistics.''' fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) fields = utils.key_with_slash_to_nested_dict(fields) fields.pop('alarm_id') fields['type'] = 'threshold' if 'threshold_rule' in fields and 'query' in fields['threshold_rule']: fields['threshold_rule']['query'] = options.cli_to_array( fields['threshold_rule']['query']) try: alarm = cc.alarms.update(args.alarm_id, **fields) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) _display_alarm(alarm) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm to update.') @common_alarm_arguments() @utils.arg('--alarm_ids', action='append', metavar='', dest='combination_rule/alarm_ids', help='List of alarm id') @utils.arg('---operator', metavar='', dest='combination_rule/operator', help='Operator to compare with, one of: ' + str( ALARM_COMBINATION_OPERATORS)) @utils.arg('--repeat-actions', dest='repeat_actions', metavar='{True|False}', type=utils.string_to_bool, help=('True if actions should be repeatedly notified ' 'while alarm remains in target state')) def do_alarm_combination_update(cc, args={}): '''Update an existing alarm based on state of other alarms.''' fields = dict(filter(lambda x: not (x[1] is None), vars(args).items())) fields = utils.key_with_slash_to_nested_dict(fields) fields.pop('alarm_id') fields['type'] = 'combination' try: alarm = cc.alarms.update(args.alarm_id, **fields) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) _display_alarm(alarm) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm to delete.') def do_alarm_delete(cc, args={}): '''Delete an alarm.''' try: cc.alarms.delete(args.alarm_id) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm state to set.') @utils.arg('--state', metavar='', required=True, help='State of the alarm, one of: ' + str(ALARM_STATES)) def do_alarm_state_set(cc, args={}): '''Set the state of an alarm.''' try: state = cc.alarms.set_state(args.alarm_id, args.state) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) utils.print_dict({'state': state}, wrap=72) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm state to show.') def do_alarm_state_get(cc, args={}): '''Get the state of an alarm.''' try: state = cc.alarms.get_state(args.alarm_id) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) utils.print_dict({'state': state}, wrap=72) @utils.arg('-a', '--alarm_id', metavar='', required=True, help='ID of the alarm for which history is shown.') @utils.arg('-q', '--query', metavar='', help='key[op]value; list.') def do_alarm_history(cc, args={}): '''Display the change history of an alarm.''' kwargs = dict(alarm_id=args.alarm_id, q=options.cli_to_array(args.query)) try: history = cc.alarms.get_history(**kwargs) except exc.HTTPNotFound: raise exc.CommandError('Alarm not found: %s' % args.alarm_id) field_labels = ['Type', 'Timestamp', 'Detail'] fields = ['type', 'timestamp', 'detail'] utils.print_list(history, fields, field_labels, formatters={'detail': alarm_change_detail_formatter}, sortby=1) @utils.arg('-q', '--query', metavar='', help='key[op]value; list.') def do_resource_list(cc, args={}): '''List the resources.''' resources = cc.resources.list(q=options.cli_to_array(args.query)) field_labels = ['Resource ID', 'Source', 'User ID', 'Project ID'] fields = ['resource_id', 'source', 'user_id', 'project_id'] utils.print_list(resources, fields, field_labels, sortby=1) @utils.arg('-r', '--resource_id', metavar='', required=True, help='ID of the resource to show.') def do_resource_show(cc, args={}): '''Show the resource.''' try: resource = cc.resources.get(args.resource_id) except exc.HTTPNotFound: raise exc.CommandError('Resource not found: %s' % args.resource_id) else: fields = ['resource_id', 'source', 'user_id', 'project_id', 'metadata'] data = dict([(f, getattr(resource, f, '')) for f in fields]) utils.print_dict(data, wrap=72) python-ceilometerclient-1.0.8/AUTHORS0000664000175300017540000000213012247122147020571 0ustar jenkinsjenkins00000000000000Alex Gaynor Andreas Jaeger Angus Salkeld Bartosz Górski Brian Waldon ChenZheng Chuck Short Cyril Roelandt Dan Florea Dirk Mueller Eoghan Glynn Eric Pendergrass Gordon Chung Guangyu Suo Ilya Tyaptin James E. Blair Jason Zhang Julien Danjou Kieran Spear Kui Shi Lianhao Lu Mehdi Abaakouk Monty Taylor Nejc Saje OpenStack Jenkins Sascha Peilicke Stefano Zilli ZhiQiang Fan chenxiao fujioka yuuichi yolanda.robla python-ceilometerclient-1.0.8/openstack-common.conf0000664000175300017540000000033512247122076023653 0ustar jenkinsjenkins00000000000000[DEFAULT] # The list of modules to copy from openstack-common module=cliutils module=importutils module=install_venv_common module=py3kcompat # The base module to hold the copy of openstack.common base=ceilometerclient