From 1aff44b7b81b60c659b6ba7e14086e1788d55d9d Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sat, 17 Oct 2015 16:04:33 -0400 Subject: [PATCH] Retire stackforge/python-libraclient --- .gitignore | 17 - .gitreview | 5 - .testr.conf | 4 - CONTRIBUTING.rst | 17 - HACKING.rst | 59 -- LICENSE | 175 ----- MANIFEST.in | 9 - README.rst | 8 +- build_pdf.sh | 6 - debian/changelog | 50 -- debian/compat | 1 - debian/control | 16 - debian/copyright | 21 - debian/pyversions | 1 - debian/rules | 7 - doc/command.rst | 321 -------- doc/conf.py | 234 ------ doc/examples.rst | 102 --- doc/index.rst | 10 - doc/installation.rst | 87 --- doc/introduction.rst | 8 - libraclient/__init__.py | 16 - libraclient/client.py | 38 - libraclient/openstack/__init__.py | 13 - libraclient/openstack/common/__init__.py | 13 - .../openstack/common/apiclient/__init__.py | 16 - .../openstack/common/apiclient/auth.py | 229 ------ .../openstack/common/apiclient/base.py | 544 -------------- .../openstack/common/apiclient/client.py | 366 --------- .../openstack/common/apiclient/exceptions.py | 443 ----------- .../openstack/common/apiclient/fake_client.py | 172 ----- .../openstack/common/apiclient/keystone.py | 202 ----- libraclient/openstack/common/cliutils.py | 213 ------ libraclient/openstack/common/gettextutils.py | 373 ---------- libraclient/openstack/common/importutils.py | 68 -- .../openstack/common/py3kcompat/__init__.py | 17 - .../openstack/common/py3kcompat/urlutils.py | 51 -- libraclient/openstack/common/strutils.py | 218 ------ libraclient/shell.py | 698 ------------------ libraclient/utils.py | 116 --- libraclient/v1_1/__init__.py | 13 - libraclient/v1_1/algorithms.py | 27 - libraclient/v1_1/base.py | 24 - libraclient/v1_1/client.py | 86 --- libraclient/v1_1/limits.py | 32 - libraclient/v1_1/loadbalancer.py | 312 -------- libraclient/v1_1/protocols.py | 27 - libraclient/v1_1/shell.py | 360 --------- openstack-common.conf | 9 - requirements.txt | 5 - setup.cfg | 44 -- setup.py | 22 - test-requirements.txt | 5 - tests/__init__.py | 13 - tests/test_lbaas_client.py | 7 - tox.ini | 37 - 56 files changed, 7 insertions(+), 5980 deletions(-) delete mode 100644 .gitignore delete mode 100644 .gitreview delete mode 100644 .testr.conf delete mode 100644 CONTRIBUTING.rst delete mode 100644 HACKING.rst delete mode 100644 LICENSE delete mode 100644 MANIFEST.in delete mode 100755 build_pdf.sh delete mode 100644 debian/changelog delete mode 100644 debian/compat delete mode 100644 debian/control delete mode 100644 debian/copyright delete mode 100644 debian/pyversions delete mode 100755 debian/rules delete mode 100644 doc/command.rst delete mode 100644 doc/conf.py delete mode 100644 doc/examples.rst delete mode 100644 doc/index.rst delete mode 100644 doc/installation.rst delete mode 100644 doc/introduction.rst delete mode 100644 libraclient/__init__.py delete mode 100644 libraclient/client.py delete mode 100644 libraclient/openstack/__init__.py delete mode 100644 libraclient/openstack/common/__init__.py delete mode 100644 libraclient/openstack/common/apiclient/__init__.py delete mode 100644 libraclient/openstack/common/apiclient/auth.py delete mode 100644 libraclient/openstack/common/apiclient/base.py delete mode 100644 libraclient/openstack/common/apiclient/client.py delete mode 100644 libraclient/openstack/common/apiclient/exceptions.py delete mode 100644 libraclient/openstack/common/apiclient/fake_client.py delete mode 100644 libraclient/openstack/common/apiclient/keystone.py delete mode 100644 libraclient/openstack/common/cliutils.py delete mode 100644 libraclient/openstack/common/gettextutils.py delete mode 100644 libraclient/openstack/common/importutils.py delete mode 100644 libraclient/openstack/common/py3kcompat/__init__.py delete mode 100644 libraclient/openstack/common/py3kcompat/urlutils.py delete mode 100644 libraclient/openstack/common/strutils.py delete mode 100644 libraclient/shell.py delete mode 100644 libraclient/utils.py delete mode 100644 libraclient/v1_1/__init__.py delete mode 100644 libraclient/v1_1/algorithms.py delete mode 100644 libraclient/v1_1/base.py delete mode 100644 libraclient/v1_1/client.py delete mode 100644 libraclient/v1_1/limits.py delete mode 100644 libraclient/v1_1/loadbalancer.py delete mode 100644 libraclient/v1_1/protocols.py delete mode 100644 libraclient/v1_1/shell.py delete mode 100644 openstack-common.conf delete mode 100644 requirements.txt delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 test-requirements.txt delete mode 100644 tests/__init__.py delete mode 100644 tests/test_lbaas_client.py delete mode 100644 tox.ini diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 11a98e9..0000000 --- a/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -*.pyc -.testrepository -.tox -*.egg-info -build/ -dist/ -debian/files -debian/python-libraclient.debhelper.log -debian/python-libraclient.postinst.debhelper -debian/python-libraclient.prerm.debhelper -debian/python-libraclient.substvars -debian/python-libraclient/ -AUTHORS -ChangeLog -doc/api -*.deb -*.swp diff --git a/.gitreview b/.gitreview deleted file mode 100644 index 24e9a28..0000000 --- a/.gitreview +++ /dev/null @@ -1,5 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=stackforge/python-libraclient.git - diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 081907d..0000000 --- a/.testr.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 8194733..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,17 +0,0 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps in the "If you're a developer, start here" -section of this page: - - http://wiki.openstack.org/HowToContribute - -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: - - http://wiki.openstack.org/GerritWorkflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/libra diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index 60fcd5b..0000000 --- a/HACKING.rst +++ /dev/null @@ -1,59 +0,0 @@ -Libra Client Style Commandments -=============================== - -- Step 1: Read the OpenStack Style Commandments - http://docs.openstack.org/developer/hacking -- Step 2: Read on - - -Libra Client Specific Commandments ----------------------------------- -None so far - -Text encoding -------------- -- All text within python code should be of type 'unicode'. - - WRONG: - - >>> s = 'foo' - >>> s - 'foo' - >>> type(s) - - - RIGHT: - - >>> u = u'foo' - >>> u - u'foo' - >>> type(u) - - -- Transitions between internal unicode and external strings should always - be immediately and explicitly encoded or decoded. - -- All external text that is not explicitly encoded (database storage, - commandline arguments, etc.) should be presumed to be encoded as utf-8. - - WRONG: - - mystring = infile.readline() - myreturnstring = do_some_magic_with(mystring) - outfile.write(myreturnstring) - - RIGHT: - - mystring = infile.readline() - mytext = s.decode('utf-8') - returntext = do_some_magic_with(mytext) - returnstring = returntext.encode('utf-8') - outfile.write(returnstring) - -Running Tests -------------- -The testing system is based on a combination of tox and testr. If you just -want to run the whole suite, run `tox` and all will be fine. However, if -you'd like to dig in a bit more, you might want to learn some things about -testr itself. A basic walkthrough for OpenStack can be found at -http://wiki.openstack.org/testr diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 67db858..0000000 --- a/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - 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. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 3928293..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,9 +0,0 @@ -include README - -exclude .gitignore -exclude .gitreview - -global-exclude *.pyc - -graft doc -graft tools diff --git a/README.rst b/README.rst index a67ef41..9006052 100644 --- a/README.rst +++ b/README.rst @@ -1 +1,7 @@ -Libra command line client +This project is no longer maintained. + +The contents of this repository are still available in the Git source code +management system. To see the contents of this repository before it reached +its end of life, please check out the previous commit with +"git checkout HEAD^1". + diff --git a/build_pdf.sh b/build_pdf.sh deleted file mode 100755 index edf0d43..0000000 --- a/build_pdf.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -python setup.py build_sphinx_latex -# Fix option double dashes in latex output -perl -i -pe 's/\\bfcode\{--(.*)\}/\\bfcode\{-\{\}-\1\}/g' build/sphinx/latex/*.tex -perl -i -pe 's/\\index\{(.*?)--(.*?)\}/\\index\{\1-\{\}-\2\}/g' build/sphinx/latex/*.tex -make -C build/sphinx/latex all-pdf diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index d25738b..0000000 --- a/debian/changelog +++ /dev/null @@ -1,50 +0,0 @@ -python-libraclient (1.2.5-1) UNRELEASED; urgency=low - - * Adds support for Load Balancer log snapshotting - - -- Andrew Hutchings Wed, 27 Mar 2013 20:24:06 +0000 - -python-libraclient (1.2.4-1) UNRELEASED; urgency=low - - * Adds input filtering and error handling for --node options - * Adds virtualips command - - -- Andrew Hutchings Thu, 14 Mar 2013 14:17:04 +0000 - -python-libraclient (1.2.3-1) UNRELEASED; urgency=low - - * Fixes broken exception handler - - -- Andrew Hutchings Wed, 20 Feb 2013 19:31:25 +0000 - -python-libraclient (1.2.2-1) UNRELEASED; urgency=low - - * Allow newer versions of Novaclient again - * Fix tests for newer versions - - -- Andrew Hutchings Wed, 20 Feb 2013 08:52:39 +0000 - -python-libraclient (1.2.1-1) UNRELEASED; urgency=low - - * Don't use broken version of Novaclient - - -- Andrew Hutchings Fri, 15 Feb 2013 17:00:25 +0000 - -python-libraclient (1.2-1) UNRELEASED; urgency=low - - * Fixed dependencies - * Various bug fixes - - -- Andrew Hutchings Fri, 25 Jan 2013 08:59:41 -0800 - -python-libraclient (1.1-1) UNRELEASED; urgency=low - - * Fixed debian building for tests directory - - -- Andrew Hutchings Thu, 24 Jan 2013 10:36:00 -0800 - -python-libraclient (1.0-1) UNRELEASED; urgency=low - - * Initial release. - - -- Andrew Hutchings Thu, 03 Jan 2013 15:31:58 +0000 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index ec63514..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/debian/control b/debian/control deleted file mode 100644 index f3f80b4..0000000 --- a/debian/control +++ /dev/null @@ -1,16 +0,0 @@ -Source: python-libraclient -Section: python -Priority: optional -Maintainer: Andrew Hutchings (LinuxJedi) -Build-Depends: debhelper (>=9), python-support (>=1.0), cdbs (>=0.4.111), python-all-dev -Build-Depends-Indep: python-sphinx (>=1.0) -Standards-Version: 3.9.4 -Homepage: https://launchpad.net/libra - -Package: python-libraclient -Architecture: all -XB-Python-Version: ${python-Versions} -Depends: ${misc:Depends}, ${python:Depends} -Provides: ${python:Provides} -Description: Client for an Openstack Load Balancer as a Service - diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index 68081c9..0000000 --- a/debian/copyright +++ /dev/null @@ -1,21 +0,0 @@ -Format: http://dep.debian.net/deps/dep5 -Upstream-Name: python-libraclient -Source: https://launchpad.net/libra/ - -Files: * -Copyright: 2012 Hewlett-Packard Development Company, L.P. -License: Apache 2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the complete text of the Apache License 2.0 can - be found in "/usr/share/common-licenses/Apache-2.0" diff --git a/debian/pyversions b/debian/pyversions deleted file mode 100644 index 3ad2293..0000000 --- a/debian/pyversions +++ /dev/null @@ -1 +0,0 @@ -2.7- diff --git a/debian/rules b/debian/rules deleted file mode 100755 index d3830a9..0000000 --- a/debian/rules +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- - -WITH_PYTHON2 = $(shell test -f /usr/bin/dh_python2 && echo "--with python2") - -%: - dh $@ ${WITH_PYTHON2} diff --git a/doc/command.rst b/doc/command.rst deleted file mode 100644 index 4084abf..0000000 --- a/doc/command.rst +++ /dev/null @@ -1,321 +0,0 @@ -Usage -===== - -Synopsis --------- - -:program:`libra` [:ref:`GENERAL OPTIONS `] [:ref:`COMMAND `] [*COMMAND_OPTIONS*] - -Description ------------ - -:program:`libra` is a utility designed to communicate with Atlas API -based Load Balancer as a Service systems. - -.. _libra-options: - -Global Options --------------- - -.. program:: libra - -.. option:: --help, -h - - Show help message and exit - -.. option:: --debug - - Turn on HTTP debugging for requests - -.. option:: --insecure - - Don't validate SSL certs - -.. option:: --os_bypass_url - - URL to use as an endpoint instead of the one specified by the Service - Catalog - -.. option:: --service_type - - Alternative service type to use for your cloud provider (default is - 'hpext:lbaas') - -.. option:: --os_auth_url - - The OpenStack authentication URL. Default is ``OS_AUTH_URL`` or - ``LIBRA_URL`` environment variables - -.. option:: --os_username - - The user name to use for authentication. Default is ``OS_USERNAME`` or - ``LIBRA_USERNAME`` environment variables - -.. option:: --os_password - - The password to use for authentication. Default is ``OS_PASSWORD`` or - ``LIBRA_PASSWORD`` environment variables - -.. option:: --os_tenant_name - - The tenant to authenticate to. Default is ``OS_TENANT_NAME`` or - ``LIBRA_PROJECT_ID`` environment variables - -.. option:: --os_region_name - - The region the load balancer is located. Default is ``OS_REGION_NAME`` or - ``LIBRA_REGION_NAME`` environment variables - -.. _libra-commands: - -Client Commands ---------------- - -.. program:: libra algorithms - -algorithms -^^^^^^^^^^ - -Gets a list of supported algorithms - -.. program:: libra create - -create -^^^^^^ - -Create a load balancer - -.. option:: --name - - The name of the node to be created - -.. option:: --port - - The port the load balancer will listen on - -.. option:: --protocol - - The protocol type for the load balancer (HTTP, TCP or GALERA). - The Galera option adds support for deadlock avoidance in Galera clusters, - see `Serveral Nine's Blog `_ on this. - -.. option:: --node - - The IP and port for a load balancer node (can be used multiple times to add multiple nodes). - Additional node options may be specified after the ip:port portion in a option=value format. - -.. option:: --vip - - The virtual IP ID of an existing load balancer to attach to - -.. program:: libra delete - -delete -^^^^^^ - -Delete a load balancer - -.. option:: - - The ID of the load balancer - -.. program:: libra limits - -limits -^^^^^^ - -Show the API limits for the user - -.. program:: libra list - -list -^^^^ - -List all load balancers - -.. option:: --deleted - - Show deleted load balancers - -.. program:: libra logs - -logs -^^^^ - -Send a snapshot of logs to an object store - -.. option:: - - The ID of the load balancer - -.. option:: --storage - - Storage type - -.. option:: --endpoint - - Object store endpoint to use - -.. option:: --basepath - - Object store based directory - -.. option:: --token - - Object store authentication token - -.. program:: libra update - -update -^^^^^^ - -Update a load balancer's configuration - -.. option:: - - The ID of the load balancer - -.. option:: --name - - A new name for the load balancer - -.. option:: --algorithm - - A new algorithm for the load balancer - -.. program:: libra monitor-list - -monitor-list -^^^^^^^^^^^^ - -List the health monitor for a load balancer - -.. option:: - - The ID of the load balancer - -.. program:: libra monitor-delete - -monitor-delete -^^^^^^^^^^^^^^ - -Delete the health monitor for a load balancer - -.. option:: - - The ID of the load balancer - -.. program:: libra monitor-update - -monitor-update -^^^^^^^^^^^^^^ - -Update the health monitor for a load balancer - -.. option:: - - The ID of the load balancer - -.. program:: libra node-add - -node-add -^^^^^^^^ - -Add a node to a load balancer - -.. option:: - - The ID of the load balancer - -.. option:: --node - - The node address in ip:port format (can be used multiple times to add multiple nodes). - Additional node options may be specified after the ip:port portion in a option=value format. - -.. program:: libra node-delete - -node-delete -^^^^^^^^^^^ - -Delete a node from the load balancer - -.. option:: - - The ID of the load balancer - -.. option:: - - The ID of the node to be removed - -.. program:: libra node-list - -node-list -^^^^^^^^^ - -List the nodes in a load balancer - -.. option:: - - The ID of the load balancer - -.. program:: libra node-update - -node-update -^^^^^^^^^^^ - -Update a node's state in a load balancer - -.. option:: - - The ID of the load balancer - -.. option:: - - The ID of the node to be updated - -.. option:: --condition - - The new state of the node (either ENABLED or DISABLED) - -.. program:: libra node-show - -node-show -^^^^^^^^^ - -Get the status of a node in a load balancer - -.. option:: - - The ID of the load balancer - -.. option:: - - The ID of the node in the load balancer -.. program:: libra protocols - -protocols -^^^^^^^^^ - -Gets a list of supported protocols - -.. program:: libra show - -show -^^^^ - -Get the status of a single load balancer - -.. option:: - - The ID of the load balancer - -.. program:: libra virtualips - -virtualips -^^^^^^^^^^ - -Get a list of virtual IPs - -.. option:: - - The ID of the load balancer diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index 35da40c..0000000 --- a/doc/conf.py +++ /dev/null @@ -1,234 +0,0 @@ -# -*- coding: utf-8 -*- -# -# OpenStack CI documentation build configuration file, created by -# sphinx-quickstart on Mon Jul 18 13:42:23 2011. -# -# This file is execfile()d with the current directory set to its containing -# dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import datetime -import sys - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ---------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -#extensions = ['rst2pdf.pdfbuilder'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'LBaaS Command Line Client' -copyright = u'2012, Andrew Hutchings' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = "%d-%02d-%02d-beta" % ( - datetime.datetime.now().year, - datetime.datetime.now().month, - datetime.datetime.now().day -) -# The full version, including alpha/beta/rc tags. -release = "%d-%02d-%02d-beta" % ( - datetime.datetime.now().year, - datetime.datetime.now().month, - datetime.datetime.now().day -) - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'LibraClent' - - -# -- Options for LaTeX output ------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, -# documentclass [howto/manual]). -latex_documents = [ - ('index', 'python-libraclient-{0}.tex'.format(version), u'Libra Client Documentation', - u'Andrew Hutchings', 'manual'), -] - -#pdf_documents = [('index', 'Libra-{0}'.format(version), u'Libra Client, Worker and Pool Manager Documentation', u'Andrew Hutchings and David Shrewsbury')] - -#pdf_break_level = 1 - -#pdf_stylesheets = ['sphinx', 'libra'] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output ------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -#man_pages = [ -# ('index', 'lbaas', u'LBaaS Client', -# [u'Andrew Hutchings'], 1) -#] diff --git a/doc/examples.rst b/doc/examples.rst deleted file mode 100644 index 9f787b7..0000000 --- a/doc/examples.rst +++ /dev/null @@ -1,102 +0,0 @@ -Examples -======== - -Create Load Balancer --------------------- - -.. code-block:: bash - - libra --os_auth_url=https://company.com/openstack/auth/url \ - --os_username=username --os_password=pasword --os_tenant_name=tenant \ - --os_region_name=region create --name=my_load_balancer \ - --node 192.168.1.1:80 --node 192.168.1.2:80 - -This example will create a basic load balancer which will listen on port 80 and -direct traffic in a round-robin fashion to two nodes, 192.168.1.1 and -192.168.1.2. Both these nodes are web servers listening on port 80. The Libra -Client will then return a table similar to the below: - -+------------+----------------------------------------+ -| Property | Value | -+------------+----------------------------------------+ -| status | BUILD | -| updated | 2013-10-31T11:59:24 | -| protocol | HTTP | -| name | test | -| algorithm | ROUND_ROBIN | -| created | 2013-10-31T11:59:24 | -| virtualIps | | -| port | 80 | -| nodes | | -| | | -| id | 80303 | -+------------+----------------------------------------+ - -Create a Load Balancer with Node Options ----------------------------------------- - -.. code-block:: bash - - libra --os_auth_url=https://company.com/openstack/auth/url \ - --os_username=username --os_password=pasword --os_tenant_name=tenant \ - --os_region_name=region create --name=my_load_balancer \ - --node 192.168.1.1:80:weight=1 --node 192.168.1.2:80:weight=2 - -Nearly identical to the above example, this creates a new load balancer -with two nodes, but one is more heavily weighted than the other, causing -it to accept more traffic. - -Create a Shared Load Balancer ------------------------------ - -It is possible for a single logical load balancer to balancer traffic for both -HTTP and HTTPS for a site. For this example we will add an HTTPS load balancer -to the load balancer we created previously: - -.. code-block:: bash - - libra --os_auth_url=https://company.com/openstack/auth/url \ - --os_username=username --os_password=pasword --os_tenant_name=tenant \ - --os_region_name=region create --name=my_load_balancer \ - --node 192.168.1.1:443 --node 192.168.1.2:443 --protocol=TCP --port=443 \ - --vip=52 - -We have taken the IP ID which was provided in the original create and given this -as a VIP number in the command. We are also setting to TCP mode so the SSL -termination happens at the web server and set the load balancer to listen on -port 443. The result is as follows: - -+------------+----------------------------------------+ -| Property | Value | -+------------+----------------------------------------+ -| status | BUILD | -| updated | 2013-10-31T11:59:24 | -| protocol | HTTP | -| name | test | -| algorithm | ROUND_ROBIN | -| created | 2013-10-31T11:59:24 | -| virtualIps | | -| port | 80 | -| nodes | | -| | | -| id | 80303 | -+------------+----------------------------------------+ - -Add a Node ----------- - -.. code-block:: bash - - libra --os_auth_url=https://company.com/openstack/auth/url \ - --os_username=username --os_password=pasword --os_tenant_name=tenant \ - --os_region_name=region node-add 158 --node=192.168.1.3:443 - -In this example we have take the ID of the load balancer of the previos example -to add a web server to. The result should look something like this: - -+----+-------------+------+-----------+--------+ -| ID | Address | Port | Condition | Status | -+----+-------------+------+-----------+--------+ -| | 192.168.1.3 | 443 | | | -+----+-------------+------+-----------+--------+ - diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 54e0e22..0000000 --- a/doc/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Libra Command Line Client -========================= - -.. toctree:: - :maxdepth: 2 - - introduction - installation - command - examples diff --git a/doc/installation.rst b/doc/installation.rst deleted file mode 100644 index 12562d3..0000000 --- a/doc/installation.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. _installation: - -Installation -============ - -.. _install-ppa: - -From Ubuntu Package via PPA ---------------------------- - -1. Install utility - -:: - - sudo apt-get install python-software-properties - -2. Add the PPA - -:: - - sudo apt-add-repository ppa:libra-core/ppa - -3. Update the package indexes - -:: - - sudo apt-get update - -4. Install packages - -:: - - sudo apt-get install python-libraclient - - -.. _install-pypi: - -From PyPI ---------- - -The :program:`python-libraclient` package is published on `PyPI `_ and so can be installed using the pip tool, which will manage installing all -python dependencies. - -.. note:: The pip tool isn't bundled by default with some versions of the different distributions, please install it typically using a package manager for the platform you use. - -.. note:: Needs to be done in a Virtual Environment or as root. - -.. code-block:: shell-session - - pip install python-libraclient - -.. warning:: - - The packages on PyPI may lag behind the git repo in functionality. - -.. _install-source: - -Setup the client from source ----------------------------- -If you want the latest version, straight from github: - -.. code-block:: shell-session - - git clone git@github.com:stackforge/python-libraclient.git - cd python-libraclient - virtualenv .venv - source .venv/bin/activate - pip install -r requirements.txt -r test-requirements.txt - python setup.py install - - -.. _install-development: - -Setup the client in development mode ------------------------------------- -Installing in development mode allows your to make changes to the source code & test directly without having to re-run the "python setup.py install" -step. You can find out more about this in the `Development Mode `_ online docs. - -.. code-block:: shell-session - - git clone git@github.com:stackforge/python-libraclient.git - cd python-libraclient - virtualenv .venv - source .venv/bin/activate - pip install -r requirements.txt -r test-requirements.txt - python setup.py develop - diff --git a/doc/introduction.rst b/doc/introduction.rst deleted file mode 100644 index 1d88fee..0000000 --- a/doc/introduction.rst +++ /dev/null @@ -1,8 +0,0 @@ -Introduction -============ - -Libra Client is a Python command line client which is used to manipulate Atlas -API compatible OpenStack Load Balancer as a Service installations. - -It is designed to be similar to the Python Nova client and in fact uses this -as a common base code. diff --git a/libraclient/__init__.py b/libraclient/__init__.py deleted file mode 100644 index 2b4b281..0000000 --- a/libraclient/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 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. -import pbr.version - -__version__ = pbr.version.VersionInfo('python-libraclient').version_string() diff --git a/libraclient/client.py b/libraclient/client.py deleted file mode 100644 index f07900b..0000000 --- a/libraclient/client.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 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. - -from libraclient.openstack.common.apiclient import exceptions - -import pkg_resources - - -def _find_versions(): - versions = {} - for e in list(pkg_resources.iter_entry_points('libraclient.versions')): - versions[e.name] = (e.module_name, e.load()) - return versions - - -def get_version(version): - versions = _find_versions() - try: - return versions[version][1] - except KeyError: - raise exceptions.UnsupportedVersion('%s is not a supported version.' % - version) - - -def VersionedClient(version, *args, **kw): - cls = get_version(version) - return cls(*args, **kw) diff --git a/libraclient/openstack/__init__.py b/libraclient/openstack/__init__.py deleted file mode 100644 index 582348c..0000000 --- a/libraclient/openstack/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2012 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. diff --git a/libraclient/openstack/common/__init__.py b/libraclient/openstack/common/__init__.py deleted file mode 100644 index 582348c..0000000 --- a/libraclient/openstack/common/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2012 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. diff --git a/libraclient/openstack/common/apiclient/__init__.py b/libraclient/openstack/common/apiclient/__init__.py deleted file mode 100644 index d5d0022..0000000 --- a/libraclient/openstack/common/apiclient/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. diff --git a/libraclient/openstack/common/apiclient/auth.py b/libraclient/openstack/common/apiclient/auth.py deleted file mode 100644 index 101b69b..0000000 --- a/libraclient/openstack/common/apiclient/auth.py +++ /dev/null @@ -1,229 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 libraclient.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 = "openstack.common.apiclient.auth" - mgr = extension.ExtensionManager(ep_namespace) - if mgr.extensions: - 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 requred 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(_discovered_plugins.iterkeys()): - 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", - "bypass_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 - """ \ No newline at end of file diff --git a/libraclient/openstack/common/apiclient/base.py b/libraclient/openstack/common/apiclient/base.py deleted file mode 100644 index 33bc99f..0000000 --- a/libraclient/openstack/common/apiclient/base.py +++ /dev/null @@ -1,544 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 six -import urllib - -from libraclient.openstack.common.apiclient import exceptions -from libraclient.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 _extract_data(self, data, response_key=None): - """ - Extract the data within response_key in data. - - :param data: The data to handle. - :param response_key: The response key to extract. - """ - return data[response_key] if response_key else data - - def _make_obj(self, data, obj_class=None): - """ - Make a object out of obj_class. - - :param data: The data to handle. - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - """ - cls = obj_class or self.resource_class - return cls(self, data, loaded=True) - - def _list(self, url, response_key=None, 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., 'server' - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - :param return_raw: flag to force returning raw JSON instead of - Python object of self.resource_class - :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=None, return_raw=False, obj_class=None): - """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' - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - :param return_raw: flag to force returning raw JSON instead of - Python object of self.resource_class - """ - # NOTE(ekarlso): If the response key is None, then we just return the body. - body = self.client.get(url).json() - data = self._extract_data(body, response_key=response_key) - - if return_raw: - return data - else: - return self._make_obj(data, obj_class=obj_class) - - 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=None, return_raw=False, - obj_class=None): - """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., 'server' - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - :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() - data = self._extract_data(body, response_key=response_key) - - if return_raw: - return data - else: - return self._make_obj(data, obj_class=obj_class) - - def _put(self, url, json=None, response_key=None, return_raw=False, - obj_class=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., 'server' - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - :param return_raw: flag to force returning raw JSON instead of - Python object of self.resource_class - """ - resp = self.client.put(url, json=json) - # PUT requests may not return a body - if resp.content: - body = resp.json() - data = self._extract_data(body, response_key=response_key) - - if return_raw: - return data - else: - return self._make_obj(data, obj_class=obj_class) - - def _patch(self, url, json=None, response_key=None, return_raw=False, - obj_class=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., 'server' - :param obj_class: class for constructing the returned objects - (self.resource_class will be used by default) - :param return_raw: flag to force returning raw JSON instead of - Python object of self.resource_class - """ - body = self.client.patch(url, json=json).json() - data = self._extract_data(body, response_key=response_key) - - if return_raw: - return data - else: - return self._make_obj(data, obj_class=obj_class) - - 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 diff --git a/libraclient/openstack/common/apiclient/client.py b/libraclient/openstack/common/apiclient/client.py deleted file mode 100644 index eaad443..0000000 --- a/libraclient/openstack/common/apiclient/client.py +++ /dev/null @@ -1,366 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 libraclient.openstack.common.apiclient import exceptions -from libraclient.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 exeptions 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 = "libraclient.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, service_type=None, endpoint_type=None, - extensions=None): - - self.service_type = service_type - self.endpoint_type = endpoint_type - - 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) diff --git a/libraclient/openstack/common/apiclient/exceptions.py b/libraclient/openstack/common/apiclient/exceptions.py deleted file mode 100644 index b14e33c..0000000 --- a/libraclient/openstack/common/apiclient/exceptions.py +++ /dev/null @@ -1,443 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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" - - -# In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__() -# so we can do this: -# _code_map = dict((c.http_status, c) -# for c in HttpError.__subclasses__()) -# _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) diff --git a/libraclient/openstack/common/apiclient/fake_client.py b/libraclient/openstack/common/apiclient/fake_client.py deleted file mode 100644 index 28623ba..0000000 --- a/libraclient/openstack/common/apiclient/fake_client.py +++ /dev/null @@ -1,172 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 urlparse - -import requests - -from libraclient.openstack.common.apiclient import client - - -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 = urlparse.parse_qsl(urlparse.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, - }) diff --git a/libraclient/openstack/common/apiclient/keystone.py b/libraclient/openstack/common/apiclient/keystone.py deleted file mode 100644 index 154c56c..0000000 --- a/libraclient/openstack/common/apiclient/keystone.py +++ /dev/null @@ -1,202 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 abc -import logging - -from libraclient.openstack.common.apiclient import auth -from libraclient.openstack.common.apiclient import exceptions - -from keystoneclient import access - - -logger = logging.getLogger(__name__) - - -class KeystoneBaseAuthPlugin(auth.BaseAuthPlugin): - access_info = None - - def _endpoint(self, url=None): - bypass_url = self.opts.get('bypass_url', '') - if bypass_url: - url = bypass_url - return url - - def _do_authenticate(self, http_client): - resp = self._get_auth_response(http_client) - try: - body = resp.json() - except ValueError as ex: - raise exceptions.AuthorizationFailure(ex) - self.access_info = access.AccessInfo.factory( - resp, body, region_name=http_client.region_name) - - - -class KeystoneAuthPluginV2(KeystoneBaseAuthPlugin): - auth_system = "keystone2" - opt_names = [ - "auth_url", - "user_id", - "username", - "password", - "token", - "tenant_id", - "tenant_name", - 'bypass_url' - ] - - def sufficient_options(self): - have_identity = ( - self.opts.get("token") or - (self.opts.get("username") and self.opts.get("password"))) - if not (self.opts.get("auth_url") and have_identity): - req_opts = ("auth_url", "username", "password", "token") - raise exceptions.AuthPluginOptionsMissing( - [opt for opt in req_opts if not self.opts.get(opt)]) - - def _get_auth_response(self, http_client): - headers = {} - token = self.opts.get("token") - if token: - params = {"auth": {"token": {"id": token}}} - headers["X-Auth-Token"] = token - else: - params = { - "auth": { - "passwordCredentials": { - "username": self.opts.get("username"), - "password": self.opts.get("password"), - } - } - } - if self.opts.get("tenant_id"): - params["auth"]["tenantId"] = self.opts.get("tenant_id") - elif self.opts.get("tenant_name"): - params["auth"]["tenantName"] = self.opts.get("tenant_name") - return http_client.request( - "POST", - http_client.concat_url(self.opts.get("auth_url"), "/tokens"), - allow_redirects=True, - headers=headers, - json=params) - - def token_and_endpoint(self, endpoint_type, service_type): - if self.opts['token'] and self.opts['bypass_url']: - return self.opts['token'], self.opts['bypass_url'] - url = self.access_info.service_catalog.url_for( - service_type=service_type, - endpoint_type=endpoint_type) - return self.access_info.auth_token, self._endpoint(url) - - -class KeystoneAuthPluginV3(KeystoneBaseAuthPlugin): - auth_system = "keystone3" - opt_names = [ - "auth_url", - "user_id", - "username", - "user_domain_id", - "user_domain_name", - "password", - "domain_id", - "domain_name", - "project_id", - "project_name", - "project_domain_id", - "project_domain_name", - "token", - "bypass_url" - ] - - def sufficient_options(self): - have_identity = ( - self.opts.get("token") or - ((self.opts.get("username") or self.opts.get("user_id")) - and self.opts.get("password"))) - if not (self.opts.get("auth_url") and have_identity): - req_opts = ("auth_url", "username", "user_id", "password", "token") - raise exceptions.AuthPluginOptionsMissing( - [opt for opt in req_opts if not self.opts.get(opt)]) - - def _set_id_or_name(self, dct, key, id_key, name_key): - value = self.opts.get(id_key) - if value: - dct[key] = {"id": value} - return - value = self.opts.get(name_key) - if value: - dct[key] = {"name": value} - - def _get_auth_response(self, http_client): - domain_scoped = (self.opts.get("domain_id") or - self.opts.get("domain_name")) - project_scoped = (self.opts.get("project_id") or - self.opts.get("project_name")) - - if domain_scoped and project_scoped: - raise ValueError("Authentication cannot be scoped to both domain" - " and project.") - headers = {} - body = {"auth": {"identity": {}}} - ident = body["auth"]["identity"] - - token = self.opts.get("token") - if token: - headers["X-Auth-Token"] = token - - ident["methods"] = ["token"] - ident["token"] = {} - ident["token"]["id"] = token - else: - # password authentication - ident["methods"] = ["password"] - ident["password"] = {} - self._set_id_or_name(ident["password"], "user", - "user_id", "username") - user = ident["password"]["user"] - user["password"] = self.opts.get("password") - - if "name" in user: - self._set_id_or_name(user, "domain", - "user_domain_id", "user_domain_name") - - if domain_scoped or project_scoped: - body["auth"]["scope"] = {} - scope = body["auth"]["scope"] - self._set_id_or_name(scope, "domain", "domain_id", "domain_name") - if "domain" not in scope: - # use project_id or project_name - self._set_id_or_name(scope, "project", - "project_id", "project_name") - if "name" in scope["project"]: - self._set_id_or_name( - scope["project"], "domain", - "project_domain_id", "project_domain_name") - - return http_client.request( - "POST", - http_client.concat_url(self.opts.get("auth_url"), "/auth/tokens"), - allow_redirects=True, - headers=headers, - json=body) - - def token_and_endpoint(self, endpoint_type, service_type): - url = self.access_info.service_catalog.url_for( - service_type=service_type, - endpoint_type=endpoint_type) - return self.access_info.auth_token, self._endpoint(url) diff --git a/libraclient/openstack/common/cliutils.py b/libraclient/openstack/common/cliutils.py deleted file mode 100644 index f55da8c..0000000 --- a/libraclient/openstack/common/cliutils.py +++ /dev/null @@ -1,213 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 - -from libraclient.openstack.common.apiclient import exceptions -from libraclient.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: - sortby = None - else: - sortby = fields[sortby_index] - pt = prettytable.PrettyTable(fields, caching=False) - pt.align = 'l' - - for o in objs: - row = [] - for field in fields: - if formatters and 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(sortby=sortby))) - - -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, basestring) 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 xrange(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 diff --git a/libraclient/openstack/common/gettextutils.py b/libraclient/openstack/common/gettextutils.py deleted file mode 100644 index bd2454c..0000000 --- a/libraclient/openstack/common/gettextutils.py +++ /dev/null @@ -1,373 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 libraclient.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('libraclient'.upper() + '_LOCALEDIR') -_t = gettext.translation('libraclient', 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, 'libraclient') - 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 all projects udpate - 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) diff --git a/libraclient/openstack/common/importutils.py b/libraclient/openstack/common/importutils.py deleted file mode 100644 index 7a303f9..0000000 --- a/libraclient/openstack/common/importutils.py +++ /dev/null @@ -1,68 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 diff --git a/libraclient/openstack/common/py3kcompat/__init__.py b/libraclient/openstack/common/py3kcompat/__init__.py deleted file mode 100644 index dca1c7a..0000000 --- a/libraclient/openstack/common/py3kcompat/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# 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. -# \ No newline at end of file diff --git a/libraclient/openstack/common/py3kcompat/urlutils.py b/libraclient/openstack/common/py3kcompat/urlutils.py deleted file mode 100644 index 540964f..0000000 --- a/libraclient/openstack/common/py3kcompat/urlutils.py +++ /dev/null @@ -1,51 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# 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.parse - - 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 -else: - # python2 - import urllib - 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 \ No newline at end of file diff --git a/libraclient/openstack/common/strutils.py b/libraclient/openstack/common/strutils.py deleted file mode 100644 index 979b5d2..0000000 --- a/libraclient/openstack/common/strutils.py +++ /dev/null @@ -1,218 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 libraclient.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 isntance 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 isntance 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) diff --git a/libraclient/shell.py b/libraclient/shell.py deleted file mode 100644 index d0468d2..0000000 --- a/libraclient/shell.py +++ /dev/null @@ -1,698 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# 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. -# -# NOTE: Partially copied from python-novaclient -""" -CLI (Command Line Interface) for Libra LBaaS tools -""" - -from __future__ import print_function - -import argparse -import getpass -import glob -import imp -import itertools -import os -import pkgutil -import sys -import logging - -import pkg_resources -import six - -HAS_KEYRING = False -all_errors = ValueError -try: - import keyring - HAS_KEYRING = True - try: - if isinstance(keyring.get_keyring(), keyring.backend.GnomeKeyring): - import gnomekeyring - all_errors = (ValueError, - gnomekeyring.IOError, - gnomekeyring.NoKeyringDaemonError) - except Exception: - pass -except ImportError: - pass - -import libraclient -from libraclient.client import VersionedClient -from libraclient.openstack.common.apiclient import auth -from libraclient.openstack.common.apiclient import base -from libraclient.openstack.common.apiclient import client -from libraclient.openstack.common.apiclient import exceptions as exc -from libraclient.openstack.common import cliutils -from libraclient.openstack.common import strutils -from libraclient.v1_1 import shell as shell_v1 - - -DEFAULT_API_VERSION = "1.1" -DEFAULT_ENDPOINT_TYPE = 'publicURL' -DEFAULT_SERVICE_TYPE = 'hpext:lbaas' -DEFAULT_SERVICE_NAME = 'libra' - -logger = logging.getLogger(__name__) - - -def positive_non_zero_float(text): - if text is None: - return None - try: - value = float(text) - except ValueError: - msg = "%s must be a float" % text - raise argparse.ArgumentTypeError(msg) - if value <= 0: - msg = "%s must be greater than 0" % text - raise argparse.ArgumentTypeError(msg) - return value - - -class SecretsHelper(object): - def __init__(self, args, client): - self.args = args - self.client = client - self.key = None - - def _validate_string(self, text): - if text is None or len(text) == 0: - return False - return True - - def _make_key(self): - if self.key is not None: - return self.key - keys = [ - self.client.auth_plugin.opts['auth_url'], - self.client.auth_plugin.opts['tenant_id'], - self.client.auth_plugin.opts['username'], - self.args.os_region_name, - self.args.endpoint_type, - self.args.service_type, - self.args.service_name, - ] - for (index, key) in enumerate(keys): - if key is None: - keys[index] = '?' - else: - keys[index] = str(keys[index]) - self.key = "/".join(keys) - return self.key - - def _prompt_password(self, verify=True): - pw = None - if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): - # Check for Ctl-D - try: - while True: - pw1 = getpass.getpass('OS Password: ') - if verify: - pw2 = getpass.getpass('Please verify: ') - else: - pw2 = pw1 - if pw1 == pw2 and self._validate_string(pw1): - pw = pw1 - break - except EOFError: - pass - return pw - - def save(self, client): - if not HAS_KEYRING or not self.args.os_cache: - return - self.client = client - auth_token, endpoint = client.auth_plugin.token_and_endpoint( - self.args.endpoint_type, self.args.service_type) - tenant_id = client.auth_plugin.access_info.tenant_id - if (auth_token == self.auth_token and - endpoint == self.endpoint): - # Nothing changed.... - return - if not all([endpoint, auth_token, tenant_id]): - raise ValueError("Unable to save empty management url/auth token") - value = "|".join([str(auth_token), - str(endpoint), - str(tenant_id)]) - keyring.set_password("libraclient_auth", self._make_key(), value) - - @property - def password(self): - if self._validate_string(self.args.os_password): - return self.args.os_password - verify_pass = strutils.bool_from_string( - cliutils.env("OS_VERIFY_PASSWORD")) - return self._prompt_password(verify_pass) - - @property - def endpoint(self): - if not HAS_KEYRING or not self.args.os_cache: - return None - url = None - try: - block = keyring.get_password('libraclient_auth', self._make_key()) - if block: - _token, url, _tenant_id = block.split('|', 2) - except all_errors: - pass - return url - - @property - def auth_token(self): - # Now is where it gets complicated since we - # want to look into the keyring module, if it - # exists and see if anything was provided in that - # file that we can use. - if not HAS_KEYRING or not self.args.os_cache: - return None - token = None - try: - block = keyring.get_password('libraclient_auth', self._make_key()) - if block: - token, _endpoint, _tenant_id = block.split('|', 2) - except all_errors: - pass - return token - - @property - def tenant_id(self): - if not HAS_KEYRING or not self.args.os_cache: - return None - tenant_id = None - try: - block = keyring.get_password('libraclient_auth', self._make_key()) - if block: - _token, _endpoint, tenant_id = block.split('|', 2) - except all_errors: - pass - return tenant_id - - -class LibraClientArgumentParser(argparse.ArgumentParser): - - def __init__(self, *args, **kwargs): - super(LibraClientArgumentParser, self).__init__(*args, **kwargs) - - def error(self, message): - """error(message: string) - - Prints a usage message incorporating the message to stderr and - exits. - """ - self.print_usage(sys.stderr) - # FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value - choose_from = ' (choose from' - progparts = self.prog.partition(' ') - self.exit(2, "error: %(errmsg)s\nTry '%(mainp)s help %(subp)s'" - " for more information.\n" % - {'errmsg': message.split(choose_from)[0], - 'mainp': progparts[0], - 'subp': progparts[2]}) - - -# I'm picky about my shell help. -class OpenStackHelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(OpenStackHelpFormatter, self).start_section(heading) - - -class LibraShell(object): - def get_base_parser(self): - parser = LibraClientArgumentParser( - prog='libra', - description=__doc__.strip(), - epilog='See "libraclient help COMMAND" ' - 'for help on a specific command.', - add_help=False, - formatter_class=OpenStackHelpFormatter, - ) - - # Global arguments - parser.add_argument( - '-h', '--help', - action='store_true', - help=argparse.SUPPRESS, - ) - - parser.add_argument( - '--version', - action='version', - version=libraclient.__version__) - - parser.add_argument( - '--debug', - default=False, - action='store_true', - help="Print debugging output") - - parser.add_argument( - '--no-cache', - default=not strutils.bool_from_string( - cliutils.env('OS_NO_CACHE', default='true')), - action='store_false', - dest='os_cache', - help=argparse.SUPPRESS) - parser.add_argument( - '--no_cache', - action='store_false', - dest='os_cache', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-cache', - default=cliutils.env('OS_CACHE', default=False), - action='store_true', - help="Use the auth token cache.") - - parser.add_argument( - '--timings', - default=False, - action='store_true', - help="Print call timing info") - - parser.add_argument( - '--api-timeout', - default=600, - metavar='', - type=positive_non_zero_float, - help="Set HTTP call timeout (in seconds)") - - parser.add_argument( - '--os-tenant-id', - metavar='', - default=cliutils.env('OS_TENANT_ID'), - help='Defaults to env[OS_TENANT_ID].') - - parser.add_argument( - '--os-region-name', - metavar='', - default=cliutils.env('OS_REGION_NAME', 'LIBRA_REGION_NAME'), - help='Defaults to env[OS_REGION_NAME].') - parser.add_argument( - '--os_region_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--service-type', - metavar='', - default=cliutils.env('LIBRA_SERVICE_TYPE', - default=DEFAULT_SERVICE_TYPE), - help='Defaults to libra for most actions') - parser.add_argument( - '--service_type', - help=argparse.SUPPRESS) - - parser.add_argument( - '--service-name', - metavar='', - default=cliutils.env('LIBRA_SERVICE_NAME', - default=DEFAULT_SERVICE_NAME), - help='Defaults to env[LIBRA_SERVICE_NAME]') - parser.add_argument( - '--service_name', - help=argparse.SUPPRESS) - - parser.add_argument( - '--endpoint-type', - metavar='', - default=cliutils.env('LIBRA_ENDPOINT_TYPE', - default=DEFAULT_ENDPOINT_TYPE), - help='Defaults to env[LIBRA_ENDPOINT_TYPE] or ' - + DEFAULT_ENDPOINT_TYPE + '.') - - parser.add_argument( - '--libra-api-version', - metavar='', - default=cliutils.env('LIBRA_API_VERSION', - default=DEFAULT_API_VERSION), - help='Accepts 1.1' - 'defaults to env[LIBRA_API_VERSION].') - parser.add_argument( - '--os_compute_api_version', - help=argparse.SUPPRESS) - - parser.add_argument( - '--os-cacert', - metavar='', - default=cliutils.env('OS_CACERT', default=None), - help='Specify a CA bundle file to use in ' - 'verifying a TLS (https) server certificate. ' - 'Defaults to env[OS_CACERT]') - - parser.add_argument( - '--insecure', - default=cliutils.env('LIBRA_INSECURE', default=False), - action='store_true', - help="Explicitly allow libraclient 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.") - - # The auth-system-plugins might require some extra options - auth.load_auth_system_opts(parser) - - return parser - - def get_subcommand_parser(self, version): - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='') - - actions_module = shell_v1 - - self._find_actions(subparsers, actions_module) - self._find_actions(subparsers, self) - - for extension in self.extensions: - self._find_actions(subparsers, extension.module) - - self._add_bash_completion_subparser(subparsers) - - return parser - - def _discover_extensions(self, version): - extensions = [] - for name, module in itertools.chain( - self._discover_via_python_path(), - self._discover_via_contrib_path(version), - self._discover_via_entry_points()): - - extension = base.Extension(name, module) - extensions.append(extension) - - return extensions - - def _discover_via_python_path(self): - for (module_loader, name, _ispkg) in pkgutil.iter_modules(): - if name.endswith('_python_libraclient_ext'): - if not hasattr(module_loader, 'load_module'): - # Python 2.6 compat: actually get an ImpImporter obj - module_loader = module_loader.find_module(name) - - module = module_loader.load_module(name) - if hasattr(module, 'extension_name'): - name = module.extension_name - - yield name, module - - def _discover_via_contrib_path(self, version): - module_path = os.path.dirname(os.path.abspath(__file__)) - version_str = "v%s" % version.replace('.', '_') - ext_path = os.path.join(module_path, version_str, 'contrib') - ext_glob = os.path.join(ext_path, "*.py") - - for ext_path in glob.iglob(ext_glob): - name = os.path.basename(ext_path)[:-3] - - if name == "__init__": - continue - - module = imp.load_source(name, ext_path) - yield name, module - - def _discover_via_entry_points(self): - for ep in pkg_resources.iter_entry_points('libraclient.extension'): - name = ep.name - module = ep.load() - - yield name, module - - def _add_bash_completion_subparser(self, subparsers): - subparser = subparsers.add_parser( - 'bash_completion', - add_help=False, - formatter_class=OpenStackHelpFormatter - ) - self.subcommands['bash_completion'] = subparser - subparser.set_defaults(func=self.do_bash_completion) - - 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 '' - action_help = desc.strip() - arguments = getattr(callback, 'arguments', []) - - subparser = subparsers.add_parser( - command, - help=action_help, - description=desc, - add_help=False, - formatter_class=OpenStackHelpFormatter - ) - 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_debugging(self, debug): - if not debug: - return - - streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" - # Set up the root logger to debug so that the submodules can - # print debug messages - logging.basicConfig(level=logging.DEBUG, - format=streamformat) - - def main(self, argv): - - # Parse args once to find version and debug settings - parser = self.get_base_parser() - (options, args) = parser.parse_known_args(argv) - self.setup_debugging(options.debug) - - # Discover available auth plugins - auth.discover_auth_systems() - - # build available subcommands based on version - self.extensions = self._discover_extensions( - options.libra_api_version) - - self._run_extension_hooks('__pre_parse_args__') - - if '--endpoint_type' in argv: - spot = argv.index('--endpoint_type') - argv[spot] = '--endpoint-type' - - subcommand_parser = self.get_subcommand_parser( - options.os_compute_api_version) - self.parser = subcommand_parser - - if options.help or not argv: - subcommand_parser.print_help() - return 0 - - args = subcommand_parser.parse_args(argv) - self._run_extension_hooks('__post_parse_args__', args) - - # Short-circuit and deal with help right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - elif args.func == self.do_bash_completion: - self.do_bash_completion(args) - return 0 - - os_username = args.os_username - os_password = args.os_password - os_tenant_name = args.os_tenant_name - os_tenant_id = args.os_tenant_id - os_auth_url = args.os_auth_url - os_region_name = args.os_region_name - os_auth_system = args.os_auth_system - endpoint_type = args.endpoint_type - insecure = args.insecure - service_type = args.service_type - service_name = args.service_name - os_cache = args.os_cache - cacert = args.os_cacert - timeout = args.api_timeout - - if not os_auth_system: - os_auth_system = 'keystone2' - - auth_plugin = auth.load_plugin(os_auth_system) - - os_password = None - - # FIXME(usrleon): Here should be restrict for project id same as - # for os_username or os_password but for compatibility it is not. - if not cliutils.isunauthenticated(args.func): - if auth_plugin: - auth_plugin.parse_opts(args) - - if not auth_plugin or not auth_plugin.opts: - if not os_username: - raise exc.CommandError( - "You must provide a username" - " via either --os-username or env[OS_USERNAME]") - - if not os_tenant_name and not os_tenant_id: - raise exc.CommandError( - "You must provide a tenant name " - "or tenant id via --os-tenant-name, " - "--os-tenant-id, env[OS_TENANT_NAME] " - "or env[OS_TENANT_ID]") - - if not os_auth_url: - if os_auth_system and os_auth_system != 'keystone': - os_auth_url = auth_plugin.get_auth_url() - - if not os_auth_url: - raise exc.CommandError( - "You must provide an auth url " - "via either --os-auth-url or env[OS_AUTH_URL] " - "or specify an auth_system which defines a " - "default url with --os-auth-system " - "or env[OS_AUTH_SYSTEM]") - - if not (os_tenant_name or os_tenant_id): - raise exc.CommandError( - "You must provide a tenant_id " - "via either --os-tenant-id or env[OS_TENANT_ID]") - - if not os_auth_url: - raise exc.CommandError( - "You must provide an auth url " - "via either --os-auth-url or env[OS_AUTH_URL]") - - http_client = client.HTTPClient( - auth_plugin, - region_name=os_region_name, - endpoint_type=endpoint_type, - debug=args.debug, - verify=args.insecure) - - self.cs = VersionedClient( - options.libra_api_version, - http_client, - endpoint_type=endpoint_type, - service_type=service_type) - - # Now check for the password/token of which pieces of the - # identifying keyring key can come from the underlying client - if not cliutils.isunauthenticated(args.func): - helper = SecretsHelper(args, self.cs.http_client) - - if not args.os_token and not args.os_tenant_id: - if helper.tenant_id and helper.auth_token and helper.endpoint: - auth_plugin.opts.update({ - 'tenant_id': helper.tenant_id, - 'token': helper.auth_token, - 'bypass_url': helper.endpoint}) - else: - # Auth using token must have failed or not happened - # at all, so now switch to password mode and save - # the token when its gotten... using our keyring - # saver - auth_plugin.opts['password'] = helper.password - self.cs.http_client.keyring_saver = helper - self.cs.http_client.authenticate() - else: - # If we're in token mode but no bypass_url we should auth.. - if not args.os_bypass_url: - self.cs.http_client.authenticate() - try: - args.func(self.cs, args) - except exc.Unauthorized: - raise exc.CommandError("Invalid OpenStack libra credentials.") - except exc.AuthorizationFailure, e: - raise exc.CommandError("Unable to authorize user") - - if args.timings: - self._dump_timings(self.cs.get_timings()) - - def _dump_timings(self, timings): - class Tyme(object): - def __init__(self, url, seconds): - self.url = url - self.seconds = seconds - results = [Tyme(url, end - start) for url, start, end in timings] - total = 0.0 - for tyme in results: - total += tyme.seconds - results.append(Tyme("Total", total)) - cliutils.print_list(results, ["url", "seconds"], sortby_index=None) - - def _run_extension_hooks(self, hook_type, *args, **kwargs): - """Run hooks for all registered extensions.""" - for extension in self.extensions: - extension.run_hooks(hook_type, *args, **kwargs) - - def do_bash_completion(self, _args): - """ - Prints all of the commands and options to stdout so that the - libra.bash_completion script doesn't have to hard code them. - """ - commands = set() - options = set() - for sc_str, sc in self.subcommands.items(): - commands.add(sc_str) - for option in sc._optionals._option_string_actions.keys(): - options.add(option) - - commands.remove('bash-completion') - commands.remove('bash_completion') - print(' '.join(commands | options)) - - @cliutils.arg('command', metavar='', nargs='?', - help='Display help for ') - def do_help(self, args): - """ - Display help about this program or one of its subcommands. - """ - if args.command: - 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() - - -def main(): - try: - if sys.version_info >= (3, 0): - LibraShell().main(sys.argv[1:]) - else: - LibraShell().main(map(strutils.safe_decode, - sys.argv[1:])) - except KeyboardInterrupt: - print("... terminating libra client", file=sys.stderr) - sys.exit(130) - except Exception as e: - logger.debug(e, exc_info=1) - msg = 'ERROR: %s' % e.message - - if hasattr(e, 'details'): - msg = '%s, DETAILS: %s' % (msg, e.details) - if not isinstance(msg, six.string_types): - msg = str(msg) - print("%s" % strutils.safe_encode(msg), file=sys.stderr) - sys.exit(1) diff --git a/libraclient/utils.py b/libraclient/utils.py deleted file mode 100644 index 61cb51e..0000000 --- a/libraclient/utils.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright 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. - -import prettytable -from libraclient.openstack.common import strutils - - -def _field(orig_field, titled=True): - """ - Allow for writin short-hand field stuff like n='server', dn='Server' - """ - field = {} - - aliases = { - 'n': 'name', - 'dn': 'display', - 'f': 'formatter' - } - if isinstance(orig_field, dict): - for alias, name in aliases.items(): - if alias in orig_field: - field[name] = orig_field[alias] - elif name in orig_field: - field[name] = orig_field[name] - elif isinstance(orig_field, basestring): - field['name'] = orig_field - elif isinstance(orig_field, tuple): - field['name'], field['display'] = orig_field - - if 'display' not in field: - dn = field['name'] - if titled: - dn = dn.title() - field['display'] = dn - return field - - -def _get_fields(obj): - # NOTE: Resource class - try: - i = obj._info - except AttributeError: - pass - # NOTE: dict - if isinstance(obj, dict): - i = obj - return [{'name': n} for n in i.keys()] - - -def _get_field_data(obj, field): - if 'formatter' in field: - return field['formatter'](obj[field['name']]) - else: - if isinstance(obj, dict): - return obj[field['name']] - elif hasattr(obj, '_info'): - return getattr(obj, field['name']) - - -def create_row(obj, fields=None, titled=False): - """ - :param obj: a :class:`dict` or :class:`Resource` - :param fields: A :class:`list` of :class:`dicts` describing fields to do. - Default: obj.keys() if dict - :param formatters: Field formatters. - """ - fields = [_field(f, titled=titled) for f in fields or _get_fields(obj)] - - row = [] - for field in fields: - row.append(_get_field_data(obj, field)) - return row - - -def print_list(objs, fields=None, sort_by=None, titled=False): - # If no fields are given use objs[0] - fields = [_field(f, titled=titled) for f in fields or _get_fields(objs[0])] - - # Set the display names for headers. - field_names = [f['display'] for f in fields] - - # Sort by column - if sort_by is None: - sortby = None - else: - sortby = fields[sortby_index] - pt = prettytable.PrettyTable(field_names, caching=False) - pt.align = 'l' - - for o in objs: - row = create_row(o, fields=fields, titled=False) - pt.add_row(row) - - if objs: - print(strutils.safe_encode(pt.get_string(sortby=sortby))) - - -def print_dict(obj, dict_property='Property', fields=None, titled=False): - fields = [_field(f, titled=titled) for f in fields or _get_fields(obj)] - - pt = prettytable.PrettyTable([dict_property, 'Value'], caching=False) - pt.align = 'l' - for field in fields: - pt.add_row([field['display'], _get_field_data(obj, field)]) - print(strutils.safe_encode(pt.get_string())) diff --git a/libraclient/v1_1/__init__.py b/libraclient/v1_1/__init__.py deleted file mode 100644 index 92bd912..0000000 --- a/libraclient/v1_1/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 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. diff --git a/libraclient/v1_1/algorithms.py b/libraclient/v1_1/algorithms.py deleted file mode 100644 index 5258682..0000000 --- a/libraclient/v1_1/algorithms.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 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. -from libraclient.openstack.common.apiclient import base -from libraclient.v1_1.base import Manager - - -class Algorithm(base.Resource): - def __repr__(self): - return '' % self.name - - -class AlgorithmManager(Manager): - resource_class = Algorithm - - def list(self): - return self._list('/algorithms', 'algorithms') diff --git a/libraclient/v1_1/base.py b/libraclient/v1_1/base.py deleted file mode 100644 index 319f53b..0000000 --- a/libraclient/v1_1/base.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 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. -from urllib import urlencode -from libraclient.openstack.common.apiclient.base import ManagerWithFind - - -class Manager(ManagerWithFind): - def build_url(self, url, params): - q = urlencode(params) if params else '' - return '%(url)s%(params)s' % { - 'url': url, - 'params': '?%s' % q - } diff --git a/libraclient/v1_1/client.py b/libraclient/v1_1/client.py deleted file mode 100644 index bc154c6..0000000 --- a/libraclient/v1_1/client.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 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. -from libraclient.openstack.common.apiclient import client -from libraclient.openstack.common.apiclient import exceptions -from libraclient.v1_1.algorithms import AlgorithmManager -from libraclient.v1_1.loadbalancer import LoadBalancerManager -from libraclient.v1_1.limits import LimitManager -from libraclient.v1_1.protocols import ProtocolManager - - -# NOTE(LinuxJedi): Override novaclient's error handler as we send messages in -# a slightly different format which causes novaclient's to throw an exception - -def from_response(response, method, url): - """ - Return an instance of an ClientException or subclass - based on an httplib2 response. - - Usage:: - - resp, body = http.request(...) - if resp.status != 200: - raise exception_from_response(resp, body) - """ - kwargs = { - 'http_status': response.status_code, - 'response': response, - 'method': method, - 'url': url, - } - if response.headers: - kwargs['request_id'] = response.headers.get( - 'x-compute-request-id', None) - if "retry-after" in response.headers: - kwargs["retry_after"] = response.headers["retry-after"] - - 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 isinstance(body, dict): - kwargs['message'] = body.get('faultString', None) or \ - body.get('message', None) - kwargs["details"] = body.get("details", None) - elif content_type.startswith("text/"): - kwargs["details"] = response.text - - try: - cls = exceptions._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) - - -exceptions.from_response = from_response - - -class Client(client.BaseClient): - def __init__(self, *args, **kw): - super(Client, self).__init__(*args, **kw) - self.algorithms = AlgorithmManager(self) - self.loadbalancers = LoadBalancerManager(self) - self.limits = LimitManager(self) - self.protocols = ProtocolManager(self) diff --git a/libraclient/v1_1/limits.py b/libraclient/v1_1/limits.py deleted file mode 100644 index 033dc0b..0000000 --- a/libraclient/v1_1/limits.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 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. -from libraclient.openstack.common.apiclient import base - - -class Limit(base.Resource): - def __repr__(self): - return '' % self.name - - -class LimitManager(base.BaseManager): - resource_class = Limit - - def list_limits(self): - limits = [] - json = self.client.get('/limits').json() - for lname, lvalues in json['limits'].items(): - values = lvalues['values'] - values['name'] = lname - limits.append(Limit(self, values)) - return limits diff --git a/libraclient/v1_1/loadbalancer.py b/libraclient/v1_1/loadbalancer.py deleted file mode 100644 index 4a13686..0000000 --- a/libraclient/v1_1/loadbalancer.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright 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. -from libraclient.openstack.common.apiclient.base import getid, Resource -from libraclient.v1_1.base import Manager - -import socket - - -class Node(Resource): - def __repr__(self): - s = '' - return s.format(**self._info) - - -class Monitor(Resource): - def __repr__(self): - return '' % self.name - - -class VirtualIP(Resource): - def __repr__(self): - s = '' - return s.format(**self._info) - - -class LoadBalancer(Resource): - def __init__(self, manager, info, loaded=False): - info['nodes'] = [Node(self, n) for n in info.pop('nodes', [])] - info['virtualIps'] = [VirtualIP(self, n) - for n in info.pop('virtualIps', [])] - super(LoadBalancer, self).__init__(manager, info, loaded=False) - - def __repr__(self): - return '' % self.name - - def delete(self): - self.manager.delete(self) - - def update(self, **kw): - self.manager.update(self, **kw) - - def create_node(self, node): - return self.manager.create_node(node) - - def list_nodes(self): - return self.manager.list_nodes(self) - - def get_node(self, node): - return self.manager.get_node(self, node) - - def update_node(self, node, condition=None, weight=None): - return self.manager.update_node(self, node, - condition=condition, weight=weight) - - def delete_node(self, node): - return self.manager.delete_node(self, node) - - def update_monitor(self, type_, delay, timeout, - attempts, path=None): - return self.manager.update_monitor( - self, type_=type_, delay=delay, timeout=timeout, attempts=attempts, - path=path) - - def delete_monitor(self, lb): - self.manager.delete_monitor(lb) - - def list_vip(self): - return self.manager.list_vip(self) - - -class LoadBalancerManager(Manager): - resource_class = LoadBalancer - - def _parse_nodes(self, nodes): - out_nodes = [] - try: - for node in nodes: - nodeopts = node.split(':') - ipaddr = nodeopts[0] - port = nodeopts[1] - weight, backup = None, None - - # Test IP valid - # TODO: change to pton when we want to support IPv6 - socket.inet_aton(ipaddr) - # Test port valid - if int(port) < 0 or int(port) > 65535: - raise Exception('Port out of range') - - # Process the rest of the node options as key=value - for kv in nodeopts[2:]: - key, value = kv.split('=') - key = key.lower() - value = value.upper() - if key == 'weight': - weight = int(value) - elif key == 'backup': - backup = value # 'TRUE' or 'FALSE' - else: - raise Exception("Unknown node option '%s'" % key) - - node_def = {'address': ipaddr, 'port': port} - if weight: - node_def['weight'] = weight - if backup: - node_def['backup'] = backup - - out_nodes.append(node_def) - except Exception as e: - raise Exception("Invalid value specified for --node: %s" % e) - return out_nodes - - def create(self, name, nodes, port=None, protocol=None, algorithm=None, - virtual_ip=None): - """ - Create a LoadBalancer from given values. - - :param name: The name / display name. - :param nodes: Nodes. - :param port: Numeric port (80, 443 for example.) - :param protocol: Protocol to use (TCP / HTTP for example.) - :param algorithm: Algorithm (ROUND_ROBIN for example.) - :param virtual_ip: VIP ID to set if Shared LB. - """ - parsed_nodes = self._parse_nodes(nodes) - body = { - 'name': name, - 'nodes': parsed_nodes, - } - if port is not None: - body['port'] = port - if protocol is not None: - body['protocol'] = protocol - if algorithm is not None: - body['algorithm'] = algorithm - if virtual_ip is not None: - body['virtualIps'] = [{'id': virtual_ip}] - return self._post('/loadbalancers', body) - - def get(self, lb): - """ - Get a LoadBalancer. - - :param lb: The :class:`LoadBalancer` (or its ID) to update. - """ - lb = self._get('/loadbalancers/%s' % getid(lb)) - return lb - - def list(self, deleted=False): - """ - List loadBalancers. - - :param deleted: Show deleted LoadBalancers. - """ - params = {} - if deleted: - params['status'] = 'DELETED' - - url = self.build_url('/loadbalancers', params) - lbs = self._list(url, 'loadBalancers') - return lbs - - def update(self, lb, name=None, algorithm=None): - """ - Update a LoadBalancer - - :param lb: The :class:`LoadBalancer` (or its ID). - :param name: Set the name of the LoadBalancer. - :param algorithm: Algorithm (ROUND_ROBIN for example.) - """ - data = {} - if name is not None: - data['name'] = name - if algorithm is not None: - data['algorithm'] = algorithm - return self._put('/loadbalancers/%s' % getid(lb), data) - - def delete(self, lb): - """ - Delete a LoadBalancer - - :param lb: The :class:`LoadBalancer` (or its ID). - """ - self._delete('/loadbalancers/%s' % getid(lb)) - - def create_node(self, lb, node): - data = {} - data['nodes'] = self._parse_nodes(node) - - url = '/loadbalancers/%s/nodes' % getid(lb) - nodes = self._post(url, data, return_raw=True, response_key='nodes') - return [Node(self, n) for n in nodes] - - def get_node(self, lb, node): - """ - Get a Node belonging to a LoadBalancer - - :param lb: The :class:`LoadBalancer` (or its ID). - :param node: The :class:`Node` (or its ID). - """ - url = '/loadbalancers/%s/nodes/%s' % (getid(lb), getid(node)) - return self._get(url, obj_class=Node) - - def list_nodes(self, lb): - """ - List Nodes belonging to a LoadBalancer. - - :param lb: The :class:`LoadBalancer` (or its ID).. - """ - url = '/loadbalancers/%s/nodes' % getid(lb) - return self._list(url, 'nodes', obj_class=Node) - - def update_node(self, lb, node, condition=None, weight=None): - """ - Update a node - - :param lb: The :class:`LoadBalancer` (or its ID). - :param node: The :class:`Node` (or its ID). - :param condition: Set the conditioon. - :param weight: Set the weight. - """ - data = {} - if condition is not None: - data['condition'] = condition - if weight is not None: - data['weight'] = weight - url = '/loadbalancers/%s/nodes/%s' % (getid(lb), getid(node)) - return self._put(url, data, obj_class=Node) - - def delete_node(self, lb, node): - """ - Delete a node from a LoadBalancer. - - :param lb: The :class:`LoadBalancer` (or its ID). - :param node: The :class:`Node` (or its ID). - """ - url = '/loadbalancers/%s/nodes/%s' % (getid(lb), getid(node)) - self._delete(url) - - def get_monitor(self, lb): - """ - Get a Monitor for a LoadBalancer - - :param lb: The :class:`LoadBalancer` (or its ID). - """ - url = '/loadbalancers/%s/healthmonitor' % getid(lb) - return self._get(url, obj_class=Monitor) - - def update_monitor(self, lb, type_, delay, timeout, - attempts, path=None): - """ - Update a Monitor in a LoadBalancer - - :param lb: The :class:`LoadBalancer` (or its ID). - :param type_: Monitor type. - :param delay: Numeric delay, must be less then timeout. - :param timeout: Numeric timeout, must be geater then delay. - :param attempts: Max attempts before deactivation. - :param path: URI path when using HTTP type. - """ - data = {} - data['type'] = type_ - if timeout > delay: - raise ValueError('Timeout can\'t be greater than Delay') - data['delay'] = delay - data['timeout'] = timeout - data['attemptsBeforeDeactivation'] = attempts - - if type_.upper() != 'CONNECT': - data['path'] = path - - url = '/loadbalancers/%s/healthmonitor' % getid(lb) - return self._put(url, data, obj_class=Monitor) - - def delete_monitor(self, lb): - """ - Delete monitor from a LoadBalancer - - :param lb: The :class:`LoadBalancer` (or its ID). - """ - url = '/loadbalancers/%s/healthmonitor' % getid(lb) - self._delete(url) - - def list_vip(self, lb): - """ - List Virtual IPs for a LoadBalancer - - :param lb: The :class:`LoadBalancer` (or its ID) to update. - """ - return self._list('loadbalancers/%s/virtualips' % getid(lb), - response_key='virtualIps', obj_class=VirtualIP) - - def send_logs(self, lb, values): - """ - Send a snapshot of logs somewhere. - - :param lb: The :class:`LoadBalancer` (or its ID). - :param storage: Storage type. - :param kw: The values to send it with, pass as kw. - """ - self.client.post('/loadbalancers/%s/logs' % getid(lb), json=values) diff --git a/libraclient/v1_1/protocols.py b/libraclient/v1_1/protocols.py deleted file mode 100644 index 4984e73..0000000 --- a/libraclient/v1_1/protocols.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 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. -from libraclient.openstack.common.apiclient import base -from libraclient.v1_1.base import Manager - - -class Protocol(base.Resource): - def __repr__(self): - return '' % self.name - - -class ProtocolManager(Manager): - resource_class = Protocol - - def list(self): - return self._list('/protocols', 'protocols') diff --git a/libraclient/v1_1/shell.py b/libraclient/v1_1/shell.py deleted file mode 100644 index 91cbe5c..0000000 --- a/libraclient/v1_1/shell.py +++ /dev/null @@ -1,360 +0,0 @@ -# Copyright 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. -from libraclient.openstack.common import cliutils -from libraclient import utils - - -def _format(data): - return "\n".join(map(str, data)) - - -NODE_FIELDS = [("id", "ID"), "address", "port", "condition", "status"] - -LB_FIELDS = [ - ('id', 'ID'), - 'name', - 'protocol', - 'port', - 'status', - 'algorithm', - 'created', - 'updated', - ('nodeCount', 'Node Count') -] - -LB_DETAIL_FIELDS = [ - ('id', 'ID'), - 'name', - 'protocol', - 'port', - 'status', - 'algorithm', - 'created', - 'updated', - ('nodeCount', 'Node Count'), - ('nodes', 'Node listing'), - ('virtualIps', 'Virtual IPs') -] - - -@cliutils.arg( - '--name', - type=str, - help='Name of the new LoadBalancer.', - required=True) -@cliutils.arg( - '--port', - help='port for the load balancer, 80 is default') -@cliutils.arg( - '--protocol', - help='protocol for the load balancer, HTTP is default', - choices=['HTTP', 'TCP', 'GALERA']) -@cliutils.arg( - '--algorithm', - help='algorithm for the load balancer ROUND_ROBIN is default', - choices=['LEAST_CONNECTIONS', 'ROUND_ROBIN']) -@cliutils.arg( - '--node', - help='a node for the load balancer in ip:port format', - action='append', required=True) -@cliutils.arg( - '--vip', - help='the virtual IP to attach the load balancer to') -def do_create(cs, args): - """ - Create a LoadBalancer. - """ - data = {} - data['name'] = args.name - - lb = cs.loadbalancers.create( - name=args.name, - port=args.port, - protocol=args.protocol, - algorithm=args.algorithm, - nodes=args.node, - virtual_ip=args.vip) - - info = lb._info - info['nodes'] = _format(info['nodes']) - info['virtualIps'] = _format(info['virtualIps']) - utils.print_dict(info, titled=True) - - -@cliutils.arg( - 'id', - type=str, - help='ID to get') -def do_show(cs, args): - """ - Show a LoadBalancer. - """ - lb = cs.loadbalancers.get(args.id) - - info = lb._info - info['nodes'] = _format(info['nodes']) - info['virtualIps'] = _format(info['virtualIps']) - - utils.print_dict(info, fields=LB_DETAIL_FIELDS, titled=True) - - -@cliutils.arg( - '--deleted', - default=False, - action='store_true', - help='Display deleted LBs.') -def do_list(cs, args): - """ - List loadbalancers. - """ - lbs = cs.loadbalancers.list(deleted=args.deleted) - utils.print_list(lbs, fields=LB_FIELDS, titled=True) - - -@cliutils.arg('id', help='load balancer ID') -@cliutils.arg('--name', help='new name for the load balancer') -@cliutils.arg('--algorithm', - help='new algorithm for the load balancer', - choices=['LEAST_CONNECTIONS', 'ROUND_ROBIN']) -def do_update(cs, args): - """ - Update a LoadBalancer. - """ - cs.loadbalancers.update( - args.id, - name=args.name, - algorithm=args.algorithm) - - -@cliutils.arg( - 'id', - type=int, - help='ID to delete') -def do_delete(cs, args): - """ - Delete a LoadBalancer. - """ - cs.loadbalancers.delete(args.id) - - -@cliutils.arg('id', help='load balancer ID') -@cliutils.arg('--node', help='node to add in ip:port form', - required=True, action='append') -def do_node_create(cs, args): - """ - Create a LoadBalancer Node. - """ - nodes = cs.loadbalancers.create_node(args.id, args.node) - utils.print_list(nodes, fields=NODE_FIELDS, titled=True) - - -@cliutils.arg( - 'lb_id', - type=str, - help='ID of the LoadBalancer the Nodes belongs to.') -def do_node_list(cs, args): - """ - List LoadBalancer Nodes. - """ - nodes = cs.loadbalancers.list_nodes(args.lb_id) - utils.print_list(nodes, fields=NODE_FIELDS, titled=True) - - -@cliutils.arg( - 'lb_id', - type=str, - help='ID of the LoadBalancer that the Node belongs to.') -@cliutils.arg( - 'node_id', - type=str, - help='ID of the Node to show.') -def do_node_show(cs, args): - """ - Show a Node belonging to a LoadBalancer. - """ - node = cs.loadbalancers.get_node(args.lb_id, args.node_id) - utils.print_dict(node, fields=NODE_FIELDS, titled=True) - - -@cliutils.arg( - 'lb_id', - type=str, - help='ID of the LoadBalancer the Nodes belongs to.') -@cliutils.arg( - 'node_id', - help='node ID to modify') -@cliutils.arg( - '--condition', - help='the new state for the node', - choices=['ENABLED', 'DISABLED']) -@cliutils.arg( - '--weight', - type=int, - default=1, - metavar='COUNT', - help='node weight ratio as compared to other nodes') -def do_node_update(cs, args): - """ - Update a Node belonging to a LoadBalancer. - """ - cs.loadbalancers.update_node( - args.lb_id, args.node_id, condition=args.condition, weight=args.weight) - - -@cliutils.arg( - 'lb_id', - type=str, - help='ID of the LoadBalancer that the Node belongs to.') -@cliutils.arg( - 'node_id', - type=str, - help='ID of the Node to show.') -def do_node_delete(cs, args): - """ - Delete a Node belonging to a LoadBalancer - """ - cs.loadbalancers.delete_node(args.lb_id, args.node_id) - - -@cliutils.arg( - 'lb_id', - type=str, - help='ID of the LoadBalancer that the Node belongs to.') -def do_monitor_show(cs, args): - """ - Show a LoadBalancer's Monitor. - """ - monitor = cs.loadbalancers.get_monitor(args.lb_id) - utils.print_dict(monitor._info) - - -@cliutils.arg( - 'lb_id', - type=str, - help='ID of the LoadBalancer that the Node belongs to.') -@cliutils.arg( - '--type', - choices=['CONNECT', 'HTTP'], - help='health monitor type', required=True) -@cliutils.arg( - '--delay', - type=int, - metavar='SECONDS', - help='time between health monitor calls', required=True) -@cliutils.arg( - '--timeout', - type=int, - metavar='SECONDS', - help='time to wait before monitor times out', required=True) -@cliutils.arg( - '--attempts', - type=int, - metavar='COUNT', - help='connection attempts before marking node as bad', required=True) -@cliutils.arg( - '--path', - help='URI path for health check') -def do_monitor_update(cs, args): - """ - Update a LoadBalancer's Monitor. - """ - monitor = cs.loadbalancers.update_monitor( - args.lb_id, type_=args.type, delay=args.delay, timeout=args.timeout, - attempts=args.attempts, path=args.path) - utils.print_dict(monitor._info) - - -@cliutils.arg( - 'lb_id', - type=str, - help='ID of the LoadBalancer that the Node belongs to.') -def do_monitor_delete(cs, args): - """ - Delete / reset a LoadBalancer's Monitor. - """ - cs.loadbalancers.delete_monitor(args.lb_id) - - -@cliutils.arg( - 'id', - type=str, - help='ID to show Virtual IPs for.') -def do_virtualips(cs, args): - """ - Show VirtualIPs for a LoadBalancer. - """ - vips = cs.loadbalancers.list_vip(args.id) - fields = [ - 'id', - 'type', - ('ipVersion', 'IP Version'), - 'address' - ] - utils.print_list(vips, fields=fields, titled=True) - - -# Non LB specific commands -def do_algorithm_list(cs, args): - """ - List out supported algorithms. - """ - algs = cs.algorithms.list() - fields = [('name', 'Algorithm Name')] - utils.print_list(algs, fields=fields) - - -# TODO: Figure out the printing of this one -def do_limit_list(cs, args): - """ - List out limits - """ - limits = cs.limits.list_limits() - out = [] - for l in limits: - info = l._info - del info['name'] - info = "\n".join(['%s: %s' % (k, info[k]) - for k in sorted(info.keys())]) - out.append({'name': l.name, 'info': info}) - fields = ['name', 'info'] - utils.print_list(out, fields=fields) - - -def do_protocol_list(cs, args): - """ - List Protocols - """ - protocols = cs.protocols.list() - utils.print_list(protocols, titled=True) - - -@cliutils.arg('id', help='load balancer ID') -@cliutils.arg('--storage', help='storage type', choices=['Swift']) -@cliutils.arg('--endpoint', help='object store endpoint to use') -@cliutils.arg('--basepath', help='object store based directory') -@cliutils.arg('--token', help='object store authentication token') -def do_logs(cs, args): - """ - Send a snapshot of logs to Swift or elsewhere. - """ - data = {} - if args.storage: - data['objectStoreType'] = args.storage - if args.endpoint: - data['objectStoreEndpoint'] = args.endpoint - if args.basepath: - data['objectStoreBasePath'] = args.basepath - if args.token: - data['authToken'] = args.token - cs.loadbalancers.send_logs(args.id, data) diff --git a/openstack-common.conf b/openstack-common.conf deleted file mode 100644 index 8354305..0000000 --- a/openstack-common.conf +++ /dev/null @@ -1,9 +0,0 @@ -[DEFAULT] - -# The list of modules to copy from openstack-common -module=apiclient -module=cliutils -module=strutils - -# The base module to hold the copy of openstack.common -base=libraclient diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 14414a5..0000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -python_novaclient>=2.14.1,!=2.14.2 -PrettyTable>=0.7,<0.8 -babel -stevedore -six>=1.4.1 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3fa4414..0000000 --- a/setup.cfg +++ /dev/null @@ -1,44 +0,0 @@ -[metadata] -name = python-libraclient -summary = Client library for Libra LBaaS tools -description-file = - README.rst -license = Apache License, Version 2.0 -author = Andrew Hutchings -author-email = andrew@linuxjedi.co.uk -home-page = https://launchpad.net/libra -classifier = - Development Status :: 5 - Production/Stable - Environment :: Console - Environment :: OpenStack - Intended Audience :: Developers - Intended Audience :: Information Technology - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - Programming Language :: Python - - -[files] -packages = - libraclient - -[entry_points] -console_scripts = - libra = libraclient.shell:main - -libraclient.versions = - 1.1 = libraclient.v1_1.client:Client - -libraclient.extension = - -openstack.common.apiclient.auth = - keystone2 = libraclient.openstack.common.apiclient.keystone:KeystoneAuthPluginV2 - keystone3 = libraclient.openstack.common.apiclient.keystone:KeystoneAuthPluginV3 - -[build_sphinx] -source-dir = doc -build-dir = build/sphinx -all_files = 1 - -[upload_sphinx] -upload-dir = build/sphinx/html diff --git a/setup.py b/setup.py deleted file mode 100644 index 70c2b3f..0000000 --- a/setup.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/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) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 01cdf46..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -flake8 -mock -sphinx>=1.1.2 -testrepository>=0.0.8 -testtools>=0.9.22 diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 92bd912..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 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. diff --git a/tests/test_lbaas_client.py b/tests/test_lbaas_client.py deleted file mode 100644 index 5d058ec..0000000 --- a/tests/test_lbaas_client.py +++ /dev/null @@ -1,7 +0,0 @@ -import testtools - - -class test_dummy(testtools.TestCase): - - def test_dummy(self): - self.assertEqual(1, 1) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index d70057a..0000000 --- a/tox.ini +++ /dev/null @@ -1,37 +0,0 @@ -[tox] -envlist = py27,pep8 - -[testenv] -setenv = VIRTUAL_ENV={envdir} - LANG=en_US.UTF-8 - LANGUAGE=en_US:en - LC_ALL=C - OS_STDOUT_NOCAPTURE=False - OS_STDERR_NOCAPTURE=False - -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -commands = python setup.py testr --testr-args='{posargs}' - -[testenv:pep8] -commands = flake8 - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -commands = python setup.py testr --coverage --testr-args='{posargs}' - -[tox:jenkins] -downloadcache = ~/cache/pip - -[flake8] -# H233 Python 3.x incompatible use of print operator -# H302 import only modules -# H303 no wildcard import -# H306 imports not in alphabetical orde -# H404 multi line docstring should start with a summary -# H501 Do not use locals() for string formatting -ignore = F403,F841,F812,F821,H233,H302,H303,H306,H404,H501 -show-source = True -exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build \ No newline at end of file