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