pax_global_header00006660000000000000000000000064126640676320014526gustar00rootroot0000000000000052 comment=781aec763e6f46f070c960e5e19a4c0d5be1ecb8 tempest-lib-1.0.0/000077500000000000000000000000001266406763200137515ustar00rootroot00000000000000tempest-lib-1.0.0/.coveragerc000066400000000000000000000001731266406763200160730ustar00rootroot00000000000000[run] branch = True source = tempest_lib omit = tempest_lib/tests/*,tempest_lib/openstack/* [report] ignore_errors = True tempest-lib-1.0.0/.gitignore000066400000000000000000000010061266406763200157360ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg* *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports cover/ .coverage* !.coveragerc .tox nosetests.xml .testrepository # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Complexity output/*.html output/*/index.html # Sphinx doc/build releasenotes/build # pbr generates these AUTHORS ChangeLog # Editors *~ .*.swp #Broken migrations tempest tempest-lib-1.0.0/.gitreview000066400000000000000000000001171266406763200157560ustar00rootroot00000000000000[gerrit] host=review.openstack.org port=29418 project=openstack/tempest-lib.gittempest-lib-1.0.0/.mailmap000066400000000000000000000001301266406763200153640ustar00rootroot00000000000000# Format is: # # tempest-lib-1.0.0/.testr.conf000066400000000000000000000004761266406763200160460ustar00rootroot00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--listtempest-lib-1.0.0/CONTRIBUTING.rst000066400000000000000000000010321266406763200164060ustar00rootroot00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: http://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/tempest tempest-lib-1.0.0/HACKING.rst000066400000000000000000000002421266406763200155450ustar00rootroot00000000000000tempest-lib Style Commandments =============================================== Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/tempest-lib-1.0.0/LICENSE000066400000000000000000000236361266406763200147700ustar00rootroot00000000000000 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. tempest-lib-1.0.0/README.rst000066400000000000000000000022631266406763200154430ustar00rootroot00000000000000=========== tempest-lib =========== OpenStack Functional Testing Library * Free software: Apache license * Documentation: http://docs.openstack.org/developer/tempest-lib * Source: http://git.openstack.org/cgit/openstack/tempest-lib * Bugs: http://bugs.launchpad.net/tempest tempest-lib is a library of common functionality that was originally in tempest (or similar in scope to tempest) **As of the 1.0.0 release tempest-lib as a separate repository and project is deprecated. The library now exists as part of the tempest project, all future development will occur there. To use the library for future releases update your imports from tempest_lib to tempest.lib, and add tempest>=10 to your project requirements** Features -------- Some of the current functionality exposed from the library includes: * OpenStack python-* client CLI testing framework * subunit-trace: A output filter for subunit streams. Useful in conjunction with calling a test runner that emits subunit * A unified REST Client * Utility functions: * skip_because: Skip a test because of a bug * find_test_caller: Perform stack introspection to find the test caller. common methods tempest-lib-1.0.0/babel.cfg000066400000000000000000000000201266406763200154670ustar00rootroot00000000000000[python: **.py] tempest-lib-1.0.0/doc/000077500000000000000000000000001266406763200145165ustar00rootroot00000000000000tempest-lib-1.0.0/doc/source/000077500000000000000000000000001266406763200160165ustar00rootroot00000000000000tempest-lib-1.0.0/doc/source/cli.rst000066400000000000000000000005061266406763200173200ustar00rootroot00000000000000.. _cli: CLI Testing Framework Usage =========================== ------------------- The cli.base module ------------------- .. automodule:: tempest_lib.cli.base :members: ---------------------------- The cli.output_parser module ---------------------------- .. automodule:: tempest_lib.cli.output_parser :members: tempest-lib-1.0.0/doc/source/conf.py000077500000000000000000000046321266406763200173250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', #'sphinx.ext.intersphinx', 'oslosphinx' ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'tempest-lib' copyright = u'2013, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None}tempest-lib-1.0.0/doc/source/contributing.rst000066400000000000000000000001121266406763200212510ustar00rootroot00000000000000============ Contributing ============ .. include:: ../../CONTRIBUTING.rsttempest-lib-1.0.0/doc/source/decorators.rst000066400000000000000000000002711266406763200207150ustar00rootroot00000000000000.. _decorators: Decorators Usage Guide ====================== --------------------- The decorators module --------------------- .. automodule:: tempest_lib.decorators :members: tempest-lib-1.0.0/doc/source/history.rst000066400000000000000000000000351266406763200202470ustar00rootroot00000000000000.. include:: ../../ChangeLog tempest-lib-1.0.0/doc/source/index.rst000066400000000000000000000010311266406763200176520ustar00rootroot00000000000000.. tempest-lib documentation master file, created by sphinx-quickstart on Tue Jul 9 22:26:36 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to tempest-lib's documentation! ======================================================== Contents: .. toctree:: :maxdepth: 2 readme installation usage contributing cli decorators history Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` tempest-lib-1.0.0/doc/source/installation.rst000066400000000000000000000003111266406763200212440ustar00rootroot00000000000000============ Installation ============ At the command line:: $ pip install tempest-lib Or, if you have virtualenvwrapper installed:: $ mkvirtualenv tempest-lib $ pip install tempest-libtempest-lib-1.0.0/doc/source/readme.rst000066400000000000000000000000351266406763200200030ustar00rootroot00000000000000.. include:: ../../README.rsttempest-lib-1.0.0/doc/source/rest_client.rst000066400000000000000000000002711266406763200210630ustar00rootroot00000000000000.. _rest_client: Rest Client Usage ================= ---------------------- The rest_client module ---------------------- .. automodule:: tempest_lib.common.rest_client :members: tempest-lib-1.0.0/doc/source/usage.rst000066400000000000000000000006641266406763200176620ustar00rootroot00000000000000======== Usage ======== To use tempest-lib in a project:: import tempest_lib :ref:`cli` ---------- The CLI testing framework allows you to test the command line interface for an OpenStack project's python-*client :ref:`decorators` ----------------- These decorators enable common utility functions inside of your test suite :ref:`rest_client` ------------------ The base building block for making a project specific client tempest-lib-1.0.0/doc/source/utils.rst000066400000000000000000000002211266406763200177030ustar00rootroot00000000000000.. _utils: Utils Usage =========== --------------- The misc module --------------- .. automodule:: tempest_lib.common.utils.misc :members: tempest-lib-1.0.0/releasenotes/000077500000000000000000000000001266406763200164425ustar00rootroot00000000000000tempest-lib-1.0.0/releasenotes/notes/000077500000000000000000000000001266406763200175725ustar00rootroot00000000000000tempest-lib-1.0.0/releasenotes/notes/tempest-lib-deprecation-3ec1ece17a8db9c5.yaml000066400000000000000000000012111266406763200276270ustar00rootroot00000000000000--- prelude: > Starting with tempest-lib 1.0.0 the tempest-lib development has moved back into the tempest repository. After this release to get future code updates to tempest-lib code you need change your requirements to require tempest instead of tempest-lib and update your tepmest_lib imports to use tempest.lib instead. deprecations: - Tempest-lib itself is deprecated. Development of the tempest library interface will occur in tempest in the future. critical: - The 1.0.0 release is the last planned release for tempes-lib. All future development for the library interface will occur in tempest in the future. tempest-lib-1.0.0/releasenotes/source/000077500000000000000000000000001266406763200177425ustar00rootroot00000000000000tempest-lib-1.0.0/releasenotes/source/conf.py000066400000000000000000000204561266406763200212500ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # tempest-lib Release Notes documentation build configuration file, created by # sphinx-quickstart on Thu Nov 5 11:50:32 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'oslosphinx', 'reno.sphinxext', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'tempest-lib Release Notes' copyright = u'2016, tempest-lib Developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # from pbr import version # The full version, including alpha/beta/rc tags. release = version.VersionInfo('tempest_lib').version_string_with_vcs() # The short X.Y version. version = version.VersionInfo('tempest_lib').canonical_version_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " 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'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a 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 = 'tempest-libReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'tempest-libReleaseNotes.tex', u'tempest-lib Release Notes ' 'Documentation', u'tempest-lib Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'novareleasenotes', u'tempest-lib Release Notes Documentation', [u'tempest-lib developers'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'tempest-libReleaseNotes', u'tempest-lib Release Notes Documentation', u'tempest-lib developers', 'tempest-libReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False tempest-lib-1.0.0/releasenotes/source/index.rst000066400000000000000000000003631266406763200216050ustar00rootroot00000000000000Welcome to tempest-lib Release Notes documentation! =================================================== Contents ======== .. toctree:: :maxdepth: 2 unreleased Indices and tables ================== * :ref:`genindex` * :ref:`search` tempest-lib-1.0.0/releasenotes/source/unreleased.rst000066400000000000000000000001531266406763200226220ustar00rootroot00000000000000============================ Current Series Release Notes ============================ .. release-notes:: tempest-lib-1.0.0/requirements.txt000066400000000000000000000007201266406763200172340ustar00rootroot00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 Babel>=1.3 # BSD fixtures>=1.3.1 # Apache-2.0/BSD iso8601>=0.1.9 # MIT jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT httplib2>=0.7.5 # MIT paramiko>=1.16.0 # LGPL six>=1.9.0 # MIT oslo.log>=1.14.0 # Apache-2.0 os-testr>=0.4.1 # Apache-2.0 tempest-lib-1.0.0/setup.cfg000066400000000000000000000024331266406763200155740ustar00rootroot00000000000000[metadata] name = tempest-lib summary = OpenStack Functional Testing Library description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = http://www.openstack.org/ license = Apache License, Version 2.0 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.4 [files] packages = tempest_lib [entry_points] console_scripts = skip-tracker = tempest_lib.cmd.skip_tracker:main check-uuid = tempest_lib.cmd.check_uuid:run [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 [upload_sphinx] upload-dir = doc/build/html [compile_catalog] directory = tempest_lib/locale domain = tempest-lib [update_catalog] domain = tempest-lib output_dir = tempest_lib/locale input_file = tempest_lib/locale/tempest-lib.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = tempest_lib/locale/tempest-lib.pot tempest-lib-1.0.0/setup.py000066400000000000000000000020041266406763200154570ustar00rootroot00000000000000# 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 # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=1.8'], pbr=True) tempest-lib-1.0.0/tempest_lib/000077500000000000000000000000001266406763200162605ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/__init__.py000066400000000000000000000020721266406763200203720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import warnings import pbr.version __version__ = pbr.version.VersionInfo( 'tempest_lib').version_string() # Emit a warning for tempest-lib deprecation. We want the warning to # be displayed only once. warnings.simplefilter('once', category=DeprecationWarning) warnings.warn( 'tempest-lib is deprecated for future bug-fixes and code changes in favor ' 'of tempest. Please change your imports from tempest_lib to tempest.lib', DeprecationWarning) # And back to normal! warnings.resetwarnings() tempest-lib-1.0.0/tempest_lib/api_schema/000077500000000000000000000000001266406763200203515ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/__init__.py000066400000000000000000000000001266406763200224500ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/response/000077500000000000000000000000001266406763200222075ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/response/__init__.py000066400000000000000000000000001266406763200243060ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/000077500000000000000000000000001266406763200236635ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/__init__.py000066400000000000000000000000001266406763200257620ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/000077500000000000000000000000001266406763200244325ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/__init__.py000066400000000000000000000000001266406763200265310ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/agents.py000066400000000000000000000046021266406763200262670ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. common_agent_info = { 'type': 'object', 'properties': { 'agent_id': {'type': ['integer', 'string']}, 'hypervisor': {'type': 'string'}, 'os': {'type': 'string'}, 'architecture': {'type': 'string'}, 'version': {'type': 'string'}, 'url': {'type': 'string', 'format': 'uri'}, 'md5hash': {'type': 'string'} }, 'additionalProperties': False, 'required': ['agent_id', 'hypervisor', 'os', 'architecture', 'version', 'url', 'md5hash'] } list_agents = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'agents': { 'type': 'array', 'items': common_agent_info } }, 'additionalProperties': False, 'required': ['agents'] } } create_agent = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'agent': common_agent_info }, 'additionalProperties': False, 'required': ['agent'] } } update_agent = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'agent': { 'type': 'object', 'properties': { 'agent_id': {'type': ['integer', 'string']}, 'version': {'type': 'string'}, 'url': {'type': 'string', 'format': 'uri'}, 'md5hash': {'type': 'string'} }, 'additionalProperties': False, 'required': ['agent_id', 'version', 'url', 'md5hash'] } }, 'additionalProperties': False, 'required': ['agent'] } } delete_agent = { 'status_code': [200] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/aggregates.py000066400000000000000000000052551266406763200271240ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy # create-aggregate api doesn't have 'hosts' and 'metadata' attributes. aggregate_for_create = { 'type': 'object', 'properties': { 'availability_zone': {'type': ['string', 'null']}, 'created_at': {'type': 'string'}, 'deleted': {'type': 'boolean'}, 'deleted_at': {'type': ['string', 'null']}, 'id': {'type': 'integer'}, 'name': {'type': 'string'}, 'updated_at': {'type': ['string', 'null']} }, 'additionalProperties': False, 'required': ['availability_zone', 'created_at', 'deleted', 'deleted_at', 'id', 'name', 'updated_at'], } common_aggregate_info = copy.deepcopy(aggregate_for_create) common_aggregate_info['properties'].update({ 'hosts': {'type': 'array'}, 'metadata': {'type': 'object'} }) common_aggregate_info['required'].extend(['hosts', 'metadata']) list_aggregates = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'aggregates': { 'type': 'array', 'items': common_aggregate_info } }, 'additionalProperties': False, 'required': ['aggregates'], } } get_aggregate = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'aggregate': common_aggregate_info }, 'additionalProperties': False, 'required': ['aggregate'], } } aggregate_set_metadata = get_aggregate # The 'updated_at' attribute of 'update_aggregate' can't be null. update_aggregate = copy.deepcopy(get_aggregate) update_aggregate['response_body']['properties']['aggregate']['properties'][ 'updated_at'] = { 'type': 'string' } delete_aggregate = { 'status_code': [200] } create_aggregate = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'aggregate': aggregate_for_create }, 'additionalProperties': False, 'required': ['aggregate'], } } aggregate_add_remove_host = get_aggregate tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/availability_zone.py000066400000000000000000000052761266406763200305230ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy base = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'availabilityZoneInfo': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'zoneName': {'type': 'string'}, 'zoneState': { 'type': 'object', 'properties': { 'available': {'type': 'boolean'} }, 'additionalProperties': False, 'required': ['available'] }, # NOTE: Here is the difference between detail and # non-detail. 'hosts': {'type': 'null'} }, 'additionalProperties': False, 'required': ['zoneName', 'zoneState', 'hosts'] } } }, 'additionalProperties': False, 'required': ['availabilityZoneInfo'] } } detail = { 'type': 'object', 'patternProperties': { # NOTE: Here is for a hostname '^[a-zA-Z0-9-_.]+$': { 'type': 'object', 'patternProperties': { # NOTE: Here is for a service name '^.*$': { 'type': 'object', 'properties': { 'available': {'type': 'boolean'}, 'active': {'type': 'boolean'}, 'updated_at': {'type': ['string', 'null']} }, 'additionalProperties': False, 'required': ['available', 'active', 'updated_at'] } } } } } list_availability_zone_list = copy.deepcopy(base) list_availability_zone_list_detail = copy.deepcopy(base) list_availability_zone_list_detail['response_body']['properties'][ 'availabilityZoneInfo']['items']['properties']['hosts'] = detail tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/baremetal_nodes.py000066400000000000000000000037101266406763200301310ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy node = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'interfaces': {'type': 'array'}, 'host': {'type': 'string'}, 'task_state': {'type': ['string', 'null']}, 'cpus': {'type': ['integer', 'string']}, 'memory_mb': {'type': ['integer', 'string']}, 'disk_gb': {'type': ['integer', 'string']}, }, 'additionalProperties': False, 'required': ['id', 'interfaces', 'host', 'task_state', 'cpus', 'memory_mb', 'disk_gb'] } list_baremetal_nodes = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'nodes': { 'type': 'array', 'items': node } }, 'additionalProperties': False, 'required': ['nodes'] } } baremetal_node = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'node': node }, 'additionalProperties': False, 'required': ['node'] } } get_baremetal_node = copy.deepcopy(baremetal_node) get_baremetal_node['response_body']['properties']['node'][ 'properties'].update({'instance_uuid': {'type': ['string', 'null']}}) get_baremetal_node['response_body']['properties']['node'][ 'required'].append('instance_uuid') tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/certificates.py000066400000000000000000000026001266406763200274470ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy _common_schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'certificate': { 'type': 'object', 'properties': { 'data': {'type': 'string'}, 'private_key': {'type': 'string'}, }, 'additionalProperties': False, 'required': ['data', 'private_key'] } }, 'additionalProperties': False, 'required': ['certificate'] } } get_certificate = copy.deepcopy(_common_schema) get_certificate['response_body']['properties']['certificate'][ 'properties']['private_key'].update({'type': 'null'}) create_certificate = copy.deepcopy(_common_schema) tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/extensions.py000066400000000000000000000033551266406763200272110ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. list_extensions = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'extensions': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'updated': { 'type': 'string', 'format': 'data-time' }, 'name': {'type': 'string'}, 'links': {'type': 'array'}, 'namespace': { 'type': 'string', 'format': 'uri' }, 'alias': {'type': 'string'}, 'description': {'type': 'string'} }, 'additionalProperties': False, 'required': ['updated', 'name', 'links', 'namespace', 'alias', 'description'] } } }, 'additionalProperties': False, 'required': ['extensions'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/fixed_ips.py000066400000000000000000000026111266406763200267560ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.api_schema.response.compute.v2_1 import parameter_types get_fixed_ip = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'fixed_ip': { 'type': 'object', 'properties': { 'address': parameter_types.ip_address, 'cidr': {'type': 'string'}, 'host': {'type': 'string'}, 'hostname': {'type': 'string'} }, 'additionalProperties': False, 'required': ['address', 'cidr', 'host', 'hostname'] } }, 'additionalProperties': False, 'required': ['fixed_ip'] } } reserve_unreserve_fixed_ip = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/flavors.py000066400000000000000000000064611266406763200264670ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.api_schema.response.compute.v2_1 import parameter_types list_flavors = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'flavors': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'name': {'type': 'string'}, 'links': parameter_types.links, 'id': {'type': 'string'} }, 'additionalProperties': False, 'required': ['name', 'links', 'id'] } }, 'flavors_links': parameter_types.links }, 'additionalProperties': False, # NOTE(gmann): flavors_links attribute is not necessary # to be present always So it is not 'required'. 'required': ['flavors'] } } common_flavor_info = { 'type': 'object', 'properties': { 'name': {'type': 'string'}, 'links': parameter_types.links, 'ram': {'type': 'integer'}, 'vcpus': {'type': 'integer'}, # 'swap' attributes comes as integer value but if it is empty # it comes as "". So defining type of as string and integer. 'swap': {'type': ['integer', 'string']}, 'disk': {'type': 'integer'}, 'id': {'type': 'string'}, 'OS-FLV-DISABLED:disabled': {'type': 'boolean'}, 'os-flavor-access:is_public': {'type': 'boolean'}, 'rxtx_factor': {'type': 'number'}, 'OS-FLV-EXT-DATA:ephemeral': {'type': 'integer'} }, 'additionalProperties': False, # 'OS-FLV-DISABLED', 'os-flavor-access', 'rxtx_factor' and # 'OS-FLV-EXT-DATA' are API extensions. So they are not 'required'. 'required': ['name', 'links', 'ram', 'vcpus', 'swap', 'disk', 'id'] } list_flavors_details = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'flavors': { 'type': 'array', 'items': common_flavor_info }, # NOTE(gmann): flavors_links attribute is not necessary # to be present always So it is not 'required'. 'flavors_links': parameter_types.links }, 'additionalProperties': False, 'required': ['flavors'] } } unset_flavor_extra_specs = { 'status_code': [200] } create_get_flavor_details = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'flavor': common_flavor_info }, 'additionalProperties': False, 'required': ['flavor'] } } delete_flavor = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/flavors_access.py000066400000000000000000000024311266406763200300010ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. add_remove_list_flavor_access = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'flavor_access': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'flavor_id': {'type': 'string'}, 'tenant_id': {'type': 'string'}, }, 'additionalProperties': False, 'required': ['flavor_id', 'tenant_id'], } } }, 'additionalProperties': False, 'required': ['flavor_access'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/flavors_extra_specs.py000066400000000000000000000023651266406763200310660ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. set_get_flavor_extra_specs = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'extra_specs': { 'type': 'object', 'patternProperties': { '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'} } } }, 'additionalProperties': False, 'required': ['extra_specs'] } } set_get_flavor_extra_specs_key = { 'status_code': [200], 'response_body': { 'type': 'object', 'patternProperties': { '^[a-zA-Z0-9_\-\. :]+$': {'type': 'string'} } } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/floating_ips.py000066400000000000000000000111051266406763200274600ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.api_schema.response.compute.v2_1 import parameter_types common_floating_ip_info = { 'type': 'object', 'properties': { # NOTE: Now the type of 'id' is integer, but # here allows 'string' also because we will be # able to change it to 'uuid' in the future. 'id': {'type': ['integer', 'string']}, 'pool': {'type': ['string', 'null']}, 'instance_id': {'type': ['string', 'null']}, 'ip': parameter_types.ip_address, 'fixed_ip': parameter_types.ip_address }, 'additionalProperties': False, 'required': ['id', 'pool', 'instance_id', 'ip', 'fixed_ip'], } list_floating_ips = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'floating_ips': { 'type': 'array', 'items': common_floating_ip_info }, }, 'additionalProperties': False, 'required': ['floating_ips'], } } create_get_floating_ip = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'floating_ip': common_floating_ip_info }, 'additionalProperties': False, 'required': ['floating_ip'], } } list_floating_ip_pools = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'floating_ip_pools': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'name': {'type': 'string'} }, 'additionalProperties': False, 'required': ['name'], } } }, 'additionalProperties': False, 'required': ['floating_ip_pools'], } } add_remove_floating_ip = { 'status_code': [202] } create_floating_ips_bulk = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'floating_ips_bulk_create': { 'type': 'object', 'properties': { 'interface': {'type': ['string', 'null']}, 'ip_range': {'type': 'string'}, 'pool': {'type': ['string', 'null']}, }, 'additionalProperties': False, 'required': ['interface', 'ip_range', 'pool'], } }, 'additionalProperties': False, 'required': ['floating_ips_bulk_create'], } } delete_floating_ips_bulk = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'floating_ips_bulk_delete': {'type': 'string'} }, 'additionalProperties': False, 'required': ['floating_ips_bulk_delete'], } } list_floating_ips_bulk = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'floating_ip_info': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'address': parameter_types.ip_address, 'instance_uuid': {'type': ['string', 'null']}, 'interface': {'type': ['string', 'null']}, 'pool': {'type': ['string', 'null']}, 'project_id': {'type': ['string', 'null']}, 'fixed_ip': parameter_types.ip_address }, 'additionalProperties': False, # NOTE: fixed_ip is introduced after JUNO release, # So it is not defined as 'required'. 'required': ['address', 'instance_uuid', 'interface', 'pool', 'project_id'], } } }, 'additionalProperties': False, 'required': ['floating_ip_info'], } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/hosts.py000066400000000000000000000071671266406763200261570ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy list_hosts = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'hosts': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'host_name': {'type': 'string'}, 'service': {'type': 'string'}, 'zone': {'type': 'string'} }, 'additionalProperties': False, 'required': ['host_name', 'service', 'zone'] } } }, 'additionalProperties': False, 'required': ['hosts'] } } get_host_detail = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'host': { 'type': 'array', 'item': { 'type': 'object', 'properties': { 'resource': { 'type': 'object', 'properties': { 'cpu': {'type': 'integer'}, 'disk_gb': {'type': 'integer'}, 'host': {'type': 'string'}, 'memory_mb': {'type': 'integer'}, 'project': {'type': 'string'} }, 'additionalProperties': False, 'required': ['cpu', 'disk_gb', 'host', 'memory_mb', 'project'] } }, 'additionalProperties': False, 'required': ['resource'] } } }, 'additionalProperties': False, 'required': ['host'] } } startup_host = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'host': {'type': 'string'}, 'power_action': {'enum': ['startup']} }, 'additionalProperties': False, 'required': ['host', 'power_action'] } } # The 'power_action' attribute of 'shutdown_host' API is 'shutdown' shutdown_host = copy.deepcopy(startup_host) shutdown_host['response_body']['properties']['power_action'] = { 'enum': ['shutdown'] } # The 'power_action' attribute of 'reboot_host' API is 'reboot' reboot_host = copy.deepcopy(startup_host) reboot_host['response_body']['properties']['power_action'] = { 'enum': ['reboot'] } update_host = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'host': {'type': 'string'}, 'maintenance_mode': {'enum': ['on_maintenance', 'off_maintenance']}, 'status': {'enum': ['enabled', 'disabled']} }, 'additionalProperties': False, 'required': ['host', 'maintenance_mode', 'status'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/hypervisors.py000066400000000000000000000156721266406763200274140ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.api_schema.response.compute.v2_1 import parameter_types get_hypervisor_statistics = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'hypervisor_statistics': { 'type': 'object', 'properties': { 'count': {'type': 'integer'}, 'current_workload': {'type': 'integer'}, 'disk_available_least': {'type': ['integer', 'null']}, 'free_disk_gb': {'type': 'integer'}, 'free_ram_mb': {'type': 'integer'}, 'local_gb': {'type': 'integer'}, 'local_gb_used': {'type': 'integer'}, 'memory_mb': {'type': 'integer'}, 'memory_mb_used': {'type': 'integer'}, 'running_vms': {'type': 'integer'}, 'vcpus': {'type': 'integer'}, 'vcpus_used': {'type': 'integer'} }, 'additionalProperties': False, 'required': ['count', 'current_workload', 'disk_available_least', 'free_disk_gb', 'free_ram_mb', 'local_gb', 'local_gb_used', 'memory_mb', 'memory_mb_used', 'running_vms', 'vcpus', 'vcpus_used'] } }, 'additionalProperties': False, 'required': ['hypervisor_statistics'] } } hypervisor_detail = { 'type': 'object', 'properties': { 'status': {'type': 'string'}, 'state': {'type': 'string'}, 'cpu_info': {'type': 'string'}, 'current_workload': {'type': 'integer'}, 'disk_available_least': {'type': ['integer', 'null']}, 'host_ip': parameter_types.ip_address, 'free_disk_gb': {'type': 'integer'}, 'free_ram_mb': {'type': 'integer'}, 'hypervisor_hostname': {'type': 'string'}, 'hypervisor_type': {'type': 'string'}, 'hypervisor_version': {'type': 'integer'}, 'id': {'type': ['integer', 'string']}, 'local_gb': {'type': 'integer'}, 'local_gb_used': {'type': 'integer'}, 'memory_mb': {'type': 'integer'}, 'memory_mb_used': {'type': 'integer'}, 'running_vms': {'type': 'integer'}, 'service': { 'type': 'object', 'properties': { 'host': {'type': 'string'}, 'id': {'type': ['integer', 'string']}, 'disabled_reason': {'type': ['string', 'null']} }, 'additionalProperties': False, 'required': ['host', 'id'] }, 'vcpus': {'type': 'integer'}, 'vcpus_used': {'type': 'integer'} }, 'additionalProperties': False, # NOTE: When loading os-hypervisor-status extension, # a response contains status and state. So these params # should not be required. 'required': ['cpu_info', 'current_workload', 'disk_available_least', 'host_ip', 'free_disk_gb', 'free_ram_mb', 'hypervisor_hostname', 'hypervisor_type', 'hypervisor_version', 'id', 'local_gb', 'local_gb_used', 'memory_mb', 'memory_mb_used', 'running_vms', 'service', 'vcpus', 'vcpus_used'] } list_hypervisors_detail = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'hypervisors': { 'type': 'array', 'items': hypervisor_detail } }, 'additionalProperties': False, 'required': ['hypervisors'] } } get_hypervisor = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'hypervisor': hypervisor_detail }, 'additionalProperties': False, 'required': ['hypervisor'] } } list_search_hypervisors = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'hypervisors': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'status': {'type': 'string'}, 'state': {'type': 'string'}, 'id': {'type': ['integer', 'string']}, 'hypervisor_hostname': {'type': 'string'} }, 'additionalProperties': False, # NOTE: When loading os-hypervisor-status extension, # a response contains status and state. So these params # should not be required. 'required': ['id', 'hypervisor_hostname'] } } }, 'additionalProperties': False, 'required': ['hypervisors'] } } get_hypervisor_uptime = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'hypervisor': { 'type': 'object', 'properties': { 'status': {'type': 'string'}, 'state': {'type': 'string'}, 'id': {'type': ['integer', 'string']}, 'hypervisor_hostname': {'type': 'string'}, 'uptime': {'type': 'string'} }, 'additionalProperties': False, # NOTE: When loading os-hypervisor-status extension, # a response contains status and state. So these params # should not be required. 'required': ['id', 'hypervisor_hostname', 'uptime'] } }, 'additionalProperties': False, 'required': ['hypervisor'] } } get_hypervisors_servers = copy.deepcopy(list_search_hypervisors) get_hypervisors_servers['response_body']['properties']['hypervisors']['items'][ 'properties']['servers'] = { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'uuid': {'type': 'string'}, 'name': {'type': 'string'} }, 'additionalProperties': False, } } # In V2 API, if there is no servers (VM) on the Hypervisor host then 'servers' # attribute will not be present in response body So it is not 'required'. tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/images.py000066400000000000000000000107161266406763200262560ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.api_schema.response.compute.v2_1 import parameter_types image_links = copy.deepcopy(parameter_types.links) image_links['items']['properties'].update({'type': {'type': 'string'}}) common_image_schema = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'status': {'type': 'string'}, 'updated': {'type': 'string'}, 'links': image_links, 'name': {'type': ['string', 'null']}, 'created': {'type': 'string'}, 'minDisk': {'type': 'integer'}, 'minRam': {'type': 'integer'}, 'progress': {'type': 'integer'}, 'metadata': {'type': 'object'}, 'server': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'links': parameter_types.links }, 'additionalProperties': False, 'required': ['id', 'links'] }, 'OS-EXT-IMG-SIZE:size': {'type': ['integer', 'null']}, 'OS-DCF:diskConfig': {'type': 'string'} }, 'additionalProperties': False, # 'server' attributes only comes in response body if image is # associated with any server. 'OS-EXT-IMG-SIZE:size' & 'OS-DCF:diskConfig' # are API extension, So those are not defined as 'required'. 'required': ['id', 'status', 'updated', 'links', 'name', 'created', 'minDisk', 'minRam', 'progress', 'metadata'] } get_image = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'image': common_image_schema }, 'additionalProperties': False, 'required': ['image'] } } list_images = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'images': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'links': image_links, 'name': {'type': 'string'} }, 'additionalProperties': False, 'required': ['id', 'links', 'name'] } }, 'images_links': parameter_types.links }, 'additionalProperties': False, # NOTE(gmann): images_links attribute is not necessary to be # present always So it is not 'required'. 'required': ['images'] } } create_image = { 'status_code': [202], 'response_header': { 'type': 'object', 'properties': parameter_types.response_header } } create_image['response_header']['properties'].update( {'location': { 'type': 'string', 'format': 'uri'} } ) create_image['response_header']['required'] = ['location'] delete = { 'status_code': [204] } image_metadata = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'metadata': {'type': 'object'} }, 'additionalProperties': False, 'required': ['metadata'] } } image_meta_item = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'meta': {'type': 'object'} }, 'additionalProperties': False, 'required': ['meta'] } } list_images_details = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'images': { 'type': 'array', 'items': common_image_schema }, 'images_links': parameter_types.links }, 'additionalProperties': False, # NOTE(gmann): images_links attribute is not necessary to be # present always So it is not 'required'. 'required': ['images'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/instance_usage_audit_logs.py000066400000000000000000000042251266406763200322110ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. common_instance_usage_audit_log = { 'type': 'object', 'properties': { 'hosts_not_run': { 'type': 'array', 'items': {'type': 'string'} }, 'log': {'type': 'object'}, 'num_hosts': {'type': 'integer'}, 'num_hosts_done': {'type': 'integer'}, 'num_hosts_not_run': {'type': 'integer'}, 'num_hosts_running': {'type': 'integer'}, 'overall_status': {'type': 'string'}, 'period_beginning': {'type': 'string'}, 'period_ending': {'type': 'string'}, 'total_errors': {'type': 'integer'}, 'total_instances': {'type': 'integer'} }, 'additionalProperties': False, 'required': ['hosts_not_run', 'log', 'num_hosts', 'num_hosts_done', 'num_hosts_not_run', 'num_hosts_running', 'overall_status', 'period_beginning', 'period_ending', 'total_errors', 'total_instances'] } get_instance_usage_audit_log = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'instance_usage_audit_log': common_instance_usage_audit_log }, 'additionalProperties': False, 'required': ['instance_usage_audit_log'] } } list_instance_usage_audit_log = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'instance_usage_audit_logs': common_instance_usage_audit_log }, 'additionalProperties': False, 'required': ['instance_usage_audit_logs'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/interfaces.py000066400000000000000000000043441266406763200271340ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.api_schema.response.compute.v2_1 import parameter_types interface_common_info = { 'type': 'object', 'properties': { 'port_state': {'type': 'string'}, 'fixed_ips': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'subnet_id': { 'type': 'string', 'format': 'uuid' }, 'ip_address': parameter_types.ip_address }, 'additionalProperties': False, 'required': ['subnet_id', 'ip_address'] } }, 'port_id': {'type': 'string', 'format': 'uuid'}, 'net_id': {'type': 'string', 'format': 'uuid'}, 'mac_addr': parameter_types.mac_address }, 'additionalProperties': False, 'required': ['port_state', 'fixed_ips', 'port_id', 'net_id', 'mac_addr'] } get_create_interfaces = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'interfaceAttachment': interface_common_info }, 'additionalProperties': False, 'required': ['interfaceAttachment'] } } list_interfaces = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'interfaceAttachments': { 'type': 'array', 'items': interface_common_info } }, 'additionalProperties': False, 'required': ['interfaceAttachments'] } } delete_interface = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/keypairs.py000066400000000000000000000075301266406763200266400ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. get_keypair = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'keypair': { 'type': 'object', 'properties': { 'public_key': {'type': 'string'}, 'name': {'type': 'string'}, 'fingerprint': {'type': 'string'}, 'user_id': {'type': 'string'}, 'deleted': {'type': 'boolean'}, 'created_at': {'type': 'string'}, 'updated_at': {'type': ['string', 'null']}, 'deleted_at': {'type': ['string', 'null']}, 'id': {'type': 'integer'} }, 'additionalProperties': False, # When we run the get keypair API, response body includes # all the above mentioned attributes. # But in Nova API sample file, response body includes only # 'public_key', 'name' & 'fingerprint'. So only 'public_key', # 'name' & 'fingerprint' are defined as 'required'. 'required': ['public_key', 'name', 'fingerprint'] } }, 'additionalProperties': False, 'required': ['keypair'] } } create_keypair = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'keypair': { 'type': 'object', 'properties': { 'fingerprint': {'type': 'string'}, 'name': {'type': 'string'}, 'public_key': {'type': 'string'}, 'user_id': {'type': 'string'}, 'private_key': {'type': 'string'} }, 'additionalProperties': False, # When create keypair API is being called with 'Public key' # (Importing keypair) then, response body does not contain # 'private_key' So it is not defined as 'required' 'required': ['fingerprint', 'name', 'public_key', 'user_id'] } }, 'additionalProperties': False, 'required': ['keypair'] } } delete_keypair = { 'status_code': [202], } list_keypairs = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'keypairs': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'keypair': { 'type': 'object', 'properties': { 'public_key': {'type': 'string'}, 'name': {'type': 'string'}, 'fingerprint': {'type': 'string'} }, 'additionalProperties': False, 'required': ['public_key', 'name', 'fingerprint'] } }, 'additionalProperties': False, 'required': ['keypair'] } } }, 'additionalProperties': False, 'required': ['keypairs'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/limits.py000066400000000000000000000123151266406763200263070ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. get_limit = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'limits': { 'type': 'object', 'properties': { 'absolute': { 'type': 'object', 'properties': { 'maxTotalRAMSize': {'type': 'integer'}, 'totalCoresUsed': {'type': 'integer'}, 'maxTotalInstances': {'type': 'integer'}, 'maxTotalFloatingIps': {'type': 'integer'}, 'totalSecurityGroupsUsed': {'type': 'integer'}, 'maxTotalCores': {'type': 'integer'}, 'totalFloatingIpsUsed': {'type': 'integer'}, 'maxSecurityGroups': {'type': 'integer'}, 'maxServerMeta': {'type': 'integer'}, 'maxPersonality': {'type': 'integer'}, 'maxImageMeta': {'type': 'integer'}, 'maxPersonalitySize': {'type': 'integer'}, 'maxSecurityGroupRules': {'type': 'integer'}, 'maxTotalKeypairs': {'type': 'integer'}, 'totalRAMUsed': {'type': 'integer'}, 'totalInstancesUsed': {'type': 'integer'}, 'maxServerGroupMembers': {'type': 'integer'}, 'maxServerGroups': {'type': 'integer'}, 'totalServerGroupsUsed': {'type': 'integer'} }, 'additionalProperties': False, # NOTE(gmann): maxServerGroupMembers, maxServerGroups # and totalServerGroupsUsed are API extension, # and some environments return a response without these # attributes.So they are not 'required'. 'required': ['maxImageMeta', 'maxPersonality', 'maxPersonalitySize', 'maxSecurityGroupRules', 'maxSecurityGroups', 'maxServerMeta', 'maxTotalCores', 'maxTotalFloatingIps', 'maxTotalInstances', 'maxTotalKeypairs', 'maxTotalRAMSize', 'totalCoresUsed', 'totalFloatingIpsUsed', 'totalInstancesUsed', 'totalRAMUsed', 'totalSecurityGroupsUsed'] }, 'rate': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'limit': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'next-available': {'type': 'string'}, 'remaining': {'type': 'integer'}, 'unit': {'type': 'string'}, 'value': {'type': 'integer'}, 'verb': {'type': 'string'} }, 'additionalProperties': False, } }, 'regex': {'type': 'string'}, 'uri': {'type': 'string'} }, 'additionalProperties': False, } } }, 'additionalProperties': False, 'required': ['absolute', 'rate'] } }, 'additionalProperties': False, 'required': ['limits'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/migrations.py000066400000000000000000000043241266406763200271630ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. list_migrations = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'migrations': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': 'integer'}, 'status': {'type': ['string', 'null']}, 'instance_uuid': {'type': ['string', 'null']}, 'source_node': {'type': ['string', 'null']}, 'source_compute': {'type': ['string', 'null']}, 'dest_node': {'type': ['string', 'null']}, 'dest_compute': {'type': ['string', 'null']}, 'dest_host': {'type': ['string', 'null']}, 'old_instance_type_id': {'type': ['integer', 'null']}, 'new_instance_type_id': {'type': ['integer', 'null']}, 'created_at': {'type': 'string'}, 'updated_at': {'type': ['string', 'null']} }, 'additionalProperties': False, 'required': [ 'id', 'status', 'instance_uuid', 'source_node', 'source_compute', 'dest_node', 'dest_compute', 'dest_host', 'old_instance_type_id', 'new_instance_type_id', 'created_at', 'updated_at' ] } } }, 'additionalProperties': False, 'required': ['migrations'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/parameter_types.py000066400000000000000000000050001266406763200302030ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. links = { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'href': { 'type': 'string', 'format': 'uri' }, 'rel': {'type': 'string'} }, 'additionalProperties': False, 'required': ['href', 'rel'] } } mac_address = { 'type': 'string', 'pattern': '(?:[a-f0-9]{2}:){5}[a-f0-9]{2}' } ip_address = { 'oneOf': [ { 'type': 'string', 'oneOf': [ {'format': 'ipv4'}, {'format': 'ipv6'} ] }, {'type': 'null'} ] } access_ip_v4 = { 'type': 'string', 'oneOf': [{'format': 'ipv4'}, {'enum': ['']}] } access_ip_v6 = { 'type': 'string', 'oneOf': [{'format': 'ipv6'}, {'enum': ['']}] } addresses = { 'type': 'object', 'patternProperties': { # NOTE: Here is for 'private' or something. '^[a-zA-Z0-9-_.]+$': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'version': {'type': 'integer'}, 'addr': { 'type': 'string', 'oneOf': [ {'format': 'ipv4'}, {'format': 'ipv6'} ] } }, 'additionalProperties': False, 'required': ['version', 'addr'] } } } } response_header = { 'connection': {'type': 'string'}, 'content-length': {'type': 'string'}, 'content-type': {'type': 'string'}, 'status': {'type': 'string'}, 'x-compute-request-id': {'type': 'string'}, 'vary': {'type': 'string'}, 'x-openstack-nova-api-version': {'type': 'string'}, 'date': { 'type': 'string', 'format': 'data-time' } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/quota_classes.py000066400000000000000000000027261266406763200276610ustar00rootroot00000000000000# Copyright 2014 IBM Corporation. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.api_schema.response.compute.v2_1 import quotas # NOTE(mriedem): os-quota-class-sets responses are the same as os-quota-sets # except for the key in the response body is quota_class_set instead of # quota_set, so update this copy of the schema from os-quota-sets. get_quota_class_set = copy.deepcopy(quotas.get_quota_set) get_quota_class_set['response_body']['properties']['quota_class_set'] = ( get_quota_class_set['response_body']['properties'].pop('quota_set')) get_quota_class_set['response_body']['required'] = ['quota_class_set'] update_quota_class_set = copy.deepcopy(quotas.update_quota_set) update_quota_class_set['response_body']['properties']['quota_class_set'] = ( update_quota_class_set['response_body']['properties'].pop('quota_set')) update_quota_class_set['response_body']['required'] = ['quota_class_set'] tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/quotas.py000066400000000000000000000052321266406763200263220ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy update_quota_set = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'quota_set': { 'type': 'object', 'properties': { 'instances': {'type': 'integer'}, 'cores': {'type': 'integer'}, 'ram': {'type': 'integer'}, 'floating_ips': {'type': 'integer'}, 'fixed_ips': {'type': 'integer'}, 'metadata_items': {'type': 'integer'}, 'key_pairs': {'type': 'integer'}, 'security_groups': {'type': 'integer'}, 'security_group_rules': {'type': 'integer'}, 'server_group_members': {'type': 'integer'}, 'server_groups': {'type': 'integer'}, 'injected_files': {'type': 'integer'}, 'injected_file_content_bytes': {'type': 'integer'}, 'injected_file_path_bytes': {'type': 'integer'} }, 'additionalProperties': False, # NOTE: server_group_members and server_groups are represented # when enabling quota_server_group extension. So they should # not be required. 'required': ['instances', 'cores', 'ram', 'floating_ips', 'fixed_ips', 'metadata_items', 'key_pairs', 'security_groups', 'security_group_rules', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes'] } }, 'additionalProperties': False, 'required': ['quota_set'] } } get_quota_set = copy.deepcopy(update_quota_set) get_quota_set['response_body']['properties']['quota_set']['properties'][ 'id'] = {'type': 'string'} get_quota_set['response_body']['properties']['quota_set']['required'].extend([ 'id']) delete_quota = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/security_group_default_rule.py000066400000000000000000000040001266406763200326140ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. common_security_group_default_rule_info = { 'type': 'object', 'properties': { 'from_port': {'type': 'integer'}, 'id': {'type': 'integer'}, 'ip_protocol': {'type': 'string'}, 'ip_range': { 'type': 'object', 'properties': { 'cidr': {'type': 'string'} }, 'additionalProperties': False, 'required': ['cidr'], }, 'to_port': {'type': 'integer'}, }, 'additionalProperties': False, 'required': ['from_port', 'id', 'ip_protocol', 'ip_range', 'to_port'], } create_get_security_group_default_rule = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'security_group_default_rule': common_security_group_default_rule_info }, 'additionalProperties': False, 'required': ['security_group_default_rule'] } } delete_security_group_default_rule = { 'status_code': [204] } list_security_group_default_rules = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'security_group_default_rules': { 'type': 'array', 'items': common_security_group_default_rule_info } }, 'additionalProperties': False, 'required': ['security_group_default_rules'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/security_groups.py000066400000000000000000000066211266406763200302570ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. common_security_group_rule = { 'from_port': {'type': ['integer', 'null']}, 'to_port': {'type': ['integer', 'null']}, 'group': { 'type': 'object', 'properties': { 'tenant_id': {'type': 'string'}, 'name': {'type': 'string'} }, 'additionalProperties': False, }, 'ip_protocol': {'type': ['string', 'null']}, # 'parent_group_id' can be UUID so defining it as 'string' also. 'parent_group_id': {'type': ['string', 'integer', 'null']}, 'ip_range': { 'type': 'object', 'properties': { 'cidr': {'type': 'string'} }, 'additionalProperties': False, # When optional argument is provided in request body # like 'group_id' then, attribute 'cidr' does not # comes in response body. So it is not 'required'. }, 'id': {'type': ['string', 'integer']} } common_security_group = { 'type': 'object', 'properties': { 'id': {'type': ['integer', 'string']}, 'name': {'type': 'string'}, 'tenant_id': {'type': 'string'}, 'rules': { 'type': 'array', 'items': { 'type': ['object', 'null'], 'properties': common_security_group_rule, 'additionalProperties': False, } }, 'description': {'type': 'string'}, }, 'additionalProperties': False, 'required': ['id', 'name', 'tenant_id', 'rules', 'description'], } list_security_groups = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'security_groups': { 'type': 'array', 'items': common_security_group } }, 'additionalProperties': False, 'required': ['security_groups'] } } get_security_group = create_security_group = update_security_group = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'security_group': common_security_group }, 'additionalProperties': False, 'required': ['security_group'] } } delete_security_group = { 'status_code': [202] } create_security_group_rule = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'security_group_rule': { 'type': 'object', 'properties': common_security_group_rule, 'additionalProperties': False, 'required': ['from_port', 'to_port', 'group', 'ip_protocol', 'parent_group_id', 'id', 'ip_range'] } }, 'additionalProperties': False, 'required': ['security_group_rule'] } } delete_security_group_rule = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/servers.py000066400000000000000000000401651266406763200265030ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.api_schema.response.compute.v2_1 import parameter_types create_server = { 'status_code': [202], 'response_body': { 'type': 'object', 'properties': { 'server': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'security_groups': {'type': 'array'}, 'links': parameter_types.links, 'OS-DCF:diskConfig': {'type': 'string'} }, 'additionalProperties': False, # NOTE: OS-DCF:diskConfig & security_groups are API extension, # and some environments return a response without these # attributes.So they are not 'required'. 'required': ['id', 'links'] } }, 'additionalProperties': False, 'required': ['server'] } } create_server_with_admin_pass = copy.deepcopy(create_server) create_server_with_admin_pass['response_body']['properties']['server'][ 'properties'].update({'adminPass': {'type': 'string'}}) create_server_with_admin_pass['response_body']['properties']['server'][ 'required'].append('adminPass') list_servers = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'servers': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'links': parameter_types.links, 'name': {'type': 'string'} }, 'additionalProperties': False, 'required': ['id', 'links', 'name'] } }, 'servers_links': parameter_types.links }, 'additionalProperties': False, # NOTE(gmann): servers_links attribute is not necessary to be # present always So it is not 'required'. 'required': ['servers'] } } delete_server = { 'status_code': [204], } common_show_server = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'name': {'type': 'string'}, 'status': {'type': 'string'}, 'image': {'oneOf': [ {'type': 'object', 'properties': { 'id': {'type': 'string'}, 'links': parameter_types.links }, 'additionalProperties': False, 'required': ['id', 'links']}, {'type': ['string', 'null']} ]}, 'flavor': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'links': parameter_types.links }, 'additionalProperties': False, 'required': ['id', 'links'] }, 'fault': { 'type': 'object', 'properties': { 'code': {'type': 'integer'}, 'created': {'type': 'string'}, 'message': {'type': 'string'}, 'details': {'type': 'string'}, }, 'additionalProperties': False, # NOTE(gmann): 'details' is not necessary to be present # in the 'fault'. So it is not defined as 'required'. 'required': ['code', 'created', 'message'] }, 'user_id': {'type': 'string'}, 'tenant_id': {'type': 'string'}, 'created': {'type': 'string'}, 'updated': {'type': 'string'}, 'progress': {'type': 'integer'}, 'metadata': {'type': 'object'}, 'links': parameter_types.links, 'addresses': parameter_types.addresses, 'hostId': {'type': 'string'}, 'OS-DCF:diskConfig': {'type': 'string'}, 'accessIPv4': parameter_types.access_ip_v4, 'accessIPv6': parameter_types.access_ip_v6 }, 'additionalProperties': False, # NOTE(GMann): 'progress' attribute is present in the response # only when server's status is one of the progress statuses # ("ACTIVE","BUILD", "REBUILD", "RESIZE","VERIFY_RESIZE") # 'fault' attribute is present in the response # only when server's status is one of the "ERROR", "DELETED". # OS-DCF:diskConfig and accessIPv4/v6 are API # extensions, and some environments return a response # without these attributes.So these are not defined as 'required'. 'required': ['id', 'name', 'status', 'image', 'flavor', 'user_id', 'tenant_id', 'created', 'updated', 'metadata', 'links', 'addresses', 'hostId'] } update_server = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'server': common_show_server }, 'additionalProperties': False, 'required': ['server'] } } server_detail = copy.deepcopy(common_show_server) server_detail['properties'].update({ 'key_name': {'type': ['string', 'null']}, 'security_groups': {'type': 'array'}, # NOTE: Non-admin users also can see "OS-SRV-USG" and "OS-EXT-AZ" # attributes. 'OS-SRV-USG:launched_at': {'type': ['string', 'null']}, 'OS-SRV-USG:terminated_at': {'type': ['string', 'null']}, 'OS-EXT-AZ:availability_zone': {'type': 'string'}, # NOTE: Admin users only can see "OS-EXT-STS" and "OS-EXT-SRV-ATTR" # attributes. 'OS-EXT-STS:task_state': {'type': ['string', 'null']}, 'OS-EXT-STS:vm_state': {'type': 'string'}, 'OS-EXT-STS:power_state': {'type': 'integer'}, 'OS-EXT-SRV-ATTR:host': {'type': ['string', 'null']}, 'OS-EXT-SRV-ATTR:instance_name': {'type': 'string'}, 'OS-EXT-SRV-ATTR:hypervisor_hostname': {'type': ['string', 'null']}, 'os-extended-volumes:volumes_attached': {'type': 'array'}, 'config_drive': {'type': 'string'} }) server_detail['properties']['addresses']['patternProperties'][ '^[a-zA-Z0-9-_.]+$']['items']['properties'].update({ 'OS-EXT-IPS:type': {'type': 'string'}, 'OS-EXT-IPS-MAC:mac_addr': parameter_types.mac_address}) # NOTE(gmann): Update OS-EXT-IPS:type and OS-EXT-IPS-MAC:mac_addr # attributes in server address. Those are API extension, # and some environments return a response without # these attributes. So they are not 'required'. get_server = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'server': server_detail }, 'additionalProperties': False, 'required': ['server'] } } list_servers_detail = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'servers': { 'type': 'array', 'items': server_detail }, 'servers_links': parameter_types.links }, 'additionalProperties': False, # NOTE(gmann): servers_links attribute is not necessary to be # present always So it is not 'required'. 'required': ['servers'] } } rebuild_server = copy.deepcopy(update_server) rebuild_server['status_code'] = [202] rebuild_server_with_admin_pass = copy.deepcopy(rebuild_server) rebuild_server_with_admin_pass['response_body']['properties']['server'][ 'properties'].update({'adminPass': {'type': 'string'}}) rebuild_server_with_admin_pass['response_body']['properties']['server'][ 'required'].append('adminPass') rescue_server = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'adminPass': {'type': 'string'} }, 'additionalProperties': False, 'required': ['adminPass'] } } list_virtual_interfaces = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'virtual_interfaces': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'mac_address': parameter_types.mac_address, 'OS-EXT-VIF-NET:net_id': {'type': 'string'} }, 'additionalProperties': False, # 'OS-EXT-VIF-NET:net_id' is API extension So it is # not defined as 'required' 'required': ['id', 'mac_address'] } } }, 'additionalProperties': False, 'required': ['virtual_interfaces'] } } common_attach_volume_info = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'device': {'type': 'string'}, 'volumeId': {'type': 'string'}, 'serverId': {'type': ['integer', 'string']} }, 'additionalProperties': False, 'required': ['id', 'device', 'volumeId', 'serverId'] } attach_volume = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'volumeAttachment': common_attach_volume_info }, 'additionalProperties': False, 'required': ['volumeAttachment'] } } detach_volume = { 'status_code': [202] } show_volume_attachment = copy.deepcopy(attach_volume) show_volume_attachment['response_body']['properties'][ 'volumeAttachment']['properties'].update({'serverId': {'type': 'string'}}) list_volume_attachments = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'volumeAttachments': { 'type': 'array', 'items': common_attach_volume_info } }, 'additionalProperties': False, 'required': ['volumeAttachments'] } } list_volume_attachments['response_body']['properties'][ 'volumeAttachments']['items']['properties'].update( {'serverId': {'type': 'string'}}) list_addresses_by_network = { 'status_code': [200], 'response_body': parameter_types.addresses } list_addresses = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'addresses': parameter_types.addresses }, 'additionalProperties': False, 'required': ['addresses'] } } common_server_group = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'name': {'type': 'string'}, 'policies': { 'type': 'array', 'items': {'type': 'string'} }, # 'members' attribute contains the array of instance's UUID of # instances present in server group 'members': { 'type': 'array', 'items': {'type': 'string'} }, 'metadata': {'type': 'object'} }, 'additionalProperties': False, 'required': ['id', 'name', 'policies', 'members', 'metadata'] } create_show_server_group = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'server_group': common_server_group }, 'additionalProperties': False, 'required': ['server_group'] } } delete_server_group = { 'status_code': [204] } list_server_groups = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'server_groups': { 'type': 'array', 'items': common_server_group } }, 'additionalProperties': False, 'required': ['server_groups'] } } instance_actions = { 'type': 'object', 'properties': { 'action': {'type': 'string'}, 'request_id': {'type': 'string'}, 'user_id': {'type': 'string'}, 'project_id': {'type': 'string'}, 'start_time': {'type': 'string'}, 'message': {'type': ['string', 'null']}, 'instance_uuid': {'type': 'string'} }, 'additionalProperties': False, 'required': ['action', 'request_id', 'user_id', 'project_id', 'start_time', 'message', 'instance_uuid'] } instance_action_events = { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'event': {'type': 'string'}, 'start_time': {'type': 'string'}, 'finish_time': {'type': 'string'}, 'result': {'type': 'string'}, 'traceback': {'type': ['string', 'null']} }, 'additionalProperties': False, 'required': ['event', 'start_time', 'finish_time', 'result', 'traceback'] } } list_instance_actions = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'instanceActions': { 'type': 'array', 'items': instance_actions } }, 'additionalProperties': False, 'required': ['instanceActions'] } } instance_actions_with_events = copy.deepcopy(instance_actions) instance_actions_with_events['properties'].update({ 'events': instance_action_events}) # 'events' does not come in response body always so it is not # defined as 'required' show_instance_action = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'instanceAction': instance_actions_with_events }, 'additionalProperties': False, 'required': ['instanceAction'] } } show_password = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'password': {'type': 'string'} }, 'additionalProperties': False, 'required': ['password'] } } get_vnc_console = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'console': { 'type': 'object', 'properties': { 'type': {'type': 'string'}, 'url': { 'type': 'string', 'format': 'uri' } }, 'additionalProperties': False, 'required': ['type', 'url'] } }, 'additionalProperties': False, 'required': ['console'] } } get_console_output = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'output': {'type': 'string'} }, 'additionalProperties': False, 'required': ['output'] } } set_server_metadata = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'metadata': { 'type': 'object', 'patternProperties': { '^.+$': {'type': 'string'} } } }, 'additionalProperties': False, 'required': ['metadata'] } } list_server_metadata = copy.deepcopy(set_server_metadata) update_server_metadata = copy.deepcopy(set_server_metadata) delete_server_metadata_item = { 'status_code': [204] } set_show_server_metadata_item = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'meta': { 'type': 'object', 'patternProperties': { '^.+$': {'type': 'string'} } } }, 'additionalProperties': False, 'required': ['meta'] } } server_actions_common_schema = { 'status_code': [202] } server_actions_delete_password = { 'status_code': [204] } server_actions_confirm_resize = copy.deepcopy( server_actions_delete_password) update_attached_volume = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/services.py000066400000000000000000000045141266406763200266330ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. list_services = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'services': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': ['integer', 'string'], 'pattern': '^[a-zA-Z!]*@[0-9]+$'}, 'zone': {'type': 'string'}, 'host': {'type': 'string'}, 'state': {'type': 'string'}, 'binary': {'type': 'string'}, 'status': {'type': 'string'}, 'updated_at': {'type': ['string', 'null']}, 'disabled_reason': {'type': ['string', 'null']} }, 'additionalProperties': False, 'required': ['id', 'zone', 'host', 'state', 'binary', 'status', 'updated_at', 'disabled_reason'] } } }, 'additionalProperties': False, 'required': ['services'] } } enable_disable_service = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'service': { 'type': 'object', 'properties': { 'status': {'type': 'string'}, 'binary': {'type': 'string'}, 'host': {'type': 'string'} }, 'additionalProperties': False, 'required': ['status', 'binary', 'host'] } }, 'additionalProperties': False, 'required': ['service'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/snapshots.py000066400000000000000000000034251266406763200270320ustar00rootroot00000000000000# Copyright 2015 Fujitsu(fnst) Corporation # 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. common_snapshot_info = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'volumeId': {'type': 'string'}, 'status': {'type': 'string'}, 'size': {'type': 'integer'}, 'createdAt': {'type': 'string'}, 'displayName': {'type': ['string', 'null']}, 'displayDescription': {'type': ['string', 'null']} }, 'additionalProperties': False, 'required': ['id', 'volumeId', 'status', 'size', 'createdAt', 'displayName', 'displayDescription'] } create_get_snapshot = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'snapshot': common_snapshot_info }, 'additionalProperties': False, 'required': ['snapshot'] } } list_snapshots = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'snapshots': { 'type': 'array', 'items': common_snapshot_info } }, 'additionalProperties': False, 'required': ['snapshots'] } } delete_snapshot = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/tenant_networks.py000066400000000000000000000026771266406763200302450ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. 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. param_network = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'cidr': {'type': ['string', 'null']}, 'label': {'type': 'string'} }, 'additionalProperties': False, 'required': ['id', 'cidr', 'label'] } list_tenant_networks = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'networks': { 'type': 'array', 'items': param_network } }, 'additionalProperties': False, 'required': ['networks'] } } get_tenant_network = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'network': param_network }, 'additionalProperties': False, 'required': ['network'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/tenant_usages.py000066400000000000000000000057751266406763200276620ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy _server_usages = { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'ended_at': { 'oneOf': [ {'type': 'string'}, {'type': 'null'} ] }, 'flavor': {'type': 'string'}, 'hours': {'type': 'number'}, 'instance_id': {'type': 'string'}, 'local_gb': {'type': 'integer'}, 'memory_mb': {'type': 'integer'}, 'name': {'type': 'string'}, 'started_at': {'type': 'string'}, 'state': {'type': 'string'}, 'tenant_id': {'type': 'string'}, 'uptime': {'type': 'integer'}, 'vcpus': {'type': 'integer'}, }, 'required': ['ended_at', 'flavor', 'hours', 'instance_id', 'local_gb', 'memory_mb', 'name', 'started_at', 'state', 'tenant_id', 'uptime', 'vcpus'] } } _tenant_usage_list = { 'type': 'object', 'properties': { 'server_usages': _server_usages, 'start': {'type': 'string'}, 'stop': {'type': 'string'}, 'tenant_id': {'type': 'string'}, 'total_hours': {'type': 'number'}, 'total_local_gb_usage': {'type': 'number'}, 'total_memory_mb_usage': {'type': 'number'}, 'total_vcpus_usage': {'type': 'number'}, }, 'required': ['start', 'stop', 'tenant_id', 'total_hours', 'total_local_gb_usage', 'total_memory_mb_usage', 'total_vcpus_usage'] } # 'required' of get_tenant is different from list_tenant's. _tenant_usage_get = copy.deepcopy(_tenant_usage_list) _tenant_usage_get['required'] = ['server_usages', 'start', 'stop', 'tenant_id', 'total_hours', 'total_local_gb_usage', 'total_memory_mb_usage', 'total_vcpus_usage'] list_tenant_usage = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'tenant_usages': { 'type': 'array', 'items': _tenant_usage_list } }, 'required': ['tenant_usages'] } } get_tenant_usage = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'tenant_usage': _tenant_usage_get }, 'required': ['tenant_usage'] } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/versions.py000066400000000000000000000061461266406763200266630ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy _version = { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'links': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'href': {'type': 'string', 'format': 'uri'}, 'rel': {'type': 'string'}, 'type': {'type': 'string'}, }, 'required': ['href', 'rel'], 'additionalProperties': False } }, 'status': {'type': 'string'}, 'updated': {'type': 'string', 'format': 'date-time'}, 'version': {'type': 'string'}, 'min_version': {'type': 'string'}, 'media-types': { 'type': 'array', 'properties': { 'base': {'type': 'string'}, 'type': {'type': 'string'}, } }, }, # NOTE: version and min_version have been added since Kilo, # so they should not be required. # NOTE(sdague): media-types only shows up in single version requests. 'required': ['id', 'links', 'status', 'updated'], 'additionalProperties': False } list_versions = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'versions': { 'type': 'array', 'items': _version } }, 'required': ['versions'], 'additionalProperties': False } } _detail_get_version = copy.deepcopy(_version) _detail_get_version['properties'].pop('min_version') _detail_get_version['properties'].pop('version') _detail_get_version['properties'].pop('updated') _detail_get_version['properties']['media-types'] = { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'base': {'type': 'string'}, 'type': {'type': 'string'} } } } _detail_get_version['required'] = ['id', 'links', 'status', 'media-types'] get_version = { 'status_code': [300], 'response_body': { 'type': 'object', 'properties': { 'choices': { 'type': 'array', 'items': _detail_get_version } }, 'required': ['choices'], 'additionalProperties': False } } get_one_version = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'version': _version }, 'additionalProperties': False } } tempest-lib-1.0.0/tempest_lib/api_schema/response/compute/v2_1/volumes.py000066400000000000000000000124631266406763200265040ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. 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. create_get_volume = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'volume': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'status': {'type': 'string'}, 'displayName': {'type': ['string', 'null']}, 'availabilityZone': {'type': 'string'}, 'createdAt': {'type': 'string'}, 'displayDescription': {'type': ['string', 'null']}, 'volumeType': {'type': ['string', 'null']}, 'snapshotId': {'type': ['string', 'null']}, 'metadata': {'type': 'object'}, 'size': {'type': 'integer'}, 'attachments': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'device': {'type': 'string'}, 'volumeId': {'type': 'string'}, 'serverId': {'type': 'string'} }, 'additionalProperties': False, # NOTE- If volume is not attached to any server # then, 'attachments' attributes comes as array # with empty objects "[{}]" due to that elements # of 'attachments' cannot defined as 'required'. # If it would come as empty array "[]" then, # those elements can be defined as 'required'. } } }, 'additionalProperties': False, 'required': ['id', 'status', 'displayName', 'availabilityZone', 'createdAt', 'displayDescription', 'volumeType', 'snapshotId', 'metadata', 'size', 'attachments'] } }, 'additionalProperties': False, 'required': ['volume'] } } list_volumes = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'volumes': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'status': {'type': 'string'}, 'displayName': {'type': ['string', 'null']}, 'availabilityZone': {'type': 'string'}, 'createdAt': {'type': 'string'}, 'displayDescription': {'type': ['string', 'null']}, 'volumeType': {'type': ['string', 'null']}, 'snapshotId': {'type': ['string', 'null']}, 'metadata': {'type': 'object'}, 'size': {'type': 'integer'}, 'attachments': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'id': {'type': 'string'}, 'device': {'type': 'string'}, 'volumeId': {'type': 'string'}, 'serverId': {'type': 'string'} }, 'additionalProperties': False, # NOTE- If volume is not attached to any server # then, 'attachments' attributes comes as array # with empty object "[{}]" due to that elements # of 'attachments' cannot defined as 'required' # If it would come as empty array "[]" then, # those elements can be defined as 'required'. } } }, 'additionalProperties': False, 'required': ['id', 'status', 'displayName', 'availabilityZone', 'createdAt', 'displayDescription', 'volumeType', 'snapshotId', 'metadata', 'size', 'attachments'] } } }, 'additionalProperties': False, 'required': ['volumes'] } } delete_volume = { 'status_code': [202] } tempest-lib-1.0.0/tempest_lib/auth.py000066400000000000000000000630741266406763200176050ustar00rootroot00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # 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 copy import datetime import re from oslo_log import log as logging import six from six.moves.urllib import parse as urlparse from tempest_lib import exceptions from tempest_lib.services.identity.v2 import token_client as json_v2id from tempest_lib.services.identity.v3 import token_client as json_v3id ISO8601_FLOAT_SECONDS = '%Y-%m-%dT%H:%M:%S.%fZ' ISO8601_INT_SECONDS = '%Y-%m-%dT%H:%M:%SZ' LOG = logging.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) class AuthProvider(object): """Provide authentication""" def __init__(self, credentials): """Auth provider __init__ :param credentials: credentials for authentication """ if self.check_credentials(credentials): self.credentials = credentials else: if isinstance(credentials, Credentials): password = credentials.get('password') message = "Credentials are: " + str(credentials) if password is None: message += " Password is not defined." else: message += " Password is defined." raise exceptions.InvalidCredentials(message) else: raise TypeError("credentials object is of type %s, which is" " not a valid Credentials object type." % credentials.__class__.__name__) self.cache = None self.alt_auth_data = None self.alt_part = None def __str__(self): return "Creds :{creds}, cached auth data: {cache}".format( creds=self.credentials, cache=self.cache) @abc.abstractmethod def _decorate_request(self, filters, method, url, headers=None, body=None, auth_data=None): """Decorate request with authentication data""" return @abc.abstractmethod def _get_auth(self): return @abc.abstractmethod def _fill_credentials(self, auth_data_body): return def fill_credentials(self): """Fill credentials object with data from auth""" auth_data = self.get_auth() self._fill_credentials(auth_data[1]) return self.credentials @classmethod def check_credentials(cls, credentials): """Verify credentials are valid.""" return isinstance(credentials, Credentials) and credentials.is_valid() @property def auth_data(self): return self.get_auth() @auth_data.deleter def auth_data(self): self.clear_auth() def get_auth(self): """Returns auth from cache if available, else auth first""" if self.cache is None or self.is_expired(self.cache): self.set_auth() return self.cache def set_auth(self): """Forces setting auth. Forces setting auth, ignores cache if it exists. Refills credentials """ self.cache = self._get_auth() self._fill_credentials(self.cache[1]) def clear_auth(self): """Clear access cache Can be called to clear the access cache so that next request will fetch a new token and base_url. """ self.cache = None self.credentials.reset() @abc.abstractmethod def is_expired(self, auth_data): return def auth_request(self, method, url, headers=None, body=None, filters=None): """Obtains auth data and decorates a request with that. :param method: HTTP method of the request :param url: relative URL of the request (path) :param headers: HTTP headers of the request :param body: HTTP body in case of POST / PUT :param filters: select a base URL out of the catalog :returns a Tuple (url, headers, body) """ orig_req = dict(url=url, headers=headers, body=body) auth_url, auth_headers, auth_body = self._decorate_request( filters, method, url, headers, body) auth_req = dict(url=auth_url, headers=auth_headers, body=auth_body) # Overwrite part if the request if it has been requested if self.alt_part is not None: if self.alt_auth_data is not None: alt_url, alt_headers, alt_body = self._decorate_request( filters, method, url, headers, body, auth_data=self.alt_auth_data) alt_auth_req = dict(url=alt_url, headers=alt_headers, body=alt_body) if auth_req[self.alt_part] == alt_auth_req[self.alt_part]: raise exceptions.BadAltAuth(part=self.alt_part) auth_req[self.alt_part] = alt_auth_req[self.alt_part] else: # If the requested part is not affected by auth, we are # not altering auth as expected, raise an exception if auth_req[self.alt_part] == orig_req[self.alt_part]: raise exceptions.BadAltAuth(part=self.alt_part) # If alt auth data is None, skip auth in the requested part auth_req[self.alt_part] = orig_req[self.alt_part] # Next auth request will be normal, unless otherwise requested self.reset_alt_auth_data() return auth_req['url'], auth_req['headers'], auth_req['body'] def reset_alt_auth_data(self): """Configure auth provider to provide valid authentication data""" self.alt_part = None self.alt_auth_data = None def set_alt_auth_data(self, request_part, auth_data): """Alternate auth data on next request Configure auth provider to provide alt authentication data on a part of the *next* auth_request. If credentials are None, set invalid data. :param request_part: request part to contain invalid auth: url, headers, body :param auth_data: alternative auth_data from which to get the invalid data to be injected """ self.alt_part = request_part self.alt_auth_data = auth_data @abc.abstractmethod def base_url(self, filters, auth_data=None): """Extracts the base_url based on provided filters""" return class KeystoneAuthProvider(AuthProvider): EXPIRY_DATE_FORMATS = (ISO8601_FLOAT_SECONDS, ISO8601_INT_SECONDS) token_expiry_threshold = datetime.timedelta(seconds=60) def __init__(self, credentials, auth_url, disable_ssl_certificate_validation=None, ca_certs=None, trace_requests=None): super(KeystoneAuthProvider, self).__init__(credentials) self.dsvm = disable_ssl_certificate_validation self.ca_certs = ca_certs self.trace_requests = trace_requests self.auth_client = self._auth_client(auth_url) def _decorate_request(self, filters, method, url, headers=None, body=None, auth_data=None): if auth_data is None: auth_data = self.auth_data token, _ = auth_data base_url = self.base_url(filters=filters, auth_data=auth_data) # build authenticated request # returns new request, it does not touch the original values _headers = copy.deepcopy(headers) if headers is not None else {} _headers['X-Auth-Token'] = str(token) if url is None or url == "": _url = base_url else: # Join base URL and url, and remove multiple contiguous slashes _url = "/".join([base_url, url]) parts = [x for x in urlparse.urlparse(_url)] parts[2] = re.sub("/{2,}", "/", parts[2]) _url = urlparse.urlunparse(parts) # no change to method or body return str(_url), _headers, body @abc.abstractmethod def _auth_client(self): return @abc.abstractmethod def _auth_params(self): return def _get_auth(self): # Bypasses the cache auth_func = getattr(self.auth_client, 'get_token') auth_params = self._auth_params() # returns token, auth_data token, auth_data = auth_func(**auth_params) return token, auth_data def _parse_expiry_time(self, expiry_string): expiry = None for date_format in self.EXPIRY_DATE_FORMATS: try: expiry = datetime.datetime.strptime( expiry_string, date_format) except ValueError: pass if expiry is None: raise ValueError( "time data '{data}' does not match any of the" "expected formats: {formats}".format( data=expiry_string, formats=self.EXPIRY_DATE_FORMATS)) return expiry def get_token(self): return self.auth_data[0] class KeystoneV2AuthProvider(KeystoneAuthProvider): def _auth_client(self, auth_url): return json_v2id.TokenClient( auth_url, disable_ssl_certificate_validation=self.dsvm, ca_certs=self.ca_certs, trace_requests=self.trace_requests) def _auth_params(self): return dict( user=self.credentials.username, password=self.credentials.password, tenant=self.credentials.tenant_name, auth_data=True) def _fill_credentials(self, auth_data_body): tenant = auth_data_body['token']['tenant'] user = auth_data_body['user'] if self.credentials.tenant_name is None: self.credentials.tenant_name = tenant['name'] if self.credentials.tenant_id is None: self.credentials.tenant_id = tenant['id'] if self.credentials.username is None: self.credentials.username = user['name'] if self.credentials.user_id is None: self.credentials.user_id = user['id'] def base_url(self, filters, auth_data=None): """Base URL from catalog Filters can be: - service: compute, image, etc - region: the service region - endpoint_type: adminURL, publicURL, internalURL - api_version: replace catalog version with this - skip_path: take just the base URL """ if auth_data is None: auth_data = self.auth_data token, _auth_data = auth_data service = filters.get('service') region = filters.get('region') endpoint_type = filters.get('endpoint_type', 'publicURL') if service is None: raise exceptions.EndpointNotFound("No service provided") _base_url = None for ep in _auth_data['serviceCatalog']: if ep["type"] == service: for _ep in ep['endpoints']: if region is not None and _ep['region'] == region: _base_url = _ep.get(endpoint_type) if not _base_url: # No region matching, use the first _base_url = ep['endpoints'][0].get(endpoint_type) break if _base_url is None: raise exceptions.EndpointNotFound(service) parts = urlparse.urlparse(_base_url) if filters.get('api_version', None) is not None: path = "/" + filters['api_version'] noversion_path = "/".join(parts.path.split("/")[2:]) if noversion_path != "": path += "/" + noversion_path _base_url = _base_url.replace(parts.path, path) if filters.get('skip_path', None) is not None and parts.path != '': _base_url = _base_url.replace(parts.path, "/") return _base_url def is_expired(self, auth_data): _, access = auth_data expiry = self._parse_expiry_time(access['token']['expires']) return (expiry - self.token_expiry_threshold <= datetime.datetime.utcnow()) class KeystoneV3AuthProvider(KeystoneAuthProvider): def _auth_client(self, auth_url): return json_v3id.V3TokenClient( auth_url, disable_ssl_certificate_validation=self.dsvm, ca_certs=self.ca_certs, trace_requests=self.trace_requests) def _auth_params(self): return dict( user_id=self.credentials.user_id, username=self.credentials.username, password=self.credentials.password, project_id=self.credentials.project_id, project_name=self.credentials.project_name, user_domain_id=self.credentials.user_domain_id, user_domain_name=self.credentials.user_domain_name, project_domain_id=self.credentials.project_domain_id, project_domain_name=self.credentials.project_domain_name, domain_id=self.credentials.domain_id, domain_name=self.credentials.domain_name, auth_data=True) def _fill_credentials(self, auth_data_body): # project or domain, depending on the scope project = auth_data_body.get('project', None) domain = auth_data_body.get('domain', None) # user is always there user = auth_data_body['user'] # Set project fields if project is not None: if self.credentials.project_name is None: self.credentials.project_name = project['name'] if self.credentials.project_id is None: self.credentials.project_id = project['id'] if self.credentials.project_domain_id is None: self.credentials.project_domain_id = project['domain']['id'] if self.credentials.project_domain_name is None: self.credentials.project_domain_name = ( project['domain']['name']) # Set domain fields if domain is not None: if self.credentials.domain_id is None: self.credentials.domain_id = domain['id'] if self.credentials.domain_name is None: self.credentials.domain_name = domain['name'] # Set user fields if self.credentials.username is None: self.credentials.username = user['name'] if self.credentials.user_id is None: self.credentials.user_id = user['id'] if self.credentials.user_domain_id is None: self.credentials.user_domain_id = user['domain']['id'] if self.credentials.user_domain_name is None: self.credentials.user_domain_name = user['domain']['name'] def base_url(self, filters, auth_data=None): """Base URL from catalog Filters can be: - service: compute, image, etc - region: the service region - endpoint_type: adminURL, publicURL, internalURL - api_version: replace catalog version with this - skip_path: take just the base URL """ if auth_data is None: auth_data = self.auth_data token, _auth_data = auth_data service = filters.get('service') region = filters.get('region') endpoint_type = filters.get('endpoint_type', 'public') if service is None: raise exceptions.EndpointNotFound("No service provided") if 'URL' in endpoint_type: endpoint_type = endpoint_type.replace('URL', '') _base_url = None catalog = _auth_data['catalog'] # Select entries with matching service type service_catalog = [ep for ep in catalog if ep['type'] == service] if len(service_catalog) > 0: service_catalog = service_catalog[0]['endpoints'] else: # No matching service raise exceptions.EndpointNotFound(service) # Filter by endpoint type (interface) filtered_catalog = [ep for ep in service_catalog if ep['interface'] == endpoint_type] if len(filtered_catalog) == 0: # No matching type, keep all and try matching by region at least filtered_catalog = service_catalog # Filter by region filtered_catalog = [ep for ep in filtered_catalog if ep['region'] == region] if len(filtered_catalog) == 0: # No matching region, take the first endpoint filtered_catalog = [service_catalog[0]] # There should be only one match. If not take the first. _base_url = filtered_catalog[0].get('url', None) if _base_url is None: raise exceptions.EndpointNotFound(service) parts = urlparse.urlparse(_base_url) if filters.get('api_version', None) is not None: path = "/" + filters['api_version'] noversion_path = "/".join(parts.path.split("/")[2:]) if noversion_path != "": path += "/" + noversion_path _base_url = _base_url.replace(parts.path, path) if filters.get('skip_path', None) is not None: _base_url = _base_url.replace(parts.path, "/") return _base_url def is_expired(self, auth_data): _, access = auth_data expiry = self._parse_expiry_time(access['expires_at']) return (expiry - self.token_expiry_threshold <= datetime.datetime.utcnow()) def is_identity_version_supported(identity_version): return identity_version in IDENTITY_VERSION def get_credentials(auth_url, fill_in=True, identity_version='v2', disable_ssl_certificate_validation=None, ca_certs=None, trace_requests=None, **kwargs): """Builds a credentials object based on the configured auth_version :param auth_url (string): Full URI of the OpenStack Identity API(Keystone) which is used to fetch the token from Identity service. :param fill_in (boolean): obtain a token and fill in all credential details provided by the identity service. When fill_in is not specified, credentials are not validated. Validation can be invoked by invoking ``is_valid()`` :param identity_version (string): identity API version is used to select the matching auth provider and credentials class :param disable_ssl_certificate_validation: whether to enforce SSL certificate validation in SSL API requests to the auth system :param ca_certs: CA certificate bundle for validation of certificates in SSL API requests to the auth system :param trace_requests: trace in log API requests to the auth system :param kwargs (dict): Dict of credential key/value pairs Examples: Returns credentials from the provided parameters: >>> get_credentials(username='foo', password='bar') Returns credentials including IDs: >>> get_credentials(username='foo', password='bar', fill_in=True) """ if not is_identity_version_supported(identity_version): raise exceptions.InvalidIdentityVersion( identity_version=identity_version) credential_class, auth_provider_class = IDENTITY_VERSION.get( identity_version) creds = credential_class(**kwargs) # Fill in the credentials fields that were not specified if fill_in: dsvm = disable_ssl_certificate_validation auth_provider = auth_provider_class( creds, auth_url, disable_ssl_certificate_validation=dsvm, ca_certs=ca_certs, trace_requests=trace_requests) creds = auth_provider.fill_credentials() return creds class Credentials(object): """Set of credentials for accessing OpenStack services ATTRIBUTES: list of valid class attributes representing credentials. """ ATTRIBUTES = [] def __init__(self, **kwargs): """Enforce the available attributes at init time (only). Additional attributes can still be set afterwards if tests need to do so. """ self._initial = kwargs self._apply_credentials(kwargs) def _apply_credentials(self, attr): for key in attr.keys(): if key in self.ATTRIBUTES: setattr(self, key, attr[key]) else: msg = '%s is not a valid attr for %s' % (key, self.__class__) raise exceptions.InvalidCredentials(msg) def __str__(self): """Represent only attributes included in self.ATTRIBUTES""" attrs = [attr for attr in self.ATTRIBUTES if attr is not 'password'] _repr = dict((k, getattr(self, k)) for k in attrs) return str(_repr) def __eq__(self, other): """Credentials are equal if attributes in self.ATTRIBUTES are equal""" return str(self) == str(other) def __getattr__(self, key): # If an attribute is set, __getattr__ is not invoked # If an attribute is not set, and it is a known one, return None if key in self.ATTRIBUTES: return None else: raise AttributeError def __delitem__(self, key): # For backwards compatibility, support dict behaviour if key in self.ATTRIBUTES: delattr(self, key) else: raise AttributeError def get(self, item, default=None): # In this patch act as dict for backward compatibility try: return getattr(self, item) except AttributeError: return default def get_init_attributes(self): return self._initial.keys() def is_valid(self): raise NotImplementedError def reset(self): # First delete all known attributes for key in self.ATTRIBUTES: if getattr(self, key) is not None: delattr(self, key) # Then re-apply initial setup self._apply_credentials(self._initial) class KeystoneV2Credentials(Credentials): ATTRIBUTES = ['username', 'password', 'tenant_name', 'user_id', 'tenant_id'] def is_valid(self): """Check of credentials (no API call) Minimum set of valid credentials, are username and password. Tenant is optional. """ return None not in (self.username, self.password) class KeystoneV3Credentials(Credentials): """Credentials suitable for the Keystone Identity V3 API""" ATTRIBUTES = ['domain_id', 'domain_name', 'password', 'username', 'project_domain_id', 'project_domain_name', 'project_id', 'project_name', 'tenant_id', 'tenant_name', 'user_domain_id', 'user_domain_name', 'user_id'] def __setattr__(self, key, value): parent = super(KeystoneV3Credentials, self) # for tenant_* set both project and tenant if key == 'tenant_id': parent.__setattr__('project_id', value) elif key == 'tenant_name': parent.__setattr__('project_name', value) # for project_* set both project and tenant if key == 'project_id': parent.__setattr__('tenant_id', value) elif key == 'project_name': parent.__setattr__('tenant_name', value) # for *_domain_* set both user and project if not set yet if key == 'user_domain_id': if self.project_domain_id is None: parent.__setattr__('project_domain_id', value) if key == 'project_domain_id': if self.user_domain_id is None: parent.__setattr__('user_domain_id', value) if key == 'user_domain_name': if self.project_domain_name is None: parent.__setattr__('project_domain_name', value) if key == 'project_domain_name': if self.user_domain_name is None: parent.__setattr__('user_domain_name', value) # support domain_name coming from config if key == 'domain_name': parent.__setattr__('user_domain_name', value) parent.__setattr__('project_domain_name', value) # finally trigger default behaviour for all attributes parent.__setattr__(key, value) def is_valid(self): """Check of credentials (no API call) Valid combinations of v3 credentials (excluding token, scope) - User id, password (optional domain) - User name, password and its domain id/name For the scope, valid combinations are: - None - Project id (optional domain) - Project name and its domain id/name - Domain id - Domain name """ valid_user_domain = any( [self.user_domain_id is not None, self.user_domain_name is not None]) valid_project_domain = any( [self.project_domain_id is not None, self.project_domain_name is not None]) valid_user = any( [self.user_id is not None, self.username is not None and valid_user_domain]) valid_project_scope = any( [self.project_name is None and self.project_id is None, self.project_id is not None, self.project_name is not None and valid_project_domain]) valid_domain_scope = any( [self.domain_id is None and self.domain_name is None, self.domain_id or self.domain_name]) return all([self.password is not None, valid_user, valid_project_scope and valid_domain_scope]) IDENTITY_VERSION = {'v2': (KeystoneV2Credentials, KeystoneV2AuthProvider), 'v3': (KeystoneV3Credentials, KeystoneV3AuthProvider)} tempest-lib-1.0.0/tempest_lib/base.py000066400000000000000000000056141266406763200175520ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import os import fixtures import testtools LOG = logging.getLogger(__name__) class BaseTestCase(testtools.testcase.WithAttributes, testtools.TestCase): setUpClassCalled = False # NOTE(sdague): log_format is defined inline here instead of using the oslo # default because going through the config path recouples config to the # stress tests too early, and depending on testr order will fail unit tests log_format = ('%(asctime)s %(process)d %(levelname)-8s ' '[%(name)s] %(message)s') @classmethod def setUpClass(cls): if hasattr(super(BaseTestCase, cls), 'setUpClass'): super(BaseTestCase, cls).setUpClass() cls.setUpClassCalled = True @classmethod def tearDownClass(cls): if hasattr(super(BaseTestCase, cls), 'tearDownClass'): super(BaseTestCase, cls).tearDownClass() def setUp(self): super(BaseTestCase, self).setUp() if not self.setUpClassCalled: raise RuntimeError("setUpClass does not calls the super's" "setUpClass in the " + self.__class__.__name__) test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: test_timeout = int(test_timeout) except ValueError: test_timeout = 0 if test_timeout > 0: self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or os.environ.get('OS_STDOUT_CAPTURE') == '1'): stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or os.environ.get('OS_STDERR_CAPTURE') == '1'): stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) if (os.environ.get('OS_LOG_CAPTURE') != 'False' and os.environ.get('OS_LOG_CAPTURE') != '0'): self.useFixture(fixtures.LoggerFixture(nuke_handlers=False, format=self.log_format, level=None)) tempest-lib-1.0.0/tempest_lib/cli/000077500000000000000000000000001266406763200170275ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/cli/__init__.py000066400000000000000000000000001266406763200211260ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/cli/base.py000066400000000000000000000405501266406763200203170ustar00rootroot00000000000000# 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 logging import os import shlex import subprocess import six from tempest_lib import base import tempest_lib.cli.output_parser from tempest_lib import exceptions LOG = logging.getLogger(__name__) def execute(cmd, action, flags='', params='', fail_ok=False, merge_stderr=False, cli_dir='/usr/bin'): """Executes specified command for the given action. :param cmd: command to be executed :type cmd: string :param action: string of the cli command to run :type action: string :param flags: any optional cli flags to use :type flags: string :param params: string of any optional positional args to use :type params: string :param fail_ok: boolean if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param merge_stderr: boolean if True the stderr buffer is merged into stdout :type merge_stderr: boolean :param cli_dir: The path where the cmd can be executed :type cli_dir: string """ cmd = ' '.join([os.path.join(cli_dir, cmd), flags, action, params]) LOG.info("running: '%s'" % cmd) if six.PY2: cmd = cmd.encode('utf-8') cmd = shlex.split(cmd) result = '' result_err = '' stdout = subprocess.PIPE stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr) result, result_err = proc.communicate() if not fail_ok and proc.returncode != 0: raise exceptions.CommandFailed(proc.returncode, cmd, result, result_err) if six.PY2: return result else: return os.fsdecode(result) class CLIClient(object): """Class to use OpenStack official python client CLI's with auth :param username: The username to authenticate with :type username: string :param password: The password to authenticate with :type password: string :param tenant_name: The name of the tenant to use with the client calls :type tenant_name: string :param uri: The auth uri for the OpenStack Deployment :type uri: string :param cli_dir: The path where the python client binaries are installed. defaults to /usr/bin :type cli_dir: string :param insecure: if True, --insecure is passed to python client binaries. :type insecure: boolean """ def __init__(self, username='', password='', tenant_name='', uri='', cli_dir='', insecure=False, *args, **kwargs): """Initialize a new CLIClient object.""" super(CLIClient, self).__init__() self.cli_dir = cli_dir if cli_dir else '/usr/bin' self.username = username self.tenant_name = tenant_name self.password = password self.uri = uri self.insecure = insecure def nova(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): """Executes nova command for the given action. :param action: the cli command to run using nova :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'nova', action, flags, params, fail_ok, merge_stderr) def nova_manage(self, action, flags='', params='', fail_ok=False, merge_stderr=False): """Executes nova-manage command for the given action. :param action: the cli command to run using nova-manage :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ return execute( 'nova-manage', action, flags, params, fail_ok, merge_stderr, self.cli_dir) def keystone(self, action, flags='', params='', fail_ok=False, merge_stderr=False): """Executes keystone command for the given action. :param action: the cli command to run using keystone :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ return self.cmd_with_auth( 'keystone', action, flags, params, fail_ok, merge_stderr) def glance(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): """Executes glance command for the given action. :param action: the cli command to run using glance :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --os-endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'glance', action, flags, params, fail_ok, merge_stderr) def ceilometer(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): """Executes ceilometer command for the given action. :param action: the cli command to run using ceilometer :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --os-endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'ceilometer', action, flags, params, fail_ok, merge_stderr) def heat(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): """Executes heat command for the given action. :param action: the cli command to run using heat :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --os-endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'heat', action, flags, params, fail_ok, merge_stderr) def cinder(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): """Executes cinder command for the given action. :param action: the cli command to run using cinder :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'cinder', action, flags, params, fail_ok, merge_stderr) def swift(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): """Executes swift command for the given action. :param action: the cli command to run using swift :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --os-endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'swift', action, flags, params, fail_ok, merge_stderr) def neutron(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): """Executes neutron command for the given action. :param action: the cli command to run using neutron :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'neutron', action, flags, params, fail_ok, merge_stderr) def sahara(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=True): """Executes sahara command for the given action. :param action: the cli command to run using sahara :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ flags += ' --endpoint-type %s' % endpoint_type return self.cmd_with_auth( 'sahara', action, flags, params, fail_ok, merge_stderr) def openstack(self, action, flags='', params='', fail_ok=False, merge_stderr=False): """Executes openstack command for the given action. :param action: the cli command to run using openstack :type action: string :param flags: any optional cli flags to use :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ return self.cmd_with_auth( 'openstack', action, flags, params, fail_ok, merge_stderr) def cmd_with_auth(self, cmd, action, flags='', params='', fail_ok=False, merge_stderr=False): """Executes given command with auth attributes appended. :param cmd: command to be executed :type cmd: string :param action: command on cli to run :type action: string :param flags: optional cli flags to use :type flags: string :param params: optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean """ creds = ('--os-username %s --os-tenant-name %s --os-password %s ' '--os-auth-url %s' % (self.username, self.tenant_name, self.password, self.uri)) if self.insecure: flags = creds + ' --insecure ' + flags else: flags = creds + ' ' + flags return execute(cmd, action, flags, params, fail_ok, merge_stderr, self.cli_dir) class ClientTestBase(base.BaseTestCase): """Base test class for testing the OpenStack client CLI interfaces.""" def setUp(self): super(ClientTestBase, self).setUp() self.clients = self._get_clients() self.parser = tempest_lib.cli.output_parser def _get_clients(self): """Abstract method to initialize CLIClient object. This method must be overloaded in child test classes. It should be used to initialize the CLIClient object with the appropriate credentials during the setUp() phase of tests. """ raise NotImplementedError def assertTableStruct(self, items, field_names): """Verify that all items has keys listed in field_names. :param items: items to assert are field names in the output table :type items: list :param field_names: field names from the output table of the cmd :type field_names: list """ for item in items: for field in field_names: self.assertIn(field, item) def assertFirstLineStartsWith(self, lines, beginning): """Verify that the first line starts with a string :param lines: strings for each line of output :type lines: list :param beginning: verify this is at the beginning of the first line :type beginning: string """ self.assertTrue(lines[0].startswith(beginning), msg=('Beginning of first line has invalid content: %s' % lines[:3])) tempest-lib-1.0.0/tempest_lib/cli/output_parser.py000066400000000000000000000113041266406763200223140ustar00rootroot00000000000000# 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. """Collection of utilities for parsing CLI clients output.""" import logging import re from tempest_lib import exceptions LOG = logging.getLogger(__name__) delimiter_line = re.compile('^\+\-[\+\-]+\-\+$') def details_multiple(output_lines, with_label=False): """Return list of dicts with item details from cli output tables. If with_label is True, key '__label' is added to each items dict. For more about 'label' see OutputParser.tables(). """ items = [] tables_ = tables(output_lines) for table_ in tables_: if ('Property' not in table_['headers'] or 'Value' not in table_['headers']): raise exceptions.InvalidStructure() item = {} for value in table_['values']: item[value[0]] = value[1] if with_label: item['__label'] = table_['label'] items.append(item) return items def details(output_lines, with_label=False): """Return dict with details of first item (table) found in output.""" items = details_multiple(output_lines, with_label) return items[0] def listing(output_lines): """Return list of dicts with basic item info parsed from cli output.""" items = [] table_ = table(output_lines) for row in table_['values']: item = {} for col_idx, col_key in enumerate(table_['headers']): item[col_key] = row[col_idx] items.append(item) return items def tables(output_lines): """Find all ascii-tables in output and parse them. Return list of tables parsed from cli output as dicts. (see OutputParser.table()) And, if found, label key (separated line preceding the table) is added to each tables dict. """ tables_ = [] table_ = [] label = None start = False header = False if not isinstance(output_lines, list): output_lines = output_lines.split('\n') for line in output_lines: if delimiter_line.match(line): if not start: start = True elif not header: # we are after head area header = True else: # table ends here start = header = None table_.append(line) parsed = table(table_) parsed['label'] = label tables_.append(parsed) table_ = [] label = None continue if start: table_.append(line) else: if label is None: label = line else: LOG.warning('Invalid line between tables: %s' % line) if len(table_) > 0: LOG.warning('Missing end of table') return tables_ def table(output_lines): """Parse single table from cli output. Return dict with list of column names in 'headers' key and rows in 'values' key. """ table_ = {'headers': [], 'values': []} columns = None if not isinstance(output_lines, list): output_lines = output_lines.split('\n') if not output_lines[-1]: # skip last line if empty (just newline at the end) output_lines = output_lines[:-1] for line in output_lines: if delimiter_line.match(line): columns = _table_columns(line) continue if '|' not in line: LOG.warning('skipping invalid table line: %s' % line) continue row = [] for col in columns: row.append(line[col[0]:col[1]].strip()) if table_['headers']: table_['values'].append(row) else: table_['headers'] = row return table_ def _table_columns(first_table_row): """Find column ranges in output line. Return list of tuples (start,end) for each column detected by plus (+) characters in delimiter line. """ positions = [] start = 1 # there is '+' at 0 while start < len(first_table_row): end = first_table_row.find('+', start) if end == -1: break positions.append((start, end)) start = end + 1 return positions tempest-lib-1.0.0/tempest_lib/cmd/000077500000000000000000000000001266406763200170235ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/cmd/__init__.py000066400000000000000000000000001266406763200211220ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/cmd/check_uuid.py000077500000000000000000000326741266406763200215170ustar00rootroot00000000000000#!/usr/bin/env python # Copyright 2014 Mirantis, 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. import argparse import ast import importlib import inspect import os import sys import unittest import uuid import six.moves.urllib.parse as urlparse DECORATOR_MODULE = 'test' DECORATOR_NAME = 'idempotent_id' DECORATOR_IMPORT = 'tempest.%s' % DECORATOR_MODULE IMPORT_LINE = 'from tempest import %s' % DECORATOR_MODULE DECORATOR_TEMPLATE = "@%s.%s('%%s')" % (DECORATOR_MODULE, DECORATOR_NAME) UNIT_TESTS_EXCLUDE = 'tempest.tests' class SourcePatcher(object): """"Lazy patcher for python source files""" def __init__(self): self.source_files = None self.patches = None self.clear() def clear(self): """Clear inner state""" self.source_files = {} self.patches = {} @staticmethod def _quote(s): return urlparse.quote(s) @staticmethod def _unquote(s): return urlparse.unquote(s) def add_patch(self, filename, patch, line_no): """Add lazy patch""" if filename not in self.source_files: with open(filename) as f: self.source_files[filename] = self._quote(f.read()) patch_id = str(uuid.uuid4()) if not patch.endswith('\n'): patch += '\n' self.patches[patch_id] = self._quote(patch) lines = self.source_files[filename].split(self._quote('\n')) lines[line_no - 1] = ''.join(('{%s:s}' % patch_id, lines[line_no - 1])) self.source_files[filename] = self._quote('\n').join(lines) def _save_changes(self, filename, source): print('%s fixed' % filename) with open(filename, 'w') as f: f.write(source) def apply_patches(self): """Apply all patches""" for filename in self.source_files: patched_source = self._unquote( self.source_files[filename].format(**self.patches) ) self._save_changes(filename, patched_source) self.clear() class TestChecker(object): def __init__(self, package): self.package = package self.base_path = os.path.abspath(os.path.dirname(package.__file__)) def _path_to_package(self, path): relative_path = path[len(self.base_path) + 1:] if relative_path: return '.'.join((self.package.__name__,) + tuple(relative_path.split('/'))) else: return self.package.__name__ def _modules_search(self): """Recursive search for python modules in base package""" modules = [] for root, dirs, files in os.walk(self.base_path): if not os.path.exists(os.path.join(root, '__init__.py')): continue root_package = self._path_to_package(root) for item in files: if item.endswith('.py'): module_name = '.'.join((root_package, os.path.splitext(item)[0])) if not module_name.startswith(UNIT_TESTS_EXCLUDE): modules.append(module_name) return modules @staticmethod def _get_idempotent_id(test_node): """Return key-value dict with all metadata from @test.idempotent_id""" idempotent_id = None for decorator in test_node.decorator_list: if (hasattr(decorator, 'func') and hasattr(decorator.func, 'attr') and decorator.func.attr == DECORATOR_NAME and hasattr(decorator.func, 'value') and decorator.func.value.id == DECORATOR_MODULE): for arg in decorator.args: idempotent_id = ast.literal_eval(arg) return idempotent_id @staticmethod def _is_decorator(line): return line.strip().startswith('@') @staticmethod def _is_def(line): return line.strip().startswith('def ') def _add_uuid_to_test(self, patcher, test_node, source_path): with open(source_path) as src: src_lines = src.read().split('\n') lineno = test_node.lineno insert_position = lineno while True: if (self._is_def(src_lines[lineno - 1]) or (self._is_decorator(src_lines[lineno - 1]) and (DECORATOR_TEMPLATE.split('(')[0] <= src_lines[lineno - 1].strip().split('(')[0]))): insert_position = lineno break lineno += 1 patcher.add_patch( source_path, ' ' * test_node.col_offset + DECORATOR_TEMPLATE % uuid.uuid4(), insert_position ) @staticmethod def _is_test_case(module, node): if (node.__class__ is ast.ClassDef and hasattr(module, node.name) and inspect.isclass(getattr(module, node.name))): return issubclass(getattr(module, node.name), unittest.TestCase) @staticmethod def _is_test_method(node): return (node.__class__ is ast.FunctionDef and node.name.startswith('test_')) @staticmethod def _next_node(body, node): if body.index(node) < len(body): return body[body.index(node) + 1] @staticmethod def _import_name(node): if type(node) == ast.Import: return node.names[0].name elif type(node) == ast.ImportFrom: return '%s.%s' % (node.module, node.names[0].name) def _add_import_for_test_uuid(self, patcher, src_parsed, source_path): with open(source_path) as f: src_lines = f.read().split('\n') line_no = 0 tempest_imports = [node for node in src_parsed.body if self._import_name(node) and 'tempest.' in self._import_name(node)] if not tempest_imports: import_snippet = '\n'.join(('', IMPORT_LINE, '')) else: for node in tempest_imports: if self._import_name(node) < DECORATOR_IMPORT: continue else: line_no = node.lineno import_snippet = IMPORT_LINE break else: line_no = tempest_imports[-1].lineno while True: if (not src_lines[line_no - 1] or getattr(self._next_node(src_parsed.body, tempest_imports[-1]), 'lineno') == line_no or line_no == len(src_lines)): break line_no += 1 import_snippet = '\n'.join((IMPORT_LINE, '')) patcher.add_patch(source_path, import_snippet, line_no) def get_tests(self): """Get test methods with sources from base package with metadata""" tests = {} for module_name in self._modules_search(): tests[module_name] = {} module = importlib.import_module(module_name) source_path = '.'.join( (os.path.splitext(module.__file__)[0], 'py') ) with open(source_path, 'r') as f: source = f.read() tests[module_name]['source_path'] = source_path tests[module_name]['tests'] = {} source_parsed = ast.parse(source) tests[module_name]['ast'] = source_parsed tests[module_name]['import_valid'] = ( hasattr(module, DECORATOR_MODULE) and inspect.ismodule(getattr(module, DECORATOR_MODULE)) ) test_cases = (node for node in source_parsed.body if self._is_test_case(module, node)) for node in test_cases: for subnode in filter(self._is_test_method, node.body): test_name = '%s.%s' % (node.name, subnode.name) tests[module_name]['tests'][test_name] = subnode return tests @staticmethod def _filter_tests(function, tests): """Filter tests with condition 'function(test_node) == True'""" result = {} for module_name in tests: for test_name in tests[module_name]['tests']: if function(module_name, test_name, tests): if module_name not in result: result[module_name] = { 'ast': tests[module_name]['ast'], 'source_path': tests[module_name]['source_path'], 'import_valid': tests[module_name]['import_valid'], 'tests': {} } result[module_name]['tests'][test_name] = \ tests[module_name]['tests'][test_name] return result def find_untagged(self, tests): """Filter all tests without uuid in metadata""" def check_uuid_in_meta(module_name, test_name, tests): idempotent_id = self._get_idempotent_id( tests[module_name]['tests'][test_name]) return not idempotent_id return self._filter_tests(check_uuid_in_meta, tests) def report_collisions(self, tests): """Reports collisions if there are any Returns true if collisions exist. """ uuids = {} def report(module_name, test_name, tests): test_uuid = self._get_idempotent_id( tests[module_name]['tests'][test_name]) if not test_uuid: return if test_uuid in uuids: error_str = "%s:%s\n uuid %s collision: %s<->%s\n%s:%s" % ( tests[module_name]['source_path'], tests[module_name]['tests'][test_name].lineno, test_uuid, test_name, uuids[test_uuid]['test_name'], uuids[test_uuid]['source_path'], uuids[test_uuid]['test_node'].lineno, ) print(error_str) print("cannot automatically resolve the collision, please " "manually remove the duplicate value on the new test.") return True else: uuids[test_uuid] = { 'module': module_name, 'test_name': test_name, 'test_node': tests[module_name]['tests'][test_name], 'source_path': tests[module_name]['source_path'] } return bool(self._filter_tests(report, tests)) def report_untagged(self, tests): """Reports untagged tests if there are any Returns true if untagged tests exist. """ def report(module_name, test_name, tests): error_str = "%s:%s\nmissing @test.idempotent_id('...')\n%s\n" % ( tests[module_name]['source_path'], tests[module_name]['tests'][test_name].lineno, test_name ) print(error_str) return True return bool(self._filter_tests(report, tests)) def fix_tests(self, tests): """Add uuids to all specified in tests and fix it in source files""" patcher = SourcePatcher() for module_name in tests: add_import_once = True for test_name in tests[module_name]['tests']: if not tests[module_name]['import_valid'] and add_import_once: self._add_import_for_test_uuid( patcher, tests[module_name]['ast'], tests[module_name]['source_path'] ) add_import_once = False self._add_uuid_to_test( patcher, tests[module_name]['tests'][test_name], tests[module_name]['source_path']) patcher.apply_patches() def run(): parser = argparse.ArgumentParser() parser.add_argument('--package', action='store', dest='package', default='tempest', type=str, help='Package with tests') parser.add_argument('--fix', action='store_true', dest='fix_tests', help='Attempt to fix tests without UUIDs') args = parser.parse_args() sys.path.append(os.path.join(os.path.dirname(__file__), '..')) pkg = importlib.import_module(args.package) checker = TestChecker(pkg) errors = False tests = checker.get_tests() untagged = checker.find_untagged(tests) errors = checker.report_collisions(tests) or errors if args.fix_tests and untagged: checker.fix_tests(untagged) else: errors = checker.report_untagged(untagged) or errors if errors: sys.exit("@test.idempotent_id existence and uniqueness checks failed\n" "Run 'tox -v -euuidgen' to automatically fix tests with\n" "missing @test.idempotent_id decorators.") if __name__ == '__main__': run() tempest-lib-1.0.0/tempest_lib/cmd/skip_tracker.py000077500000000000000000000125321266406763200220640ustar00rootroot00000000000000#!/usr/bin/env python2 # Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Track test skips via launchpadlib API and raise alerts if a bug is fixed but a skip is still in the Tempest test code """ import argparse import logging import os import re try: from launchpadlib import launchpad except ImportError: launchpad = None LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache') def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('test_path', help='Path of test dir') return parser.parse_args() def info(msg, *args, **kwargs): logging.info(msg, *args, **kwargs) def debug(msg, *args, **kwargs): logging.debug(msg, *args, **kwargs) def find_skips(start): """Find the entire list of skiped tests. Returns a list of tuples (method, bug) that represent test methods that have been decorated to skip because of a particular bug. """ results = {} debug("Searching in %s", start) for root, _dirs, files in os.walk(start): for name in files: if name.startswith('test_') and name.endswith('py'): path = os.path.join(root, name) debug("Searching in %s", path) temp_result = find_skips_in_file(path) for method_name, bug_no in temp_result: if results.get(bug_no): result_dict = results.get(bug_no) if result_dict.get(name): result_dict[name].append(method_name) else: result_dict[name] = [method_name] results[bug_no] = result_dict else: results[bug_no] = {name: [method_name]} return results def find_skips_in_file(path): """Return the skip tuples in a test file.""" BUG_RE = re.compile(r'\s*@.*skip_because\(bug=[\'"](\d+)[\'"]') DEF_RE = re.compile(r'\s*def (\w+)\(') bug_found = False results = [] lines = open(path, 'rb').readlines() for x, line in enumerate(lines): if not bug_found: res = BUG_RE.match(line) if res: bug_no = int(res.group(1)) debug("Found bug skip %s on line %d", bug_no, x + 1) bug_found = True else: res = DEF_RE.match(line) if res: method = res.group(1) debug("Found test method %s skips for bug %d", method, bug_no) results.append((method, bug_no)) bug_found = False return results def get_results(result_dict): results = [] for bug_no in result_dict.keys(): for method in result_dict[bug_no]: results.append((method, bug_no)) return results def main(): logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) parser = parse_args() results = find_skips(parser.test_path) unique_bugs = sorted(set([bug for (method, bug) in get_results(results)])) unskips = [] duplicates = [] info("Total bug skips found: %d", len(results)) info("Total unique bugs causing skips: %d", len(unique_bugs)) if launchpad is not None: lp = launchpad.Launchpad.login_anonymously('grabbing bugs', 'production', LPCACHEDIR) else: print("To check the bug status launchpadlib should be installed") exit(1) for bug_no in unique_bugs: bug = lp.bugs[bug_no] duplicate = bug.duplicate_of_link if duplicate is not None: dup_id = duplicate.split('/')[-1] duplicates.append((bug_no, dup_id)) for task in bug.bug_tasks: info("Bug #%7s (%12s - %12s)", bug_no, task.importance, task.status) if task.status in ('Fix Released', 'Fix Committed'): unskips.append(bug_no) for bug_id, dup_id in duplicates: if bug_id not in unskips: dup_bug = lp.bugs[dup_id] for task in dup_bug.bug_tasks: info("Bug #%7s is a duplicate of Bug#%7s (%12s - %12s)", bug_id, dup_id, task.importance, task.status) if task.status in ('Fix Released', 'Fix Committed'): unskips.append(bug_id) unskips = sorted(set(unskips)) if unskips: print("The following bugs have been fixed and the corresponding skips") print("should be removed from the test cases:") print() for bug in unskips: message = " %7s in " % bug locations = ["%s" % x for x in results[bug].keys()] message += " and ".join(locations) print(message) if __name__ == '__main__': main() tempest-lib-1.0.0/tempest_lib/common/000077500000000000000000000000001266406763200175505ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/common/__init__.py000066400000000000000000000000001266406763200216470ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/common/http.py000066400000000000000000000017601266406763200211050ustar00rootroot00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 Citrix Systems, Inc. # 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 httplib2 class ClosingHttp(httplib2.Http): def request(self, *args, **kwargs): original_headers = kwargs.get('headers', {}) new_headers = dict(original_headers, connection='close') new_kwargs = dict(kwargs, headers=new_headers) return super(ClosingHttp, self).request(*args, **new_kwargs) tempest-lib-1.0.0/tempest_lib/common/rest_client.py000066400000000000000000001126751266406763200224510ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # 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. import collections import logging as real_logging import re import time import jsonschema from oslo_log import log as logging from oslo_serialization import jsonutils as json import six from tempest_lib.common import http from tempest_lib.common.utils import misc as misc_utils from tempest_lib import exceptions # redrive rate limited calls at most twice MAX_RECURSION_DEPTH = 2 # All the successful HTTP status codes from RFC 7231 & 4918 HTTP_SUCCESS = (200, 201, 202, 203, 204, 205, 206, 207) # All the redirection HTTP status codes from RFC 7231 & 4918 HTTP_REDIRECTION = (300, 301, 302, 303, 304, 305, 306, 307) # JSON Schema validator and format checker used for JSON Schema validation JSONSCHEMA_VALIDATOR = jsonschema.Draft4Validator FORMAT_CHECKER = jsonschema.draft4_format_checker class RestClient(object): """Unified OpenStack RestClient class This class is used for building openstack api clients on top of. It is intended to provide a base layer for wrapping outgoing http requests in keystone auth as well as providing response code checking and error handling. :param auth_provider: an auth provider object used to wrap requests in auth :param str service: The service name to use for the catalog lookup :param str region: The region to use for the catalog lookup :param str endpoint_type: The endpoint type to use for the catalog lookup :param int build_interval: Time in seconds between to status checks in wait loops :param int build_timeout: Timeout in seconds to wait for a wait operation. :param bool disable_ssl_certificate_validation: Set to true to disable ssl certificate validation :param str ca_certs: File containing the CA Bundle to use in verifying a TLS server cert :param str trace_request: Regex to use for specifying logging the entirety of the request and response payload """ TYPE = "json" # The version of the API this client implements api_version = None LOG = logging.getLogger(__name__) def __init__(self, auth_provider, service, region, endpoint_type='publicURL', build_interval=1, build_timeout=60, disable_ssl_certificate_validation=False, ca_certs=None, trace_requests=''): self.auth_provider = auth_provider self.service = service self.region = region self.endpoint_type = endpoint_type self.build_interval = build_interval self.build_timeout = build_timeout self.trace_requests = trace_requests self._skip_path = False self.general_header_lc = set(('cache-control', 'connection', 'date', 'pragma', 'trailer', 'transfer-encoding', 'via', 'warning')) self.response_header_lc = set(('accept-ranges', 'age', 'etag', 'location', 'proxy-authenticate', 'retry-after', 'server', 'vary', 'www-authenticate')) dscv = disable_ssl_certificate_validation self.http_obj = http.ClosingHttp( disable_ssl_certificate_validation=dscv, ca_certs=ca_certs) def _get_type(self): return self.TYPE def get_headers(self, accept_type=None, send_type=None): """Return the default headers which will be used with outgoing requests :param str accept_type: The media type to use for the Accept header, if one isn't provided the object var TYPE will be used :param str send_type: The media-type to use for the Content-Type header, if one isn't provided the object var TYPE will be used :rtype: dict :return: The dictionary of headers which can be used in the headers dict for outgoing request """ if accept_type is None: accept_type = self._get_type() if send_type is None: send_type = self._get_type() return {'Content-Type': 'application/%s' % send_type, 'Accept': 'application/%s' % accept_type} def __str__(self): STRING_LIMIT = 80 str_format = ("service:%s, base_url:%s, " "filters: %s, build_interval:%s, build_timeout:%s" "\ntoken:%s..., \nheaders:%s...") return str_format % (self.service, self.base_url, self.filters, self.build_interval, self.build_timeout, str(self.token)[0:STRING_LIMIT], str(self.get_headers())[0:STRING_LIMIT]) @property def user(self): """The username used for requests :rtype: string :return: The username being used for requests """ return self.auth_provider.credentials.username @property def user_id(self): """The user_id used for requests :rtype: string :return: The user id being used for requests """ return self.auth_provider.credentials.user_id @property def tenant_name(self): """The tenant/project being used for requests :rtype: string :return: The tenant/project name being used for requests """ return self.auth_provider.credentials.tenant_name @property def tenant_id(self): """The tenant/project id being used for requests :rtype: string :return: The tenant/project id being used for requests """ return self.auth_provider.credentials.tenant_id @property def password(self): """The password being used for requests :rtype: string :return: The password being used for requests """ return self.auth_provider.credentials.password @property def base_url(self): return self.auth_provider.base_url(filters=self.filters) @property def token(self): return self.auth_provider.get_token() @property def filters(self): _filters = dict( service=self.service, endpoint_type=self.endpoint_type, region=self.region ) if self.api_version is not None: _filters['api_version'] = self.api_version if self._skip_path: _filters['skip_path'] = self._skip_path return _filters def skip_path(self): """When set, ignore the path part of the base URL from the catalog""" self._skip_path = True def reset_path(self): """When reset, use the base URL from the catalog as-is""" self._skip_path = False @classmethod def expected_success(cls, expected_code, read_code): """Check expected success response code against the http response :param int expected_code: The response code that is expected. Optionally a list of integers can be used to specify multiple valid success codes :param int read_code: The response code which was returned in the response :raises AssertionError: if the expected_code isn't a valid http success response code :raises exceptions.InvalidHttpSuccessCode: if the read code isn't an expected http success code """ assert_msg = ("This function only allowed to use for HTTP status" "codes which explicitly defined in the RFC 7231 & 4918." "{0} is not a defined Success Code!" ).format(expected_code) if isinstance(expected_code, list): for code in expected_code: assert code in HTTP_SUCCESS + HTTP_REDIRECTION, assert_msg else: assert expected_code in HTTP_SUCCESS + HTTP_REDIRECTION, assert_msg # NOTE(afazekas): the http status code above 400 is processed by # the _error_checker method if read_code < 400: pattern = """Unexpected http success status code {0}, The expected status code is {1}""" if ((not isinstance(expected_code, list) and (read_code != expected_code)) or (isinstance(expected_code, list) and (read_code not in expected_code))): details = pattern.format(read_code, expected_code) raise exceptions.InvalidHttpSuccessCode(details) def post(self, url, body, headers=None, extra_headers=False): """Send a HTTP POST request using keystone auth :param str url: the relative url to send the post request to :param dict body: the request body :param dict headers: The headers to use for the request :param dict extra_headers: If the headers returned by the get_headers() method are to be used but additional headers are needed in the request pass them in as a dict :return: a tuple with the first entry containing the response headers and the second the response body :rtype: tuple """ return self.request('POST', url, extra_headers, headers, body) def get(self, url, headers=None, extra_headers=False): """Send a HTTP GET request using keystone service catalog and auth :param str url: the relative url to send the post request to :param dict headers: The headers to use for the request :param dict extra_headers: If the headers returned by the get_headers() method are to be used but additional headers are needed in the request pass them in as a dict :return: a tuple with the first entry containing the response headers and the second the response body :rtype: tuple """ return self.request('GET', url, extra_headers, headers) def delete(self, url, headers=None, body=None, extra_headers=False): """Send a HTTP DELETE request using keystone service catalog and auth :param str url: the relative url to send the post request to :param dict headers: The headers to use for the request :param dict body: the request body :param dict extra_headers: If the headers returned by the get_headers() method are to be used but additional headers are needed in the request pass them in as a dict :return: a tuple with the first entry containing the response headers and the second the response body :rtype: tuple """ return self.request('DELETE', url, extra_headers, headers, body) def patch(self, url, body, headers=None, extra_headers=False): """Send a HTTP PATCH request using keystone service catalog and auth :param str url: the relative url to send the post request to :param dict body: the request body :param dict headers: The headers to use for the request :param dict extra_headers: If the headers returned by the get_headers() method are to be used but additional headers are needed in the request pass them in as a dict :return: a tuple with the first entry containing the response headers and the second the response body :rtype: tuple """ return self.request('PATCH', url, extra_headers, headers, body) def put(self, url, body, headers=None, extra_headers=False): """Send a HTTP PUT request using keystone service catalog and auth :param str url: the relative url to send the post request to :param dict body: the request body :param dict headers: The headers to use for the request :param dict extra_headers: If the headers returned by the get_headers() method are to be used but additional headers are needed in the request pass them in as a dict :return: a tuple with the first entry containing the response headers and the second the response body :rtype: tuple """ return self.request('PUT', url, extra_headers, headers, body) def head(self, url, headers=None, extra_headers=False): """Send a HTTP HEAD request using keystone service catalog and auth :param str url: the relative url to send the post request to :param dict headers: The headers to use for the request :param dict extra_headers: If the headers returned by the get_headers() method are to be used but additional headers are needed in the request pass them in as a dict :return: a tuple with the first entry containing the response headers and the second the response body :rtype: tuple """ return self.request('HEAD', url, extra_headers, headers) def copy(self, url, headers=None, extra_headers=False): """Send a HTTP COPY request using keystone service catalog and auth :param str url: the relative url to send the post request to :param dict headers: The headers to use for the request :param dict extra_headers: If the headers returned by the get_headers() method are to be used but additional headers are needed in the request pass them in as a dict :return: a tuple with the first entry containing the response headers and the second the response body :rtype: tuple """ return self.request('COPY', url, extra_headers, headers) def get_versions(self): """Get the versions on a endpoint from the keystone catalog This method will make a GET request on the baseurl from the keystone catalog to return a list of API versions. It is expected that a GET on the endpoint in the catalog will return a list of supported API versions. :return tuple with response headers and list of version numbers :rtype: tuple """ resp, body = self.get('') body = self._parse_resp(body) versions = map(lambda x: x['id'], body) return resp, versions def _get_request_id(self, resp): for i in ('x-openstack-request-id', 'x-compute-request-id'): if i in resp: return resp[i] return "" def _safe_body(self, body, maxlen=4096): # convert a structure into a string safely try: text = six.text_type(body) except UnicodeDecodeError: # if this isn't actually text, return marker that return "" if len(text) > maxlen: return text[:maxlen] else: return text def _log_request_start(self, method, req_url, req_headers=None, req_body=None): if req_headers is None: req_headers = {} caller_name = misc_utils.find_test_caller() if self.trace_requests and re.search(self.trace_requests, caller_name): self.LOG.debug('Starting Request (%s): %s %s' % (caller_name, method, req_url)) def _log_request_full(self, method, req_url, resp, secs="", req_headers=None, req_body=None, resp_body=None, caller_name=None, extra=None): if 'X-Auth-Token' in req_headers: req_headers['X-Auth-Token'] = '' log_fmt = """Request - Headers: %s Body: %s Response - Headers: %s Body: %s""" self.LOG.debug( log_fmt % ( str(req_headers), self._safe_body(req_body), str(resp), self._safe_body(resp_body)), extra=extra) def _log_request(self, method, req_url, resp, secs="", req_headers=None, req_body=None, resp_body=None): if req_headers is None: req_headers = {} # if we have the request id, put it in the right part of the log extra = dict(request_id=self._get_request_id(resp)) # NOTE(sdague): while we still have 6 callers to this function # we're going to just provide work around on who is actually # providing timings by gracefully adding no content if they don't. # Once we're down to 1 caller, clean this up. caller_name = misc_utils.find_test_caller() if secs: secs = " %.3fs" % secs self.LOG.info( 'Request (%s): %s %s %s%s' % ( caller_name, resp['status'], method, req_url, secs), extra=extra) # Also look everything at DEBUG if you want to filter this # out, don't run at debug. if self.LOG.isEnabledFor(real_logging.DEBUG): self._log_request_full(method, req_url, resp, secs, req_headers, req_body, resp_body, caller_name, extra) def _parse_resp(self, body): try: body = json.loads(body) except ValueError: return body # We assume, that if the first value of the deserialized body's # item set is a dict or a list, that we just return the first value # of deserialized body. # Essentially "cutting out" the first placeholder element in a body # that looks like this: # # { # "users": [ # ... # ] # } try: # Ensure there are not more than one top-level keys # NOTE(freerunner): Ensure, that JSON is not nullable to # to prevent StopIteration Exception if len(body.keys()) != 1: return body # Just return the "wrapped" element first_key, first_item = six.next(six.iteritems(body)) if isinstance(first_item, (dict, list)): return first_item except (ValueError, IndexError): pass return body def response_checker(self, method, resp, resp_body): """A sanity check on the response from a HTTP request This method does a sanity check on whether the response from an HTTP request conforms the HTTP RFC. :param str method: The HTTP verb of the request associated with the response being passed in. :param resp: The response headers :param resp_body: The body of the response :raises ResponseWithNonEmptyBody: If the response with the status code is not supposed to have a body :raises ResponseWithEntity: If the response code is 205 but has an entity """ if (resp.status in set((204, 205, 304)) or resp.status < 200 or method.upper() == 'HEAD') and resp_body: raise exceptions.ResponseWithNonEmptyBody(status=resp.status) # NOTE(afazekas): # If the HTTP Status Code is 205 # 'The response MUST NOT include an entity.' # A HTTP entity has an entity-body and an 'entity-header'. # In the HTTP response specification (Section 6) the 'entity-header' # 'generic-header' and 'response-header' are in OR relation. # All headers not in the above two group are considered as entity # header in every interpretation. if (resp.status == 205 and 0 != len(set(resp.keys()) - set(('status',)) - self.response_header_lc - self.general_header_lc)): raise exceptions.ResponseWithEntity() # NOTE(afazekas) # Now the swift sometimes (delete not empty container) # returns with non json error response, we can create new rest class # for swift. # Usually RFC2616 says error responses SHOULD contain an explanation. # The warning is normal for SHOULD/SHOULD NOT case # Likely it will cause an error if method != 'HEAD' and not resp_body and resp.status >= 400: self.LOG.warning("status >= 400 response with empty body") def _request(self, method, url, headers=None, body=None): """A simple HTTP request interface.""" # Authenticate the request with the auth provider req_url, req_headers, req_body = self.auth_provider.auth_request( method, url, headers, body, self.filters) # Do the actual request, and time it start = time.time() self._log_request_start(method, req_url) resp, resp_body = self.raw_request( req_url, method, headers=req_headers, body=req_body) end = time.time() self._log_request(method, req_url, resp, secs=(end - start), req_headers=req_headers, req_body=req_body, resp_body=resp_body) # Verify HTTP response codes self.response_checker(method, resp, resp_body) return resp, resp_body def raw_request(self, url, method, headers=None, body=None): """Send a raw HTTP request without the keystone catalog or auth This method sends a HTTP request in the same manner as the request() method, however it does so without using keystone auth or the catalog to determine the base url. Additionally no response handling is done the results from the request are just returned. :param str url: Full url to send the request :param str method: The HTTP verb to use for the request :param str headers: Headers to use for the request if none are specifed the headers :param str body: Body to to send with the request :rtype: tuple :return: a tuple with the first entry containing the response headers and the second the response body """ if headers is None: headers = self.get_headers() return self.http_obj.request(url, method, headers=headers, body=body) def request(self, method, url, extra_headers=False, headers=None, body=None): """Send a HTTP request with keystone auth and using the catalog This method will send an HTTP request using keystone auth in the headers and the catalog to determine the endpoint to use for the baseurl to send the request to. Additionally When a response is received it will check it to see if an error response was received. If it was an exception will be raised to enable it to be handled quickly. This method will also handle rate-limiting, if a 413 response code is received it will retry the request after waiting the 'retry-after' duration from the header. :param str method: The HTTP verb to use for the request :param str url: Relative url to send the request to :param dict extra_headers: If specified without the headers kwarg the headers sent with the request will be the combination from the get_headers() method and this kwarg :param dict headers: Headers to use for the request if none are specifed the headers returned from the get_headers() method are used. If the request explicitly requires no headers use an empty dict. :param str body: Body to to send with the request :rtype: tuple :return: a tuple with the first entry containing the response headers and the second the response body :raises UnexpectedContentType: If the content-type of the response isn't an expect type :raises Unauthorized: If a 401 response code is received :raises Forbidden: If a 403 response code is received :raises NotFound: If a 404 response code is received :raises BadRequest: If a 400 response code is received :raises Gone: If a 410 response code is received :raises Conflict: If a 409 response code is received :raises OverLimit: If a 413 response code is received and over_limit is not in the response body :raises RateLimitExceeded: If a 413 response code is received and over_limit is in the response body :raises InvalidContentType: If a 415 response code is received :raises UnprocessableEntity: If a 422 response code is received :raises InvalidHTTPResponseBody: The response body wasn't valid JSON and couldn't be parsed :raises NotImplemented: If a 501 response code is received :raises ServerFault: If a 500 response code is received :raises UnexpectedResponseCode: If a response code above 400 is received and it doesn't fall into any of the handled checks """ # if extra_headers is True # default headers would be added to headers retry = 0 if headers is None: # NOTE(vponomaryov): if some client do not need headers, # it should explicitly pass empty dict headers = self.get_headers() elif extra_headers: try: headers = headers.copy() headers.update(self.get_headers()) except (ValueError, TypeError): headers = self.get_headers() resp, resp_body = self._request(method, url, headers=headers, body=body) while (resp.status == 413 and 'retry-after' in resp and not self.is_absolute_limit( resp, self._parse_resp(resp_body)) and retry < MAX_RECURSION_DEPTH): retry += 1 delay = int(resp['retry-after']) time.sleep(delay) resp, resp_body = self._request(method, url, headers=headers, body=body) self._error_checker(method, url, headers, body, resp, resp_body) return resp, resp_body def _error_checker(self, method, url, headers, body, resp, resp_body): # NOTE(mtreinish): Check for httplib response from glance_http. The # object can't be used here because importing httplib breaks httplib2. # If another object from a class not imported were passed here as # resp this could possibly fail if str(type(resp)) == "": ctype = resp.getheader('content-type') else: try: ctype = resp['content-type'] # NOTE(mtreinish): Keystone delete user responses doesn't have a # content-type header. (They don't have a body) So just pretend it # is set. except KeyError: ctype = 'application/json' # It is not an error response if resp.status < 400: return JSON_ENC = ['application/json', 'application/json; charset=utf-8'] # NOTE(mtreinish): This is for compatibility with Glance and swift # APIs. These are the return content types that Glance api v1 # (and occasionally swift) are using. TXT_ENC = ['text/plain', 'text/html', 'text/html; charset=utf-8', 'text/plain; charset=utf-8'] if ctype.lower() in JSON_ENC: parse_resp = True elif ctype.lower() in TXT_ENC: parse_resp = False else: raise exceptions.UnexpectedContentType(str(resp.status), resp=resp) if resp.status == 401: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Unauthorized(resp_body, resp=resp) if resp.status == 403: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Forbidden(resp_body, resp=resp) if resp.status == 404: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.NotFound(resp_body, resp=resp) if resp.status == 400: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.BadRequest(resp_body, resp=resp) if resp.status == 410: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Gone(resp_body, resp=resp) if resp.status == 409: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.Conflict(resp_body, resp=resp) if resp.status == 413: if parse_resp: resp_body = self._parse_resp(resp_body) if self.is_absolute_limit(resp, resp_body): raise exceptions.OverLimit(resp_body, resp=resp) else: raise exceptions.RateLimitExceeded(resp_body, resp=resp) if resp.status == 415: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.InvalidContentType(resp_body, resp=resp) if resp.status == 422: if parse_resp: resp_body = self._parse_resp(resp_body) raise exceptions.UnprocessableEntity(resp_body, resp=resp) if resp.status in (500, 501): message = resp_body if parse_resp: try: resp_body = self._parse_resp(resp_body) except ValueError: # If response body is a non-json string message. # Use resp_body as is and raise InvalidResponseBody # exception. raise exceptions.InvalidHTTPResponseBody(message) else: if isinstance(resp_body, dict): # I'm seeing both computeFault # and cloudServersFault come back. # Will file a bug to fix, but leave as is for now. if 'cloudServersFault' in resp_body: message = resp_body['cloudServersFault']['message'] elif 'computeFault' in resp_body: message = resp_body['computeFault']['message'] elif 'error' in resp_body: message = resp_body['error']['message'] elif 'message' in resp_body: message = resp_body['message'] else: message = resp_body if resp.status == 501: raise exceptions.NotImplemented(resp_body, resp=resp, message=message) else: raise exceptions.ServerFault(resp_body, resp=resp, message=message) if resp.status >= 400: raise exceptions.UnexpectedResponseCode(str(resp.status), resp=resp) def is_absolute_limit(self, resp, resp_body): if (not isinstance(resp_body, collections.Mapping) or 'retry-after' not in resp): return True over_limit = resp_body.get('overLimit', None) if not over_limit: return True return 'exceed' in over_limit.get('message', 'blabla') def wait_for_resource_deletion(self, id): """Waits for a resource to be deleted This method will loop over is_resource_deleted until either is_resource_deleted returns True or the build timeout is reached. This depends on is_resource_deleted being implemented :param str id: The id of the resource to check :raises TimeoutException: If the build_timeout has elapsed and the resource still hasn't been deleted """ start_time = int(time.time()) while True: if self.is_resource_deleted(id): return if int(time.time()) - start_time >= self.build_timeout: message = ('Failed to delete %(resource_type)s %(id)s within ' 'the required time (%(timeout)s s).' % {'resource_type': self.resource_type, 'id': id, 'timeout': self.build_timeout}) caller = misc_utils.find_test_caller() if caller: message = '(%s) %s' % (caller, message) raise exceptions.TimeoutException(message) time.sleep(self.build_interval) def is_resource_deleted(self, id): """Subclasses override with specific deletion detection.""" message = ('"%s" does not implement is_resource_deleted' % self.__class__.__name__) raise NotImplementedError(message) @property def resource_type(self): """Returns the primary type of resource this client works with.""" return 'resource' @classmethod def validate_response(cls, schema, resp, body): # Only check the response if the status code is a success code # TODO(cyeoh): Eventually we should be able to verify that a failure # code if it exists is something that we expect. This is explicitly # declared in the V3 API and so we should be able to export this in # the response schema. For now we'll ignore it. if resp.status in HTTP_SUCCESS + HTTP_REDIRECTION: cls.expected_success(schema['status_code'], resp.status) # Check the body of a response body_schema = schema.get('response_body') if body_schema: try: jsonschema.validate(body, body_schema, cls=JSONSCHEMA_VALIDATOR, format_checker=FORMAT_CHECKER) except jsonschema.ValidationError as ex: msg = ("HTTP response body is invalid (%s)") % ex raise exceptions.InvalidHTTPResponseBody(msg) else: if body: msg = ("HTTP response body should not exist (%s)") % body raise exceptions.InvalidHTTPResponseBody(msg) # Check the header of a response header_schema = schema.get('response_header') if header_schema: try: jsonschema.validate(resp, header_schema, cls=JSONSCHEMA_VALIDATOR, format_checker=FORMAT_CHECKER) except jsonschema.ValidationError as ex: msg = ("HTTP response header is invalid (%s)") % ex raise exceptions.InvalidHTTPResponseHeader(msg) class ResponseBody(dict): """Class that wraps an http response and dict body into a single value. Callers that receive this object will normally use it as a dict but can extract the response if needed. """ def __init__(self, response, body=None): body_data = body or {} self.update(body_data) self.response = response def __str__(self): body = super(ResponseBody, self).__str__() return "response: %s\nBody: %s" % (self.response, body) class ResponseBodyData(object): """Class that wraps an http response and string data into a single value. """ def __init__(self, response, data): self.response = response self.data = data def __str__(self): return "response: %s\nBody: %s" % (self.response, self.data) class ResponseBodyList(list): """Class that wraps an http response and list body into a single value. Callers that receive this object will normally use it as a list but can extract the response if needed. """ def __init__(self, response, body=None): body_data = body or [] self.extend(body_data) self.response = response def __str__(self): body = super(ResponseBodyList, self).__str__() return "response: %s\nBody: %s" % (self.response, body) tempest-lib-1.0.0/tempest_lib/common/ssh.py000066400000000000000000000155161266406763200207270ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import select import socket import time import warnings from oslo_log import log as logging import six from tempest_lib import exceptions with warnings.catch_warnings(): warnings.simplefilter("ignore") import paramiko LOG = logging.getLogger(__name__) class Client(object): def __init__(self, host, username, password=None, timeout=300, pkey=None, channel_timeout=10, look_for_keys=False, key_filename=None): self.host = host self.username = username self.password = password if isinstance(pkey, six.string_types): pkey = paramiko.RSAKey.from_private_key( six.StringIO(str(pkey))) self.pkey = pkey self.look_for_keys = look_for_keys self.key_filename = key_filename self.timeout = int(timeout) self.channel_timeout = float(channel_timeout) self.buf_size = 1024 def _get_ssh_connection(self, sleep=1.5, backoff=1): """Returns an ssh connection to the specified host.""" bsleep = sleep ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy( paramiko.AutoAddPolicy()) _start_time = time.time() if self.pkey is not None: LOG.info("Creating ssh connection to '%s' as '%s'" " with public key authentication", self.host, self.username) else: LOG.info("Creating ssh connection to '%s' as '%s'" " with password %s", self.host, self.username, str(self.password)) attempts = 0 while True: try: ssh.connect(self.host, username=self.username, password=self.password, look_for_keys=self.look_for_keys, key_filename=self.key_filename, timeout=self.channel_timeout, pkey=self.pkey) LOG.info("ssh connection to %s@%s successfully created", self.username, self.host) return ssh except (EOFError, socket.error, paramiko.SSHException) as e: if self._is_timed_out(_start_time): LOG.exception("Failed to establish authenticated ssh" " connection to %s@%s after %d attempts", self.username, self.host, attempts) raise exceptions.SSHTimeout(host=self.host, user=self.username, password=self.password) bsleep += backoff attempts += 1 LOG.warning("Failed to establish authenticated ssh" " connection to %s@%s (%s). Number attempts: %s." " Retry after %d seconds.", self.username, self.host, e, attempts, bsleep) time.sleep(bsleep) def _is_timed_out(self, start_time): return (time.time() - self.timeout) > start_time @staticmethod def _can_system_poll(): return hasattr(select, 'poll') def exec_command(self, cmd, encoding="utf-8"): """Execute the specified command on the server Note that this method is reading whole command outputs to memory, thus shouldn't be used for large outputs. :param str cmd: Command to run at remote server. :param str encoding: Encoding for result from paramiko. Result will not be decoded if None. :returns: data read from standard output of the command. :raises: SSHExecCommandFailed if command returns nonzero status. The exception contains command status stderr content. :raises: TimeoutException if cmd doesn't end when timeout expires. """ ssh = self._get_ssh_connection() transport = ssh.get_transport() channel = transport.open_session() channel.fileno() # Register event pipe channel.exec_command(cmd) channel.shutdown_write() exit_status = channel.recv_exit_status() # If the executing host is linux-based, poll the channel if self._can_system_poll(): out_data_chunks = [] err_data_chunks = [] poll = select.poll() poll.register(channel, select.POLLIN) start_time = time.time() while True: ready = poll.poll(self.channel_timeout) if not any(ready): if not self._is_timed_out(start_time): continue raise exceptions.TimeoutException( "Command: '{0}' executed on host '{1}'.".format( cmd, self.host)) if not ready[0]: # If there is nothing to read. continue out_chunk = err_chunk = None if channel.recv_ready(): out_chunk = channel.recv(self.buf_size) out_data_chunks += out_chunk, if channel.recv_stderr_ready(): err_chunk = channel.recv_stderr(self.buf_size) err_data_chunks += err_chunk, if channel.closed and not err_chunk and not out_chunk: break out_data = b''.join(out_data_chunks) err_data = b''.join(err_data_chunks) # Just read from the channels else: out_file = channel.makefile('rb', self.buf_size) err_file = channel.makefile_stderr('rb', self.buf_size) out_data = out_file.read() err_data = err_file.read() if encoding: out_data = out_data.decode(encoding) err_data = err_data.decode(encoding) if 0 != exit_status: raise exceptions.SSHExecCommandFailed( command=cmd, exit_status=exit_status, stderr=err_data, stdout=out_data) return out_data def test_connection_auth(self): """Raises an exception when we can not connect to server via ssh.""" connection = self._get_ssh_connection() connection.close() tempest-lib-1.0.0/tempest_lib/common/utils/000077500000000000000000000000001266406763200207105ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/common/utils/__init__.py000066400000000000000000000000001266406763200230070ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/common/utils/data_utils.py000066400000000000000000000137101266406763200234150ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import netaddr import random import string import uuid def rand_uuid(): """Generate a random UUID string :return: a random UUID (e.g. '1dc12c7d-60eb-4b61-a7a2-17cf210155b6') :rtype: string """ return str(uuid.uuid4()) def rand_uuid_hex(): """Generate a random UUID hex string :return: a random UUID (e.g. '0b98cf96d90447bda4b46f31aeb1508c') :rtype: string """ return uuid.uuid4().hex def rand_name(name='', prefix=None): """Generate a random name that inclues a random number :param str name: The name that you want to include :param str prefix: The prefix that you want to include :return: a random name. The format is '---'. (e.g. 'prefixfoo-1308607012-namebar-154876201') :rtype: string """ randbits = str(random.randint(1, 0x7fffffff)) rand_name = randbits if name: rand_name = name + '-' + rand_name if prefix: rand_name = prefix + '-' + rand_name return rand_name def rand_password(length=15): """Generate a random password :param int length: The length of password that you expect to set (If it's smaller than 3, it's same as 3.) :return: a random password. The format is '-- -' (e.g. 'G2*ac8&lKFFgh%2') :rtype: string """ upper = random.choice(string.ascii_uppercase) ascii_char = string.ascii_letters digits = string.digits digit = random.choice(string.digits) puncs = '~!@#$%^&*_=+' punc = random.choice(puncs) seed = ascii_char + digits + puncs pre = upper + digit + punc password = pre + ''.join(random.choice(seed) for x in range(length - 3)) return password def rand_url(): """Generate a random url that inclues a random number :return: a random url. The format is 'https://url-.com'. (e.g. 'https://url-154876201.com') :rtype: string """ randbits = str(random.randint(1, 0x7fffffff)) return 'https://url-' + randbits + '.com' def rand_int_id(start=0, end=0x7fffffff): """Generate a random integer value :param int start: The value that you expect to start here :param int end: The value that you expect to end here :return: a random integer value :rtype: int """ return random.randint(start, end) def rand_mac_address(): """Generate an Ethernet MAC address :return: an random Ethernet MAC address :rtype: string """ # NOTE(vish): We would prefer to use 0xfe here to ensure that linux # bridge mac addresses don't change, but it appears to # conflict with libvirt, so we use the next highest octet # that has the unicast and locally administered bits set # properly: 0xfa. # Discussion: https://bugs.launchpad.net/nova/+bug/921838 mac = [0xfa, 0x16, 0x3e, random.randint(0x00, 0xff), random.randint(0x00, 0xff), random.randint(0x00, 0xff)] return ':'.join(["%02x" % x for x in mac]) def parse_image_id(image_ref): """Return the image id from a given image ref This function just returns the last word of the given image ref string splitting with '/'. :param str image_ref: a string that includes the image id :return: the image id string :rtype: string """ return image_ref.rsplit('/')[-1] def arbitrary_string(size=4, base_text=None): """Return size characters from base_text This generates a string with an arbitrary number of characters, generated by looping the base_text string. If the size is smaller than the size of base_text, returning string is shrinked to the size. :param int size: a returning charactors size :param str base_text: a string you want to repeat :return: size string :rtype: string """ if not base_text: base_text = 'test' return ''.join(itertools.islice(itertools.cycle(base_text), size)) def random_bytes(size=1024): """Return size randomly selected bytes as a string :param int size: a returning bytes size :return: size randomly bytes :rtype: string """ return ''.join([chr(random.randint(0, 255)) for i in range(size)]) def get_ipv6_addr_by_EUI64(cidr, mac): """Generate a IPv6 addr by EUI-64 with CIDR and MAC :param str cidr: a IPv6 CIDR :param str mac: a MAC address :return: an IPv6 Address :rtype: netaddr.IPAddress """ # Check if the prefix is IPv4 address is_ipv4 = netaddr.valid_ipv4(cidr) if is_ipv4: msg = "Unable to generate IP address by EUI64 for IPv4 prefix" raise TypeError(msg) try: eui64 = int(netaddr.EUI(mac).eui64()) prefix = netaddr.IPNetwork(cidr) return netaddr.IPAddress(prefix.first + eui64 ^ (1 << 57)) except (ValueError, netaddr.AddrFormatError): raise TypeError('Bad prefix or mac format for generating IPv6 ' 'address by EUI-64: %(prefix)s, %(mac)s:' % {'prefix': cidr, 'mac': mac}) except TypeError: raise TypeError('Bad prefix type for generate IPv6 address by ' 'EUI-64: %s' % cidr) tempest-lib-1.0.0/tempest_lib/common/utils/misc.py000066400000000000000000000057701266406763200222260ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import inspect import re from oslo_log import log as logging LOG = logging.getLogger(__name__) def singleton(cls): """Simple wrapper for classes that should only have a single instance.""" instances = {} def getinstance(): if cls not in instances: instances[cls] = cls() return instances[cls] return getinstance def find_test_caller(): """Find the caller class and test name. Because we know that the interesting things that call us are test_* methods, and various kinds of setUp / tearDown, we can look through the call stack to find appropriate methods, and the class we were in when those were called. """ caller_name = None names = [] frame = inspect.currentframe() is_cleanup = False # Start climbing the ladder until we hit a good method while True: try: frame = frame.f_back name = frame.f_code.co_name names.append(name) if re.search("^(test_|setUp|tearDown)", name): cname = "" if 'self' in frame.f_locals: cname = frame.f_locals['self'].__class__.__name__ if 'cls' in frame.f_locals: cname = frame.f_locals['cls'].__name__ caller_name = cname + ":" + name break elif re.search("^_run_cleanup", name): is_cleanup = True elif name == 'main': caller_name = 'main' break else: cname = "" if 'self' in frame.f_locals: cname = frame.f_locals['self'].__class__.__name__ if 'cls' in frame.f_locals: cname = frame.f_locals['cls'].__name__ # the fact that we are running cleanups is indicated pretty # deep in the stack, so if we see that we want to just # start looking for a real class name, and declare victory # once we do. if is_cleanup and cname: if not re.search("^RunTest", cname): caller_name = cname + ":_run_cleanups" break except Exception: break # prevents frame leaks del frame if caller_name is None: LOG.debug("Sane call name not found in %s" % names) return caller_name tempest-lib-1.0.0/tempest_lib/decorators.py000066400000000000000000000053651266406763200210100ustar00rootroot00000000000000# Copyright 2015 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 functools import uuid import six import testtools def skip_because(*args, **kwargs): """A decorator useful to skip tests hitting known bugs @param bug: bug number causing the test to skip @param condition: optional condition to be True for the skip to have place """ def decorator(f): @functools.wraps(f) def wrapper(self, *func_args, **func_kwargs): skip = False if "condition" in kwargs: if kwargs["condition"] is True: skip = True else: skip = True if "bug" in kwargs and skip is True: if not kwargs['bug'].isdigit(): raise ValueError('bug must be a valid bug number') msg = "Skipped until Bug: %s is resolved." % kwargs["bug"] raise testtools.TestCase.skipException(msg) return f(self, *func_args, **func_kwargs) return wrapper return decorator def idempotent_id(id): """Stub for metadata decorator""" if not isinstance(id, six.string_types): raise TypeError('Test idempotent_id must be string not %s' '' % type(id).__name__) uuid.UUID(id) def decorator(f): f = testtools.testcase.attr('id-%s' % id)(f) if f.__doc__: f.__doc__ = 'Test idempotent id: %s\n%s' % (id, f.__doc__) else: f.__doc__ = 'Test idempotent id: %s' % id return f return decorator class skip_unless_attr(object): """Decorator to skip tests if a specified attr does not exists or False""" def __init__(self, attr, msg=None): self.attr = attr self.message = msg or ("Test case attribute %s not found " "or False") % attr def __call__(self, func): def _skipper(*args, **kw): """Wrapped skipper function.""" testobj = args[0] if not getattr(testobj, self.attr, False): raise testtools.TestCase.skipException(self.message) func(*args, **kw) _skipper.__name__ = func.__name__ _skipper.__doc__ = func.__doc__ return _skipper tempest-lib-1.0.0/tempest_lib/exceptions.py000066400000000000000000000136721266406763200210240ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import testtools class TempestException(Exception): """Base Tempest Exception To correctly use this class, inherit from it and define a 'message' property. That message will get printf'd with the keyword arguments provided to the constructor. """ message = "An unknown exception occurred" def __init__(self, *args, **kwargs): super(TempestException, self).__init__() try: self._error_string = self.message % kwargs except Exception: # at least get the core message out if something happened self._error_string = self.message if len(args) > 0: # If there is a non-kwarg parameter, assume it's the error # message or reason description and tack it on to the end # of the exception message # Convert all arguments into their string representations... args = ["%s" % arg for arg in args] self._error_string = (self._error_string + "\nDetails: %s" % '\n'.join(args)) def __str__(self): return self._error_string class RestClientException(TempestException, testtools.TestCase.failureException): def __init__(self, resp_body=None, *args, **kwargs): if 'resp' in kwargs: self.resp = kwargs.get('resp') self.resp_body = resp_body message = kwargs.get("message", resp_body) super(RestClientException, self).__init__(message, *args, **kwargs) class OtherRestClientException(RestClientException): pass class ServerRestClientException(RestClientException): pass class ClientRestClientException(RestClientException): pass class InvalidHttpSuccessCode(OtherRestClientException): message = "The success code is different than the expected one" class NotFound(ClientRestClientException): message = "Object not found" class Unauthorized(ClientRestClientException): message = 'Unauthorized' class Forbidden(ClientRestClientException): message = "Forbidden" class TimeoutException(OtherRestClientException): message = "Request timed out" class BadRequest(ClientRestClientException): message = "Bad request" class UnprocessableEntity(ClientRestClientException): message = "Unprocessable entity" class RateLimitExceeded(ClientRestClientException): message = "Rate limit exceeded" class OverLimit(ClientRestClientException): message = "Quota exceeded" class ServerFault(ServerRestClientException): message = "Got server fault" class NotImplemented(ServerRestClientException): message = "Got NotImplemented error" class Conflict(ClientRestClientException): message = "An object with that identifier already exists" class Gone(ClientRestClientException): message = "The requested resource is no longer available" class ResponseWithNonEmptyBody(OtherRestClientException): message = ("RFC Violation! Response with %(status)d HTTP Status Code " "MUST NOT have a body") class ResponseWithEntity(OtherRestClientException): message = ("RFC Violation! Response with 205 HTTP Status Code " "MUST NOT have an entity") class InvalidHTTPResponseBody(OtherRestClientException): message = "HTTP response body is invalid json or xml" class InvalidHTTPResponseHeader(OtherRestClientException): message = "HTTP response header is invalid" class InvalidContentType(ClientRestClientException): message = "Invalid content type provided" class UnexpectedContentType(OtherRestClientException): message = "Unexpected content type provided" class UnexpectedResponseCode(OtherRestClientException): message = "Unexpected response code received" class InvalidStructure(TempestException): message = "Invalid structure of table with details" class BadAltAuth(TempestException): """Used when trying and failing to change to alt creds. If alt creds end up the same as primary creds, use this exception. This is often going to be the case when you assume project_id is in the url, but it's not. """ message = "The alt auth looks the same as primary auth for %(part)s" class CommandFailed(Exception): def __init__(self, returncode, cmd, output, stderr): super(CommandFailed, self).__init__() self.returncode = returncode self.cmd = cmd self.stdout = output self.stderr = stderr def __str__(self): return ("Command '%s' returned non-zero exit status %d.\n" "stdout:\n%s\n" "stderr:\n%s" % (self.cmd, self.returncode, self.stdout, self.stderr)) class IdentityError(TempestException): message = "Got identity error" class EndpointNotFound(TempestException): message = "Endpoint not found" class InvalidCredentials(TempestException): message = "Invalid Credentials" class SSHTimeout(TempestException): message = ("Connection to the %(host)s via SSH timed out.\n" "User: %(user)s, Password: %(password)s") class SSHExecCommandFailed(TempestException): """Raised when remotely executed command returns nonzero status.""" message = ("Command '%(command)s', exit status: %(exit_status)d, " "stderr:\n%(stderr)s\n" "stdout:\n%(stdout)s") tempest-lib-1.0.0/tempest_lib/services/000077500000000000000000000000001266406763200201035ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/__init__.py000066400000000000000000000000001266406763200222020ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/compute/000077500000000000000000000000001266406763200215575ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/compute/__init__.py000066400000000000000000000000001266406763200236560ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/compute/agents_client.py000066400000000000000000000046701266406763200247570ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import agents as schema from tempest_lib.common import rest_client class AgentsClient(rest_client.RestClient): """Tests Agents API""" def list_agents(self, **params): """List all agent builds.""" url = 'os-agents' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_agents, resp, body) return rest_client.ResponseBody(resp, body) def create_agent(self, **kwargs): """Create an agent build. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#agentbuild """ post_body = json.dumps({'agent': kwargs}) resp, body = self.post('os-agents', post_body) body = json.loads(body) self.validate_response(schema.create_agent, resp, body) return rest_client.ResponseBody(resp, body) def delete_agent(self, agent_id): """Delete an existing agent build.""" resp, body = self.delete("os-agents/%s" % agent_id) self.validate_response(schema.delete_agent, resp, body) return rest_client.ResponseBody(resp, body) def update_agent(self, agent_id, **kwargs): """Update an agent build. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updatebuild """ put_body = json.dumps({'para': kwargs}) resp, body = self.put('os-agents/%s' % agent_id, put_body) body = json.loads(body) self.validate_response(schema.update_agent, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/aggregates_client.py000066400000000000000000000110731266406763200256020ustar00rootroot00000000000000# Copyright 2013 NEC Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import aggregates as schema from tempest_lib.common import rest_client from tempest_lib import exceptions as lib_exc class AggregatesClient(rest_client.RestClient): def list_aggregates(self): """Get aggregate list.""" resp, body = self.get("os-aggregates") body = json.loads(body) self.validate_response(schema.list_aggregates, resp, body) return rest_client.ResponseBody(resp, body) def show_aggregate(self, aggregate_id): """Get details of the given aggregate.""" resp, body = self.get("os-aggregates/%s" % aggregate_id) body = json.loads(body) self.validate_response(schema.get_aggregate, resp, body) return rest_client.ResponseBody(resp, body) def create_aggregate(self, **kwargs): """Create a new aggregate. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createaggregate """ post_body = json.dumps({'aggregate': kwargs}) resp, body = self.post('os-aggregates', post_body) body = json.loads(body) self.validate_response(schema.create_aggregate, resp, body) return rest_client.ResponseBody(resp, body) def update_aggregate(self, aggregate_id, **kwargs): """Update an aggregate. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updateaggregate """ put_body = json.dumps({'aggregate': kwargs}) resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body) body = json.loads(body) self.validate_response(schema.update_aggregate, resp, body) return rest_client.ResponseBody(resp, body) def delete_aggregate(self, aggregate_id): """Delete the given aggregate.""" resp, body = self.delete("os-aggregates/%s" % aggregate_id) self.validate_response(schema.delete_aggregate, resp, body) return rest_client.ResponseBody(resp, body) def is_resource_deleted(self, id): try: self.show_aggregate(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Return the primary type of resource this client works with.""" return 'aggregate' def add_host(self, aggregate_id, **kwargs): """Add a host to the given aggregate. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#addhost """ post_body = json.dumps({'add_host': kwargs}) resp, body = self.post('os-aggregates/%s/action' % aggregate_id, post_body) body = json.loads(body) self.validate_response(schema.aggregate_add_remove_host, resp, body) return rest_client.ResponseBody(resp, body) def remove_host(self, aggregate_id, **kwargs): """Remove a host from the given aggregate. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#removehost """ post_body = json.dumps({'remove_host': kwargs}) resp, body = self.post('os-aggregates/%s/action' % aggregate_id, post_body) body = json.loads(body) self.validate_response(schema.aggregate_add_remove_host, resp, body) return rest_client.ResponseBody(resp, body) def set_metadata(self, aggregate_id, **kwargs): """Replace the aggregate's existing metadata with new metadata.""" post_body = json.dumps({'set_metadata': kwargs}) resp, body = self.post('os-aggregates/%s/action' % aggregate_id, post_body) body = json.loads(body) self.validate_response(schema.aggregate_set_metadata, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/availability_zone_client.py000066400000000000000000000024401266406763200271740ustar00rootroot00000000000000# Copyright 2013 NEC Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import availability_zone \ as schema from tempest_lib.common import rest_client class AvailabilityZoneClient(rest_client.RestClient): def list_availability_zones(self, detail=False): url = 'os-availability-zone' schema_list = schema.list_availability_zone_list if detail: url += '/detail' schema_list = schema.list_availability_zone_list_detail resp, body = self.get(url) body = json.loads(body) self.validate_response(schema_list, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/baremetal_nodes_client.py000066400000000000000000000032311266406763200266120ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import baremetal_nodes \ as schema from tempest_lib.common import rest_client class BaremetalNodesClient(rest_client.RestClient): """Tests Baremetal API""" def list_baremetal_nodes(self, **params): """List all baremetal nodes.""" url = 'os-baremetal-nodes' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_baremetal_nodes, resp, body) return rest_client.ResponseBody(resp, body) def show_baremetal_node(self, baremetal_node_id): """Return the details of a single baremetal node.""" url = 'os-baremetal-nodes/%s' % baremetal_node_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.get_baremetal_node, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/certificates_client.py000066400000000000000000000026331266406763200261400ustar00rootroot00000000000000# 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. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import certificates as schema from tempest_lib.common import rest_client class CertificatesClient(rest_client.RestClient): def show_certificate(self, certificate_id): url = "os-certificates/%s" % certificate_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.get_certificate, resp, body) return rest_client.ResponseBody(resp, body) def create_certificate(self): """Create a certificate.""" url = "os-certificates" resp, body = self.post(url, None) body = json.loads(body) self.validate_response(schema.create_certificate, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/extensions_client.py000066400000000000000000000024231266406763200256670ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import extensions as schema from tempest_lib.common import rest_client class ExtensionsClient(rest_client.RestClient): def list_extensions(self): url = 'extensions' resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_extensions, resp, body) return rest_client.ResponseBody(resp, body) def show_extension(self, extension_alias): resp, body = self.get('extensions/%s' % extension_alias) body = json.loads(body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/fixed_ips_client.py000066400000000000000000000030531266406763200254420ustar00rootroot00000000000000# 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. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import fixed_ips as schema from tempest_lib.common import rest_client class FixedIPsClient(rest_client.RestClient): def show_fixed_ip(self, fixed_ip): url = "os-fixed-ips/%s" % fixed_ip resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.get_fixed_ip, resp, body) return rest_client.ResponseBody(resp, body) def reserve_fixed_ip(self, fixed_ip, **kwargs): """Reserve/Unreserve a fixed IP. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#reserveIP """ url = "os-fixed-ips/%s/action" % fixed_ip resp, body = self.post(url, json.dumps(kwargs)) self.validate_response(schema.reserve_unreserve_fixed_ip, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/flavors_client.py000066400000000000000000000162001266406763200251420ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import flavors as schema from tempest_lib.api_schema.response.compute.v2_1 import flavors_access \ as schema_access from tempest_lib.api_schema.response.compute.v2_1 import flavors_extra_specs \ as schema_extra_specs from tempest_lib.common import rest_client class FlavorsClient(rest_client.RestClient): def list_flavors(self, detail=False, **params): url = 'flavors' _schema = schema.list_flavors if detail: url += '/detail' _schema = schema.list_flavors_details if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(_schema, resp, body) return rest_client.ResponseBody(resp, body) def show_flavor(self, flavor_id): resp, body = self.get("flavors/%s" % flavor_id) body = json.loads(body) self.validate_response(schema.create_get_flavor_details, resp, body) return rest_client.ResponseBody(resp, body) def create_flavor(self, **kwargs): """Create a new flavor or instance type. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#create-flavors """ if kwargs.get('ephemeral'): kwargs['OS-FLV-EXT-DATA:ephemeral'] = kwargs.pop('ephemeral') if kwargs.get('is_public'): kwargs['os-flavor-access:is_public'] = kwargs.pop('is_public') post_body = json.dumps({'flavor': kwargs}) resp, body = self.post('flavors', post_body) body = json.loads(body) self.validate_response(schema.create_get_flavor_details, resp, body) return rest_client.ResponseBody(resp, body) def delete_flavor(self, flavor_id): """Delete the given flavor.""" resp, body = self.delete("flavors/{0}".format(flavor_id)) self.validate_response(schema.delete_flavor, resp, body) return rest_client.ResponseBody(resp, body) def is_resource_deleted(self, id): # Did not use show_flavor(id) for verification as it gives # 200 ok even for deleted id. LP #981263 # we can remove the loop here and use get by ID when bug gets sortedout flavors = self.list_flavors(detail=True)['flavors'] for flavor in flavors: if flavor['id'] == id: return False return True @property def resource_type(self): """Return the primary type of resource this client works with.""" return 'flavor' def set_flavor_extra_spec(self, flavor_id, **kwargs): """Set extra Specs to the mentioned flavor. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updateFlavorExtraSpec """ post_body = json.dumps({'extra_specs': kwargs}) resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id, post_body) body = json.loads(body) self.validate_response(schema_extra_specs.set_get_flavor_extra_specs, resp, body) return rest_client.ResponseBody(resp, body) def list_flavor_extra_specs(self, flavor_id): """Get extra Specs details of the mentioned flavor.""" resp, body = self.get('flavors/%s/os-extra_specs' % flavor_id) body = json.loads(body) self.validate_response(schema_extra_specs.set_get_flavor_extra_specs, resp, body) return rest_client.ResponseBody(resp, body) def show_flavor_extra_spec(self, flavor_id, key): """Get extra Specs key-value of the mentioned flavor and key.""" resp, body = self.get('flavors/%s/os-extra_specs/%s' % (flavor_id, key)) body = json.loads(body) self.validate_response( schema_extra_specs.set_get_flavor_extra_specs_key, resp, body) return rest_client.ResponseBody(resp, body) def update_flavor_extra_spec(self, flavor_id, key, **kwargs): """Update specified extra Specs of the mentioned flavor and key. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updateflavorspec """ resp, body = self.put('flavors/%s/os-extra_specs/%s' % (flavor_id, key), json.dumps(kwargs)) body = json.loads(body) self.validate_response( schema_extra_specs.set_get_flavor_extra_specs_key, resp, body) return rest_client.ResponseBody(resp, body) def unset_flavor_extra_spec(self, flavor_id, key): """Unset extra Specs from the mentioned flavor.""" resp, body = self.delete('flavors/%s/os-extra_specs/%s' % (flavor_id, key)) self.validate_response(schema.unset_flavor_extra_specs, resp, body) return rest_client.ResponseBody(resp, body) def list_flavor_access(self, flavor_id): """Get flavor access information given the flavor id.""" resp, body = self.get('flavors/%s/os-flavor-access' % flavor_id) body = json.loads(body) self.validate_response(schema_access.add_remove_list_flavor_access, resp, body) return rest_client.ResponseBody(resp, body) def add_flavor_access(self, flavor_id, tenant_id): """Add flavor access for the specified tenant.""" post_body = { 'addTenantAccess': { 'tenant': tenant_id } } post_body = json.dumps(post_body) resp, body = self.post('flavors/%s/action' % flavor_id, post_body) body = json.loads(body) self.validate_response(schema_access.add_remove_list_flavor_access, resp, body) return rest_client.ResponseBody(resp, body) def remove_flavor_access(self, flavor_id, tenant_id): """Remove flavor access from the specified tenant.""" post_body = { 'removeTenantAccess': { 'tenant': tenant_id } } post_body = json.dumps(post_body) resp, body = self.post('flavors/%s/action' % flavor_id, post_body) body = json.loads(body) self.validate_response(schema_access.add_remove_list_flavor_access, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/floating_ip_pools_client.py000066400000000000000000000024361266406763200272030ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import floating_ips as schema from tempest_lib.common import rest_client class FloatingIPPoolsClient(rest_client.RestClient): def list_floating_ip_pools(self, params=None): """Gets all floating IP Pools list.""" url = 'os-floating-ip-pools' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_floating_ip_pools, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/floating_ips_bulk_client.py000066400000000000000000000040551266406763200271660ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import floating_ips as schema from tempest_lib.common import rest_client class FloatingIPsBulkClient(rest_client.RestClient): def create_floating_ips_bulk(self, ip_range, pool, interface): """Allocate floating IPs in bulk.""" post_body = { 'ip_range': ip_range, 'pool': pool, 'interface': interface } post_body = json.dumps({'floating_ips_bulk_create': post_body}) resp, body = self.post('os-floating-ips-bulk', post_body) body = json.loads(body) self.validate_response(schema.create_floating_ips_bulk, resp, body) return rest_client.ResponseBody(resp, body) def list_floating_ips_bulk(self): """Gets all floating IPs in bulk.""" resp, body = self.get('os-floating-ips-bulk') body = json.loads(body) self.validate_response(schema.list_floating_ips_bulk, resp, body) return rest_client.ResponseBody(resp, body) def delete_floating_ips_bulk(self, ip_range): """Deletes the provided floating IPs in bulk.""" post_body = json.dumps({'ip_range': ip_range}) resp, body = self.put('os-floating-ips-bulk/delete', post_body) body = json.loads(body) self.validate_response(schema.delete_floating_ips_bulk, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/floating_ips_client.py000066400000000000000000000075331266406763200261550ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import floating_ips as schema from tempest_lib.common import rest_client from tempest_lib import exceptions as lib_exc class FloatingIPsClient(rest_client.RestClient): def list_floating_ips(self, **params): """Returns a list of all floating IPs filtered by any parameters.""" url = 'os-floating-ips' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_floating_ips, resp, body) return rest_client.ResponseBody(resp, body) def show_floating_ip(self, floating_ip_id): """Get the details of a floating IP.""" url = "os-floating-ips/%s" % floating_ip_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.create_get_floating_ip, resp, body) return rest_client.ResponseBody(resp, body) def create_floating_ip(self, **kwargs): """Allocate a floating IP to the project. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createFloatingIP """ url = 'os-floating-ips' post_body = json.dumps(kwargs) resp, body = self.post(url, post_body) body = json.loads(body) self.validate_response(schema.create_get_floating_ip, resp, body) return rest_client.ResponseBody(resp, body) def delete_floating_ip(self, floating_ip_id): """Deletes the provided floating IP from the project.""" url = "os-floating-ips/%s" % floating_ip_id resp, body = self.delete(url) self.validate_response(schema.add_remove_floating_ip, resp, body) return rest_client.ResponseBody(resp, body) def associate_floating_ip_to_server(self, floating_ip, server_id): """Associate the provided floating IP to a specific server.""" url = "servers/%s/action" % server_id post_body = { 'addFloatingIp': { 'address': floating_ip, } } post_body = json.dumps(post_body) resp, body = self.post(url, post_body) self.validate_response(schema.add_remove_floating_ip, resp, body) return rest_client.ResponseBody(resp, body) def disassociate_floating_ip_from_server(self, floating_ip, server_id): """Disassociate the provided floating IP from a specific server.""" url = "servers/%s/action" % server_id post_body = { 'removeFloatingIp': { 'address': floating_ip, } } post_body = json.dumps(post_body) resp, body = self.post(url, post_body) self.validate_response(schema.add_remove_floating_ip, resp, body) return rest_client.ResponseBody(resp, body) def is_resource_deleted(self, id): try: self.show_floating_ip(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Returns the primary type of resource this client works with.""" return 'floating_ip' tempest-lib-1.0.0/tempest_lib/services/compute/hosts_client.py000066400000000000000000000056301266406763200246330ustar00rootroot00000000000000# Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import hosts as schema from tempest_lib.common import rest_client class HostsClient(rest_client.RestClient): def list_hosts(self, **params): """List all hosts.""" url = 'os-hosts' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_hosts, resp, body) return rest_client.ResponseBody(resp, body) def show_host(self, hostname): """Show detail information for the host.""" resp, body = self.get("os-hosts/%s" % hostname) body = json.loads(body) self.validate_response(schema.get_host_detail, resp, body) return rest_client.ResponseBody(resp, body) def update_host(self, hostname, **kwargs): """Update a host. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#enablehost """ request_body = { 'status': None, 'maintenance_mode': None, } request_body.update(**kwargs) request_body = json.dumps(request_body) resp, body = self.put("os-hosts/%s" % hostname, request_body) body = json.loads(body) self.validate_response(schema.update_host, resp, body) return rest_client.ResponseBody(resp, body) def startup_host(self, hostname): """Startup a host.""" resp, body = self.get("os-hosts/%s/startup" % hostname) body = json.loads(body) self.validate_response(schema.startup_host, resp, body) return rest_client.ResponseBody(resp, body) def shutdown_host(self, hostname): """Shutdown a host.""" resp, body = self.get("os-hosts/%s/shutdown" % hostname) body = json.loads(body) self.validate_response(schema.shutdown_host, resp, body) return rest_client.ResponseBody(resp, body) def reboot_host(self, hostname): """Reboot a host.""" resp, body = self.get("os-hosts/%s/reboot" % hostname) body = json.loads(body) self.validate_response(schema.reboot_host, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/hypervisor_client.py000066400000000000000000000056601266406763200257100ustar00rootroot00000000000000# Copyright 2013 IBM Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import hypervisors as schema from tempest_lib.common import rest_client class HypervisorClient(rest_client.RestClient): def list_hypervisors(self, detail=False): """List hypervisors information.""" url = 'os-hypervisors' _schema = schema.list_search_hypervisors if detail: url += '/detail' _schema = schema.list_hypervisors_detail resp, body = self.get(url) body = json.loads(body) self.validate_response(_schema, resp, body) return rest_client.ResponseBody(resp, body) def show_hypervisor(self, hypervisor_id): """Display the details of the specified hypervisor.""" resp, body = self.get('os-hypervisors/%s' % hypervisor_id) body = json.loads(body) self.validate_response(schema.get_hypervisor, resp, body) return rest_client.ResponseBody(resp, body) def list_servers_on_hypervisor(self, hypervisor_name): """List instances belonging to the specified hypervisor.""" resp, body = self.get('os-hypervisors/%s/servers' % hypervisor_name) body = json.loads(body) self.validate_response(schema.get_hypervisors_servers, resp, body) return rest_client.ResponseBody(resp, body) def show_hypervisor_statistics(self): """Get hypervisor statistics over all compute nodes.""" resp, body = self.get('os-hypervisors/statistics') body = json.loads(body) self.validate_response(schema.get_hypervisor_statistics, resp, body) return rest_client.ResponseBody(resp, body) def show_hypervisor_uptime(self, hypervisor_id): """Display the uptime of the specified hypervisor.""" resp, body = self.get('os-hypervisors/%s/uptime' % hypervisor_id) body = json.loads(body) self.validate_response(schema.get_hypervisor_uptime, resp, body) return rest_client.ResponseBody(resp, body) def search_hypervisor(self, hypervisor_name): """Search specified hypervisor.""" resp, body = self.get('os-hypervisors/%s/search' % hypervisor_name) body = json.loads(body) self.validate_response(schema.list_search_hypervisors, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/images_client.py000066400000000000000000000130361266406763200247370ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import images as schema from tempest_lib.common import rest_client from tempest_lib import exceptions as lib_exc class ImagesClient(rest_client.RestClient): def create_image(self, server_id, **kwargs): """Create an image of the original server. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createImage """ post_body = {'createImage': kwargs} post_body = json.dumps(post_body) resp, body = self.post('servers/%s/action' % server_id, post_body) self.validate_response(schema.create_image, resp, body) return rest_client.ResponseBody(resp, body) def list_images(self, detail=False, **params): """Return a list of all images filtered by any parameter. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#listImages """ url = 'images' _schema = schema.list_images if detail: url += '/detail' _schema = schema.list_images_details if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(_schema, resp, body) return rest_client.ResponseBody(resp, body) def show_image(self, image_id): """Return the details of a single image.""" resp, body = self.get("images/%s" % image_id) self.expected_success(200, resp.status) body = json.loads(body) self.validate_response(schema.get_image, resp, body) return rest_client.ResponseBody(resp, body) def delete_image(self, image_id): """Delete the provided image.""" resp, body = self.delete("images/%s" % image_id) self.validate_response(schema.delete, resp, body) return rest_client.ResponseBody(resp, body) def list_image_metadata(self, image_id): """List all metadata items for an image.""" resp, body = self.get("images/%s/metadata" % image_id) body = json.loads(body) self.validate_response(schema.image_metadata, resp, body) return rest_client.ResponseBody(resp, body) def set_image_metadata(self, image_id, meta): """Set the metadata for an image. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createImageMetadata """ post_body = json.dumps({'metadata': meta}) resp, body = self.put('images/%s/metadata' % image_id, post_body) body = json.loads(body) self.validate_response(schema.image_metadata, resp, body) return rest_client.ResponseBody(resp, body) def update_image_metadata(self, image_id, meta): """Update the metadata for an image. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updateImageMetadata """ post_body = json.dumps({'metadata': meta}) resp, body = self.post('images/%s/metadata' % image_id, post_body) body = json.loads(body) self.validate_response(schema.image_metadata, resp, body) return rest_client.ResponseBody(resp, body) def show_image_metadata_item(self, image_id, key): """Return the value for a specific image metadata key.""" resp, body = self.get("images/%s/metadata/%s" % (image_id, key)) body = json.loads(body) self.validate_response(schema.image_meta_item, resp, body) return rest_client.ResponseBody(resp, body) def set_image_metadata_item(self, image_id, key, meta): """Set the value for a specific image metadata key. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#setImageMetadataItem """ post_body = json.dumps({'meta': meta}) resp, body = self.put('images/%s/metadata/%s' % (image_id, key), post_body) body = json.loads(body) self.validate_response(schema.image_meta_item, resp, body) return rest_client.ResponseBody(resp, body) def delete_image_metadata_item(self, image_id, key): """Delete a single image metadata key/value pair.""" resp, body = self.delete("images/%s/metadata/%s" % (image_id, key)) self.validate_response(schema.delete, resp, body) return rest_client.ResponseBody(resp, body) def is_resource_deleted(self, id): try: self.show_image(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Return the primary type of resource this client works with.""" return 'image' tempest-lib-1.0.0/tempest_lib/services/compute/instance_usage_audit_log_client.py000066400000000000000000000027661266406763200305210ustar00rootroot00000000000000# Copyright 2013 IBM Corporation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import \ instance_usage_audit_logs as schema from tempest_lib.common import rest_client class InstanceUsagesAuditLogClient(rest_client.RestClient): def list_instance_usage_audit_logs(self): url = 'os-instance_usage_audit_log' resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_instance_usage_audit_log, resp, body) return rest_client.ResponseBody(resp, body) def show_instance_usage_audit_log(self, time_before): url = 'os-instance_usage_audit_log/%s' % time_before resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.get_instance_usage_audit_log, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/interfaces_client.py000066400000000000000000000045311266406763200256150ustar00rootroot00000000000000# 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. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import interfaces as schema from tempest_lib.common import rest_client class InterfacesClient(rest_client.RestClient): def list_interfaces(self, server_id): resp, body = self.get('servers/%s/os-interface' % server_id) body = json.loads(body) self.validate_response(schema.list_interfaces, resp, body) return rest_client.ResponseBody(resp, body) def create_interface(self, server_id, **kwargs): """Create an interface. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createAttachInterface """ post_body = {'interfaceAttachment': kwargs} post_body = json.dumps(post_body) resp, body = self.post('servers/%s/os-interface' % server_id, body=post_body) body = json.loads(body) self.validate_response(schema.get_create_interfaces, resp, body) return rest_client.ResponseBody(resp, body) def show_interface(self, server_id, port_id): resp, body = self.get('servers/%s/os-interface/%s' % (server_id, port_id)) body = json.loads(body) self.validate_response(schema.get_create_interfaces, resp, body) return rest_client.ResponseBody(resp, body) def delete_interface(self, server_id, port_id): resp, body = self.delete('servers/%s/os-interface/%s' % (server_id, port_id)) self.validate_response(schema.delete_interface, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/keypairs_client.py000066400000000000000000000037661266406763200253320ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import keypairs as schema from tempest_lib.common import rest_client class KeyPairsClient(rest_client.RestClient): def list_keypairs(self): resp, body = self.get("os-keypairs") body = json.loads(body) self.validate_response(schema.list_keypairs, resp, body) return rest_client.ResponseBody(resp, body) def show_keypair(self, keypair_name): resp, body = self.get("os-keypairs/%s" % keypair_name) body = json.loads(body) self.validate_response(schema.get_keypair, resp, body) return rest_client.ResponseBody(resp, body) def create_keypair(self, **kwargs): """Create a keypair. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createKeypair """ post_body = json.dumps({'keypair': kwargs}) resp, body = self.post("os-keypairs", body=post_body) body = json.loads(body) self.validate_response(schema.create_keypair, resp, body) return rest_client.ResponseBody(resp, body) def delete_keypair(self, keypair_name): resp, body = self.delete("os-keypairs/%s" % keypair_name) self.validate_response(schema.delete_keypair, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/limits_client.py000066400000000000000000000020461266406763200247720ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import limits as schema from tempest_lib.common import rest_client class LimitsClient(rest_client.RestClient): def show_limits(self): resp, body = self.get("limits") body = json.loads(body) self.validate_response(schema.get_limit, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/migrations_client.py000066400000000000000000000025521266406763200256470ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. # # 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 oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import migrations as schema from tempest_lib.common import rest_client class MigrationsClient(rest_client.RestClient): def list_migrations(self, **params): """List all migrations. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#returnmigrations """ url = 'os-migrations' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_migrations, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/networks_client.py000066400000000000000000000023021266406763200253400ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.common import rest_client class NetworksClient(rest_client.RestClient): def list_networks(self): resp, body = self.get("os-networks") body = json.loads(body) self.expected_success(200, resp.status) return rest_client.ResponseBody(resp, body) def show_network(self, network_id): resp, body = self.get("os-networks/%s" % network_id) body = json.loads(body) self.expected_success(200, resp.status) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/quota_classes_client.py000066400000000000000000000035301266406763200263360ustar00rootroot00000000000000# Copyright 2012 NTT Data # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1\ import quota_classes as classes_schema from tempest_lib.common import rest_client class QuotaClassesClient(rest_client.RestClient): def show_quota_class_set(self, quota_class_id): """List the quota class set for a quota class.""" url = 'os-quota-class-sets/%s' % quota_class_id resp, body = self.get(url) body = json.loads(body) self.validate_response(classes_schema.get_quota_class_set, resp, body) return rest_client.ResponseBody(resp, body) def update_quota_class_set(self, quota_class_id, **kwargs): """Update the quota class's limits for one or more resources. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updatequota """ post_body = json.dumps({'quota_class_set': kwargs}) resp, body = self.put('os-quota-class-sets/%s' % quota_class_id, post_body) body = json.loads(body) self.validate_response(classes_schema.update_quota_class_set, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/quotas_client.py000066400000000000000000000051001266406763200247770ustar00rootroot00000000000000# Copyright 2012 NTT Data # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import quotas as schema from tempest_lib.common import rest_client class QuotasClient(rest_client.RestClient): def show_quota_set(self, tenant_id, user_id=None): """List the quota set for a tenant.""" url = 'os-quota-sets/%s' % tenant_id if user_id: url += '?user_id=%s' % user_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.get_quota_set, resp, body) return rest_client.ResponseBody(resp, body) def show_default_quota_set(self, tenant_id): """List the default quota set for a tenant.""" url = 'os-quota-sets/%s/defaults' % tenant_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.get_quota_set, resp, body) return rest_client.ResponseBody(resp, body) def update_quota_set(self, tenant_id, user_id=None, **kwargs): """Updates the tenant's quota limits for one or more resources. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updatesquotatenant """ post_body = json.dumps({'quota_set': kwargs}) if user_id: resp, body = self.put('os-quota-sets/%s?user_id=%s' % (tenant_id, user_id), post_body) else: resp, body = self.put('os-quota-sets/%s' % tenant_id, post_body) body = json.loads(body) self.validate_response(schema.update_quota_set, resp, body) return rest_client.ResponseBody(resp, body) def delete_quota_set(self, tenant_id): """Delete the tenant's quota set.""" resp, body = self.delete('os-quota-sets/%s' % tenant_id) self.validate_response(schema.delete_quota, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/security_group_default_rules_client.py000066400000000000000000000055171266406763200315000ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import \ security_group_default_rule as schema from tempest_lib.common import rest_client class SecurityGroupDefaultRulesClient(rest_client.RestClient): def create_security_default_group_rule(self, **kwargs): """Create security group default rule. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html #createSecGroupDefaultRule """ post_body = json.dumps({'security_group_default_rule': kwargs}) url = 'os-security-group-default-rules' resp, body = self.post(url, post_body) body = json.loads(body) self.validate_response(schema.create_get_security_group_default_rule, resp, body) return rest_client.ResponseBody(resp, body) def delete_security_group_default_rule(self, security_group_default_rule_id): """Delete the provided Security Group default rule.""" resp, body = self.delete('os-security-group-default-rules/%s' % ( security_group_default_rule_id)) self.validate_response(schema.delete_security_group_default_rule, resp, body) return rest_client.ResponseBody(resp, body) def list_security_group_default_rules(self): """List all Security Group default rules.""" resp, body = self.get('os-security-group-default-rules') body = json.loads(body) self.validate_response(schema.list_security_group_default_rules, resp, body) return rest_client.ResponseBody(resp, body) def show_security_group_default_rule(self, security_group_default_rule_id): """Return the details of provided Security Group default rule.""" resp, body = self.get('os-security-group-default-rules/%s' % security_group_default_rule_id) body = json.loads(body) self.validate_response(schema.create_get_security_group_default_rule, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/security_group_rules_client.py000066400000000000000000000034011266406763200277620ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import \ security_groups as schema from tempest_lib.common import rest_client class SecurityGroupRulesClient(rest_client.RestClient): def create_security_group_rule(self, **kwargs): """Create a new security group rule. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createSecGroupRule """ post_body = json.dumps({'security_group_rule': kwargs}) url = 'os-security-group-rules' resp, body = self.post(url, post_body) body = json.loads(body) self.validate_response(schema.create_security_group_rule, resp, body) return rest_client.ResponseBody(resp, body) def delete_security_group_rule(self, group_rule_id): """Deletes the provided Security Group rule.""" resp, body = self.delete('os-security-group-rules/%s' % group_rule_id) self.validate_response(schema.delete_security_group_rule, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/security_groups_client.py000066400000000000000000000065721266406763200267470ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import \ security_groups as schema from tempest_lib.common import rest_client from tempest_lib import exceptions as lib_exc class SecurityGroupsClient(rest_client.RestClient): def list_security_groups(self, **params): """List all security groups for a user.""" url = 'os-security-groups' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_security_groups, resp, body) return rest_client.ResponseBody(resp, body) def show_security_group(self, security_group_id): """Get the details of a Security Group.""" url = "os-security-groups/%s" % security_group_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.get_security_group, resp, body) return rest_client.ResponseBody(resp, body) def create_security_group(self, **kwargs): """Create a new security group. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createSecGroup """ post_body = json.dumps({'security_group': kwargs}) resp, body = self.post('os-security-groups', post_body) body = json.loads(body) self.validate_response(schema.get_security_group, resp, body) return rest_client.ResponseBody(resp, body) def update_security_group(self, security_group_id, **kwargs): """Update a security group. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updateSecGroup """ post_body = json.dumps({'security_group': kwargs}) resp, body = self.put('os-security-groups/%s' % security_group_id, post_body) body = json.loads(body) self.validate_response(schema.update_security_group, resp, body) return rest_client.ResponseBody(resp, body) def delete_security_group(self, security_group_id): """Delete the provided Security Group.""" resp, body = self.delete( 'os-security-groups/%s' % security_group_id) self.validate_response(schema.delete_security_group, resp, body) return rest_client.ResponseBody(resp, body) def is_resource_deleted(self, id): try: self.show_security_group(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Return the primary type of resource this client works with.""" return 'security_group' tempest-lib-1.0.0/tempest_lib/services/compute/server_groups_client.py000066400000000000000000000044351266406763200264020ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import servers as schema from tempest_lib.common import rest_client class ServerGroupsClient(rest_client.RestClient): def create_server_group(self, **kwargs): """Create the server group. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createServerGroup """ post_body = json.dumps({'server_group': kwargs}) resp, body = self.post('os-server-groups', post_body) body = json.loads(body) self.validate_response(schema.create_show_server_group, resp, body) return rest_client.ResponseBody(resp, body) def delete_server_group(self, server_group_id): """Delete the given server-group.""" resp, body = self.delete("os-server-groups/%s" % server_group_id) self.validate_response(schema.delete_server_group, resp, body) return rest_client.ResponseBody(resp, body) def list_server_groups(self): """List the server-groups.""" resp, body = self.get("os-server-groups") body = json.loads(body) self.validate_response(schema.list_server_groups, resp, body) return rest_client.ResponseBody(resp, body) def show_server_group(self, server_group_id): """Get the details of given server_group.""" resp, body = self.get("os-server-groups/%s" % server_group_id) body = json.loads(body) self.validate_response(schema.create_show_server_group, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/servers_client.py000066400000000000000000000550371266406763200251720ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import servers as schema from tempest_lib.common import rest_client class ServersClient(rest_client.RestClient): def __init__(self, auth_provider, service, region, enable_instance_password=True, **kwargs): super(ServersClient, self).__init__( auth_provider, service, region, **kwargs) self.enable_instance_password = enable_instance_password def create_server(self, **kwargs): """Create server. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createServer Most parameters except the following are passed to the API without any changes. :param disk_config: The name is changed to OS-DCF:diskConfig :param scheduler_hints: The name is changed to os:scheduler_hints and the parameter is set in the same level as the parameter 'server'. """ body = copy.deepcopy(kwargs) if body.get('disk_config'): body['OS-DCF:diskConfig'] = body.pop('disk_config') hints = None if body.get('scheduler_hints'): hints = {'os:scheduler_hints': body.pop('scheduler_hints')} post_body = {'server': body} if hints: post_body = dict(post_body.items() + hints.items()) post_body = json.dumps(post_body) resp, body = self.post('servers', post_body) body = json.loads(body) # NOTE(maurosr): this deals with the case of multiple server create # with return reservation id set True if 'reservation_id' in body: return rest_client.ResponseBody(resp, body) if self.enable_instance_password: create_schema = schema.create_server_with_admin_pass else: create_schema = schema.create_server self.validate_response(create_schema, resp, body) return rest_client.ResponseBody(resp, body) def update_server(self, server_id, **kwargs): """Update server. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#updateServer Most parameters except the following are passed to the API without any changes. :param disk_config: The name is changed to OS-DCF:diskConfig """ if kwargs.get('disk_config'): kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config') post_body = json.dumps({'server': kwargs}) resp, body = self.put("servers/%s" % server_id, post_body) body = json.loads(body) self.validate_response(schema.update_server, resp, body) return rest_client.ResponseBody(resp, body) def show_server(self, server_id): """Get server details.""" resp, body = self.get("servers/%s" % server_id) body = json.loads(body) self.validate_response(schema.get_server, resp, body) return rest_client.ResponseBody(resp, body) def delete_server(self, server_id): """Delete server.""" resp, body = self.delete("servers/%s" % server_id) self.validate_response(schema.delete_server, resp, body) return rest_client.ResponseBody(resp, body) def list_servers(self, detail=False, **params): """List servers. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#listServers and http://developer.openstack.org/ api-ref-compute-v2.1.html#listDetailServers """ url = 'servers' _schema = schema.list_servers if detail: url += '/detail' _schema = schema.list_servers_detail if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(_schema, resp, body) return rest_client.ResponseBody(resp, body) def list_addresses(self, server_id): """Lists all addresses for a server.""" resp, body = self.get("servers/%s/ips" % server_id) body = json.loads(body) self.validate_response(schema.list_addresses, resp, body) return rest_client.ResponseBody(resp, body) def list_addresses_by_network(self, server_id, network_id): """Lists all addresses of a specific network type for a server.""" resp, body = self.get("servers/%s/ips/%s" % (server_id, network_id)) body = json.loads(body) self.validate_response(schema.list_addresses_by_network, resp, body) return rest_client.ResponseBody(resp, body) def action(self, server_id, action_name, schema=schema.server_actions_common_schema, **kwargs): post_body = json.dumps({action_name: kwargs}) resp, body = self.post('servers/%s/action' % server_id, post_body) if body: body = json.loads(body) self.validate_response(schema, resp, body) return rest_client.ResponseBody(resp, body) def create_backup(self, server_id, **kwargs): """Backup a server instance. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createBackup """ return self.action(server_id, "createBackup", **kwargs) def change_password(self, server_id, **kwargs): """Change the root password for the server. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#changePassword """ return self.action(server_id, 'changePassword', **kwargs) def show_password(self, server_id): resp, body = self.get("servers/%s/os-server-password" % server_id) body = json.loads(body) self.validate_response(schema.show_password, resp, body) return rest_client.ResponseBody(resp, body) def delete_password(self, server_id): """Removes the encrypted server password from the metadata server Note that this does not actually change the instance server password. """ resp, body = self.delete("servers/%s/os-server-password" % server_id) self.validate_response(schema.server_actions_delete_password, resp, body) return rest_client.ResponseBody(resp, body) def reboot_server(self, server_id, **kwargs): """Reboot a server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#reboot """ return self.action(server_id, 'reboot', **kwargs) def rebuild_server(self, server_id, image_ref, **kwargs): """Rebuild a server with a new image. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#rebuild Most parameters except the following are passed to the API without any changes. :param disk_config: The name is changed to OS-DCF:diskConfig """ kwargs['imageRef'] = image_ref if 'disk_config' in kwargs: kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config') if self.enable_instance_password: rebuild_schema = schema.rebuild_server_with_admin_pass else: rebuild_schema = schema.rebuild_server return self.action(server_id, 'rebuild', rebuild_schema, **kwargs) def resize_server(self, server_id, flavor_ref, **kwargs): """Change the flavor of a server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#resize Most parameters except the following are passed to the API without any changes. :param disk_config: The name is changed to OS-DCF:diskConfig """ kwargs['flavorRef'] = flavor_ref if 'disk_config' in kwargs: kwargs['OS-DCF:diskConfig'] = kwargs.pop('disk_config') return self.action(server_id, 'resize', **kwargs) def confirm_resize_server(self, server_id, **kwargs): """Confirm the flavor change for a server. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#confirmResize """ return self.action(server_id, 'confirmResize', schema.server_actions_confirm_resize, **kwargs) def revert_resize_server(self, server_id, **kwargs): """Revert a server back to its original flavor. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#revertResize """ return self.action(server_id, 'revertResize', **kwargs) def list_server_metadata(self, server_id): resp, body = self.get("servers/%s/metadata" % server_id) body = json.loads(body) self.validate_response(schema.list_server_metadata, resp, body) return rest_client.ResponseBody(resp, body) def set_server_metadata(self, server_id, meta, no_metadata_field=False): if no_metadata_field: post_body = "" else: post_body = json.dumps({'metadata': meta}) resp, body = self.put('servers/%s/metadata' % server_id, post_body) body = json.loads(body) self.validate_response(schema.set_server_metadata, resp, body) return rest_client.ResponseBody(resp, body) def update_server_metadata(self, server_id, meta): post_body = json.dumps({'metadata': meta}) resp, body = self.post('servers/%s/metadata' % server_id, post_body) body = json.loads(body) self.validate_response(schema.update_server_metadata, resp, body) return rest_client.ResponseBody(resp, body) def show_server_metadata_item(self, server_id, key): resp, body = self.get("servers/%s/metadata/%s" % (server_id, key)) body = json.loads(body) self.validate_response(schema.set_show_server_metadata_item, resp, body) return rest_client.ResponseBody(resp, body) def set_server_metadata_item(self, server_id, key, meta): post_body = json.dumps({'meta': meta}) resp, body = self.put('servers/%s/metadata/%s' % (server_id, key), post_body) body = json.loads(body) self.validate_response(schema.set_show_server_metadata_item, resp, body) return rest_client.ResponseBody(resp, body) def delete_server_metadata_item(self, server_id, key): resp, body = self.delete("servers/%s/metadata/%s" % (server_id, key)) self.validate_response(schema.delete_server_metadata_item, resp, body) return rest_client.ResponseBody(resp, body) def stop_server(self, server_id, **kwargs): return self.action(server_id, 'os-stop', **kwargs) def start_server(self, server_id, **kwargs): return self.action(server_id, 'os-start', **kwargs) def attach_volume(self, server_id, **kwargs): """Attaches a volume to a server instance.""" post_body = json.dumps({'volumeAttachment': kwargs}) resp, body = self.post('servers/%s/os-volume_attachments' % server_id, post_body) body = json.loads(body) self.validate_response(schema.attach_volume, resp, body) return rest_client.ResponseBody(resp, body) def update_attached_volume(self, server_id, attachment_id, **kwargs): """Swaps a volume attached to an instance for another volume""" post_body = json.dumps({'volumeAttachment': kwargs}) resp, body = self.put('servers/%s/os-volume_attachments/%s' % (server_id, attachment_id), post_body) self.validate_response(schema.update_attached_volume, resp, body) return rest_client.ResponseBody(resp, body) def detach_volume(self, server_id, volume_id): # noqa """Detaches a volume from a server instance.""" resp, body = self.delete('servers/%s/os-volume_attachments/%s' % (server_id, volume_id)) self.validate_response(schema.detach_volume, resp, body) return rest_client.ResponseBody(resp, body) def show_volume_attachment(self, server_id, volume_id): """Return details about the given volume attachment.""" resp, body = self.get('servers/%s/os-volume_attachments/%s' % ( server_id, volume_id)) body = json.loads(body) self.validate_response(schema.show_volume_attachment, resp, body) return rest_client.ResponseBody(resp, body) def list_volume_attachments(self, server_id): """Returns the list of volume attachments for a given instance.""" resp, body = self.get('servers/%s/os-volume_attachments' % ( server_id)) body = json.loads(body) self.validate_response(schema.list_volume_attachments, resp, body) return rest_client.ResponseBody(resp, body) def add_security_group(self, server_id, **kwargs): """Add a security group to the server. Available params: TODO """ # TODO(oomichi): The api-site doesn't contain this API description. # So the above should be changed to the api-site link after # adding the description on the api-site. # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199 return self.action(server_id, 'addSecurityGroup', **kwargs) def remove_security_group(self, server_id, **kwargs): """Remove a security group from the server. Available params: TODO """ # TODO(oomichi): The api-site doesn't contain this API description. # So the above should be changed to the api-site link after # adding the description on the api-site. # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524199 return self.action(server_id, 'removeSecurityGroup', **kwargs) def live_migrate_server(self, server_id, **kwargs): """This should be called with administrator privileges. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#migrateLive """ return self.action(server_id, 'os-migrateLive', **kwargs) def migrate_server(self, server_id, **kwargs): """Migrate a server to a new host. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#migrate """ return self.action(server_id, 'migrate', **kwargs) def lock_server(self, server_id, **kwargs): """Lock the given server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#lock """ return self.action(server_id, 'lock', **kwargs) def unlock_server(self, server_id, **kwargs): """UNlock the given server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#unlock """ return self.action(server_id, 'unlock', **kwargs) def suspend_server(self, server_id, **kwargs): """Suspend the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#suspend """ return self.action(server_id, 'suspend', **kwargs) def resume_server(self, server_id, **kwargs): """Un-suspend the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#resume """ return self.action(server_id, 'resume', **kwargs) def pause_server(self, server_id, **kwargs): """Pause the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#pause """ return self.action(server_id, 'pause', **kwargs) def unpause_server(self, server_id, **kwargs): """Un-pause the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#unpause """ return self.action(server_id, 'unpause', **kwargs) def reset_state(self, server_id, **kwargs): """Reset the state of a server to active/error. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#resetState """ return self.action(server_id, 'os-resetState', **kwargs) def shelve_server(self, server_id, **kwargs): """Shelve the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#shelve """ return self.action(server_id, 'shelve', **kwargs) def unshelve_server(self, server_id, **kwargs): """Un-shelve the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#unshelve """ return self.action(server_id, 'unshelve', **kwargs) def shelve_offload_server(self, server_id, **kwargs): """Shelve-offload the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#shelveOffload """ return self.action(server_id, 'shelveOffload', **kwargs) def get_console_output(self, server_id, **kwargs): """Get console output. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#getConsoleOutput """ return self.action(server_id, 'os-getConsoleOutput', schema.get_console_output, **kwargs) def list_virtual_interfaces(self, server_id): """List the virtual interfaces used in an instance.""" resp, body = self.get('/'.join(['servers', server_id, 'os-virtual-interfaces'])) body = json.loads(body) self.validate_response(schema.list_virtual_interfaces, resp, body) return rest_client.ResponseBody(resp, body) def rescue_server(self, server_id, **kwargs): """Rescue the provided server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#rescue """ return self.action(server_id, 'rescue', schema.rescue_server, **kwargs) def unrescue_server(self, server_id): """Unrescue the provided server.""" return self.action(server_id, 'unrescue') def show_server_diagnostics(self, server_id): """Get the usage data for a server.""" resp, body = self.get("servers/%s/diagnostics" % server_id) return rest_client.ResponseBody(resp, json.loads(body)) def list_instance_actions(self, server_id): """List the provided server action.""" resp, body = self.get("servers/%s/os-instance-actions" % server_id) body = json.loads(body) self.validate_response(schema.list_instance_actions, resp, body) return rest_client.ResponseBody(resp, body) def show_instance_action(self, server_id, request_id): """Returns the action details of the provided server.""" resp, body = self.get("servers/%s/os-instance-actions/%s" % (server_id, request_id)) body = json.loads(body) self.validate_response(schema.show_instance_action, resp, body) return rest_client.ResponseBody(resp, body) def force_delete_server(self, server_id, **kwargs): """Force delete a server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#forceDelete """ return self.action(server_id, 'forceDelete', **kwargs) def restore_soft_deleted_server(self, server_id, **kwargs): """Restore a soft-deleted server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#restore """ return self.action(server_id, 'restore', **kwargs) def reset_network(self, server_id, **kwargs): """Reset the Network of a server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#resetNetwork """ return self.action(server_id, 'resetNetwork', **kwargs) def inject_network_info(self, server_id, **kwargs): """Inject the Network Info into server. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#injectNetworkInfo """ return self.action(server_id, 'injectNetworkInfo', **kwargs) def get_vnc_console(self, server_id, **kwargs): """Get URL of VNC console. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#getVNCConsole """ return self.action(server_id, "os-getVNCConsole", schema.get_vnc_console, **kwargs) def add_fixed_ip(self, server_id, **kwargs): """Add a fixed IP to server instance. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#addFixedIp """ return self.action(server_id, 'addFixedIp', **kwargs) def remove_fixed_ip(self, server_id, **kwargs): """Remove input fixed IP from input server instance. Available params: http://developer.openstack.org/ api-ref-compute-v2.1.html#removeFixedIp """ return self.action(server_id, 'removeFixedIp', **kwargs) tempest-lib-1.0.0/tempest_lib/services/compute/services_client.py000066400000000000000000000042541266406763200253170ustar00rootroot00000000000000# Copyright 2013 NEC Corporation # 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. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import services as schema from tempest_lib.common import rest_client class ServicesClient(rest_client.RestClient): def list_services(self, **params): url = 'os-services' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_services, resp, body) return rest_client.ResponseBody(resp, body) def enable_service(self, **kwargs): """Enable service on a host. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#enableScheduling """ post_body = json.dumps(kwargs) resp, body = self.put('os-services/enable', post_body) body = json.loads(body) self.validate_response(schema.enable_disable_service, resp, body) return rest_client.ResponseBody(resp, body) def disable_service(self, **kwargs): """Disable service on a host. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#disableScheduling """ post_body = json.dumps(kwargs) resp, body = self.put('os-services/disable', post_body) body = json.loads(body) self.validate_response(schema.enable_disable_service, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/snapshots_client.py000066400000000000000000000053101266406763200255100ustar00rootroot00000000000000# Copyright 2015 Fujitsu(fnst) Corporation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import snapshots as schema from tempest_lib.common import rest_client from tempest_lib import exceptions as lib_exc class SnapshotsClient(rest_client.RestClient): def create_snapshot(self, volume_id, **kwargs): """Create a snapshot. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createSnapshot """ post_body = { 'volume_id': volume_id } post_body.update(kwargs) post_body = json.dumps({'snapshot': post_body}) resp, body = self.post('os-snapshots', post_body) body = json.loads(body) self.validate_response(schema.create_get_snapshot, resp, body) return rest_client.ResponseBody(resp, body) def show_snapshot(self, snapshot_id): url = "os-snapshots/%s" % snapshot_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.create_get_snapshot, resp, body) return rest_client.ResponseBody(resp, body) def list_snapshots(self, detail=False, params=None): url = 'os-snapshots' if detail: url += '/detail' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_snapshots, resp, body) return rest_client.ResponseBody(resp, body) def delete_snapshot(self, snapshot_id): resp, body = self.delete("os-snapshots/%s" % snapshot_id) self.validate_response(schema.delete_snapshot, resp, body) return rest_client.ResponseBody(resp, body) def is_resource_deleted(self, id): try: self.show_snapshot(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Return the primary type of resource this client works with.""" return 'snapshot' tempest-lib-1.0.0/tempest_lib/services/compute/tenant_networks_client.py000066400000000000000000000026061266406763200267200ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from tempest_lib.api_schema.response.compute.v2_1 import tenant_networks from tempest_lib.common import rest_client class TenantNetworksClient(rest_client.RestClient): def list_tenant_networks(self): resp, body = self.get("os-tenant-networks") body = json.loads(body) self.validate_response(tenant_networks.list_tenant_networks, resp, body) return rest_client.ResponseBody(resp, body) def show_tenant_network(self, network_id): resp, body = self.get("os-tenant-networks/%s" % network_id) body = json.loads(body) self.validate_response(tenant_networks.get_tenant_network, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/tenant_usages_client.py000066400000000000000000000031221266406763200263250ustar00rootroot00000000000000# Copyright 2013 NEC Corporation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import tenant_usages from tempest_lib.common import rest_client class TenantUsagesClient(rest_client.RestClient): def list_tenant_usages(self, **params): url = 'os-simple-tenant-usage' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(tenant_usages.list_tenant_usage, resp, body) return rest_client.ResponseBody(resp, body) def show_tenant_usage(self, tenant_id, **params): url = 'os-simple-tenant-usage/%s' % tenant_id if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(tenant_usages.get_tenant_usage, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/versions_client.py000066400000000000000000000043711266406763200253440ustar00rootroot00000000000000# Copyright (c) 2015 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 oslo_serialization import jsonutils as json from six.moves import urllib from tempest_lib.api_schema.response.compute.v2_1 import versions as schema from tempest_lib.common import rest_client class VersionsClient(rest_client.RestClient): def _get_base_version_url(self): # NOTE: The URL which is gotten from keystone's catalog contains # API version and project-id like "v2/{project-id}", but we need # to access the URL which doesn't contain them for getting API # versions. For that, here should use raw_request() instead of # get(). endpoint = self.base_url url = urllib.parse.urlparse(endpoint) return '%s://%s/' % (url.scheme, url.netloc) def list_versions(self): version_url = self._get_base_version_url() resp, body = self.raw_request(version_url, 'GET') body = json.loads(body) self.validate_response(schema.list_versions, resp, body) return rest_client.ResponseBody(resp, body) def get_version_by_url(self, version_url): """Get the version document by url. This gets the version document for a url, useful in testing the contents of things like /v2/ or /v2.1/ in Nova. That controller needs authenticated access, so we have to get ourselves a token before making the request. """ # we need a token for this request resp, body = self.raw_request(version_url, 'GET', {'X-Auth-Token': self.token}) body = json.loads(body) self.validate_response(schema.get_one_version, resp, body) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/compute/volumes_client.py000066400000000000000000000052611266406763200251650ustar00rootroot00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.api_schema.response.compute.v2_1 import volumes as schema from tempest_lib.common import rest_client from tempest_lib import exceptions as lib_exc class VolumesClient(rest_client.RestClient): def list_volumes(self, detail=False, **params): """List all the volumes created.""" url = 'os-volumes' if detail: url += '/detail' if params: url += '?%s' % urllib.urlencode(params) resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.list_volumes, resp, body) return rest_client.ResponseBody(resp, body) def show_volume(self, volume_id): """Return the details of a single volume.""" url = "os-volumes/%s" % volume_id resp, body = self.get(url) body = json.loads(body) self.validate_response(schema.create_get_volume, resp, body) return rest_client.ResponseBody(resp, body) def create_volume(self, **kwargs): """Create a new Volume. Available params: see http://developer.openstack.org/ api-ref-compute-v2.1.html#createVolume """ post_body = json.dumps({'volume': kwargs}) resp, body = self.post('os-volumes', post_body) body = json.loads(body) self.validate_response(schema.create_get_volume, resp, body) return rest_client.ResponseBody(resp, body) def delete_volume(self, volume_id): """Delete the Specified Volume.""" resp, body = self.delete("os-volumes/%s" % volume_id) self.validate_response(schema.delete_volume, resp, body) return rest_client.ResponseBody(resp, body) def is_resource_deleted(self, id): try: self.show_volume(id) except lib_exc.NotFound: return True return False @property def resource_type(self): """Return the primary type of resource this client works with.""" return 'volume' tempest-lib-1.0.0/tempest_lib/services/identity/000077500000000000000000000000001266406763200217345ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/identity/__init__.py000066400000000000000000000000001266406763200240330ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/identity/v2/000077500000000000000000000000001266406763200222635ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/identity/v2/__init__.py000066400000000000000000000000001266406763200243620ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/identity/v2/token_client.py000066400000000000000000000102061266406763200253120ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from oslo_serialization import jsonutils as json from tempest_lib.common import rest_client from tempest_lib import exceptions class TokenClient(rest_client.RestClient): def __init__(self, auth_url, disable_ssl_certificate_validation=None, ca_certs=None, trace_requests=None): dscv = disable_ssl_certificate_validation super(TokenClient, self).__init__( None, None, None, disable_ssl_certificate_validation=dscv, ca_certs=ca_certs, trace_requests=trace_requests) if auth_url is None: raise exceptions.IdentityError("Couldn't determine auth_url") # Normalize URI to ensure /tokens is in it. if 'tokens' not in auth_url: auth_url = auth_url.rstrip('/') + '/tokens' self.auth_url = auth_url def auth(self, user, password, tenant=None): creds = { 'auth': { 'passwordCredentials': { 'username': user, 'password': password, }, } } if tenant: creds['auth']['tenantName'] = tenant body = json.dumps(creds, sort_keys=True) resp, body = self.post(self.auth_url, body=body) self.expected_success(200, resp.status) return rest_client.ResponseBody(resp, body['access']) def auth_token(self, token_id, tenant=None): creds = { 'auth': { 'token': { 'id': token_id, }, } } if tenant: creds['auth']['tenantName'] = tenant body = json.dumps(creds) resp, body = self.post(self.auth_url, body=body) self.expected_success(200, resp.status) return rest_client.ResponseBody(resp, body['access']) def request(self, method, url, extra_headers=False, headers=None, body=None): """A simple HTTP request interface.""" if headers is None: headers = self.get_headers(accept_type="json") elif extra_headers: try: headers.update(self.get_headers(accept_type="json")) except (ValueError, TypeError): headers = self.get_headers(accept_type="json") resp, resp_body = self.raw_request(url, method, headers=headers, body=body) self._log_request(method, url, resp, req_headers=headers, req_body='', resp_body=resp_body) if resp.status in [401, 403]: resp_body = json.loads(resp_body) raise exceptions.Unauthorized(resp_body['error']['message']) elif resp.status not in [200, 201]: raise exceptions.IdentityError( 'Unexpected status code {0}'.format(resp.status)) return resp, json.loads(resp_body) def get_token(self, user, password, tenant, auth_data=False): """Returns (token id, token data) for supplied credentials.""" body = self.auth(user, password, tenant) if auth_data: return body['token']['id'], body else: return body['token']['id'] class TokenClientJSON(TokenClient): LOG = logging.getLogger(__name__) def _warn(self): self.LOG.warning("%s class was deprecated and renamed to %s" % (self.__class__.__name__, 'TokenClient')) def __init__(self, *args, **kwargs): self._warn() super(TokenClientJSON, self).__init__(*args, **kwargs) tempest-lib-1.0.0/tempest_lib/services/identity/v3/000077500000000000000000000000001266406763200222645ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/identity/v3/__init__.py000066400000000000000000000000001266406763200243630ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/identity/v3/token_client.py000066400000000000000000000153711266406763200253230ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from oslo_serialization import jsonutils as json from tempest_lib.common import rest_client from tempest_lib import exceptions class V3TokenClient(rest_client.RestClient): def __init__(self, auth_url, disable_ssl_certificate_validation=None, ca_certs=None, trace_requests=None): dscv = disable_ssl_certificate_validation super(V3TokenClient, self).__init__( None, None, None, disable_ssl_certificate_validation=dscv, ca_certs=ca_certs, trace_requests=trace_requests) if auth_url is None: raise exceptions.IdentityError("Couldn't determine auth_url") if 'auth/tokens' not in auth_url: auth_url = auth_url.rstrip('/') + '/auth/tokens' self.auth_url = auth_url def auth(self, user_id=None, username=None, password=None, project_id=None, project_name=None, user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, domain_id=None, domain_name=None, token=None): """Obtains a token from the authentication service :param user_id: user id :param username: user name :param user_domain_id: the user domain id :param user_domain_name: the user domain name :param project_domain_id: the project domain id :param project_domain_name: the project domain name :param domain_id: a domain id to scope to :param domain_name: a domain name to scope to :param project_id: a project id to scope to :param project_name: a project name to scope to :param token: a token to re-scope. Accepts different combinations of credentials. Sample sample valid combinations: - token - token, project_name, project_domain_id - user_id, password - username, password, user_domain_id - username, password, project_name, user_domain_id, project_domain_id Validation is left to the server side. """ creds = { 'auth': { 'identity': { 'methods': [], } } } id_obj = creds['auth']['identity'] if token: id_obj['methods'].append('token') id_obj['token'] = { 'id': token } if (user_id or username) and password: id_obj['methods'].append('password') id_obj['password'] = { 'user': { 'password': password, } } if user_id: id_obj['password']['user']['id'] = user_id else: id_obj['password']['user']['name'] = username _domain = None if user_domain_id is not None: _domain = dict(id=user_domain_id) elif user_domain_name is not None: _domain = dict(name=user_domain_name) if _domain: id_obj['password']['user']['domain'] = _domain if (project_id or project_name): _project = dict() if project_id: _project['id'] = project_id elif project_name: _project['name'] = project_name if project_domain_id is not None: _project['domain'] = {'id': project_domain_id} elif project_domain_name is not None: _project['domain'] = {'name': project_domain_name} creds['auth']['scope'] = dict(project=_project) elif domain_id: creds['auth']['scope'] = dict(domain={'id': domain_id}) elif domain_name: creds['auth']['scope'] = dict(domain={'name': domain_name}) body = json.dumps(creds, sort_keys=True) resp, body = self.post(self.auth_url, body=body) self.expected_success(201, resp.status) return rest_client.ResponseBody(resp, body) def request(self, method, url, extra_headers=False, headers=None, body=None): """A simple HTTP request interface.""" if headers is None: # Always accept 'json', for xml token client too. # Because XML response is not easily # converted to the corresponding JSON one headers = self.get_headers(accept_type="json") elif extra_headers: try: headers.update(self.get_headers(accept_type="json")) except (ValueError, TypeError): headers = self.get_headers(accept_type="json") resp, resp_body = self.raw_request(url, method, headers=headers, body=body) self._log_request(method, url, resp, req_headers=headers, req_body='', resp_body=resp_body) if resp.status in [401, 403]: resp_body = json.loads(resp_body) raise exceptions.Unauthorized(resp_body['error']['message']) elif resp.status not in [200, 201, 204]: raise exceptions.IdentityError( 'Unexpected status code {0}'.format(resp.status)) return resp, json.loads(resp_body) def get_token(self, **kwargs): """Returns (token id, token data) for supplied credentials""" auth_data = kwargs.pop('auth_data', False) if not (kwargs.get('user_domain_id') or kwargs.get('user_domain_name')): kwargs['user_domain_name'] = 'Default' if not (kwargs.get('project_domain_id') or kwargs.get('project_domain_name')): kwargs['project_domain_name'] = 'Default' body = self.auth(**kwargs) token = body.response.get('x-subject-token') if auth_data: return token, body['token'] else: return token class V3TokenClientJSON(V3TokenClient): LOG = logging.getLogger(__name__) def _warn(self): self.LOG.warning("%s class was deprecated and renamed to %s" % (self.__class__.__name__, 'V3TokenClient')) def __init__(self, *args, **kwargs): self._warn() super(V3TokenClientJSON, self).__init__(*args, **kwargs) tempest-lib-1.0.0/tempest_lib/services/network/000077500000000000000000000000001266406763200215745ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/network/__init__.py000066400000000000000000000000001266406763200236730ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/services/network/agents_client.py000066400000000000000000000055561266406763200250000ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.network import base class AgentsClient(base.BaseNetworkClient): def update_agent(self, agent_id, **kwargs): """Update agent.""" # TODO(piyush): Current api-site doesn't contain this API description. # After fixing the api-site, we need to fix here also for putting the # link to api-site. # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526673 uri = '/agents/%s' % agent_id return self.update_resource(uri, kwargs) def show_agent(self, agent_id, **fields): uri = '/agents/%s' % agent_id return self.show_resource(uri, **fields) def list_agents(self, **filters): uri = '/agents' return self.list_resources(uri, **filters) def list_routers_on_l3_agent(self, agent_id): uri = '/agents/%s/l3-routers' % agent_id return self.list_resources(uri) def create_router_on_l3_agent(self, agent_id, **kwargs): # TODO(piyush): Current api-site doesn't contain this API description. # After fixing the api-site, we need to fix here also for putting the # link to api-site. # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526670 uri = '/agents/%s/l3-routers' % agent_id return self.create_resource(uri, kwargs) def delete_router_from_l3_agent(self, agent_id, router_id): uri = '/agents/%s/l3-routers/%s' % (agent_id, router_id) return self.delete_resource(uri) def list_networks_hosted_by_one_dhcp_agent(self, agent_id): uri = '/agents/%s/dhcp-networks' % agent_id return self.list_resources(uri) def delete_network_from_dhcp_agent(self, agent_id, network_id): uri = '/agents/%s/dhcp-networks/%s' % (agent_id, network_id) return self.delete_resource(uri) def add_dhcp_agent_to_network(self, agent_id, **kwargs): # TODO(piyush): Current api-site doesn't contain this API description. # After fixing the api-site, we need to fix here also for putting the # link to api-site. # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526212 uri = '/agents/%s/dhcp-networks' % agent_id return self.create_resource(uri, kwargs) tempest-lib-1.0.0/tempest_lib/services/network/base.py000066400000000000000000000051451266406763200230650ustar00rootroot00000000000000# 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 oslo_serialization import jsonutils as json from six.moves.urllib import parse as urllib from tempest_lib.common import rest_client class BaseNetworkClient(rest_client.RestClient): """Base class for Tempest REST clients for Neutron. Child classes use v2 of the Neutron API, since the V1 API has been removed from the code base. """ version = '2.0' uri_prefix = "v2.0" def list_resources(self, uri, **filters): req_uri = self.uri_prefix + uri if filters: req_uri += '?' + urllib.urlencode(filters, doseq=1) resp, body = self.get(req_uri) body = json.loads(body) self.expected_success(200, resp.status) return rest_client.ResponseBody(resp, body) def delete_resource(self, uri): req_uri = self.uri_prefix + uri resp, body = self.delete(req_uri) self.expected_success(204, resp.status) return rest_client.ResponseBody(resp, body) def show_resource(self, uri, **fields): # fields is a dict which key is 'fields' and value is a # list of field's name. An example: # {'fields': ['id', 'name']} req_uri = self.uri_prefix + uri if fields: req_uri += '?' + urllib.urlencode(fields, doseq=1) resp, body = self.get(req_uri) body = json.loads(body) self.expected_success(200, resp.status) return rest_client.ResponseBody(resp, body) def create_resource(self, uri, post_data): req_uri = self.uri_prefix + uri req_post_data = json.dumps(post_data) resp, body = self.post(req_uri, req_post_data) body = json.loads(body) self.expected_success(201, resp.status) return rest_client.ResponseBody(resp, body) def update_resource(self, uri, post_data): req_uri = self.uri_prefix + uri req_post_data = json.dumps(post_data) resp, body = self.put(req_uri, req_post_data) body = json.loads(body) self.expected_success(200, resp.status) return rest_client.ResponseBody(resp, body) tempest-lib-1.0.0/tempest_lib/services/network/extensions_client.py000066400000000000000000000016471266406763200257130ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class ExtensionsClient(base.BaseNetworkClient): def show_extension(self, ext_alias, **fields): uri = '/extensions/%s' % ext_alias return self.show_resource(uri, **fields) def list_extensions(self, **filters): uri = '/extensions' return self.list_resources(uri, **filters) tempest-lib-1.0.0/tempest_lib/services/network/floating_ips_client.py000066400000000000000000000026601266406763200261660ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class FloatingIPsClient(base.BaseNetworkClient): def create_floatingip(self, **kwargs): uri = '/floatingips' post_data = {'floatingip': kwargs} return self.create_resource(uri, post_data) def update_floatingip(self, floatingip_id, **kwargs): uri = '/floatingips/%s' % floatingip_id post_data = {'floatingip': kwargs} return self.update_resource(uri, post_data) def show_floatingip(self, floatingip_id, **fields): uri = '/floatingips/%s' % floatingip_id return self.show_resource(uri, **fields) def delete_floatingip(self, floatingip_id): uri = '/floatingips/%s' % floatingip_id return self.delete_resource(uri) def list_floatingips(self, **filters): uri = '/floatingips' return self.list_resources(uri, **filters) tempest-lib-1.0.0/tempest_lib/services/network/metering_label_rules_client.py000066400000000000000000000026061266406763200276730ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class MeteringLabelRulesClient(base.BaseNetworkClient): def create_metering_label_rule(self, **kwargs): uri = '/metering/metering-label-rules' post_data = {'metering_label_rule': kwargs} return self.create_resource(uri, post_data) def show_metering_label_rule(self, metering_label_rule_id, **fields): uri = '/metering/metering-label-rules/%s' % metering_label_rule_id return self.show_resource(uri, **fields) def delete_metering_label_rule(self, metering_label_rule_id): uri = '/metering/metering-label-rules/%s' % metering_label_rule_id return self.delete_resource(uri) def list_metering_label_rules(self, **filters): uri = '/metering/metering-label-rules' return self.list_resources(uri, **filters) tempest-lib-1.0.0/tempest_lib/services/network/metering_labels_client.py000066400000000000000000000025011266406763200266360ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class MeteringLabelsClient(base.BaseNetworkClient): def create_metering_label(self, **kwargs): uri = '/metering/metering-labels' post_data = {'metering_label': kwargs} return self.create_resource(uri, post_data) def show_metering_label(self, metering_label_id, **fields): uri = '/metering/metering-labels/%s' % metering_label_id return self.show_resource(uri, **fields) def delete_metering_label(self, metering_label_id): uri = '/metering/metering-labels/%s' % metering_label_id return self.delete_resource(uri) def list_metering_labels(self, **filters): uri = '/metering/metering-labels' return self.list_resources(uri, **filters) tempest-lib-1.0.0/tempest_lib/services/network/networks_client.py000066400000000000000000000033001266406763200253540ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class NetworksClient(base.BaseNetworkClient): def create_network(self, **kwargs): uri = '/networks' post_data = {'network': kwargs} return self.create_resource(uri, post_data) def update_network(self, network_id, **kwargs): uri = '/networks/%s' % network_id post_data = {'network': kwargs} return self.update_resource(uri, post_data) def show_network(self, network_id, **fields): uri = '/networks/%s' % network_id return self.show_resource(uri, **fields) def delete_network(self, network_id): uri = '/networks/%s' % network_id return self.delete_resource(uri) def list_networks(self, **filters): uri = '/networks' return self.list_resources(uri, **filters) def create_bulk_networks(self, **kwargs): """Create multiple networks in a single request. Available params: see http://developer.openstack.org/ api-ref-networking-v2.html#bulkCreateNetwork """ uri = '/networks' return self.create_resource(uri, kwargs) tempest-lib-1.0.0/tempest_lib/services/network/ports_client.py000066400000000000000000000031741266406763200246600ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class PortsClient(base.BaseNetworkClient): def create_port(self, **kwargs): uri = '/ports' post_data = {'port': kwargs} return self.create_resource(uri, post_data) def update_port(self, port_id, **kwargs): uri = '/ports/%s' % port_id post_data = {'port': kwargs} return self.update_resource(uri, post_data) def show_port(self, port_id, **fields): uri = '/ports/%s' % port_id return self.show_resource(uri, **fields) def delete_port(self, port_id): uri = '/ports/%s' % port_id return self.delete_resource(uri) def list_ports(self, **filters): uri = '/ports' return self.list_resources(uri, **filters) def create_bulk_ports(self, **kwargs): """Create multiple ports in a single request. Available params: see http://developer.openstack.org/ api-ref-networking-v2.html#bulkCreatePorts """ uri = '/ports' return self.create_resource(uri, kwargs) tempest-lib-1.0.0/tempest_lib/services/network/quotas_client.py000066400000000000000000000023701266406763200250220ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.network import base class QuotasClient(base.BaseNetworkClient): def update_quotas(self, tenant_id, **kwargs): put_body = {'quota': kwargs} uri = '/quotas/%s' % tenant_id return self.update_resource(uri, put_body) def reset_quotas(self, tenant_id): uri = '/quotas/%s' % tenant_id return self.delete_resource(uri) def show_quotas(self, tenant_id, **fields): uri = '/quotas/%s' % tenant_id return self.show_resource(uri, **fields) def list_quotas(self, **filters): uri = '/quotas' return self.list_resources(uri, **filters) tempest-lib-1.0.0/tempest_lib/services/network/security_group_rules_client.py000066400000000000000000000025421266406763200300040ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class SecurityGroupRulesClient(base.BaseNetworkClient): def create_security_group_rule(self, **kwargs): uri = '/security-group-rules' post_data = {'security_group_rule': kwargs} return self.create_resource(uri, post_data) def show_security_group_rule(self, security_group_rule_id, **fields): uri = '/security-group-rules/%s' % security_group_rule_id return self.show_resource(uri, **fields) def delete_security_group_rule(self, security_group_rule_id): uri = '/security-group-rules/%s' % security_group_rule_id return self.delete_resource(uri) def list_security_group_rules(self, **filters): uri = '/security-group-rules' return self.list_resources(uri, **filters) tempest-lib-1.0.0/tempest_lib/services/network/security_groups_client.py000066400000000000000000000027731266406763200267630ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class SecurityGroupsClient(base.BaseNetworkClient): def create_security_group(self, **kwargs): uri = '/security-groups' post_data = {'security_group': kwargs} return self.create_resource(uri, post_data) def update_security_group(self, security_group_id, **kwargs): uri = '/security-groups/%s' % security_group_id post_data = {'security_group': kwargs} return self.update_resource(uri, post_data) def show_security_group(self, security_group_id, **fields): uri = '/security-groups/%s' % security_group_id return self.show_resource(uri, **fields) def delete_security_group(self, security_group_id): uri = '/security-groups/%s' % security_group_id return self.delete_resource(uri) def list_security_groups(self, **filters): uri = '/security-groups' return self.list_resources(uri, **filters) tempest-lib-1.0.0/tempest_lib/services/network/subnetpools_client.py000066400000000000000000000027521266406763200260670ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.network import base class SubnetpoolsClient(base.BaseNetworkClient): def list_subnetpools(self, **filters): uri = '/subnetpools' return self.list_resources(uri, **filters) def create_subnetpool(self, **kwargs): uri = '/subnetpools' post_data = {'subnetpool': kwargs} return self.create_resource(uri, post_data) def show_subnetpool(self, subnetpool_id, **fields): uri = '/subnetpools/%s' % subnetpool_id return self.show_resource(uri, **fields) def update_subnetpool(self, subnetpool_id, **kwargs): uri = '/subnetpools/%s' % subnetpool_id post_data = {'subnetpool': kwargs} return self.update_resource(uri, post_data) def delete_subnetpool(self, subnetpool_id): uri = '/subnetpools/%s' % subnetpool_id return self.delete_resource(uri) tempest-lib-1.0.0/tempest_lib/services/network/subnets_client.py000066400000000000000000000032511266406763200251700ustar00rootroot00000000000000# 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 tempest_lib.services.network import base class SubnetsClient(base.BaseNetworkClient): def create_subnet(self, **kwargs): uri = '/subnets' post_data = {'subnet': kwargs} return self.create_resource(uri, post_data) def update_subnet(self, subnet_id, **kwargs): uri = '/subnets/%s' % subnet_id post_data = {'subnet': kwargs} return self.update_resource(uri, post_data) def show_subnet(self, subnet_id, **fields): uri = '/subnets/%s' % subnet_id return self.show_resource(uri, **fields) def delete_subnet(self, subnet_id): uri = '/subnets/%s' % subnet_id return self.delete_resource(uri) def list_subnets(self, **filters): uri = '/subnets' return self.list_resources(uri, **filters) def create_bulk_subnets(self, **kwargs): """Create multiple subnets in a single request. Available params: see http://developer.openstack.org/ api-ref-networking-v2.html#bulkCreateSubnet """ uri = '/subnets' return self.create_resource(uri, kwargs) tempest-lib-1.0.0/tempest_lib/tests/000077500000000000000000000000001266406763200174225ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/__init__.py000066400000000000000000000000001266406763200215210ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/base.py000066400000000000000000000031011266406763200207010ustar00rootroot00000000000000# Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslotest import base from oslotest import moxstubout class TestCase(base.BaseTestCase): def setUp(self): super(TestCase, self).setUp() mox_fixture = self.useFixture(moxstubout.MoxStubout()) self.mox = mox_fixture.mox self.stubs = mox_fixture.stubs def patch(self, target, **kwargs): """Returns a started `mock.patch` object for the supplied target. The caller may then call the returned patcher to create a mock object. The caller does not need to call stop() on the returned patcher object, as this method automatically adds a cleanup to the test class to stop the patcher. :param target: String module.class or module.object expression to patch :param **kwargs: Passed as-is to `mock.patch`. See mock documentation for details. """ p = mock.patch(target, **kwargs) m = p.start() self.addCleanup(p.stop) return m tempest-lib-1.0.0/tempest_lib/tests/cli/000077500000000000000000000000001266406763200201715ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/cli/__init__.py000066400000000000000000000000001266406763200222700ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/cli/test_command_failed.py000066400000000000000000000021551266406763200245270ustar00rootroot00000000000000# 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 tempest_lib import exceptions from tempest_lib.tests import base class TestOutputParser(base.TestCase): def test_command_failed_exception(self): returncode = 1 cmd = "foo" stdout = "output" stderr = "error" try: raise exceptions.CommandFailed(returncode, cmd, stdout, stderr) except exceptions.CommandFailed as e: self.assertIn(str(returncode), str(e)) self.assertIn(cmd, str(e)) self.assertIn(stdout, str(e)) self.assertIn(stderr, str(e)) tempest-lib-1.0.0/tempest_lib/tests/cli/test_execute.py000066400000000000000000000027601266406763200232510ustar00rootroot00000000000000# # 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 tempest_lib.cli import base as cli_base from tempest_lib import exceptions from tempest_lib.tests import base class TestExecute(base.TestCase): def test_execute_success(self): result = cli_base.execute("/bin/ls", action="tempest_lib", flags="-l -a") self.assertIsInstance(result, str) self.assertIn("__init__.py", result) def test_execute_failure(self): result = cli_base.execute("/bin/ls", action="tempest_lib", flags="--foobar", merge_stderr=True, fail_ok=True) self.assertIsInstance(result, str) self.assertIn("--foobar", result) def test_execute_failure_raise_exception(self): self.assertRaises(exceptions.CommandFailed, cli_base.execute, "/bin/ls", action="tempest_lib", flags="--foobar", merge_stderr=True) tempest-lib-1.0.0/tempest_lib/tests/cli/test_output_parser.py000066400000000000000000000151201266406763200245150ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.cli import output_parser from tempest_lib import exceptions from tempest_lib.tests import base class TestOutputParser(base.TestCase): OUTPUT_LINES = """ +----+------+---------+ | ID | Name | Status | +----+------+---------+ | 11 | foo | BUILD | | 21 | bar | ERROR | | 31 | bee | None | +----+------+---------+ """ OUTPUT_LINES2 = """ +----+-------+---------+ | ID | Name2 | Status2 | +----+-------+---------+ | 41 | aaa | SSSSS | | 51 | bbb | TTTTT | | 61 | ccc | AAAAA | +----+-------+---------+ """ EXPECTED_TABLE = {'headers': ['ID', 'Name', 'Status'], 'values': [['11', 'foo', 'BUILD'], ['21', 'bar', 'ERROR'], ['31', 'bee', 'None']]} EXPECTED_TABLE2 = {'headers': ['ID', 'Name2', 'Status2'], 'values': [['41', 'aaa', 'SSSSS'], ['51', 'bbb', 'TTTTT'], ['61', 'ccc', 'AAAAA']]} def test_table_with_normal_values(self): actual = output_parser.table(self.OUTPUT_LINES) self.assertIsInstance(actual, dict) self.assertEqual(self.EXPECTED_TABLE, actual) def test_table_with_list(self): output_lines = self.OUTPUT_LINES.split('\n') actual = output_parser.table(output_lines) self.assertIsInstance(actual, dict) self.assertEqual(self.EXPECTED_TABLE, actual) def test_table_with_invalid_line(self): output_lines = self.OUTPUT_LINES + "aaaa" actual = output_parser.table(output_lines) self.assertIsInstance(actual, dict) self.assertEqual(self.EXPECTED_TABLE, actual) def test_tables_with_normal_values(self): output_lines = ('test' + self.OUTPUT_LINES + 'test2' + self.OUTPUT_LINES2) expected = [{'headers': self.EXPECTED_TABLE['headers'], 'label': 'test', 'values': self.EXPECTED_TABLE['values']}, {'headers': self.EXPECTED_TABLE2['headers'], 'label': 'test2', 'values': self.EXPECTED_TABLE2['values']}] actual = output_parser.tables(output_lines) self.assertIsInstance(actual, list) self.assertEqual(expected, actual) def test_tables_with_invalid_values(self): output_lines = ('test' + self.OUTPUT_LINES + 'test2' + self.OUTPUT_LINES2 + '\n') expected = [{'headers': self.EXPECTED_TABLE['headers'], 'label': 'test', 'values': self.EXPECTED_TABLE['values']}, {'headers': self.EXPECTED_TABLE2['headers'], 'label': 'test2', 'values': self.EXPECTED_TABLE2['values']}] actual = output_parser.tables(output_lines) self.assertIsInstance(actual, list) self.assertEqual(expected, actual) def test_tables_with_invalid_line(self): output_lines = ('test' + self.OUTPUT_LINES + 'test2' + self.OUTPUT_LINES2 + '+----+-------+---------+') expected = [{'headers': self.EXPECTED_TABLE['headers'], 'label': 'test', 'values': self.EXPECTED_TABLE['values']}, {'headers': self.EXPECTED_TABLE2['headers'], 'label': 'test2', 'values': self.EXPECTED_TABLE2['values']}] actual = output_parser.tables(output_lines) self.assertIsInstance(actual, list) self.assertEqual(expected, actual) LISTING_OUTPUT = """ +----+ | ID | +----+ | 11 | | 21 | | 31 | +----+ """ def test_listing(self): expected = [{'ID': '11'}, {'ID': '21'}, {'ID': '31'}] actual = output_parser.listing(self.LISTING_OUTPUT) self.assertIsInstance(actual, list) self.assertEqual(expected, actual) def test_details_multiple_with_invalid_line(self): self.assertRaises(exceptions.InvalidStructure, output_parser.details_multiple, self.OUTPUT_LINES) DETAILS_LINES1 = """First Table +----------+--------+ | Property | Value | +----------+--------+ | foo | BUILD | | bar | ERROR | | bee | None | +----------+--------+ """ DETAILS_LINES2 = """Second Table +----------+--------+ | Property | Value | +----------+--------+ | aaa | VVVVV | | bbb | WWWWW | | ccc | XXXXX | +----------+--------+ """ def test_details_with_normal_line_label_false(self): expected = {'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'} actual = output_parser.details(self.DETAILS_LINES1) self.assertEqual(expected, actual) def test_details_with_normal_line_label_true(self): expected = {'__label': 'First Table', 'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'} actual = output_parser.details(self.DETAILS_LINES1, with_label=True) self.assertEqual(expected, actual) def test_details_multiple_with_normal_line_label_false(self): expected = [{'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'}, {'aaa': 'VVVVV', 'bbb': 'WWWWW', 'ccc': 'XXXXX'}] actual = output_parser.details_multiple(self.DETAILS_LINES1 + self.DETAILS_LINES2) self.assertIsInstance(actual, list) self.assertEqual(expected, actual) def test_details_multiple_with_normal_line_label_true(self): expected = [{'__label': 'First Table', 'foo': 'BUILD', 'bar': 'ERROR', 'bee': 'None'}, {'__label': 'Second Table', 'aaa': 'VVVVV', 'bbb': 'WWWWW', 'ccc': 'XXXXX'}] actual = output_parser.details_multiple(self.DETAILS_LINES1 + self.DETAILS_LINES2, with_label=True) self.assertIsInstance(actual, list) self.assertEqual(expected, actual) tempest-lib-1.0.0/tempest_lib/tests/common/000077500000000000000000000000001266406763200207125ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/common/__init__.py000066400000000000000000000000001266406763200230110ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/common/utils/000077500000000000000000000000001266406763200220525ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/common/utils/__init__.py000066400000000000000000000000001266406763200241510ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/common/utils/test_data_utils.py000066400000000000000000000143371266406763200256240ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. # 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 netaddr from tempest_lib.common.utils import data_utils from tempest_lib.tests import base class TestDataUtils(base.TestCase): def test_rand_uuid(self): actual = data_utils.rand_uuid() self.assertIsInstance(actual, str) self.assertRegexpMatches(actual, "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]" "{4}-[0-9a-f]{4}-[0-9a-f]{12}$") actual2 = data_utils.rand_uuid() self.assertNotEqual(actual, actual2) def test_rand_uuid_hex(self): actual = data_utils.rand_uuid_hex() self.assertIsInstance(actual, str) self.assertRegexpMatches(actual, "^[0-9a-f]{32}$") actual2 = data_utils.rand_uuid_hex() self.assertNotEqual(actual, actual2) def test_rand_name(self): actual = data_utils.rand_name() self.assertIsInstance(actual, str) actual2 = data_utils.rand_name() self.assertNotEqual(actual, actual2) actual = data_utils.rand_name('foo') self.assertTrue(actual.startswith('foo')) actual2 = data_utils.rand_name('foo') self.assertTrue(actual.startswith('foo')) self.assertNotEqual(actual, actual2) def test_rand_name_with_prefix(self): actual = data_utils.rand_name(prefix='prefix-str') self.assertIsInstance(actual, str) self.assertRegexpMatches(actual, "^prefix-str-") actual2 = data_utils.rand_name(prefix='prefix-str') self.assertNotEqual(actual, actual2) def test_rand_password(self): actual = data_utils.rand_password() self.assertIsInstance(actual, str) self.assertRegexpMatches(actual, "[A-Za-z0-9~!@#$%^&*_=+]{15,}") actual2 = data_utils.rand_password() self.assertNotEqual(actual, actual2) def test_rand_password_with_len(self): actual = data_utils.rand_password(8) self.assertIsInstance(actual, str) self.assertEqual(len(actual), 8) self.assertRegexpMatches(actual, "[A-Za-z0-9~!@#$%^&*_=+]{8}") actual2 = data_utils.rand_password(8) self.assertNotEqual(actual, actual2) def test_rand_password_with_len_2(self): actual = data_utils.rand_password(2) self.assertIsInstance(actual, str) self.assertEqual(len(actual), 3) self.assertRegexpMatches(actual, "[A-Za-z0-9~!@#$%^&*_=+]{3}") actual2 = data_utils.rand_password(2) self.assertNotEqual(actual, actual2) def test_rand_url(self): actual = data_utils.rand_url() self.assertIsInstance(actual, str) self.assertRegexpMatches(actual, "^https://url-[0-9]*\.com$") actual2 = data_utils.rand_url() self.assertNotEqual(actual, actual2) def test_rand_int(self): actual = data_utils.rand_int_id() self.assertIsInstance(actual, int) actual2 = data_utils.rand_int_id() self.assertNotEqual(actual, actual2) def test_rand_mac_address(self): actual = data_utils.rand_mac_address() self.assertIsInstance(actual, str) self.assertRegexpMatches(actual, "^([0-9a-f][0-9a-f]:){5}" "[0-9a-f][0-9a-f]$") actual2 = data_utils.rand_mac_address() self.assertNotEqual(actual, actual2) def test_parse_image_id(self): actual = data_utils.parse_image_id("/foo/bar/deadbeaf") self.assertEqual("deadbeaf", actual) def test_arbitrary_string(self): actual = data_utils.arbitrary_string() self.assertEqual(actual, "test") actual = data_utils.arbitrary_string(size=30, base_text="abc") self.assertEqual(actual, "abc" * int(30 / len("abc"))) actual = data_utils.arbitrary_string(size=5, base_text="deadbeaf") self.assertEqual(actual, "deadb") def test_random_bytes(self): actual = data_utils.random_bytes() # default size=1024 self.assertIsInstance(actual, str) self.assertRegexpMatches(actual, "^[\x00-\xFF]{1024}") actual2 = data_utils.random_bytes() self.assertNotEqual(actual, actual2) actual = data_utils.random_bytes(size=2048) self.assertRegexpMatches(actual, "^[\x00-\xFF]{2048}") def test_get_ipv6_addr_by_EUI64(self): actual = data_utils.get_ipv6_addr_by_EUI64('2001:db8::', '00:16:3e:33:44:55') self.assertIsInstance(actual, netaddr.IPAddress) self.assertEqual(actual, netaddr.IPAddress('2001:db8::216:3eff:fe33:4455')) def test_get_ipv6_addr_by_EUI64_with_IPv4_prefix(self): ipv4_prefix = '10.0.8' mac = '00:16:3e:33:44:55' self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64, ipv4_prefix, mac) def test_get_ipv6_addr_by_EUI64_bad_cidr_type(self): bad_cidr = 123 mac = '00:16:3e:33:44:55' self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64, bad_cidr, mac) def test_get_ipv6_addr_by_EUI64_bad_cidr_value(self): bad_cidr = 'bb' mac = '00:16:3e:33:44:55' self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64, bad_cidr, mac) def test_get_ipv6_addr_by_EUI64_bad_mac_value(self): cidr = '2001:db8::' bad_mac = '00:16:3e:33:44:5Z' self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64, cidr, bad_mac) def test_get_ipv6_addr_by_EUI64_bad_mac_type(self): cidr = '2001:db8::' bad_mac = 99999999999999999999 self.assertRaises(TypeError, data_utils.get_ipv6_addr_by_EUI64, cidr, bad_mac) tempest-lib-1.0.0/tempest_lib/tests/common/utils/test_misc.py000066400000000000000000000052611266406763200244220ustar00rootroot00000000000000# Copyright 2014 NEC Corporation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.common.utils import misc from tempest_lib.tests import base @misc.singleton class TestFoo(object): count = 0 def increment(self): self.count += 1 return self.count @misc.singleton class TestBar(object): count = 0 def increment(self): self.count += 1 return self.count class TestMisc(base.TestCase): def test_singleton(self): test = TestFoo() self.assertEqual(0, test.count) self.assertEqual(1, test.increment()) test2 = TestFoo() self.assertEqual(1, test.count) self.assertEqual(1, test2.count) self.assertEqual(test, test2) test3 = TestBar() self.assertNotEqual(test, test3) def test_find_test_caller_test_case(self): # Calling it from here should give us the method we're in. self.assertEqual('TestMisc:test_find_test_caller_test_case', misc.find_test_caller()) def test_find_test_caller_setup_self(self): def setUp(self): return misc.find_test_caller() self.assertEqual('TestMisc:setUp', setUp(self)) def test_find_test_caller_setup_no_self(self): def setUp(): return misc.find_test_caller() self.assertEqual(':setUp', setUp()) def test_find_test_caller_setupclass_cls(self): def setUpClass(cls): # noqa return misc.find_test_caller() self.assertEqual('TestMisc:setUpClass', setUpClass(self.__class__)) def test_find_test_caller_teardown_self(self): def tearDown(self): return misc.find_test_caller() self.assertEqual('TestMisc:tearDown', tearDown(self)) def test_find_test_caller_teardown_no_self(self): def tearDown(): return misc.find_test_caller() self.assertEqual(':tearDown', tearDown()) def test_find_test_caller_teardown_class(self): def tearDownClass(cls): # noqa return misc.find_test_caller() self.assertEqual('TestMisc:tearDownClass', tearDownClass(self.__class__)) tempest-lib-1.0.0/tempest_lib/tests/fake_auth_provider.py000066400000000000000000000021671266406763200236430ustar00rootroot00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # 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. class FakeAuthProvider(object): def __init__(self, creds_dict={}): self.credentials = FakeCredentials(creds_dict) def auth_request(self, method, url, headers=None, body=None, filters=None): return url, headers, body def base_url(self, filters, auth_data=None): return "https://example.com" class FakeCredentials(object): def __init__(self, creds_dict): for key in creds_dict.keys(): setattr(self, key, creds_dict[key]) tempest-lib-1.0.0/tempest_lib/tests/fake_credentials.py000066400000000000000000000036351266406763200232660ustar00rootroot00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib import auth class FakeCredentials(auth.Credentials): def is_valid(self): return True class FakeKeystoneV2Credentials(auth.KeystoneV2Credentials): def __init__(self): creds = dict( username='fake_username', password='fake_password', tenant_name='fake_tenant_name' ) super(FakeKeystoneV2Credentials, self).__init__(**creds) class FakeKeystoneV3Credentials(auth.KeystoneV3Credentials): """Fake credentials suitable for the Keystone Identity V3 API""" def __init__(self): creds = dict( username='fake_username', password='fake_password', user_domain_name='fake_domain_name', project_name='fake_tenant_name', project_domain_name='fake_domain_name' ) super(FakeKeystoneV3Credentials, self).__init__(**creds) class FakeKeystoneV3DomainCredentials(auth.KeystoneV3Credentials): """Fake credentials for the Keystone Identity V3 API, with no scope""" def __init__(self): creds = dict( username='fake_username', password='fake_password', user_domain_name='fake_domain_name' ) super(FakeKeystoneV3DomainCredentials, self).__init__(**creds) tempest-lib-1.0.0/tempest_lib/tests/fake_http.py000066400000000000000000000046171266406763200217510ustar00rootroot00000000000000# Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import httplib2 class fake_httplib2(object): def __init__(self, return_type=None, *args, **kwargs): self.return_type = return_type def request(self, uri, method="GET", body=None, headers=None, redirections=5, connection_type=None): if not self.return_type: fake_headers = httplib2.Response(headers) return_obj = { 'uri': uri, 'method': method, 'body': body, 'headers': headers } return (fake_headers, return_obj) elif isinstance(self.return_type, int): body = body or "fake_body" header_info = { 'content-type': 'text/plain', 'status': str(self.return_type), 'content-length': len(body) } resp_header = httplib2.Response(header_info) return (resp_header, body) else: msg = "unsupported return type %s" % self.return_type raise TypeError(msg) class fake_httplib(object): def __init__(self, headers, body=None, version=1.0, status=200, reason="Ok"): """Fake httplib implementation :param headers: dict representing HTTP response headers :param body: file-like object :param version: HTTP Version :param status: Response status code :param reason: Status code related message. """ self.body = body self.status = status self.reason = reason self.version = version self.headers = headers def getheaders(self): return copy.deepcopy(self.headers).items() def getheader(self, key, default): return self.headers.get(key, default) def read(self, amt): return self.body.read(amt) tempest-lib-1.0.0/tempest_lib/tests/fake_identity.py000066400000000000000000000106661266406763200226240ustar00rootroot00000000000000# Copyright 2014 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. import json import httplib2 FAKE_AUTH_URL = 'http://fake_uri.com/auth' TOKEN = "fake_token" ALT_TOKEN = "alt_fake_token" # Fake Identity v2 constants COMPUTE_ENDPOINTS_V2 = { "endpoints": [ { "adminURL": "http://fake_url/v2/first_endpoint/admin", "region": "NoMatchRegion", "internalURL": "http://fake_url/v2/first_endpoint/internal", "publicURL": "http://fake_url/v2/first_endpoint/public" }, { "adminURL": "http://fake_url/v2/second_endpoint/admin", "region": "FakeRegion", "internalURL": "http://fake_url/v2/second_endpoint/internal", "publicURL": "http://fake_url/v2/second_endpoint/public" }, ], "type": "compute", "name": "nova" } CATALOG_V2 = [COMPUTE_ENDPOINTS_V2, ] ALT_IDENTITY_V2_RESPONSE = { "access": { "token": { "expires": "2020-01-01T00:00:10Z", "id": ALT_TOKEN, "tenant": { "id": "fake_alt_tenant_id" }, }, "user": { "id": "fake_alt_user_id", }, "serviceCatalog": CATALOG_V2, }, } IDENTITY_V2_RESPONSE = { "access": { "token": { "expires": "2020-01-01T00:00:10Z", "id": TOKEN, "tenant": { "id": "fake_tenant_id" }, }, "user": { "id": "fake_user_id", }, "serviceCatalog": CATALOG_V2, }, } # Fake Identity V3 constants COMPUTE_ENDPOINTS_V3 = { "endpoints": [ { "id": "first_compute_fake_service", "interface": "public", "region": "NoMatchRegion", "url": "http://fake_url/v3/first_endpoint/api" }, { "id": "second_fake_service", "interface": "public", "region": "FakeRegion", "url": "http://fake_url/v3/second_endpoint/api" }, { "id": "third_fake_service", "interface": "admin", "region": "MiddleEarthRegion", "url": "http://fake_url/v3/third_endpoint/api" } ], "type": "compute", "id": "fake_compute_endpoint" } CATALOG_V3 = [COMPUTE_ENDPOINTS_V3, ] IDENTITY_V3_RESPONSE = { "token": { "methods": [ "token", "password" ], "expires_at": "2020-01-01T00:00:10.000123Z", "project": { "domain": { "id": "fake_domain_id", "name": "fake" }, "id": "project_id", "name": "project_name" }, "user": { "domain": { "id": "fake_domain_id", "name": "domain_name" }, "id": "fake_user_id", "name": "username" }, "issued_at": "2013-05-29T16:55:21.468960Z", "catalog": CATALOG_V3 } } ALT_IDENTITY_V3 = IDENTITY_V3_RESPONSE def _fake_v3_response(self, uri, method="GET", body=None, headers=None, redirections=5, connection_type=None): fake_headers = { "status": "201", "x-subject-token": TOKEN } return (httplib2.Response(fake_headers), json.dumps(IDENTITY_V3_RESPONSE)) def _fake_v2_response(self, uri, method="GET", body=None, headers=None, redirections=5, connection_type=None): return (httplib2.Response({"status": "200"}), json.dumps(IDENTITY_V2_RESPONSE)) def _fake_auth_failure_response(): # the response body isn't really used in this case, but lets send it anyway # to have a safe check in some future change on the rest client. body = { "unauthorized": { "message": "Unauthorized", "code": "401" } } return httplib2.Response({"status": "401"}), json.dumps(body) tempest-lib-1.0.0/tempest_lib/tests/services/000077500000000000000000000000001266406763200212455ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/__init__.py000066400000000000000000000000001266406763200233440ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/compute/000077500000000000000000000000001266406763200227215ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/compute/__init__.py000066400000000000000000000000001266406763200250200ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/compute/base.py000066400000000000000000000033251266406763200242100ustar00rootroot00000000000000# Copyright 2015 Deutsche Telekom AG. 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 httplib2 from oslo_serialization import jsonutils as json from oslotest import mockpatch from tempest_lib.tests import base class BaseComputeServiceTest(base.TestCase): def create_response(self, body, to_utf=False, status=200, headers=None): json_body = {} if body: json_body = json.dumps(body) if to_utf: json_body = json_body.encode('utf-8') resp_dict = {'status': status} if headers: resp_dict.update(headers) response = (httplib2.Response(resp_dict), json_body) return response def check_service_client_function(self, function, function2mock, body, to_utf=False, status=200, headers=None, **kwargs): mocked_response = self.create_response(body, to_utf, status, headers) self.useFixture(mockpatch.Patch( function2mock, return_value=mocked_response)) if kwargs: resp = function(**kwargs) else: resp = function() self.assertEqual(body, resp) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_agents_client.py000066400000000000000000000067601266406763200271620ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import agents_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestAgentsClient(base.BaseComputeServiceTest): FAKE_CREATE_AGENT = { "agent": { "url": "http://foo.com", "hypervisor": "kvm", "md5hash": "md5", "version": "2", "architecture": "x86_64", "os": "linux", "agent_id": 1 } } FAKE_UPDATE_AGENT = { "agent": { "url": "http://foo.com", "md5hash": "md5", "version": "2", "agent_id": 1 } } def setUp(self): super(TestAgentsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = agents_client.AgentsClient(fake_auth, 'compute', 'regionOne') def _test_list_agents(self, bytes_body=False): self.check_service_client_function( self.client.list_agents, 'tempest_lib.common.rest_client.RestClient.get', {"agents": []}, bytes_body) self.check_service_client_function( self.client.list_agents, 'tempest_lib.common.rest_client.RestClient.get', {"agents": []}, bytes_body, hypervisor="kvm") def _test_create_agent(self, bytes_body=False): self.check_service_client_function( self.client.create_agent, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_CREATE_AGENT, bytes_body, url="http://foo.com", hypervisor="kvm", md5hash="md5", version="2", architecture="x86_64", os="linux") def _test_delete_agent(self): self.check_service_client_function( self.client.delete_agent, 'tempest_lib.common.rest_client.RestClient.delete', {}, agent_id="1") def _test_update_agent(self, bytes_body=False): self.check_service_client_function( self.client.update_agent, 'tempest_lib.common.rest_client.RestClient.put', self.FAKE_UPDATE_AGENT, bytes_body, agent_id="1", url="http://foo.com", md5hash="md5", version="2") def test_list_agents_with_str_body(self): self._test_list_agents() def test_list_agents_with_bytes_body(self): self._test_list_agents(bytes_body=True) def test_create_agent_with_str_body(self): self._test_create_agent() def test_create_agent_with_bytes_body(self): self._test_create_agent(bytes_body=True) def test_delete_agent(self): self._test_delete_agent() def test_update_agent_with_str_body(self): self._test_update_agent() def test_update_agent_with_bytes_body(self): self._test_update_agent(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_aggregates_client.py000066400000000000000000000141141266406763200300020ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import aggregates_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestAggregatesClient(base.BaseComputeServiceTest): FAKE_SHOW_AGGREGATE = { "aggregate": { "name": "hoge", "availability_zone": None, "deleted": False, "created_at": "2015-07-16T03:07:32.000000", "updated_at": None, "hosts": [], "deleted_at": None, "id": 1, "metadata": {} } } FAKE_CREATE_AGGREGATE = { "aggregate": { "name": u'\xf4', "availability_zone": None, "deleted": False, "created_at": "2015-07-21T04:11:18.000000", "updated_at": None, "deleted_at": None, "id": 1 } } FAKE_UPDATE_AGGREGATE = { "aggregate": { "name": u'\xe9', "availability_zone": None, "deleted": False, "created_at": "2015-07-16T03:07:32.000000", "updated_at": "2015-07-23T05:16:29.000000", "hosts": [], "deleted_at": None, "id": 1, "metadata": {} } } FAKE_AGGREGATE = { "availability_zone": "nova", "created_at": "2013-08-18T12:17:56.297823", "deleted": False, "deleted_at": None, "hosts": [ "21549b2f665945baaa7101926a00143c" ], "id": 1, "metadata": { "availability_zone": "nova" }, "name": u'\xe9', "updated_at": None } FAKE_ADD_HOST = {'aggregate': FAKE_AGGREGATE} FAKE_REMOVE_HOST = {'aggregate': FAKE_AGGREGATE} FAKE_SET_METADATA = {'aggregate': FAKE_AGGREGATE} def setUp(self): super(TestAggregatesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = aggregates_client.AggregatesClient( fake_auth, 'compute', 'regionOne') def _test_list_aggregates(self, bytes_body=False): self.check_service_client_function( self.client.list_aggregates, 'tempest_lib.common.rest_client.RestClient.get', {"aggregates": []}, bytes_body) def test_list_aggregates_with_str_body(self): self._test_list_aggregates() def test_list_aggregates_with_bytes_body(self): self._test_list_aggregates(bytes_body=True) def _test_show_aggregate(self, bytes_body=False): self.check_service_client_function( self.client.show_aggregate, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SHOW_AGGREGATE, bytes_body, aggregate_id=1) def test_show_aggregate_with_str_body(self): self._test_show_aggregate() def test_show_aggregate_with_bytes_body(self): self._test_show_aggregate(bytes_body=True) def _test_create_aggregate(self, bytes_body=False): self.check_service_client_function( self.client.create_aggregate, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_CREATE_AGGREGATE, bytes_body, name='hoge') def test_create_aggregate_with_str_body(self): self._test_create_aggregate() def test_create_aggregate_with_bytes_body(self): self._test_create_aggregate(bytes_body=True) def test_delete_aggregate(self): self.check_service_client_function( self.client.delete_aggregate, 'tempest_lib.common.rest_client.RestClient.delete', {}, aggregate_id="1") def _test_update_aggregate(self, bytes_body=False): self.check_service_client_function( self.client.update_aggregate, 'tempest_lib.common.rest_client.RestClient.put', self.FAKE_UPDATE_AGGREGATE, bytes_body, aggregate_id=1) def test_update_aggregate_with_str_body(self): self._test_update_aggregate() def test_update_aggregate_with_bytes_body(self): self._test_update_aggregate(bytes_body=True) def _test_add_host(self, bytes_body=False): self.check_service_client_function( self.client.add_host, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_ADD_HOST, bytes_body, aggregate_id=1) def test_add_host_with_str_body(self): self._test_add_host() def test_add_host_with_bytes_body(self): self._test_add_host(bytes_body=True) def _test_remove_host(self, bytes_body=False): self.check_service_client_function( self.client.remove_host, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_REMOVE_HOST, bytes_body, aggregate_id=1) def test_remove_host_with_str_body(self): self._test_remove_host() def test_remove_host_with_bytes_body(self): self._test_remove_host(bytes_body=True) def _test_set_metadata(self, bytes_body=False): self.check_service_client_function( self.client.set_metadata, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_SET_METADATA, bytes_body, aggregate_id=1) def test_set_metadata_with_str_body(self): self._test_set_metadata() def test_set_metadata_with_bytes_body(self): self._test_set_metadata(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_availability_zone_client.py000066400000000000000000000036101266406763200313750ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import availability_zone_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestAvailabilityZoneClient(base.BaseComputeServiceTest): FAKE_AVAILABIRITY_ZONE_INFO = { "availabilityZoneInfo": [ { "zoneState": { "available": True }, "hosts": None, "zoneName": u'\xf4' } ] } def setUp(self): super(TestAvailabilityZoneClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = availability_zone_client.AvailabilityZoneClient( fake_auth, 'compute', 'regionOne') def test_list_availability_zones_with_str_body(self): self.check_service_client_function( self.client.list_availability_zones, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_AVAILABIRITY_ZONE_INFO) def test_list_availability_zones_with_bytes_body(self): self.check_service_client_function( self.client.list_availability_zones, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_AVAILABIRITY_ZONE_INFO, to_utf=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_baremetal_nodes_client.py000066400000000000000000000055571266406763200310300ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.services.compute import baremetal_nodes_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestBareMetalNodesClient(base.BaseComputeServiceTest): FAKE_NODE_INFO = {'cpus': '8', 'disk_gb': '64', 'host': '10.0.2.15', 'id': 'Identifier', 'instance_uuid': "null", 'interfaces': [ { "address": "20::01", "datapath_id": "null", "id": 1, "port_no": None } ], 'memory_mb': '8192', 'task_state': None} def setUp(self): super(TestBareMetalNodesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.baremetal_nodes_client = (baremetal_nodes_client. BaremetalNodesClient (fake_auth, 'compute', 'regionOne')) def _test_bareMetal_nodes(self, operation='list', bytes_body=False): if operation != 'list': expected = {"node": self.FAKE_NODE_INFO} function = self.baremetal_nodes_client.show_baremetal_node else: node_info = copy.deepcopy(self.FAKE_NODE_INFO) del node_info['instance_uuid'] expected = {"nodes": [node_info]} function = self.baremetal_nodes_client.list_baremetal_nodes self.check_service_client_function( function, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, 200, baremetal_node_id='Identifier') def test_list_bareMetal_nodes_with_str_body(self): self._test_bareMetal_nodes() def test_list_bareMetal_nodes_with_bytes_body(self): self._test_bareMetal_nodes(bytes_body=True) def test_show_bareMetal_node_with_str_body(self): self._test_bareMetal_nodes('show') def test_show_bareMetal_node_with_bytes_body(self): self._test_bareMetal_nodes('show', True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_certificates_client.py000066400000000000000000000044621266406763200303430ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.services.compute import certificates_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestCertificatesClient(base.BaseComputeServiceTest): FAKE_CERTIFICATE = { "certificate": { "data": "-----BEGIN----MIICyzCCAjSgAwI----END CERTIFICATE-----\n", "private_key": None } } def setUp(self): super(TestCertificatesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = certificates_client.CertificatesClient( fake_auth, 'compute', 'regionOne') def _test_show_certificate(self, bytes_body=False): self.check_service_client_function( self.client.show_certificate, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_CERTIFICATE, bytes_body, certificate_id="fake-id") def test_show_certificate_with_str_body(self): self._test_show_certificate() def test_show_certificate_with_bytes_body(self): self._test_show_certificate(bytes_body=True) def _test_create_certificate(self, bytes_body=False): cert = copy.deepcopy(self.FAKE_CERTIFICATE) cert['certificate']['private_key'] = "my_private_key" self.check_service_client_function( self.client.create_certificate, 'tempest_lib.common.rest_client.RestClient.post', cert, bytes_body) def test_create_certificate_with_str_body(self): self._test_create_certificate() def test_create_certificate_with_bytes_body(self): self._test_create_certificate(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_extensions_client.py000066400000000000000000000045101266406763200300670ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import extensions_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestExtensionsClient(base.BaseComputeServiceTest): FAKE_SHOW_EXTENSION = { "extension": { "updated": "2011-06-09T00:00:00Z", "name": "Multinic", "links": [], "namespace": "http://docs.openstack.org/compute/ext/multinic/api/v1.1", "alias": "NMN", "description": u'\u2740(*\xb4\u25e1`*)\u2740' } } def setUp(self): super(TestExtensionsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = extensions_client.ExtensionsClient( fake_auth, 'compute', 'regionOne') def _test_list_extensions(self, bytes_body=False): self.check_service_client_function( self.client.list_extensions, 'tempest_lib.common.rest_client.RestClient.get', {"extensions": []}, bytes_body) def test_list_extensions_with_str_body(self): self._test_list_extensions() def test_list_extensions_with_bytes_body(self): self._test_list_extensions(bytes_body=True) def _test_show_extension(self, bytes_body=False): self.check_service_client_function( self.client.show_extension, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SHOW_EXTENSION, bytes_body, extension_alias="NMN") def test_show_extension_with_str_body(self): self._test_show_extension() def test_show_extension_with_bytes_body(self): self._test_show_extension(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_fixedIPs_client.py000066400000000000000000000044521266406763200274100ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import fixed_ips_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestFixedIPsClient(base.BaseComputeServiceTest): FIXED_IP_INFO = {"fixed_ip": {"address": "10.0.0.1", "cidr": "10.11.12.0/24", "host": "localhost", "hostname": "OpenStack"}} def setUp(self): super(TestFixedIPsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.fixedIPsClient = (fixed_ips_client. FixedIPsClient (fake_auth, 'compute', 'regionOne')) def _test_show_fixed_ip(self, bytes_body=False): self.check_service_client_function( self.fixedIPsClient.show_fixed_ip, 'tempest_lib.common.rest_client.RestClient.get', self.FIXED_IP_INFO, bytes_body, status=200, fixed_ip='Identifier') def test_show_fixed_ip_with_str_body(self): self._test_show_fixed_ip() def test_show_fixed_ip_with_bytes_body(self): self._test_show_fixed_ip(True) def _test_reserve_fixed_ip(self, bytes_body=False): self.check_service_client_function( self.fixedIPsClient.reserve_fixed_ip, 'tempest_lib.common.rest_client.RestClient.post', {}, bytes_body, status=202, fixed_ip='Identifier') def test_reserve_fixed_ip_with_str_body(self): self._test_reserve_fixed_ip() def test_reserve_fixed_ip_with_bytes_body(self): self._test_reserve_fixed_ip(True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_flavors_client.py000066400000000000000000000226101266406763200273450ustar00rootroot00000000000000# Copyright 2015 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import httplib2 from oslo_serialization import jsonutils as json from oslotest import mockpatch from tempest_lib.services.compute import flavors_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestFlavorsClient(base.BaseComputeServiceTest): FAKE_FLAVOR = { "disk": 1, "id": "1", "links": [{ "href": "http://openstack.example.com/v2/openstack/flavors/1", "rel": "self"}, { "href": "http://openstack.example.com/openstack/flavors/1", "rel": "bookmark"}], "name": "m1.tiny", "ram": 512, "swap": 1, "vcpus": 1 } EXTRA_SPECS = {"extra_specs": { "key1": "value1", "key2": "value2"} } FAKE_FLAVOR_ACCESS = { "flavor_id": "10", "tenant_id": "1a951d988e264818afe520e78697dcbf" } def setUp(self): super(TestFlavorsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = flavors_client.FlavorsClient(fake_auth, 'compute', 'regionOne') def _test_list_flavors(self, bytes_body=False): flavor = copy.deepcopy(TestFlavorsClient.FAKE_FLAVOR) # Remove extra attributes for attribute in ('disk', 'vcpus', 'ram', 'swap'): del flavor[attribute] expected = {'flavors': [flavor]} self.check_service_client_function( self.client.list_flavors, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body) def test_list_flavors_str_body(self): self._test_list_flavors(bytes_body=False) def test_list_flavors_byte_body(self): self._test_list_flavors(bytes_body=True) def _test_show_flavor(self, bytes_body=False): expected = {"flavor": TestFlavorsClient.FAKE_FLAVOR} self.check_service_client_function( self.client.show_flavor, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, flavor_id='fake-id') def test_show_flavor_str_body(self): self._test_show_flavor(bytes_body=False) def test_show_flavor_byte_body(self): self._test_show_flavor(bytes_body=True) def _test_create_flavor(self, bytes_body=False): expected = {"flavor": TestFlavorsClient.FAKE_FLAVOR} request = copy.deepcopy(TestFlavorsClient.FAKE_FLAVOR) # The 'links' parameter should not be passed in del request['links'] self.check_service_client_function( self.client.create_flavor, 'tempest_lib.common.rest_client.RestClient.post', expected, bytes_body, **request) def test_create_flavor_str_body(self): self._test_create_flavor(bytes_body=False) def test_create_flavor__byte_body(self): self._test_create_flavor(bytes_body=True) def test_delete_flavor(self): self.check_service_client_function( self.client.delete_flavor, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, flavor_id='c782b7a9-33cd-45f0-b795-7f87f456408b') def _test_is_resource_deleted(self, flavor_id, is_deleted=True, bytes_body=False): body = json.dumps({'flavors': [TestFlavorsClient.FAKE_FLAVOR]}) if bytes_body: body = body.encode('utf-8') response = (httplib2.Response({'status': 200}), body) self.useFixture(mockpatch.Patch( 'tempest_lib.common.rest_client.RestClient.get', return_value=response)) self.assertEqual(is_deleted, self.client.is_resource_deleted(flavor_id)) def test_is_resource_deleted_true_str_body(self): self._test_is_resource_deleted('2', bytes_body=False) def test_is_resource_deleted_true_byte_body(self): self._test_is_resource_deleted('2', bytes_body=True) def test_is_resource_deleted_false_str_body(self): self._test_is_resource_deleted('1', is_deleted=False, bytes_body=False) def test_is_resource_deleted_false_byte_body(self): self._test_is_resource_deleted('1', is_deleted=False, bytes_body=True) def _test_set_flavor_extra_spec(self, bytes_body=False): self.check_service_client_function( self.client.set_flavor_extra_spec, 'tempest_lib.common.rest_client.RestClient.post', TestFlavorsClient.EXTRA_SPECS, bytes_body, flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6', **TestFlavorsClient.EXTRA_SPECS) def test_set_flavor_extra_spec_str_body(self): self._test_set_flavor_extra_spec(bytes_body=False) def test_set_flavor_extra_spec_byte_body(self): self._test_set_flavor_extra_spec(bytes_body=True) def _test_list_flavor_extra_specs(self, bytes_body=False): self.check_service_client_function( self.client.list_flavor_extra_specs, 'tempest_lib.common.rest_client.RestClient.get', TestFlavorsClient.EXTRA_SPECS, bytes_body, flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6') def test_list_flavor_extra_specs_str_body(self): self._test_list_flavor_extra_specs(bytes_body=False) def test_list_flavor_extra_specs__byte_body(self): self._test_list_flavor_extra_specs(bytes_body=True) def _test_show_flavor_extra_spec(self, bytes_body=False): expected = {"key": "value"} self.check_service_client_function( self.client.show_flavor_extra_spec, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6', key='key') def test_show_flavor_extra_spec_str_body(self): self._test_show_flavor_extra_spec(bytes_body=False) def test_show_flavor_extra_spec__byte_body(self): self._test_show_flavor_extra_spec(bytes_body=True) def _test_update_flavor_extra_spec(self, bytes_body=False): expected = {"key1": "value"} self.check_service_client_function( self.client.update_flavor_extra_spec, 'tempest_lib.common.rest_client.RestClient.put', expected, bytes_body, flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6', key='key1', **expected) def test_update_flavor_extra_spec_str_body(self): self._test_update_flavor_extra_spec(bytes_body=False) def test_update_flavor_extra_spec_byte_body(self): self._test_update_flavor_extra_spec(bytes_body=True) def test_unset_flavor_extra_spec(self): self.check_service_client_function( self.client.unset_flavor_extra_spec, 'tempest_lib.common.rest_client.RestClient.delete', {}, flavor_id='c782b7a9-33cd-45f0-b795-7f87f456408b', key='key') def _test_list_flavor_access(self, bytes_body=False): expected = {'flavor_access': [TestFlavorsClient.FAKE_FLAVOR_ACCESS]} self.check_service_client_function( self.client.list_flavor_access, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6') def test_list_flavor_access_str_body(self): self._test_list_flavor_access(bytes_body=False) def test_list_flavor_access_byte_body(self): self._test_list_flavor_access(bytes_body=True) def _test_add_flavor_access(self, bytes_body=False): expected = { "flavor_access": [TestFlavorsClient.FAKE_FLAVOR_ACCESS] } self.check_service_client_function( self.client.add_flavor_access, 'tempest_lib.common.rest_client.RestClient.post', expected, bytes_body, flavor_id='8c7aae5a-d315-4216-875b-ed9b6a5bcfc6', tenant_id='1a951d988e264818afe520e78697dcbf') def test_add_flavor_access_str_body(self): self._test_add_flavor_access(bytes_body=False) def test_add_flavor_access_byte_body(self): self._test_add_flavor_access(bytes_body=True) def _test_remove_flavor_access(self, bytes_body=False): expected = { "flavor_access": [TestFlavorsClient.FAKE_FLAVOR_ACCESS] } self.check_service_client_function( self.client.remove_flavor_access, 'tempest_lib.common.rest_client.RestClient.post', expected, bytes_body, flavor_id='10', tenant_id='a6edd4d66ad04245b5d2d8716ecc91e3') def test_remove_flavor_access_str_body(self): self._test_remove_flavor_access(bytes_body=False) def test_remove_flavor_access_byte_body(self): self._test_remove_flavor_access(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_floating_ip_pools_client.py000066400000000000000000000033711266406763200314030ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import floating_ip_pools_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestFloatingIPPoolsClient(base.BaseComputeServiceTest): FAKE_FLOATING_IP_POOLS = { "floating_ip_pools": [ {"name": u'\u3042'}, {"name": u'\u3044'} ] } def setUp(self): super(TestFloatingIPPoolsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = floating_ip_pools_client.FloatingIPPoolsClient( fake_auth, 'compute', 'regionOne') def test_list_floating_ip_pools_with_str_body(self): self.check_service_client_function( self.client.list_floating_ip_pools, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_FLOATING_IP_POOLS) def test_list_floating_ip_pools_with_bytes_body(self): self.check_service_client_function( self.client.list_floating_ip_pools, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_FLOATING_IP_POOLS, to_utf=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_floating_ips_bulk_client.py000066400000000000000000000064501266406763200313700ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.tests import fake_auth_provider from tempest_lib.services.compute import floating_ips_bulk_client from tempest_lib.tests.services.compute import base class TestFloatingIPsBulkClient(base.BaseComputeServiceTest): FAKE_FIP_BULK_LIST = {"floating_ip_info": [{ "address": "10.10.10.1", "instance_uuid": None, "fixed_ip": None, "interface": "eth0", "pool": "nova", "project_id": None }, { "address": "10.10.10.2", "instance_uuid": None, "fixed_ip": None, "interface": "eth0", "pool": "nova", "project_id": None }]} def setUp(self): super(TestFloatingIPsBulkClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = floating_ips_bulk_client.FloatingIPsBulkClient( fake_auth, 'compute', 'regionOne') def _test_list_floating_ips_bulk(self, bytes_body=False): self.check_service_client_function( self.client.list_floating_ips_bulk, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_FIP_BULK_LIST, to_utf=bytes_body) def _test_create_floating_ips_bulk(self, bytes_body=False): fake_fip_create_data = {"floating_ips_bulk_create": { "ip_range": "192.168.1.0/24", "pool": "nova", "interface": "eth0"}} self.check_service_client_function( self.client.create_floating_ips_bulk, 'tempest_lib.common.rest_client.RestClient.post', fake_fip_create_data, to_utf=bytes_body, ip_range="192.168.1.0/24", pool="nova", interface="eth0") def _test_delete_floating_ips_bulk(self, bytes_body=False): fake_fip_delete_data = {"floating_ips_bulk_delete": "192.168.1.0/24"} self.check_service_client_function( self.client.delete_floating_ips_bulk, 'tempest_lib.common.rest_client.RestClient.put', fake_fip_delete_data, to_utf=bytes_body, ip_range="192.168.1.0/24") def test_list_floating_ips_bulk_with_str_body(self): self._test_list_floating_ips_bulk() def test_list_floating_ips_bulk_with_bytes_body(self): self._test_list_floating_ips_bulk(True) def test_create_floating_ips_bulk_with_str_body(self): self._test_create_floating_ips_bulk() def test_create_floating_ips_bulk_with_bytes_body(self): self._test_create_floating_ips_bulk(True) def test_delete_floating_ips_bulk_with_str_body(self): self._test_delete_floating_ips_bulk() def test_delete_floating_ips_bulk_with_bytes_body(self): self._test_delete_floating_ips_bulk(True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_floating_ips_client.py000066400000000000000000000110641266406763200303500ustar00rootroot00000000000000# Copyright 2015 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslotest import mockpatch from tempest_lib import exceptions as lib_exc from tempest_lib.services.compute import floating_ips_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestFloatingIpsClient(base.BaseComputeServiceTest): floating_ip = {"fixed_ip": None, "id": "46d61064-13ba-4bf0-9557-69de824c3d6f", "instance_id": "a1daa443-a6bb-463e-aea2-104b7d912eb8", "ip": "10.10.10.1", "pool": "nova"} def setUp(self): super(TestFloatingIpsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = floating_ips_client.FloatingIPsClient( fake_auth, 'compute', 'regionOne') def _test_list_floating_ips(self, bytes_body=False): expected = {'floating_ips': [TestFloatingIpsClient.floating_ip]} self.check_service_client_function( self.client.list_floating_ips, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body) def test_list_floating_ips_str_body(self): self._test_list_floating_ips(bytes_body=False) def test_list_floating_ips_byte_body(self): self._test_list_floating_ips(bytes_body=True) def _test_show_floating_ip(self, bytes_body=False): expected = {"floating_ip": TestFloatingIpsClient.floating_ip} self.check_service_client_function( self.client.show_floating_ip, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, floating_ip_id='a1daa443-a6bb-463e-aea2-104b7d912eb8') def test_show_floating_ip_str_body(self): self._test_show_floating_ip(bytes_body=False) def test_show_floating_ip_byte_body(self): self._test_show_floating_ip(bytes_body=True) def _test_create_floating_ip(self, bytes_body=False): expected = {"floating_ip": TestFloatingIpsClient.floating_ip} self.check_service_client_function( self.client.create_floating_ip, 'tempest_lib.common.rest_client.RestClient.post', expected, bytes_body, pool_name='nova') def test_create_floating_ip_str_body(self): self._test_create_floating_ip(bytes_body=False) def test_create_floating_ip_byte_body(self): self._test_create_floating_ip(bytes_body=True) def test_delete_floating_ip(self): self.check_service_client_function( self.client.delete_floating_ip, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, floating_ip_id='fake-id') def test_associate_floating_ip_to_server(self): self.check_service_client_function( self.client.associate_floating_ip_to_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, floating_ip='10.10.10.1', server_id='c782b7a9-33cd-45f0-b795-7f87f456408b') def test_disassociate_floating_ip_from_server(self): self.check_service_client_function( self.client.disassociate_floating_ip_from_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, floating_ip='10.10.10.1', server_id='c782b7a9-33cd-45f0-b795-7f87f456408b') def test_is_resource_deleted_true(self): self.useFixture(mockpatch.Patch( 'tempest_lib.services.compute.floating_ips_client.' 'FloatingIPsClient.show_floating_ip', side_effect=lib_exc.NotFound())) self.assertTrue(self.client.is_resource_deleted('fake-id')) def test_is_resource_deleted_false(self): self.useFixture(mockpatch.Patch( 'tempest_lib.services.compute.floating_ips_client.' 'FloatingIPsClient.show_floating_ip', return_value={"floating_ip": TestFloatingIpsClient.floating_ip})) self.assertFalse(self.client.is_resource_deleted('fake-id')) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_hosts_client.py000066400000000000000000000120501266406763200270260ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import hosts_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestHostsClient(base.BaseComputeServiceTest): FAKE_HOST_DATA = { "host": { "resource": { "cpu": 1, "disk_gb": 1028, "host": "c1a7de0ac9d94e4baceae031d05caae3", "memory_mb": 8192, "project": "(total)" } }, "hosts": { "host_name": "c1a7de0ac9d94e4baceae031d05caae3", "service": "conductor", "zone": "internal" }, "enable_hosts": { "host": "65c5d5b7e3bd44308e67fc50f362aee6", "maintenance_mode": "off_maintenance", "status": "enabled" } } FAKE_CONTROL_DATA = { "shutdown": { "host": "c1a7de0ac9d94e4baceae031d05caae3", "power_action": "shutdown" }, "startup": { "host": "c1a7de0ac9d94e4baceae031d05caae3", "power_action": "startup" }, "reboot": { "host": "c1a7de0ac9d94e4baceae031d05caae3", "power_action": "reboot" }} HOST_DATA = {'host': [FAKE_HOST_DATA['host']]} HOSTS_DATA = {'hosts': [FAKE_HOST_DATA['hosts']]} ENABLE_HOST_DATA = FAKE_HOST_DATA['enable_hosts'] HOST_ID = "c1a7de0ac9d94e4baceae031d05caae3" TEST_HOST_DATA = { "status": "enable", "maintenance_mode": "disable" } def setUp(self): super(TestHostsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = hosts_client.HostsClient(fake_auth, 'compute', 'regionOne') self.params = {'hostname': self.HOST_ID} self.func2mock = { 'get': 'tempest_lib.common.rest_client.RestClient.get', 'put': 'tempest_lib.common.rest_client.RestClient.put'} def _test_host_data(self, test_type='list', bytes_body=False): expected_resp = self.HOST_DATA if test_type != 'list': function_call = self.client.show_host else: expected_resp = self.HOSTS_DATA function_call = self.client.list_hosts self.params = {'host_name': self.HOST_ID} self.check_service_client_function( function_call, self.func2mock['get'], expected_resp, bytes_body, 200, **self.params) def _test_update_hosts(self, bytes_body=False): expected_resp = self.ENABLE_HOST_DATA self.check_service_client_function( self.client.update_host, self.func2mock['put'], expected_resp, bytes_body, 200, **self.params) def _test_control_host(self, control_op='reboot', bytes_body=False): if control_op == 'start': expected_resp = self.FAKE_CONTROL_DATA['startup'] function_call = self.client.startup_host elif control_op == 'stop': expected_resp = self.FAKE_CONTROL_DATA['shutdown'] function_call = self.client.shutdown_host else: expected_resp = self.FAKE_CONTROL_DATA['reboot'] function_call = self.client.reboot_host self.check_service_client_function( function_call, self.func2mock['get'], expected_resp, bytes_body, 200, **self.params) def test_show_host_with_str_body(self): self._test_host_data('show') def test_show_host_with_bytes_body(self): self._test_host_data('show', True) def test_list_host_with_str_body(self): self._test_host_data() def test_list_host_with_bytes_body(self): self._test_host_data(bytes_body=True) def test_start_host_with_str_body(self): self._test_control_host('start') def test_start_host_with_bytes_body(self): self._test_control_host('start', True) def test_stop_host_with_str_body(self): self._test_control_host('stop') def test_stop_host_with_bytes_body(self): self._test_control_host('stop', True) def test_reboot_host_with_str_body(self): self._test_control_host('reboot') def test_reboot_host_with_bytes_body(self): self._test_control_host('reboot', True) def test_update_host_with_str_body(self): self._test_update_hosts() def test_update_host_with_bytes_body(self): self._test_update_hosts(True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_hypervisor_client.py000066400000000000000000000142431266406763200301060ustar00rootroot00000000000000# Copyright 2015 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import hypervisor_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestHypervisorClient(base.BaseComputeServiceTest): hypervisor_id = "1" hypervisor_name = "hyper.hostname.com" def setUp(self): super(TestHypervisorClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = hypervisor_client.HypervisorClient( fake_auth, 'compute', 'regionOne') def test_list_hypervisor_str_body(self): self._test_list_hypervisor(bytes_body=False) def test_list_hypervisor_byte_body(self): self._test_list_hypervisor(bytes_body=True) def _test_list_hypervisor(self, bytes_body=False): expected = {"hypervisors": [{ "id": 1, "hypervisor_hostname": "hypervisor1.hostname.com"}, { "id": 2, "hypervisor_hostname": "hypervisor2.hostname.com"}]} self.check_service_client_function( self.client.list_hypervisors, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body) def test_show_hypervisor_str_body(self): self._test_show_hypervisor(bytes_body=False) def test_show_hypervisor_byte_body(self): self._test_show_hypervisor(bytes_body=True) def _test_show_hypervisor(self, bytes_body=False): expected = {"hypervisor": { "cpu_info": "?", "current_workload": 0, "disk_available_least": 1, "host_ip": "10.10.10.10", "free_disk_gb": 1028, "free_ram_mb": 7680, "hypervisor_hostname": "fake-mini", "hypervisor_type": "fake", "hypervisor_version": 1, "id": 1, "local_gb": 1028, "local_gb_used": 0, "memory_mb": 8192, "memory_mb_used": 512, "running_vms": 0, "service": { "host": "fake_host", "id": 2}, "vcpus": 1, "vcpus_used": 0}} self.check_service_client_function( self.client.show_hypervisor, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, hypervisor_id=self.hypervisor_id) def test_list_servers_on_hypervisor_str_body(self): self._test_list_servers_on_hypervisor(bytes_body=False) def test_list_servers_on_hypervisor_byte_body(self): self._test_list_servers_on_hypervisor(bytes_body=True) def _test_list_servers_on_hypervisor(self, bytes_body=False): expected = {"hypervisors": [{ "id": 1, "hypervisor_hostname": "hyper.hostname.com", "servers": [{ "uuid": "e1ae8fc4-b72d-4c2f-a427-30dd420b6277", "name": "instance-00000001"}, { "uuid": "e1ae8fc4-b72d-4c2f-a427-30dd42066666", "name": "instance-00000002"} ]} ]} self.check_service_client_function( self.client.list_servers_on_hypervisor, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, hypervisor_name=self.hypervisor_name) def test_show_hypervisor_statistics_str_body(self): self._test_show_hypervisor_statistics(bytes_body=False) def test_show_hypervisor_statistics_byte_body(self): self._test_show_hypervisor_statistics(bytes_body=True) def _test_show_hypervisor_statistics(self, bytes_body=False): expected = { "hypervisor_statistics": { "count": 1, "current_workload": 0, "disk_available_least": 0, "free_disk_gb": 1028, "free_ram_mb": 7680, "local_gb": 1028, "local_gb_used": 0, "memory_mb": 8192, "memory_mb_used": 512, "running_vms": 0, "vcpus": 1, "vcpus_used": 0}} self.check_service_client_function( self.client.show_hypervisor_statistics, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body) def test_show_hypervisor_uptime_str_body(self): self._test_show_hypervisor_uptime(bytes_body=False) def test_show_hypervisor_uptime_byte_body(self): self._test_show_hypervisor_uptime(bytes_body=True) def _test_show_hypervisor_uptime(self, bytes_body=False): expected = { "hypervisor": { "hypervisor_hostname": "fake-mini", "id": 1, "uptime": (" 08:32:11 up 93 days, 18:25, 12 users, " " load average: 0.20, 0.12, 0.14") }} self.check_service_client_function( self.client.show_hypervisor_uptime, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, hypervisor_id=self.hypervisor_id) def test_search_hypervisor_str_body(self): self._test_search_hypervisor(bytes_body=False) def test_search_hypervisor_byte_body(self): self._test_search_hypervisor(bytes_body=True) def _test_search_hypervisor(self, bytes_body=False): expected = {"hypervisors": [{ "id": 2, "hypervisor_hostname": "hyper.hostname.com"}]} self.check_service_client_function( self.client.search_hypervisor, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, hypervisor_name=self.hypervisor_name) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_images_client.py000066400000000000000000000233411266406763200271400ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from oslotest import mockpatch from tempest_lib import exceptions as lib_exc from tempest_lib.services.compute import images_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestImagesClient(base.BaseComputeServiceTest): # Data Dictionaries used for testing # FAKE_IMAGE_METADATA = { "list": {"metadata": { "auto_disk_config": "True", "Label": "Changed" }}, "set_item": {"meta": { "auto_disk_config": "True" }}, "show_item": {"meta": { "kernel_id": "nokernel", }}, "update": {"metadata": { "kernel_id": "False", "Label": "UpdatedImage" }}, "set": {"metadata": { "Label": "Changed", "auto_disk_config": "True" }}, "delete_item": {} } FAKE_IMAGE_DATA = { "list": {"images": [ {"id": "70a599e0-31e7-49b7-b260-868f441e862b", "links": [ {"href": "http://openstack.example.com/v2/openstack" + "/images/70a599e0-31e7-49b7-b260-868f441e862b", "rel": "self" } ], "name": "fakeimage7" }]}, "show": {"image": { "created": "2011-01-01T01:02:03Z", "id": "70a599e0-31e7-49b7-b260-868f441e862b", "links": [ { "href": "http://openstack.example.com/v2/openstack" + "/images/70a599e0-31e7-49b7-b260-868f441e862b", "rel": "self" }, ], "metadata": { "architecture": "x86_64", "auto_disk_config": "True", "kernel_id": "nokernel", "ramdisk_id": "nokernel" }, "minDisk": 0, "minRam": 0, "name": "fakeimage7", "progress": 100, "status": "ACTIVE", "updated": "2011-01-01T01:02:03Z"}}, "create": {}, "delete": {} } func2mock = { 'get': 'tempest_lib.common.rest_client.RestClient.get', 'post': 'tempest_lib.common.rest_client.RestClient.post', 'put': 'tempest_lib.common.rest_client.RestClient.put', 'delete': 'tempest_lib.common.rest_client.RestClient.delete'} # Variable definition FAKE_IMAGE_ID = FAKE_IMAGE_DATA['show']['image']['id'] FAKE_SERVER_ID = "80a599e0-31e7-49b7-b260-868f441e343f" FAKE_CREATE_INFO = {'location': 'None'} FAKE_METADATA = FAKE_IMAGE_METADATA['show_item']['meta'] def setUp(self): super(TestImagesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = images_client.ImagesClient(fake_auth, "compute", "regionOne") def _test_image_operation(self, operation="delete", bytes_body=False): response_code = 200 mock_operation = self.func2mock['get'] expected_op = self.FAKE_IMAGE_DATA[operation] params = {"image_id": self.FAKE_IMAGE_ID} headers = None if operation == 'list': function = self.client.list_images elif operation == 'show': function = self.client.show_image elif operation == 'create': function = self.client.create_image mock_operation = self.func2mock['post'] params = {"server_id": self.FAKE_SERVER_ID} response_code = 202 headers = { 'connection': 'keep-alive', 'content-length': '0', 'content-type': 'application/json', 'status': '202', 'x-compute-request-id': 'req-fake', 'vary': 'accept-encoding', 'x-openstack-nova-api-version': 'v2.1', 'date': '13 Oct 2015 05:55:36 GMT', 'location': 'http://fake.com/images/fake' } else: function = self.client.delete_image mock_operation = self.func2mock['delete'] response_code = 204 self.check_service_client_function( function, mock_operation, expected_op, bytes_body, response_code, headers, **params) def _test_image_metadata(self, operation="set_item", bytes_body=False): response_code = 200 expected_op = self.FAKE_IMAGE_METADATA[operation] if operation == 'list': function = self.client.list_image_metadata mock_operation = self.func2mock['get'] params = {"image_id": self.FAKE_IMAGE_ID} elif operation == 'set': function = self.client.set_image_metadata mock_operation = self.func2mock['put'] params = {"image_id": "_dummy_data", "meta": self.FAKE_METADATA} elif operation == 'update': function = self.client.update_image_metadata mock_operation = self.func2mock['post'] params = {"image_id": self.FAKE_IMAGE_ID, "meta": self.FAKE_METADATA} elif operation == 'show_item': mock_operation = self.func2mock['get'] function = self.client.show_image_metadata_item params = {"image_id": self.FAKE_IMAGE_ID, "key": "123"} elif operation == 'delete_item': function = self.client.delete_image_metadata_item mock_operation = self.func2mock['delete'] response_code = 204 params = {"image_id": self.FAKE_IMAGE_ID, "key": "123"} else: function = self.client.set_image_metadata_item mock_operation = self.func2mock['put'] params = {"image_id": self.FAKE_IMAGE_ID, "key": "123", "meta": self.FAKE_METADATA} self.check_service_client_function( function, mock_operation, expected_op, bytes_body, response_code, **params) def _test_resource_deleted(self, bytes_body=False): params = {"id": self.FAKE_IMAGE_ID} expected_op = self.FAKE_IMAGE_DATA['show']['image'] self.useFixture(mockpatch.Patch('tempest_lib.services.compute' '.images_client.ImagesClient.show_image', side_effect=lib_exc.NotFound)) self.assertEqual(True, self.client.is_resource_deleted(**params)) tempdata = copy.deepcopy(self.FAKE_IMAGE_DATA['show']) tempdata['image']['id'] = None self.useFixture(mockpatch.Patch('tempest_lib.services.compute' '.images_client.ImagesClient.show_image', return_value=expected_op)) self.assertEqual(False, self.client.is_resource_deleted(**params)) def test_list_images_with_str_body(self): self._test_image_operation('list') def test_list_images_with_bytes_body(self): self._test_image_operation('list', True) def test_show_image_with_str_body(self): self._test_image_operation('show') def test_show_image_with_bytes_body(self): self._test_image_operation('show', True) def test_create_image_with_str_body(self): self._test_image_operation('create') def test_create_image_with_bytes_body(self): self._test_image_operation('create', True) def test_delete_image_with_str_body(self): self._test_image_operation('delete') def test_delete_image_with_bytes_body(self): self._test_image_operation('delete', True) def test_list_image_metadata_with_str_body(self): self._test_image_metadata('list') def test_list_image_metadata_with_bytes_body(self): self._test_image_metadata('list', True) def test_set_image_metadata_with_str_body(self): self._test_image_metadata('set') def test_set_image_metadata_with_bytes_body(self): self._test_image_metadata('set', True) def test_update_image_metadata_with_str_body(self): self._test_image_metadata('update') def test_update_image_metadata_with_bytes_body(self): self._test_image_metadata('update', True) def test_set_image_metadata_item_with_str_body(self): self._test_image_metadata() def test_set_image_metadata_item_with_bytes_body(self): self._test_image_metadata(bytes_body=True) def test_show_image_metadata_item_with_str_body(self): self._test_image_metadata('show_item') def test_show_image_metadata_item_with_bytes_body(self): self._test_image_metadata('show_item', True) def test_delete_image_metadata_item_with_str_body(self): self._test_image_metadata('delete_item') def test_delete_image_metadata_item_with_bytes_body(self): self._test_image_metadata('delete_item', True) def test_resource_delete_with_str_body(self): self._test_resource_deleted() def test_resource_delete_with_bytes_body(self): self._test_resource_deleted(True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_instance_usage_audit_log_client.py000066400000000000000000000055611266406763200327160ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. 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 datetime from tempest_lib.services.compute import instance_usage_audit_log_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestInstanceUsagesAuditLogClient(base.BaseComputeServiceTest): FAKE_AUDIT_LOG = { "hosts_not_run": [ "f4eb7cfd155f4574967f8b55a7faed75" ], "log": {}, "num_hosts": 1, "num_hosts_done": 0, "num_hosts_not_run": 1, "num_hosts_running": 0, "overall_status": "0 of 1 hosts done. 0 errors.", "period_beginning": "2012-12-01 00:00:00", "period_ending": "2013-01-01 00:00:00", "total_errors": 0, "total_instances": 0 } def setUp(self): super(TestInstanceUsagesAuditLogClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = (instance_usage_audit_log_client. InstanceUsagesAuditLogClient(fake_auth, 'compute', 'regionOne')) def _test_list_instance_usage_audit_logs(self, bytes_body=False): self.check_service_client_function( self.client.list_instance_usage_audit_logs, 'tempest_lib.common.rest_client.RestClient.get', {"instance_usage_audit_logs": self.FAKE_AUDIT_LOG}, bytes_body) def test_list_instance_usage_audit_logs_with_str_body(self): self._test_list_instance_usage_audit_logs() def test_list_instance_usage_audit_logs_with_bytes_body(self): self._test_list_instance_usage_audit_logs(bytes_body=True) def _test_show_instance_usage_audit_log(self, bytes_body=False): before_time = datetime.datetime(2012, 12, 1, 0, 0) self.check_service_client_function( self.client.show_instance_usage_audit_log, 'tempest_lib.common.rest_client.RestClient.get', {"instance_usage_audit_log": self.FAKE_AUDIT_LOG}, bytes_body, time_before=before_time) def test_show_instance_usage_audit_log_with_str_body(self): self._test_show_instance_usage_audit_log() def test_show_network_with_bytes_body_with_bytes_body(self): self._test_show_instance_usage_audit_log(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_interfaces_client.py000066400000000000000000000075501266406763200300220ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import interfaces_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestInterfacesClient(base.BaseComputeServiceTest): # Data Values to be used for testing # FAKE_INTERFACE_DATA = { "fixed_ips": [{ "ip_address": "192.168.1.1", "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef" }], "mac_addr": "fa:16:3e:4c:2c:30", "net_id": "3cb9bc59-5699-4588-a4b1-b87f96708bc6", "port_id": "ce531f90-199f-48c0-816c-13e38010b442", "port_state": "ACTIVE"} FAKE_SHOW_DATA = { "interfaceAttachment": FAKE_INTERFACE_DATA} FAKE_LIST_DATA = { "interfaceAttachments": [FAKE_INTERFACE_DATA]} FAKE_SERVER_ID = "ec14c864-096e-4e27-bb8a-2c2b4dc6f3f5" FAKE_PORT_ID = FAKE_SHOW_DATA['interfaceAttachment']['port_id'] func2mock = { 'delete': 'tempest_lib.common.rest_client.RestClient.delete', 'get': 'tempest_lib.common.rest_client.RestClient.get', 'post': 'tempest_lib.common.rest_client.RestClient.post'} def setUp(self): super(TestInterfacesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = interfaces_client.InterfacesClient(fake_auth, "compute", "regionOne") def _test_interface_operation(self, operation="create", bytes_body=False): response_code = 200 expected_op = self.FAKE_SHOW_DATA mock_operation = self.func2mock['get'] params = {'server_id': self.FAKE_SERVER_ID, 'port_id': self.FAKE_PORT_ID} if operation == 'list': expected_op = self.FAKE_LIST_DATA function = self.client.list_interfaces params = {'server_id': self.FAKE_SERVER_ID} elif operation == 'show': function = self.client.show_interface elif operation == 'delete': expected_op = {} mock_operation = self.func2mock['delete'] function = self.client.delete_interface response_code = 202 else: function = self.client.create_interface mock_operation = self.func2mock['post'] self.check_service_client_function( function, mock_operation, expected_op, bytes_body, response_code, **params) def test_list_interfaces_with_str_body(self): self._test_interface_operation('list') def test_list_interfaces_with_bytes_body(self): self._test_interface_operation('list', True) def test_show_interface_with_str_body(self): self._test_interface_operation('show') def test_show_interface_with_bytes_body(self): self._test_interface_operation('show', True) def test_delete_interface_with_str_body(self): self._test_interface_operation('delete') def test_delete_interface_with_bytes_body(self): self._test_interface_operation('delete', True) def test_create_interface_with_str_body(self): self._test_interface_operation() def test_create_interface_with_bytes_body(self): self._test_interface_operation(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_keypairs_client.py000066400000000000000000000064421266406763200275250ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.services.compute import keypairs_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestKeyPairsClient(base.BaseComputeServiceTest): FAKE_KEYPAIR = {"keypair": { "public_key": "ssh-rsa foo Generated-by-Nova", "name": u'\u2740(*\xb4\u25e1`*)\u2740', "user_id": "525d55f98980415ba98e634972fa4a10", "fingerprint": "76:24:66:49:d7:ca:6e:5c:77:ea:8e:bb:9c:15:5f:98" }} def setUp(self): super(TestKeyPairsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = keypairs_client.KeyPairsClient( fake_auth, 'compute', 'regionOne') def _test_list_keypairs(self, bytes_body=False): self.check_service_client_function( self.client.list_keypairs, 'tempest_lib.common.rest_client.RestClient.get', {"keypairs": []}, bytes_body) def test_list_keypairs_with_str_body(self): self._test_list_keypairs() def test_list_keypairs_with_bytes_body(self): self._test_list_keypairs(bytes_body=True) def _test_show_keypair(self, bytes_body=False): fake_keypair = copy.deepcopy(self.FAKE_KEYPAIR) fake_keypair["keypair"].update({ "deleted": False, "created_at": "2015-07-22T04:53:52.000000", "updated_at": None, "deleted_at": None, "id": 1 }) self.check_service_client_function( self.client.show_keypair, 'tempest_lib.common.rest_client.RestClient.get', fake_keypair, bytes_body, keypair_name="test") def test_show_keypair_with_str_body(self): self._test_show_keypair() def test_show_keypair_with_bytes_body(self): self._test_show_keypair(bytes_body=True) def _test_create_keypair(self, bytes_body=False): fake_keypair = copy.deepcopy(self.FAKE_KEYPAIR) fake_keypair["keypair"].update({"private_key": "foo"}) self.check_service_client_function( self.client.create_keypair, 'tempest_lib.common.rest_client.RestClient.post', fake_keypair, bytes_body, name="test") def test_create_keypair_with_str_body(self): self._test_create_keypair() def test_create_keypair_with_bytes_body(self): self._test_create_keypair(bytes_body=True) def test_delete_keypair(self): self.check_service_client_function( self.client.delete_keypair, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, keypair_name='test') tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_limits_client.py000066400000000000000000000046641266406763200272030ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import limits_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestLimitsClient(base.BaseComputeServiceTest): def setUp(self): super(TestLimitsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = limits_client.LimitsClient( fake_auth, 'compute', 'regionOne') def _test_show_limits(self, bytes_body=False): expected = { "limits": { "rate": [], "absolute": { "maxServerMeta": 128, "maxPersonality": 5, "totalServerGroupsUsed": 0, "maxImageMeta": 128, "maxPersonalitySize": 10240, "maxServerGroups": 10, "maxSecurityGroupRules": 20, "maxTotalKeypairs": 100, "totalCoresUsed": 0, "totalRAMUsed": 0, "totalInstancesUsed": 0, "maxSecurityGroups": 10, "totalFloatingIpsUsed": 0, "maxTotalCores": 20, "totalSecurityGroupsUsed": 0, "maxTotalFloatingIps": 10, "maxTotalInstances": 10, "maxTotalRAMSize": 51200, "maxServerGroupMembers": 10 } } } self.check_service_client_function( self.client.show_limits, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body) def test_show_limits_with_str_body(self): self._test_show_limits() def test_show_limits_with_bytes_body(self): self._test_show_limits(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_migrations_client.py000066400000000000000000000037601266406763200300520ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import migrations_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestMigrationsClient(base.BaseComputeServiceTest): FAKE_MIGRATION_INFO = {"migrations": [{ "created_at": "2012-10-29T13:42:02", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": 1234, "instance_uuid": "e9e4fdd7-f956-44ff-bfeb-d654a96ab3a2", "new_instance_type_id": 2, "old_instance_type_id": 1, "source_compute": "compute1", "source_node": "node1", "status": "finished", "updated_at": "2012-10-29T13:42:02"}]} def setUp(self): super(TestMigrationsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.mg_client_obj = migrations_client.MigrationsClient( fake_auth, 'compute', 'regionOne') def _test_list_migrations(self, bytes_body=False): self.check_service_client_function( self.mg_client_obj.list_migrations, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_MIGRATION_INFO, bytes_body) def test_list_migration_with_str_body(self): self._test_list_migrations() def test_list_migration_with_bytes_body(self): self._test_list_migrations(True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_networks_client.py000066400000000000000000000060021266406763200275420ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import networks_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestNetworksClient(base.BaseComputeServiceTest): FAKE_NETWORK = { "bridge": None, "vpn_public_port": None, "dhcp_start": None, "bridge_interface": None, "share_address": None, "updated_at": None, "id": "34d5ae1e-5659-49cf-af80-73bccd7d7ad3", "cidr_v6": None, "deleted_at": None, "gateway": None, "rxtx_base": None, "label": u'30d7', "priority": None, "project_id": None, "vpn_private_address": None, "deleted": None, "vlan": None, "broadcast": None, "netmask": None, "injected": None, "cidr": None, "vpn_public_address": None, "multi_host": None, "enable_dhcp": None, "dns2": None, "created_at": None, "host": None, "mtu": None, "gateway_v6": None, "netmask_v6": None, "dhcp_server": None, "dns1": None } network_id = "34d5ae1e-5659-49cf-af80-73bccd7d7ad3" FAKE_NETWORKS = [FAKE_NETWORK] def setUp(self): super(TestNetworksClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = networks_client.NetworksClient( fake_auth, 'compute', 'regionOne') def _test_list_networks(self, bytes_body=False): fake_list = {"networks": self.FAKE_NETWORKS} self.check_service_client_function( self.client.list_networks, 'tempest_lib.common.rest_client.RestClient.get', fake_list, bytes_body) def test_list_networks_with_str_body(self): self._test_list_networks() def test_list_networks_with_bytes_body(self): self._test_list_networks(bytes_body=True) def _test_show_network(self, bytes_body=False): self.check_service_client_function( self.client.show_network, 'tempest_lib.common.rest_client.RestClient.get', {"network": self.FAKE_NETWORK}, bytes_body, network_id=self.network_id ) def test_show_network_with_str_body(self): self._test_show_network() def test_show_network_with_bytes_body(self): self._test_show_network(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_quota_classes_client.py000066400000000000000000000051001266406763200305320ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.services.compute import quota_classes_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestQuotaClassesClient(base.BaseComputeServiceTest): FAKE_QUOTA_CLASS_SET = { "injected_file_content_bytes": 10240, "metadata_items": 128, "server_group_members": 10, "server_groups": 10, "ram": 51200, "floating_ips": 10, "key_pairs": 100, "id": u'\u2740(*\xb4\u25e1`*)\u2740', "instances": 10, "security_group_rules": 20, "security_groups": 10, "injected_files": 5, "cores": 20, "fixed_ips": -1, "injected_file_path_bytes": 255, } def setUp(self): super(TestQuotaClassesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = quota_classes_client.QuotaClassesClient( fake_auth, 'compute', 'regionOne') def _test_show_quota_class_set(self, bytes_body=False): fake_body = {'quota_class_set': self.FAKE_QUOTA_CLASS_SET} self.check_service_client_function( self.client.show_quota_class_set, 'tempest_lib.common.rest_client.RestClient.get', fake_body, bytes_body, quota_class_id="test") def test_show_quota_class_set_with_str_body(self): self._test_show_quota_class_set() def test_show_quota_class_set_with_bytes_body(self): self._test_show_quota_class_set(bytes_body=True) def test_update_quota_class_set(self): fake_quota_class_set = copy.deepcopy(self.FAKE_QUOTA_CLASS_SET) fake_quota_class_set.pop("id") fake_body = {'quota_class_set': fake_quota_class_set} self.check_service_client_function( self.client.update_quota_class_set, 'tempest_lib.common.rest_client.RestClient.put', fake_body, quota_class_id="test") tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_quotas_client.py000066400000000000000000000115071266406763200272100ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.services.compute import quotas_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestQuotasClient(base.BaseComputeServiceTest): FAKE_QUOTA_SET = { "quota_set": { "injected_file_content_bytes": 10240, "metadata_items": 128, "server_group_members": 10, "server_groups": 10, "ram": 51200, "floating_ips": 10, "key_pairs": 100, "id": "8421f7be61064f50b680465c07f334af", "instances": 10, "security_group_rules": 20, "injected_files": 5, "cores": 20, "fixed_ips": -1, "injected_file_path_bytes": 255, "security_groups": 10} } project_id = "8421f7be61064f50b680465c07f334af" fake_user_id = "65f09168cbb04eb593f3138b63b67b67" def setUp(self): super(TestQuotasClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = quotas_client.QuotasClient( fake_auth, 'compute', 'regionOne') def _test_show_quota_set(self, bytes_body=False, user_id=None): if user_id: self.check_service_client_function( self.client.show_quota_set, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_QUOTA_SET, to_utf=bytes_body, tenant_id=self.project_id, user_id=user_id) else: self.check_service_client_function( self.client.show_quota_set, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_QUOTA_SET, to_utf=bytes_body, tenant_id=self.project_id) def test_show_quota_set_with_str_body(self): self._test_show_quota_set() def test_show_quota_set_with_bytes_body(self): self._test_show_quota_set(bytes_body=True) def test_show_quota_set_for_user_with_str_body(self): self._test_show_quota_set(user_id=self.fake_user_id) def test_show_quota_set_for_user_with_bytes_body(self): self._test_show_quota_set(bytes_body=True, user_id=self.fake_user_id) def _test_show_default_quota_set(self, bytes_body=False): self.check_service_client_function( self.client.show_default_quota_set, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_QUOTA_SET, to_utf=bytes_body, tenant_id=self.project_id) def test_show_default_quota_set_with_str_body(self): self._test_show_default_quota_set() def test_show_default_quota_set_with_bytes_body(self): self._test_show_default_quota_set(bytes_body=True) def _test_update_quota_set(self, bytes_body=False, user_id=None): fake_quota_set = copy.deepcopy(self.FAKE_QUOTA_SET) fake_quota_set['quota_set'].pop("id") if user_id: self.check_service_client_function( self.client.update_quota_set, 'tempest_lib.common.rest_client.RestClient.put', fake_quota_set, to_utf=bytes_body, tenant_id=self.project_id, user_id=user_id) else: self.check_service_client_function( self.client.update_quota_set, 'tempest_lib.common.rest_client.RestClient.put', fake_quota_set, to_utf=bytes_body, tenant_id=self.project_id) def test_update_quota_set_with_str_body(self): self._test_update_quota_set() def test_update_quota_set_with_bytes_body(self): self._test_update_quota_set(bytes_body=True) def test_update_quota_set_for_user_with_str_body(self): self._test_update_quota_set(user_id=self.fake_user_id) def test_update_quota_set_for_user_with_bytes_body(self): self._test_update_quota_set(bytes_body=True, user_id=self.fake_user_id) def test_delete_quota_set(self): self.check_service_client_function( self.client.delete_quota_set, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, tenant_id=self.project_id) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_security_group_default_rules_client.py000066400000000000000000000072071266406763200336770ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import security_group_default_rules_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestSecurityGroupDefaultRulesClient(base.BaseComputeServiceTest): FAKE_RULE = { "from_port": 80, "id": 1, "ip_protocol": "TCP", "ip_range": { "cidr": "10.10.10.0/24" }, "to_port": 80 } def setUp(self): super(TestSecurityGroupDefaultRulesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = (security_group_default_rules_client. SecurityGroupDefaultRulesClient(fake_auth, 'compute', 'regionOne')) def _test_list_security_group_default_rules(self, bytes_body=False): self.check_service_client_function( self.client.list_security_group_default_rules, 'tempest_lib.common.rest_client.RestClient.get', {"security_group_default_rules": [self.FAKE_RULE]}, to_utf=bytes_body) def test_list_security_group_default_rules_with_str_body(self): self._test_list_security_group_default_rules() def test_list_security_group_default_rules_with_bytes_body(self): self._test_list_security_group_default_rules(bytes_body=True) def _test_show_security_group_default_rule(self, bytes_body=False): self.check_service_client_function( self.client.show_security_group_default_rule, 'tempest_lib.common.rest_client.RestClient.get', {"security_group_default_rule": self.FAKE_RULE}, to_utf=bytes_body, security_group_default_rule_id=1) def test_show_security_group_default_rule_with_str_body(self): self._test_show_security_group_default_rule() def test_show_security_group_default_rule_with_bytes_body(self): self._test_show_security_group_default_rule(bytes_body=True) def _test_create_security_default_group_rule(self, bytes_body=False): request_body = { "to_port": 80, "from_port": 80, "ip_protocol": "TCP", "cidr": "10.10.10.0/24" } self.check_service_client_function( self.client.create_security_default_group_rule, 'tempest_lib.common.rest_client.RestClient.post', {"security_group_default_rule": self.FAKE_RULE}, to_utf=bytes_body, **request_body) def test_create_security_default_group_rule_with_str_body(self): self._test_create_security_default_group_rule() def test_create_security_default_group_rule_with_bytes_body(self): self._test_create_security_default_group_rule(bytes_body=True) def test_delete_security_group_default_rule(self): self.check_service_client_function( self.client.delete_security_group_default_rule, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=204, security_group_default_rule_id=1) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_security_group_rules_client.py000066400000000000000000000050161266406763200321670ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import security_group_rules_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestSecurityGroupRulesClient(base.BaseComputeServiceTest): FAKE_SECURITY_GROUP_RULE = { "security_group_rule": { "id": "2d021cf1-ce4b-4292-994f-7a785d62a144", "ip_range": { "cidr": "0.0.0.0/0" }, "parent_group_id": "48700ff3-30b8-4e63-845f-a79c9633e9fb", "to_port": 443, "ip_protocol": "tcp", "group": {}, "from_port": 443 } } def setUp(self): super(TestSecurityGroupRulesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = security_group_rules_client.SecurityGroupRulesClient( fake_auth, 'compute', 'regionOne') def _test_create_security_group_rule(self, bytes_body=False): req_body = { "from_port": "443", "ip_protocol": "tcp", "to_port": "443", "cidr": "0.0.0.0/0", "parent_group_id": "48700ff3-30b8-4e63-845f-a79c9633e9fb" } self.check_service_client_function( self.client.create_security_group_rule, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_SECURITY_GROUP_RULE, to_utf=bytes_body, **req_body) def test_create_security_group_rule_with_str_body(self): self._test_create_security_group_rule() def test_create_security_group_rule_with_bytes_body(self): self._test_create_security_group_rule(bytes_body=True) def test_delete_security_group_rule(self): self.check_service_client_function( self.client.delete_security_group_rule, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, group_rule_id='group-id') tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_security_groups_client.py000066400000000000000000000110521266406763200311350ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslotest import mockpatch from tempest_lib import exceptions as lib_exc from tempest_lib.services.compute import security_groups_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestSecurityGroupsClient(base.BaseComputeServiceTest): FAKE_SECURITY_GROUP_INFO = [{ "description": "default", "id": "3fb26eb3-581b-4420-9963-b0879a026506", "name": "default", "rules": [], "tenant_id": "openstack" }] def setUp(self): super(TestSecurityGroupsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = security_groups_client.SecurityGroupsClient( fake_auth, 'compute', 'regionOne') def _test_list_security_groups(self, bytes_body=False): self.check_service_client_function( self.client.list_security_groups, 'tempest_lib.common.rest_client.RestClient.get', {"security_groups": self.FAKE_SECURITY_GROUP_INFO}, to_utf=bytes_body) def test_list_security_groups_with_str_body(self): self._test_list_security_groups() def test_list_security_groups_with_bytes_body(self): self._test_list_security_groups(bytes_body=True) def _test_show_security_group(self, bytes_body=False): self.check_service_client_function( self.client.show_security_group, 'tempest_lib.common.rest_client.RestClient.get', {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]}, to_utf=bytes_body, security_group_id='fake-id') def test_show_security_group_with_str_body(self): self._test_show_security_group() def test_show_security_group_with_bytes_body(self): self._test_show_security_group(bytes_body=True) def _test_create_security_group(self, bytes_body=False): post_body = {"name": "test", "description": "test_group"} self.check_service_client_function( self.client.create_security_group, 'tempest_lib.common.rest_client.RestClient.post', {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]}, to_utf=bytes_body, kwargs=post_body) def test_create_security_group_with_str_body(self): self._test_create_security_group() def test_create_security_group_with_bytes_body(self): self._test_create_security_group(bytes_body=True) def _test_update_security_group(self, bytes_body=False): req_body = {"name": "test", "description": "test_group"} self.check_service_client_function( self.client.update_security_group, 'tempest_lib.common.rest_client.RestClient.put', {"security_group": self.FAKE_SECURITY_GROUP_INFO[0]}, to_utf=bytes_body, security_group_id='fake-id', kwargs=req_body) def test_update_security_group_with_str_body(self): self._test_update_security_group() def test_update_security_group_with_bytes_body(self): self._test_update_security_group(bytes_body=True) def test_delete_security_group(self): self.check_service_client_function( self.client.delete_security_group, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, security_group_id='fake-id') def test_is_resource_deleted_true(self): mod = ('tempest_lib.services.compute.security_groups_client.' 'SecurityGroupsClient.show_security_group') self.useFixture(mockpatch.Patch(mod, side_effect=lib_exc.NotFound)) self.assertTrue(self.client.is_resource_deleted('fake-id')) def test_is_resource_deleted_false(self): mod = ('tempest_lib.services.compute.security_groups_client.' 'SecurityGroupsClient.show_security_group') self.useFixture(mockpatch.Patch(mod, return_value='success')) self.assertFalse(self.client.is_resource_deleted('fake-id')) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_server_groups_client.py000066400000000000000000000063321266406763200306010ustar00rootroot00000000000000# Copyright 2015 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import httplib2 from oslotest import mockpatch from tempest_lib.tests import fake_auth_provider from tempest_lib.services.compute import server_groups_client from tempest_lib.tests.services.compute import base class TestServerGroupsClient(base.BaseComputeServiceTest): server_group = { "id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9", "name": "test", "policies": ["anti-affinity"], "members": [], "metadata": {}} def setUp(self): super(TestServerGroupsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = server_groups_client.ServerGroupsClient( fake_auth, 'compute', 'regionOne') def _test_create_server_group(self, bytes_body=False): expected = {"server_group": TestServerGroupsClient.server_group} self.check_service_client_function( self.client.create_server_group, 'tempest_lib.common.rest_client.RestClient.post', expected, bytes_body, name='fake-group', policies=['affinity']) def test_create_server_group_str_body(self): self._test_create_server_group(bytes_body=False) def test_create_server_group_byte_body(self): self._test_create_server_group(bytes_body=True) def test_delete_server_group(self): response = (httplib2.Response({'status': 204}), None) self.useFixture(mockpatch.Patch( 'tempest_lib.common.rest_client.RestClient.delete', return_value=response)) self.client.delete_server_group('fake-group') def _test_list_server_groups(self, bytes_body=False): expected = {"server_groups": [TestServerGroupsClient.server_group]} self.check_service_client_function( self.client.list_server_groups, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body) def test_list_server_groups_str_body(self): self._test_list_server_groups(bytes_body=False) def test_list_server_groups_byte_body(self): self._test_list_server_groups(bytes_body=True) def _test_show_server_group(self, bytes_body=False): expected = {"server_group": TestServerGroupsClient.server_group} self.check_service_client_function( self.client.show_server_group, 'tempest_lib.common.rest_client.RestClient.get', expected, bytes_body, server_group_id='5bbcc3c4-1da2-4437-a48a-66f15b1b13f9') def test_show_server_group_str_body(self): self._test_show_server_group(bytes_body=False) def test_show_server_group_byte_body(self): self._test_show_server_group(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_servers_client.py000066400000000000000000001014121266406763200273600ustar00rootroot00000000000000# Copyright 2015 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.services.compute import servers_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestServersClient(base.BaseComputeServiceTest): FAKE_SERVERS = {'servers': [{ "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", "links": [ { "href": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19", "rel": "self" }, { "href": "http://os.co/616fb98f-46ca-475e-917e-2563e5a8cd19", "rel": "bookmark" } ], "name": u"new\u1234-server-test"}] } FAKE_SERVER_DIAGNOSTICS = { "cpu0_time": 17300000000, "memory": 524288, "vda_errors": -1, "vda_read": 262144, "vda_read_req": 112, "vda_write": 5778432, "vda_write_req": 488, "vnet1_rx": 2070139, "vnet1_rx_drop": 0, "vnet1_rx_errors": 0, "vnet1_rx_packets": 26701, "vnet1_tx": 140208, "vnet1_tx_drop": 0, "vnet1_tx_errors": 0, "vnet1_tx_packets": 662 } FAKE_SERVER_GET = {'server': { "accessIPv4": "", "accessIPv6": "", "addresses": { "private": [ { "addr": "192.168.0.3", "version": 4 } ] }, "created": "2012-08-20T21:11:09Z", "flavor": { "id": "1", "links": [ { "href": "http://os.com/openstack/flavors/1", "rel": "bookmark" } ] }, "hostId": "65201c14a29663e06d0748e561207d998b343e1d164bfa0aafa9c45d", "id": "893c7791-f1df-4c3d-8383-3caae9656c62", "image": { "id": "70a599e0-31e7-49b7-b260-868f441e862b", "links": [ { "href": "http://imgs/70a599e0-31e7-49b7-b260-868f441e862b", "rel": "bookmark" } ] }, "links": [ { "href": "http://v2/srvs/893c7791-f1df-4c3d-8383-3caae9656c62", "rel": "self" }, { "href": "http://srvs/893c7791-f1df-4c3d-8383-3caae9656c62", "rel": "bookmark" } ], "metadata": { u"My Server N\u1234me": u"Apa\u1234che1" }, "name": u"new\u1234-server-test", "progress": 0, "status": "ACTIVE", "tenant_id": "openstack", "updated": "2012-08-20T21:11:09Z", "user_id": "fake"} } FAKE_SERVER_POST = {"server": { "id": "616fb98f-46ca-475e-917e-2563e5a8cd19", "adminPass": "fake-admin-pass", "security_groups": [ 'fake-security-group-1', 'fake-security-group-2' ], "links": [ { "href": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19", "rel": "self" }, { "href": "http://os.co/616fb98f-46ca-475e-917e-2563e5a8cd19", "rel": "bookmark" } ], "OS-DCF:diskConfig": "fake-disk-config"} } FAKE_ADDRESS = {"addresses": { "private": [ { "addr": "192.168.0.3", "version": 4 } ]} } FAKE_COMMON_VOLUME = { "id": "a6b0875b-6b5d-4a5a-81eb-0c3aa62e5fdb", "device": "fake-device", "volumeId": "a6b0875b-46ca-475e-917e-0c3aa62e5fdb", "serverId": "616fb98f-46ca-475e-917e-2563e5a8cd19" } FAKE_VIRTUAL_INTERFACES = { "id": "a6b0875b-46ca-475e-917e-0c3aa62e5fdb", "mac_address": "00:25:90:5b:f8:c3", "OS-EXT-VIF-NET:net_id": "fake-os-net-id" } FAKE_INSTANCE_ACTIONS = { "action": "fake-action", "request_id": "16fb98f-46ca-475e-917e-2563e5a8cd19", "user_id": "16fb98f-46ca-475e-917e-2563e5a8cd12", "project_id": "16fb98f-46ca-475e-917e-2563e5a8cd34", "start_time": "09MAR2015 11:15", "message": "fake-msg", "instance_uuid": "16fb98f-46ca-475e-917e-2563e5a8cd12" } FAKE_VNC_CONSOLE = { "type": "fake-type", "url": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19" } FAKE_INSTANCE_ACTION_EVENTS = { "event": "fake-event", "start_time": "09MAR2015 11:15", "finish_time": "09MAR2015 11:15", "result": "fake-result", "traceback": "fake-trace-back" } FAKE_INSTANCE_WITH_EVENTS = copy.deepcopy(FAKE_INSTANCE_ACTIONS) FAKE_INSTANCE_WITH_EVENTS['events'] = [FAKE_INSTANCE_ACTION_EVENTS] FAKE_REBUILD_SERVER = copy.deepcopy(FAKE_SERVER_GET) FAKE_REBUILD_SERVER['server']['adminPass'] = 'fake-admin-pass' server_id = FAKE_SERVER_GET['server']['id'] network_id = 'a6b0875b-6b5d-4a5a-81eb-0c3aa62e5fdb' def setUp(self): super(TestServersClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = servers_client.ServersClient( fake_auth, 'compute', 'regionOne') def test_list_servers_with_str_body(self): self._test_list_servers() def test_list_servers_with_bytes_body(self): self._test_list_servers(bytes_body=True) def _test_list_servers(self, bytes_body=False): self.check_service_client_function( self.client.list_servers, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SERVERS, bytes_body) def test_show_server_with_str_body(self): self._test_show_server() def test_show_server_with_bytes_body(self): self._test_show_server(bytes_body=True) def _test_show_server(self, bytes_body=False): self.check_service_client_function( self.client.show_server, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SERVER_GET, bytes_body, server_id=self.server_id ) def test_delete_server(self, bytes_body=False): self.check_service_client_function( self.client.delete_server, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=204, server_id=self.server_id ) def test_create_server_with_str_body(self): self._test_create_server() def test_create_server_with_bytes_body(self): self._test_create_server(True) def _test_create_server(self, bytes_body=False): self.check_service_client_function( self.client.create_server, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_SERVER_POST, bytes_body, status=202, name='fake-name', imageRef='fake-image-ref', flavorRef='fake-flavor-ref' ) def test_list_addresses_with_str_body(self): self._test_list_addresses() def test_list_addresses_with_bytes_body(self): self._test_list_addresses(True) def _test_list_addresses(self, bytes_body=False): self.check_service_client_function( self.client.list_addresses, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_ADDRESS, bytes_body, server_id=self.server_id ) def test_list_addresses_by_network_with_str_body(self): self._test_list_addresses_by_network() def test_list_addresses_by_network_with_bytes_body(self): self._test_list_addresses_by_network(True) def _test_list_addresses_by_network(self, bytes_body=False): self.check_service_client_function( self.client.list_addresses_by_network, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_ADDRESS['addresses'], server_id=self.server_id, network_id=self.network_id ) def test_action_with_str_body(self): self._test_action() def test_action_with_bytes_body(self): self._test_action(True) def _test_action(self, bytes_body=False): self.check_service_client_function( self.client.action, 'tempest_lib.common.rest_client.RestClient.post', {}, server_id=self.server_id, action_name='fake-action-name', schema={'status_code': 200} ) def test_create_backup_with_str_body(self): self._test_create_backup() def test_create_backup_with_bytes_body(self): self._test_create_backup(True) def _test_create_backup(self, bytes_body=False): self.check_service_client_function( self.client.create_backup, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id, backup_type='fake-backup', rotation='fake-rotation', name='fake-name' ) def test_change_password_with_str_body(self): self._test_change_password() def test_change_password_with_bytes_body(self): self._test_change_password(True) def _test_change_password(self, bytes_body=False): self.check_service_client_function( self.client.change_password, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id, adminPass='fake-admin-pass' ) def test_show_password_with_str_body(self): self._test_show_password() def test_show_password_with_bytes_body(self): self._test_show_password(True) def _test_show_password(self, bytes_body=False): self.check_service_client_function( self.client.show_password, 'tempest_lib.common.rest_client.RestClient.get', {'password': 'fake-password'}, server_id=self.server_id ) def test_delete_password_with_str_body(self): self._test_delete_password() def test_delete_password_with_bytes_body(self): self._test_delete_password(True) def _test_delete_password(self, bytes_body=False): self.check_service_client_function( self.client.delete_password, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=204, server_id=self.server_id ) def test_reboot_server_with_str_body(self): self._test_reboot_server() def test_reboot_server_with_bytes_body(self): self._test_reboot_server(True) def _test_reboot_server(self, bytes_body=False): self.check_service_client_function( self.client.reboot_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id, type='fake-reboot-type' ) def test_rebuild_server_with_str_body(self): self._test_rebuild_server() def test_rebuild_server_with_bytes_body(self): self._test_rebuild_server(True) def _test_rebuild_server(self, bytes_body=False): self.check_service_client_function( self.client.rebuild_server, 'tempest_lib.common.rest_client.RestClient.post', self.FAKE_REBUILD_SERVER, status=202, server_id=self.server_id, image_ref='fake-image-ref' ) def test_resize_server_with_str_body(self): self._test_resize_server() def test_resize_server_with_bytes_body(self): self._test_resize_server(True) def _test_resize_server(self, bytes_body=False): self.check_service_client_function( self.client.resize_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id, flavor_ref='fake-flavor-ref' ) def test_confirm_resize_server_with_str_body(self): self._test_confirm_resize_server() def test_confirm_resize_server_with_bytes_body(self): self._test_confirm_resize_server(True) def _test_confirm_resize_server(self, bytes_body=False): self.check_service_client_function( self.client.confirm_resize_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=204, server_id=self.server_id ) def test_revert_resize_server_with_str_body(self): self._test_revert_resize() def test_revert_resize_server_with_bytes_body(self): self._test_revert_resize(True) def _test_revert_resize(self, bytes_body=False): self.check_service_client_function( self.client.revert_resize_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_list_server_metadata_with_str_body(self): self._test_list_server_metadata() def test_list_server_metadata_with_bytes_body(self): self._test_list_server_metadata() def _test_list_server_metadata(self, bytes_body=False): self.check_service_client_function( self.client.list_server_metadata, 'tempest_lib.common.rest_client.RestClient.get', {'metadata': {'fake-key': 'fake-meta-data'}}, server_id=self.server_id ) def test_set_server_metadata_with_str_body(self): self._test_set_server_metadata() def test_set_server_metadata_with_bytes_body(self): self._test_set_server_metadata(True) def _test_set_server_metadata(self, bytes_body=False): self.check_service_client_function( self.client.set_server_metadata, 'tempest_lib.common.rest_client.RestClient.put', {'metadata': {'fake-key': 'fake-meta-data'}}, server_id=self.server_id, meta='fake-meta' ) def test_update_server_metadata_with_str_body(self): self._test_update_server_metadata() def test_update_server_metadata_with_bytes_body(self): self._test_update_server_metadata(True) def _test_update_server_metadata(self, bytes_body=False): self.check_service_client_function( self.client.update_server_metadata, 'tempest_lib.common.rest_client.RestClient.post', {'metadata': {'fake-key': 'fake-meta-data'}}, server_id=self.server_id, meta='fake-meta' ) def test_show_server_metadata_item_with_str_body(self): self._test_show_server_metadata() def test_show_server_metadata_item_with_bytes_body(self): self._test_show_server_metadata(True) def _test_show_server_metadata(self, bytes_body=False): self.check_service_client_function( self.client.show_server_metadata_item, 'tempest_lib.common.rest_client.RestClient.get', {'meta': {'fake-key': 'fake-meta-data'}}, server_id=self.server_id, key='fake-key' ) def test_set_server_metadata_item_with_str_body(self): self._test_set_server_metadata_item() def test_set_server_metadata_item_with_bytes_body(self): self._test_set_server_metadata_item(True) def _test_set_server_metadata_item(self, bytes_body=False): self.check_service_client_function( self.client.set_server_metadata_item, 'tempest_lib.common.rest_client.RestClient.put', {'meta': {'fake-key': 'fake-meta-data'}}, server_id=self.server_id, key='fake-key', meta='fake-meta' ) def test_delete_server_metadata_item_with_str_body(self): self._test_delete_server_metadata() def test_delete_server_metadata_item_with_bytes_body(self): self._test_delete_server_metadata(True) def _test_delete_server_metadata(self, bytes_body=False): self.check_service_client_function( self.client.delete_server_metadata_item, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=204, server_id=self.server_id, key='fake-key' ) def test_stop_server_with_str_body(self): self._test_stop_server() def test_stop_server_with_bytes_body(self): self._test_stop_server(True) def _test_stop_server(self, bytes_body=False): self.check_service_client_function( self.client.stop_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_start_server_with_str_body(self): self._test_start_server() def test_start_server_with_bytes_body(self): self._test_start_server(True) def _test_start_server(self, bytes_body=False): self.check_service_client_function( self.client.start_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_attach_volume_with_str_body(self): self._test_attach_volume_server() def test_attach_volume_with_bytes_body(self): self._test_attach_volume_server(True) def _test_attach_volume_server(self, bytes_body=False): self.check_service_client_function( self.client.attach_volume, 'tempest_lib.common.rest_client.RestClient.post', {'volumeAttachment': self.FAKE_COMMON_VOLUME}, server_id=self.server_id ) def test_update_attached_volume(self): self.check_service_client_function( self.client.update_attached_volume, 'tempest_lib.common.rest_client.RestClient.put', {}, status=202, server_id=self.server_id, attachment_id='fake-attachment-id', volumeId='fake-volume-id' ) def test_detach_volume_with_str_body(self): self._test_detach_volume_server() def test_detach_volume_with_bytes_body(self): self._test_detach_volume_server(True) def _test_detach_volume_server(self, bytes_body=False): self.check_service_client_function( self.client.detach_volume, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, server_id=self.server_id, volume_id=self.FAKE_COMMON_VOLUME['volumeId'] ) def test_show_volume_attachment_with_str_body(self): self._test_show_volume_attachment() def test_show_volume_attachment_with_bytes_body(self): self._test_show_volume_attachment(True) def _test_show_volume_attachment(self, bytes_body=False): self.check_service_client_function( self.client.show_volume_attachment, 'tempest_lib.common.rest_client.RestClient.get', {'volumeAttachment': self.FAKE_COMMON_VOLUME}, server_id=self.server_id, volume_id=self.FAKE_COMMON_VOLUME['volumeId'] ) def test_list_volume_attachments_with_str_body(self): self._test_list_volume_attachments() def test_list_volume_attachments_with_bytes_body(self): self._test_list_volume_attachments(True) def _test_list_volume_attachments(self, bytes_body=False): self.check_service_client_function( self.client.list_volume_attachments, 'tempest_lib.common.rest_client.RestClient.get', {'volumeAttachments': [self.FAKE_COMMON_VOLUME]}, server_id=self.server_id ) def test_add_security_group_with_str_body(self): self._test_add_security_group() def test_add_security_group_with_bytes_body(self): self._test_add_security_group(True) def _test_add_security_group(self, bytes_body=False): self.check_service_client_function( self.client.add_security_group, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id, name='fake-name' ) def test_remove_security_group_with_str_body(self): self._test_remove_security_group() def test_remove_security_group_with_bytes_body(self): self._test_remove_security_group(True) def _test_remove_security_group(self, bytes_body=False): self.check_service_client_function( self.client.remove_security_group, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id, name='fake-name' ) def test_live_migrate_server_with_str_body(self): self._test_live_migrate_server() def test_live_migrate_server_with_bytes_body(self): self._test_live_migrate_server(True) def _test_live_migrate_server(self, bytes_body=False): self.check_service_client_function( self.client.live_migrate_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_migrate_server_with_str_body(self): self._test_migrate_server() def test_migrate_server_with_bytes_body(self): self._test_migrate_server(True) def _test_migrate_server(self, bytes_body=False): self.check_service_client_function( self.client.migrate_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_lock_server_with_str_body(self): self._test_lock_server() def test_lock_server_with_bytes_body(self): self._test_lock_server(True) def _test_lock_server(self, bytes_body=False): self.check_service_client_function( self.client.lock_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_unlock_server_with_str_body(self): self._test_unlock_server() def test_unlock_server_with_bytes_body(self): self._test_unlock_server(True) def _test_unlock_server(self, bytes_body=False): self.check_service_client_function( self.client.unlock_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_suspend_server_with_str_body(self): self._test_suspend_server() def test_suspend_server_with_bytes_body(self): self._test_suspend_server(True) def _test_suspend_server(self, bytes_body=False): self.check_service_client_function( self.client.suspend_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_resume_server_with_str_body(self): self._test_resume_server() def test_resume_server_with_bytes_body(self): self._test_resume_server(True) def _test_resume_server(self, bytes_body=False): self.check_service_client_function( self.client.resume_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_pause_server_with_str_body(self): self._test_pause_server() def test_pause_server_with_bytes_body(self): self._test_pause_server(True) def _test_pause_server(self, bytes_body=False): self.check_service_client_function( self.client.pause_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_unpause_server_with_str_body(self): self._test_unpause_server() def test_unpause_server_with_bytes_body(self): self._test_unpause_server(True) def _test_unpause_server(self, bytes_body=False): self.check_service_client_function( self.client.unpause_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_reset_state_with_str_body(self): self._test_reset_state() def test_reset_state_with_bytes_body(self): self._test_reset_state(True) def _test_reset_state(self, bytes_body=False): self.check_service_client_function( self.client.reset_state, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id, state='fake-state' ) def test_shelve_server_with_str_body(self): self._test_shelve_server() def test_shelve_server_with_bytes_body(self): self._test_shelve_server(True) def _test_shelve_server(self, bytes_body=False): self.check_service_client_function( self.client.shelve_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_unshelve_server_with_str_body(self): self._test_unshelve_server() def test_unshelve_server_with_bytes_body(self): self._test_unshelve_server(True) def _test_unshelve_server(self, bytes_body=False): self.check_service_client_function( self.client.unshelve_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_shelve_offload_server_with_str_body(self): self._test_shelve_offload_server() def test_shelve_offload_server_with_bytes_body(self): self._test_shelve_offload_server(True) def _test_shelve_offload_server(self, bytes_body=False): self.check_service_client_function( self.client.shelve_offload_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_get_console_output_with_str_body(self): self._test_get_console_output() def test_get_console_output_with_bytes_body(self): self._test_get_console_output(True) def _test_get_console_output(self, bytes_body=False): self.check_service_client_function( self.client.get_console_output, 'tempest_lib.common.rest_client.RestClient.post', {'output': 'fake-output'}, server_id=self.server_id, length='fake-length' ) def test_list_virtual_interfaces_with_str_body(self): self._test_list_virtual_interfaces() def test_list_virtual_interfaces_with_bytes_body(self): self._test_list_virtual_interfaces(True) def _test_list_virtual_interfaces(self, bytes_body=False): self.check_service_client_function( self.client.list_virtual_interfaces, 'tempest_lib.common.rest_client.RestClient.get', {'virtual_interfaces': [self.FAKE_VIRTUAL_INTERFACES]}, server_id=self.server_id ) def test_rescue_server_with_str_body(self): self._test_rescue_server() def test_rescue_server_with_bytes_body(self): self._test_rescue_server(True) def _test_rescue_server(self, bytes_body=False): self.check_service_client_function( self.client.rescue_server, 'tempest_lib.common.rest_client.RestClient.post', {'adminPass': 'fake-admin-pass'}, server_id=self.server_id ) def test_unrescue_server_with_str_body(self): self._test_unrescue_server() def test_unrescue_server_with_bytes_body(self): self._test_unrescue_server(True) def _test_unrescue_server(self, bytes_body=False): self.check_service_client_function( self.client.unrescue_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_show_server_diagnostics_with_str_body(self): self._test_show_server_diagnostics() def test_show_server_diagnostics_with_bytes_body(self): self._test_show_server_diagnostics(True) def _test_show_server_diagnostics(self, bytes_body=False): self.check_service_client_function( self.client.show_server_diagnostics, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SERVER_DIAGNOSTICS, status=200, server_id=self.server_id ) def test_list_instance_actions_with_str_body(self): self._test_list_instance_actions() def test_list_instance_actions_with_bytes_body(self): self._test_list_instance_actions(True) def _test_list_instance_actions(self, bytes_body=False): self.check_service_client_function( self.client.list_instance_actions, 'tempest_lib.common.rest_client.RestClient.get', {'instanceActions': [self.FAKE_INSTANCE_ACTIONS]}, server_id=self.server_id ) def test_show_instance_action_with_str_body(self): self._test_show_instance_action() def test_show_instance_action_with_bytes_body(self): self._test_show_instance_action(True) def _test_show_instance_action(self, bytes_body=False): self.check_service_client_function( self.client.show_instance_action, 'tempest_lib.common.rest_client.RestClient.get', {'instanceAction': self.FAKE_INSTANCE_WITH_EVENTS}, server_id=self.server_id, request_id='fake-request-id' ) def test_force_delete_server_with_str_body(self): self._test_force_delete_server() def test_force_delete_server_with_bytes_body(self): self._test_force_delete_server(True) def _test_force_delete_server(self, bytes_body=False): self.check_service_client_function( self.client.force_delete_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_restore_soft_deleted_server_with_str_body(self): self._test_restore_soft_deleted_server() def test_restore_soft_deleted_server_with_bytes_body(self): self._test_restore_soft_deleted_server(True) def _test_restore_soft_deleted_server(self, bytes_body=False): self.check_service_client_function( self.client.restore_soft_deleted_server, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_reset_network_with_str_body(self): self._test_reset_network() def test_reset_network_with_bytes_body(self): self._test_reset_network(True) def _test_reset_network(self, bytes_body=False): self.check_service_client_function( self.client.reset_network, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_inject_network_info_with_str_body(self): self._test_inject_network_info() def test_inject_network_info_with_bytes_body(self): self._test_inject_network_info(True) def _test_inject_network_info(self, bytes_body=False): self.check_service_client_function( self.client.inject_network_info, 'tempest_lib.common.rest_client.RestClient.post', {}, status=202, server_id=self.server_id ) def test_get_vnc_console_with_str_body(self): self._test_get_vnc_console() def test_get_vnc_console_with_bytes_body(self): self._test_get_vnc_console(True) def _test_get_vnc_console(self, bytes_body=False): self.check_service_client_function( self.client.get_vnc_console, 'tempest_lib.common.rest_client.RestClient.post', {'console': self.FAKE_VNC_CONSOLE}, server_id=self.server_id, type='fake-console-type' ) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_services_client.py000066400000000000000000000061761266406763200275250ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib.services.compute import services_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestServicesClient(base.BaseComputeServiceTest): FAKE_SERVICES = { "services": [{ "status": "enabled", "binary": "nova-conductor", "zone": "internal", "state": "up", "updated_at": "2015-08-19T06:50:55.000000", "host": "controller", "disabled_reason": None, "id": 1 }] } FAKE_SERVICE = { "service": { "status": "enabled", "binary": "nova-conductor", "host": "controller" } } def setUp(self): super(TestServicesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = services_client.ServicesClient( fake_auth, 'compute', 'regionOne') def test_list_services_with_str_body(self): self.check_service_client_function( self.client.list_services, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SERVICES) def test_list_services_with_bytes_body(self): self.check_service_client_function( self.client.list_services, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SERVICES, to_utf=True) def _test_enable_service(self, bytes_body=False): self.check_service_client_function( self.client.enable_service, 'tempest_lib.common.rest_client.RestClient.put', self.FAKE_SERVICE, bytes_body, host_name="nova-conductor", binary="controller") def test_enable_service_with_str_body(self): self._test_enable_service() def test_enable_service_with_bytes_body(self): self._test_enable_service(bytes_body=True) def _test_disable_service(self, bytes_body=False): fake_service = copy.deepcopy(self.FAKE_SERVICE) fake_service["service"]["status"] = "disable" self.check_service_client_function( self.client.disable_service, 'tempest_lib.common.rest_client.RestClient.put', fake_service, bytes_body, host_name="nova-conductor", binary="controller") def test_disable_service_with_str_body(self): self._test_disable_service() def test_disable_service_with_bytes_body(self): self._test_disable_service(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_snapshots_client.py000066400000000000000000000075621266406763200277240ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslotest import mockpatch from tempest_lib import exceptions as lib_exc from tempest_lib.services.compute import snapshots_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestSnapshotsClient(base.BaseComputeServiceTest): FAKE_SNAPSHOT = { "createdAt": "2015-10-02T16:27:54.724209", "displayDescription": u"Another \u1234.", "displayName": u"v\u1234-001", "id": "100", "size": 100, "status": "available", "volumeId": "12" } FAKE_SNAPSHOTS = {"snapshots": [FAKE_SNAPSHOT]} def setUp(self): super(TestSnapshotsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = snapshots_client.SnapshotsClient( fake_auth, 'compute', 'regionOne') def _test_create_snapshot(self, bytes_body=False): self.check_service_client_function( self.client.create_snapshot, 'tempest_lib.common.rest_client.RestClient.post', {"snapshot": self.FAKE_SNAPSHOT}, to_utf=bytes_body, status=200, volume_id=self.FAKE_SNAPSHOT["volumeId"]) def test_create_snapshot_with_str_body(self): self._test_create_snapshot() def test_create_shapshot_with_bytes_body(self): self._test_create_snapshot(bytes_body=True) def _test_show_snapshot(self, bytes_body=False): self.check_service_client_function( self.client.show_snapshot, 'tempest_lib.common.rest_client.RestClient.get', {"snapshot": self.FAKE_SNAPSHOT}, to_utf=bytes_body, snapshot_id=self.FAKE_SNAPSHOT["id"]) def test_show_snapshot_with_str_body(self): self._test_show_snapshot() def test_show_snapshot_with_bytes_body(self): self._test_show_snapshot(bytes_body=True) def _test_list_snapshots(self, bytes_body=False, **params): self.check_service_client_function( self.client.list_snapshots, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_SNAPSHOTS, to_utf=bytes_body, **params) def test_list_snapshots_with_str_body(self): self._test_list_snapshots() def test_list_snapshots_with_byte_body(self): self._test_list_snapshots(bytes_body=True) def test_list_snapshots_with_params(self): self._test_list_snapshots('fake') def test_delete_snapshot(self): self.check_service_client_function( self.client.delete_snapshot, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, snapshot_id=self.FAKE_SNAPSHOT['id']) def test_is_resource_deleted_true(self): module = ('tempest_lib.services.compute.snapshots_client.' 'SnapshotsClient.show_snapshot') self.useFixture(mockpatch.Patch( module, side_effect=lib_exc.NotFound)) self.assertTrue(self.client.is_resource_deleted('fake-id')) def test_is_resource_deleted_false(self): module = ('tempest_lib.services.compute.snapshots_client.' 'SnapshotsClient.show_snapshot') self.useFixture(mockpatch.Patch( module, return_value={})) self.assertFalse(self.client.is_resource_deleted('fake-id')) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_tenant_networks_client.py000066400000000000000000000044431266406763200311220ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import tenant_networks_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestTenantNetworksClient(base.BaseComputeServiceTest): FAKE_NETWORK = { "cidr": "None", "id": "c2329eb4-cc8e-4439-ac4c-932369309e36", "label": u'\u30d7' } FAKE_NETWORKS = [FAKE_NETWORK] NETWORK_ID = FAKE_NETWORK['id'] def setUp(self): super(TestTenantNetworksClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = tenant_networks_client.TenantNetworksClient( fake_auth, 'compute', 'regionOne') def _test_list_tenant_networks(self, bytes_body=False): self.check_service_client_function( self.client.list_tenant_networks, 'tempest_lib.common.rest_client.RestClient.get', {"networks": self.FAKE_NETWORKS}, bytes_body) def test_list_tenant_networks_with_str_body(self): self._test_list_tenant_networks() def test_list_tenant_networks_with_bytes_body(self): self._test_list_tenant_networks(bytes_body=True) def _test_show_tenant_network(self, bytes_body=False): self.check_service_client_function( self.client.show_tenant_network, 'tempest_lib.common.rest_client.RestClient.get', {"network": self.FAKE_NETWORK}, bytes_body, network_id=self.NETWORK_ID) def test_show_tenant_network_with_str_body(self): self._test_show_tenant_network() def test_show_tenant_network_with_bytes_body(self): self._test_show_tenant_network(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_tenant_usages_client.py000066400000000000000000000055121266406763200305330ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest_lib.services.compute import tenant_usages_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestTenantUsagesClient(base.BaseComputeServiceTest): FAKE_SERVER_USAGES = [{ "ended_at": None, "flavor": "m1.tiny", "hours": 1.0, "instance_id": "1f1deceb-17b5-4c04-84c7-e0d4499c8fe0", "local_gb": 1, "memory_mb": 512, "name": "new-server-test", "started_at": "2012-10-08T20:10:44.541277", "state": "active", "tenant_id": "openstack", "uptime": 3600, "vcpus": 1 }] FAKE_TENANT_USAGES = [{ "server_usages": FAKE_SERVER_USAGES, "start": "2012-10-08T21:10:44.587336", "stop": "2012-10-08T22:10:44.587336", "tenant_id": "openstack", "total_hours": 1, "total_local_gb_usage": 1, "total_memory_mb_usage": 512, "total_vcpus_usage": 1 }] def setUp(self): super(TestTenantUsagesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = tenant_usages_client.TenantUsagesClient( fake_auth, 'compute', 'regionOne') def _test_list_tenant_usages(self, bytes_body=False): self.check_service_client_function( self.client.list_tenant_usages, 'tempest_lib.common.rest_client.RestClient.get', {"tenant_usages": self.FAKE_TENANT_USAGES}, to_utf=bytes_body) def test_list_tenant_usages_with_str_body(self): self._test_list_tenant_usages() def test_list_tenant_usages_with_bytes_body(self): self._test_list_tenant_usages(bytes_body=True) def _test_show_tenant_usage(self, bytes_body=False): self.check_service_client_function( self.client.show_tenant_usage, 'tempest_lib.common.rest_client.RestClient.get', {"tenant_usage": self.FAKE_TENANT_USAGES[0]}, to_utf=bytes_body, tenant_id='openstack') def test_show_tenant_usage_with_str_body(self): self._test_show_tenant_usage() def test_show_tenant_usage_with_bytes_body(self): self._test_show_tenant_usage(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_versions_client.py000066400000000000000000000063651266406763200275520ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from oslotest import mockpatch from tempest_lib.services.compute import versions_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestVersionsClient(base.BaseComputeServiceTest): FAKE_INIT_VERSION = { "version": { "id": "v2.1", "links": [ { "href": "http://openstack.example.com/v2.1/", "rel": "self" }, { "href": "http://docs.openstack.org/", "rel": "describedby", "type": "text/html" } ], "status": "CURRENT", "updated": "2013-07-23T11:33:21Z", "version": "2.1", "min_version": "2.1" } } FAKE_VERSIONS_INFO = { "versions": [FAKE_INIT_VERSION["version"]] } FAKE_VERSION_INFO = copy.deepcopy(FAKE_INIT_VERSION) FAKE_VERSION_INFO["version"]["media-types"] = [ { "base": "application/json", "type": "application/vnd.openstack.compute+json;version=2.1" } ] def setUp(self): super(TestVersionsClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.versions_client = ( versions_client.VersionsClient (fake_auth, 'compute', 'regionOne')) def _test_versions_client(self, bytes_body=False): self.check_service_client_function( self.versions_client.list_versions, 'tempest_lib.common.rest_client.RestClient.raw_request', self.FAKE_VERSIONS_INFO, bytes_body, 200) def _test_get_version_by_url(self, bytes_body=False): self.useFixture(mockpatch.Patch( "tempest_lib.common.rest_client.RestClient.token", return_value="Dummy Token")) params = {"version_url": self.versions_client._get_base_version_url()} self.check_service_client_function( self.versions_client.get_version_by_url, 'tempest_lib.common.rest_client.RestClient.raw_request', self.FAKE_VERSION_INFO, bytes_body, 200, **params) def test_list_versions_client_with_str_body(self): self._test_versions_client() def test_list_versions_client_with_bytes_body(self): self._test_versions_client(bytes_body=True) def test_get_version_by_url_with_str_body(self): self._test_get_version_by_url() def test_get_version_by_url_with_bytes_body(self): self._test_get_version_by_url(bytes_body=True) tempest-lib-1.0.0/tempest_lib/tests/services/compute/test_volumes_client.py000066400000000000000000000101651266406763200273650ustar00rootroot00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from oslotest import mockpatch from tempest_lib import exceptions as lib_exc from tempest_lib.services.compute import volumes_client from tempest_lib.tests import fake_auth_provider from tempest_lib.tests.services.compute import base class TestVolumesClient(base.BaseComputeServiceTest): FAKE_VOLUME = { "id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c", "displayName": u"v\u12345ol-001", "displayDescription": u"Another \u1234volume.", "size": 30, "status": "Active", "volumeType": "289da7f8-6440-407c-9fb4-7db01ec49164", "metadata": { "contents": "junk" }, "availabilityZone": "us-east1", "snapshotId": None, "attachments": [], "createdAt": "2012-02-14T20:53:07Z" } FAKE_VOLUMES = {"volumes": [FAKE_VOLUME]} def setUp(self): super(TestVolumesClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() self.client = volumes_client.VolumesClient( fake_auth, 'compute', 'regionOne') def _test_list_volumes(self, bytes_body=False, **params): self.check_service_client_function( self.client.list_volumes, 'tempest_lib.common.rest_client.RestClient.get', self.FAKE_VOLUMES, to_utf=bytes_body, **params) def test_list_volumes_with_str_body(self): self._test_list_volumes() def test_list_volumes_with_byte_body(self): self._test_list_volumes(bytes_body=True) def test_list_volumes_with_params(self): self._test_list_volumes(name='fake') def _test_show_volume(self, bytes_body=False): self.check_service_client_function( self.client.show_volume, 'tempest_lib.common.rest_client.RestClient.get', {"volume": self.FAKE_VOLUME}, to_utf=bytes_body, volume_id=self.FAKE_VOLUME['id']) def test_show_volume_with_str_body(self): self._test_show_volume() def test_show_volume_with_bytes_body(self): self._test_show_volume(bytes_body=True) def _test_create_volume(self, bytes_body=False): post_body = copy.deepcopy(self.FAKE_VOLUME) del post_body['id'] del post_body['createdAt'] del post_body['status'] self.check_service_client_function( self.client.create_volume, 'tempest_lib.common.rest_client.RestClient.post', {"volume": self.FAKE_VOLUME}, to_utf=bytes_body, status=200, **post_body) def test_create_volume_with_str_body(self): self._test_create_volume() def test_create_volume_with_bytes_body(self): self._test_create_volume(bytes_body=True) def test_delete_volume(self): self.check_service_client_function( self.client.delete_volume, 'tempest_lib.common.rest_client.RestClient.delete', {}, status=202, volume_id=self.FAKE_VOLUME['id']) def test_is_resource_deleted_true(self): module = ('tempest_lib.services.compute.volumes_client.' 'VolumesClient.show_volume') self.useFixture(mockpatch.Patch( module, side_effect=lib_exc.NotFound)) self.assertTrue(self.client.is_resource_deleted('fake-id')) def test_is_resource_deleted_false(self): module = ('tempest_lib.services.compute.volumes_client.' 'VolumesClient.show_volume') self.useFixture(mockpatch.Patch( module, return_value={})) self.assertFalse(self.client.is_resource_deleted('fake-id')) tempest-lib-1.0.0/tempest_lib/tests/services/identity/000077500000000000000000000000001266406763200230765ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/identity/__init__.py000066400000000000000000000000001266406763200251750ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/identity/v2/000077500000000000000000000000001266406763200234255ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/identity/v2/__init__.py000066400000000000000000000000001266406763200255240ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/identity/v2/test_token_client.py000066400000000000000000000074201266406763200275170ustar00rootroot00000000000000# Copyright 2015 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 json import httplib2 from oslotest import mockpatch from tempest_lib.common import rest_client from tempest_lib import exceptions from tempest_lib.services.identity.v2 import token_client from tempest_lib.tests import base from tempest_lib.tests import fake_http class TestTokenClientV2(base.TestCase): def setUp(self): super(TestTokenClientV2, self).setUp() self.fake_200_http = fake_http.fake_httplib2(return_type=200) def test_init_without_authurl(self): self.assertRaises(exceptions.IdentityError, token_client.TokenClient, None) def test_auth(self): token_client_v2 = token_client.TokenClient('fake_url') post_mock = self.useFixture(mockpatch.PatchObject( token_client_v2, 'post', return_value=self.fake_200_http.request( 'fake_url', body={'access': {'token': 'fake_token'}}))) resp = token_client_v2.auth('fake_user', 'fake_pass') self.assertIsInstance(resp, rest_client.ResponseBody) req_dict = json.dumps({ 'auth': { 'passwordCredentials': { 'username': 'fake_user', 'password': 'fake_pass', }, } }, sort_keys=True) post_mock.mock.assert_called_once_with('fake_url/tokens', body=req_dict) def test_auth_with_tenant(self): token_client_v2 = token_client.TokenClient('fake_url') post_mock = self.useFixture(mockpatch.PatchObject( token_client_v2, 'post', return_value=self.fake_200_http.request( 'fake_url', body={'access': {'token': 'fake_token'}}))) resp = token_client_v2.auth('fake_user', 'fake_pass', 'fake_tenant') self.assertIsInstance(resp, rest_client.ResponseBody) req_dict = json.dumps({ 'auth': { 'tenantName': 'fake_tenant', 'passwordCredentials': { 'username': 'fake_user', 'password': 'fake_pass', }, } }, sort_keys=True) post_mock.mock.assert_called_once_with('fake_url/tokens', body=req_dict) def test_request_with_str_body(self): token_client_v2 = token_client.TokenClient('fake_url') self.useFixture(mockpatch.PatchObject( token_client_v2, 'raw_request', return_value=( httplib2.Response({'status': '200'}), str('{"access": {"token": "fake_token"}}')))) resp, body = token_client_v2.request('GET', 'fake_uri') self.assertIsInstance(resp, httplib2.Response) self.assertIsInstance(body, dict) def test_request_with_bytes_body(self): token_client_v2 = token_client.TokenClient('fake_url') self.useFixture(mockpatch.PatchObject( token_client_v2, 'raw_request', return_value=( httplib2.Response({'status': '200'}), bytes(b'{"access": {"token": "fake_token"}}')))) resp, body = token_client_v2.request('GET', 'fake_uri') self.assertIsInstance(resp, httplib2.Response) self.assertIsInstance(body, dict) tempest-lib-1.0.0/tempest_lib/tests/services/identity/v3/000077500000000000000000000000001266406763200234265ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/identity/v3/__init__.py000066400000000000000000000000001266406763200255250ustar00rootroot00000000000000tempest-lib-1.0.0/tempest_lib/tests/services/identity/v3/test_token_client.py000066400000000000000000000136101266406763200275160ustar00rootroot00000000000000# Copyright 2015 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 json import httplib2 from oslotest import mockpatch from tempest_lib.common import rest_client from tempest_lib import exceptions from tempest_lib.services.identity.v3 import token_client from tempest_lib.tests import base from tempest_lib.tests import fake_http class TestTokenClientV2(base.TestCase): def setUp(self): super(TestTokenClientV2, self).setUp() self.fake_201_http = fake_http.fake_httplib2(return_type=201) def test_init_without_authurl(self): self.assertRaises(exceptions.IdentityError, token_client.V3TokenClient, None) def test_auth(self): token_client_v3 = token_client.V3TokenClient('fake_url') post_mock = self.useFixture(mockpatch.PatchObject( token_client_v3, 'post', return_value=self.fake_201_http.request( 'fake_url', body={'access': {'token': 'fake_token'}}))) resp = token_client_v3.auth(username='fake_user', password='fake_pass') self.assertIsInstance(resp, rest_client.ResponseBody) req_dict = json.dumps({ 'auth': { 'identity': { 'methods': ['password'], 'password': { 'user': { 'name': 'fake_user', 'password': 'fake_pass', } } }, } }, sort_keys=True) post_mock.mock.assert_called_once_with('fake_url/auth/tokens', body=req_dict) def test_auth_with_project_id_and_domain_id(self): token_client_v3 = token_client.V3TokenClient('fake_url') post_mock = self.useFixture(mockpatch.PatchObject( token_client_v3, 'post', return_value=self.fake_201_http.request( 'fake_url', body={'access': {'token': 'fake_token'}}))) resp = token_client_v3.auth( username='fake_user', password='fake_pass', project_id='fcac2a055a294e4c82d0a9c21c620eb4', user_domain_id='14f4a9a99973404d8c20ba1d2af163ff', project_domain_id='291f63ae9ac54ee292ca09e5f72d9676') self.assertIsInstance(resp, rest_client.ResponseBody) req_dict = json.dumps({ 'auth': { 'identity': { 'methods': ['password'], 'password': { 'user': { 'name': 'fake_user', 'password': 'fake_pass', 'domain': { 'id': '14f4a9a99973404d8c20ba1d2af163ff' } } } }, 'scope': { 'project': { 'id': 'fcac2a055a294e4c82d0a9c21c620eb4', 'domain': { 'id': '291f63ae9ac54ee292ca09e5f72d9676' } } } } }, sort_keys=True) post_mock.mock.assert_called_once_with('fake_url/auth/tokens', body=req_dict) def test_auth_with_tenant(self): token_client_v2 = token_client.V3TokenClient('fake_url') post_mock = self.useFixture(mockpatch.PatchObject( token_client_v2, 'post', return_value=self.fake_201_http.request( 'fake_url', body={'access': {'token': 'fake_token'}}))) resp = token_client_v2.auth(username='fake_user', password='fake_pass', project_name='fake_tenant') self.assertIsInstance(resp, rest_client.ResponseBody) req_dict = json.dumps({ 'auth': { 'identity': { 'methods': ['password'], 'password': { 'user': { 'name': 'fake_user', 'password': 'fake_pass', } }}, 'scope': { 'project': { 'name': 'fake_tenant' } }, } }, sort_keys=True) post_mock.mock.assert_called_once_with('fake_url/auth/tokens', body=req_dict) def test_request_with_str_body(self): token_client_v3 = token_client.V3TokenClient('fake_url') self.useFixture(mockpatch.PatchObject( token_client_v3, 'raw_request', return_value=( httplib2.Response({"status": "200"}), str('{"access": {"token": "fake_token"}}')))) resp, body = token_client_v3.request('GET', 'fake_uri') self.assertIsInstance(resp, httplib2.Response) self.assertIsInstance(body, dict) def test_request_with_bytes_body(self): token_client_v3 = token_client.V3TokenClient('fake_url') self.useFixture(mockpatch.PatchObject( token_client_v3, 'raw_request', return_value=( httplib2.Response({"status": "200"}), bytes(b'{"access": {"token": "fake_token"}}')))) resp, body = token_client_v3.request('GET', 'fake_uri') self.assertIsInstance(resp, httplib2.Response) self.assertIsInstance(body, dict) tempest-lib-1.0.0/tempest_lib/tests/test_auth.py000066400000000000000000000440541266406763200220030ustar00rootroot00000000000000# Copyright 2014 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. import copy import datetime from oslotest import mockpatch from tempest_lib import auth from tempest_lib import exceptions from tempest_lib.services.identity.v2 import token_client as v2_client from tempest_lib.services.identity.v3 import token_client as v3_client from tempest_lib.tests import base from tempest_lib.tests import fake_credentials from tempest_lib.tests import fake_http from tempest_lib.tests import fake_identity def fake_get_credentials(fill_in=True, identity_version='v2', **kwargs): return fake_credentials.FakeCredentials() class BaseAuthTestsSetUp(base.TestCase): _auth_provider_class = None credentials = fake_credentials.FakeCredentials() def _auth(self, credentials, auth_url, **params): """returns auth method according to keystone""" return self._auth_provider_class(credentials, auth_url, **params) def setUp(self): super(BaseAuthTestsSetUp, self).setUp() self.fake_http = fake_http.fake_httplib2(return_type=200) self.stubs.Set(auth, 'get_credentials', fake_get_credentials) self.auth_provider = self._auth(self.credentials, fake_identity.FAKE_AUTH_URL) class TestBaseAuthProvider(BaseAuthTestsSetUp): """Tests for base AuthProvider This tests auth.AuthProvider class which is base for the other so we obviously don't test not implemented method or the ones which strongly depends on them. """ class FakeAuthProviderImpl(auth.AuthProvider): def _decorate_request(self): pass def _fill_credentials(self): pass def _get_auth(self): pass def base_url(self): pass def is_expired(self): pass _auth_provider_class = FakeAuthProviderImpl def _auth(self, credentials, auth_url, **params): """returns auth method according to keystone""" return self._auth_provider_class(credentials, **params) def test_check_credentials_bad_type(self): self.assertFalse(self.auth_provider.check_credentials([])) def test_auth_data_property_when_cache_exists(self): self.auth_provider.cache = 'foo' self.useFixture(mockpatch.PatchObject(self.auth_provider, 'is_expired', return_value=False)) self.assertEqual('foo', getattr(self.auth_provider, 'auth_data')) def test_delete_auth_data_property_through_deleter(self): self.auth_provider.cache = 'foo' del self.auth_provider.auth_data self.assertIsNone(self.auth_provider.cache) def test_delete_auth_data_property_through_clear_auth(self): self.auth_provider.cache = 'foo' self.auth_provider.clear_auth() self.assertIsNone(self.auth_provider.cache) def test_set_and_reset_alt_auth_data(self): self.auth_provider.set_alt_auth_data('foo', 'bar') self.assertEqual(self.auth_provider.alt_part, 'foo') self.assertEqual(self.auth_provider.alt_auth_data, 'bar') self.auth_provider.reset_alt_auth_data() self.assertIsNone(self.auth_provider.alt_part) self.assertIsNone(self.auth_provider.alt_auth_data) def test_auth_class(self): self.assertRaises(TypeError, auth.AuthProvider, fake_credentials.FakeCredentials) class TestKeystoneV2AuthProvider(BaseAuthTestsSetUp): _endpoints = fake_identity.IDENTITY_V2_RESPONSE['access']['serviceCatalog'] _auth_provider_class = auth.KeystoneV2AuthProvider credentials = fake_credentials.FakeKeystoneV2Credentials() def setUp(self): super(TestKeystoneV2AuthProvider, self).setUp() self.stubs.Set(v2_client.TokenClient, 'raw_request', fake_identity._fake_v2_response) self.target_url = 'test_api' def _get_fake_identity(self): return fake_identity.IDENTITY_V2_RESPONSE['access'] def _get_fake_alt_identity(self): return fake_identity.ALT_IDENTITY_V2_RESPONSE['access'] def _get_result_url_from_endpoint(self, ep, endpoint_type='publicURL', replacement=None): if replacement: return ep[endpoint_type].replace('v2', replacement) return ep[endpoint_type] def _get_token_from_fake_identity(self): return fake_identity.TOKEN def _get_from_fake_identity(self, attr): access = fake_identity.IDENTITY_V2_RESPONSE['access'] if attr == 'user_id': return access['user']['id'] elif attr == 'tenant_id': return access['token']['tenant']['id'] def _test_request_helper(self, filters, expected): url, headers, body = self.auth_provider.auth_request('GET', self.target_url, filters=filters) self.assertEqual(expected['url'], url) self.assertEqual(expected['token'], headers['X-Auth-Token']) self.assertEqual(expected['body'], body) def _auth_data_with_expiry(self, date_as_string): token, access = self.auth_provider.auth_data access['token']['expires'] = date_as_string return token, access def test_request(self): filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'FakeRegion' } url = self._get_result_url_from_endpoint( self._endpoints[0]['endpoints'][1]) + '/' + self.target_url expected = { 'body': None, 'url': url, 'token': self._get_token_from_fake_identity(), } self._test_request_helper(filters, expected) def test_request_with_alt_auth_cleans_alt(self): """Test alternate auth data for headers Assert that when the alt data is provided for headers, after an auth_request the data alt_data is cleaned-up. """ self.auth_provider.set_alt_auth_data( 'headers', (fake_identity.ALT_TOKEN, self._get_fake_alt_identity())) filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'fakeRegion' } self.auth_provider.auth_request('GET', self.target_url, filters=filters) # Assert alt auth data is clear after it self.assertIsNone(self.auth_provider.alt_part) self.assertIsNone(self.auth_provider.alt_auth_data) def _test_request_with_identical_alt_auth(self, part): """Test alternate but identical auth data for headers Assert that when the alt data is provided, but it's actually identical, an exception is raised. """ self.auth_provider.set_alt_auth_data( part, (fake_identity.TOKEN, self._get_fake_identity())) filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'fakeRegion' } self.assertRaises(exceptions.BadAltAuth, self.auth_provider.auth_request, 'GET', self.target_url, filters=filters) def test_request_with_identical_alt_auth_headers(self): self._test_request_with_identical_alt_auth('headers') def test_request_with_identical_alt_auth_url(self): self._test_request_with_identical_alt_auth('url') def test_request_with_identical_alt_auth_body(self): self._test_request_with_identical_alt_auth('body') def test_request_with_alt_part_without_alt_data(self): """Test empty alternate auth data Assert that when alt_part is defined, the corresponding original request element is kept the same. """ filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'fakeRegion' } self.auth_provider.set_alt_auth_data('headers', None) url, headers, body = self.auth_provider.auth_request('GET', self.target_url, filters=filters) # The original headers where empty self.assertNotEqual(url, self.target_url) self.assertIsNone(headers) self.assertEqual(body, None) def _test_request_with_alt_part_without_alt_data_no_change(self, body): """Test empty alternate auth data with no effect Assert that when alt_part is defined, no auth_data is provided, and the the corresponding original request element was not going to be changed anyways, and exception is raised """ filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'fakeRegion' } self.auth_provider.set_alt_auth_data('body', None) self.assertRaises(exceptions.BadAltAuth, self.auth_provider.auth_request, 'GET', self.target_url, filters=filters) def test_request_with_alt_part_without_alt_data_no_change_headers(self): self._test_request_with_alt_part_without_alt_data_no_change('headers') def test_request_with_alt_part_without_alt_data_no_change_url(self): self._test_request_with_alt_part_without_alt_data_no_change('url') def test_request_with_alt_part_without_alt_data_no_change_body(self): self._test_request_with_alt_part_without_alt_data_no_change('body') def test_request_with_bad_service(self): filters = { 'service': 'BAD_SERVICE', 'endpoint_type': 'publicURL', 'region': 'fakeRegion' } self.assertRaises(exceptions.EndpointNotFound, self.auth_provider.auth_request, 'GET', self.target_url, filters=filters) def test_request_without_service(self): filters = { 'service': None, 'endpoint_type': 'publicURL', 'region': 'fakeRegion' } self.assertRaises(exceptions.EndpointNotFound, self.auth_provider.auth_request, 'GET', self.target_url, filters=filters) def test_check_credentials_missing_attribute(self): for attr in ['username', 'password']: cred = copy.copy(self.credentials) del cred[attr] self.assertFalse(self.auth_provider.check_credentials(cred)) def test_fill_credentials(self): self.auth_provider.fill_credentials() creds = self.auth_provider.credentials for attr in ['user_id', 'tenant_id']: self.assertEqual(self._get_from_fake_identity(attr), getattr(creds, attr)) def _test_base_url_helper(self, expected_url, filters, auth_data=None): url = self.auth_provider.base_url(filters, auth_data) self.assertEqual(url, expected_url) def test_base_url(self): self.filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'FakeRegion' } expected = self._get_result_url_from_endpoint( self._endpoints[0]['endpoints'][1]) self._test_base_url_helper(expected, self.filters) def test_base_url_to_get_admin_endpoint(self): self.filters = { 'service': 'compute', 'endpoint_type': 'adminURL', 'region': 'FakeRegion' } expected = self._get_result_url_from_endpoint( self._endpoints[0]['endpoints'][1], endpoint_type='adminURL') self._test_base_url_helper(expected, self.filters) def test_base_url_unknown_region(self): """If the region is unknown, the first endpoint is returned.""" self.filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'AintNoBodyKnowThisRegion' } expected = self._get_result_url_from_endpoint( self._endpoints[0]['endpoints'][0]) self._test_base_url_helper(expected, self.filters) def test_base_url_with_non_existent_service(self): self.filters = { 'service': 'BAD_SERVICE', 'endpoint_type': 'publicURL', 'region': 'FakeRegion' } self.assertRaises(exceptions.EndpointNotFound, self._test_base_url_helper, None, self.filters) def test_base_url_without_service(self): self.filters = { 'endpoint_type': 'publicURL', 'region': 'FakeRegion' } self.assertRaises(exceptions.EndpointNotFound, self._test_base_url_helper, None, self.filters) def test_base_url_with_api_version_filter(self): self.filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'FakeRegion', 'api_version': 'v12' } expected = self._get_result_url_from_endpoint( self._endpoints[0]['endpoints'][1], replacement='v12') self._test_base_url_helper(expected, self.filters) def test_base_url_with_skip_path_filter(self): self.filters = { 'service': 'compute', 'endpoint_type': 'publicURL', 'region': 'FakeRegion', 'skip_path': True } expected = 'http://fake_url/' self._test_base_url_helper(expected, self.filters) def test_token_not_expired(self): expiry_data = datetime.datetime.utcnow() + datetime.timedelta(days=1) self._verify_expiry(expiry_data=expiry_data, should_be_expired=False) def test_token_expired(self): expiry_data = datetime.datetime.utcnow() - datetime.timedelta(hours=1) self._verify_expiry(expiry_data=expiry_data, should_be_expired=True) def test_token_not_expired_to_be_renewed(self): expiry_data = (datetime.datetime.utcnow() + self.auth_provider.token_expiry_threshold / 2) self._verify_expiry(expiry_data=expiry_data, should_be_expired=True) def _verify_expiry(self, expiry_data, should_be_expired): for expiry_format in self.auth_provider.EXPIRY_DATE_FORMATS: auth_data = self._auth_data_with_expiry( expiry_data.strftime(expiry_format)) self.assertEqual(self.auth_provider.is_expired(auth_data), should_be_expired) class TestKeystoneV3AuthProvider(TestKeystoneV2AuthProvider): _endpoints = fake_identity.IDENTITY_V3_RESPONSE['token']['catalog'] _auth_provider_class = auth.KeystoneV3AuthProvider credentials = fake_credentials.FakeKeystoneV3Credentials() def setUp(self): super(TestKeystoneV3AuthProvider, self).setUp() self.stubs.Set(v3_client.V3TokenClient, 'raw_request', fake_identity._fake_v3_response) def _get_fake_identity(self): return fake_identity.IDENTITY_V3_RESPONSE['token'] def _get_fake_alt_identity(self): return fake_identity.ALT_IDENTITY_V3['token'] def _get_result_url_from_endpoint(self, ep, replacement=None): if replacement: return ep['url'].replace('v3', replacement) return ep['url'] def _auth_data_with_expiry(self, date_as_string): token, access = self.auth_provider.auth_data access['expires_at'] = date_as_string return token, access def _get_from_fake_identity(self, attr): token = fake_identity.IDENTITY_V3_RESPONSE['token'] if attr == 'user_id': return token['user']['id'] elif attr == 'project_id': return token['project']['id'] elif attr == 'user_domain_id': return token['user']['domain']['id'] elif attr == 'project_domain_id': return token['project']['domain']['id'] def test_check_credentials_missing_attribute(self): # reset credentials to fresh ones self.credentials.reset() for attr in ['username', 'password', 'user_domain_name', 'project_domain_name']: cred = copy.copy(self.credentials) del cred[attr] self.assertFalse(self.auth_provider.check_credentials(cred), "Credentials should be invalid without %s" % attr) def test_check_domain_credentials_missing_attribute(self): # reset credentials to fresh ones self.credentials.reset() domain_creds = fake_credentials.FakeKeystoneV3DomainCredentials() for attr in ['username', 'password', 'user_domain_name']: cred = copy.copy(domain_creds) del cred[attr] self.assertFalse(self.auth_provider.check_credentials(cred), "Credentials should be invalid without %s" % attr) def test_fill_credentials(self): self.auth_provider.fill_credentials() creds = self.auth_provider.credentials for attr in ['user_id', 'project_id', 'user_domain_id', 'project_domain_id']: self.assertEqual(self._get_from_fake_identity(attr), getattr(creds, attr)) # Overwrites v2 test def test_base_url_to_get_admin_endpoint(self): self.filters = { 'service': 'compute', 'endpoint_type': 'admin', 'region': 'MiddleEarthRegion' } expected = self._get_result_url_from_endpoint( self._endpoints[0]['endpoints'][2]) self._test_base_url_helper(expected, self.filters) tempest-lib-1.0.0/tempest_lib/tests/test_base.py000066400000000000000000000036611266406763200217530ustar00rootroot00000000000000# Copyright 2014 Mirantis Inc. # 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 testtools from tempest_lib import base from tempest_lib import exceptions class TestAttr(base.BaseTestCase): def test_has_no_attrs(self): self.assertEqual( 'tempest_lib.tests.test_base.TestAttr.test_has_no_attrs', self.id() ) @testtools.testcase.attr('foo') def test_has_one_attr(self): self.assertEqual( 'tempest_lib.tests.test_base.TestAttr.test_has_one_attr[foo]', self.id() ) @testtools.testcase.attr('foo') @testtools.testcase.attr('bar') def test_has_two_attrs(self): self.assertEqual( 'tempest_lib.tests.test_base.TestAttr.test_has_two_attrs[bar,foo]', self.id(), ) class TestSetUpClass(base.BaseTestCase): @classmethod def setUpClass(cls): """Simulate absence of super() call.""" def setUp(self): try: # We expect here RuntimeError exception because 'setUpClass' # has not called 'super'. super(TestSetUpClass, self).setUp() except RuntimeError: pass else: raise exceptions.TempestException( "If you see this, then expected exception was not raised.") def test_setup_class_raises_runtime_error(self): """No-op test just to call setUp.""" tempest-lib-1.0.0/tempest_lib/tests/test_credentials.py000066400000000000000000000162701266406763200233360ustar00rootroot00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from tempest_lib import auth from tempest_lib import exceptions from tempest_lib.services.identity.v2 import token_client as v2_client from tempest_lib.services.identity.v3 import token_client as v3_client from tempest_lib.tests import base from tempest_lib.tests import fake_identity class CredentialsTests(base.TestCase): attributes = {} credentials_class = auth.Credentials def _get_credentials(self, attributes=None): if attributes is None: attributes = self.attributes return self.credentials_class(**attributes) def _check(self, credentials, credentials_class, filled): # Check the right version of credentials has been returned self.assertIsInstance(credentials, credentials_class) # Check the id attributes are filled in attributes = [x for x in credentials.ATTRIBUTES if ( '_id' in x and x != 'domain_id')] for attr in attributes: if filled: self.assertIsNotNone(getattr(credentials, attr)) else: self.assertIsNone(getattr(credentials, attr)) def test_create(self): creds = self._get_credentials() self.assertEqual(self.attributes, creds._initial) def test_create_invalid_attr(self): self.assertRaises(exceptions.InvalidCredentials, self._get_credentials, attributes=dict(invalid='fake')) def test_is_valid(self): creds = self._get_credentials() self.assertRaises(NotImplementedError, creds.is_valid) class KeystoneV2CredentialsTests(CredentialsTests): attributes = { 'username': 'fake_username', 'password': 'fake_password', 'tenant_name': 'fake_tenant_name' } identity_response = fake_identity._fake_v2_response credentials_class = auth.KeystoneV2Credentials tokenclient_class = v2_client.TokenClient identity_version = 'v2' def setUp(self): super(KeystoneV2CredentialsTests, self).setUp() self.stubs.Set(self.tokenclient_class, 'raw_request', self.identity_response) def _verify_credentials(self, credentials_class, creds_dict, filled=True): creds = auth.get_credentials(fake_identity.FAKE_AUTH_URL, fill_in=filled, identity_version=self.identity_version, **creds_dict) self._check(creds, credentials_class, filled) def test_get_credentials(self): self._verify_credentials(credentials_class=self.credentials_class, creds_dict=self.attributes) def test_get_credentials_not_filled(self): self._verify_credentials(credentials_class=self.credentials_class, creds_dict=self.attributes, filled=False) def test_is_valid(self): creds = self._get_credentials() self.assertTrue(creds.is_valid()) def _test_is_not_valid(self, ignore_key): creds = self._get_credentials() for attr in self.attributes.keys(): if attr == ignore_key: continue temp_attr = getattr(creds, attr) delattr(creds, attr) self.assertFalse(creds.is_valid(), "Credentials should be invalid without %s" % attr) setattr(creds, attr, temp_attr) def test_is_not_valid(self): # NOTE(mtreinish): A KeystoneV2 credential object is valid without # a tenant_name. So skip that check. See tempest.auth for the valid # credential requirements self._test_is_not_valid('tenant_name') def test_reset_all_attributes(self): creds = self._get_credentials() initial_creds = copy.deepcopy(creds) set_attr = creds.__dict__.keys() missing_attr = set(creds.ATTRIBUTES).difference(set_attr) # Set all unset attributes, then reset for attr in missing_attr: setattr(creds, attr, 'fake' + attr) creds.reset() # Check reset credentials are same as initial ones self.assertEqual(creds, initial_creds) def test_reset_single_attribute(self): creds = self._get_credentials() initial_creds = copy.deepcopy(creds) set_attr = creds.__dict__.keys() missing_attr = set(creds.ATTRIBUTES).difference(set_attr) # Set one unset attributes, then reset for attr in missing_attr: setattr(creds, attr, 'fake' + attr) creds.reset() # Check reset credentials are same as initial ones self.assertEqual(creds, initial_creds) class KeystoneV3CredentialsTests(KeystoneV2CredentialsTests): attributes = { 'username': 'fake_username', 'password': 'fake_password', 'project_name': 'fake_project_name', 'user_domain_name': 'fake_domain_name' } credentials_class = auth.KeystoneV3Credentials identity_response = fake_identity._fake_v3_response tokenclient_class = v3_client.V3TokenClient identity_version = 'v3' def test_is_not_valid(self): # NOTE(mtreinish) For a Keystone V3 credential object a project name # is not required to be valid, so we skip that check. See tempest.auth # for the valid credential requirements self._test_is_not_valid('project_name') def test_synced_attributes(self): attributes = self.attributes # Create V3 credentials with tenant instead of project, and user_domain for attr in ['project_id', 'user_domain_id']: attributes[attr] = 'fake_' + attr creds = self._get_credentials(attributes) self.assertEqual(creds.project_name, creds.tenant_name) self.assertEqual(creds.project_id, creds.tenant_id) self.assertEqual(creds.user_domain_name, creds.project_domain_name) self.assertEqual(creds.user_domain_id, creds.project_domain_id) # Replace user_domain with project_domain del attributes['user_domain_name'] del attributes['user_domain_id'] del attributes['project_name'] del attributes['project_id'] for attr in ['project_domain_name', 'project_domain_id', 'tenant_name', 'tenant_id']: attributes[attr] = 'fake_' + attr self.assertEqual(creds.tenant_name, creds.project_name) self.assertEqual(creds.tenant_id, creds.project_id) self.assertEqual(creds.project_domain_name, creds.user_domain_name) self.assertEqual(creds.project_domain_id, creds.user_domain_id) tempest-lib-1.0.0/tempest_lib/tests/test_decorators.py000066400000000000000000000104351266406763200232030ustar00rootroot00000000000000# Copyright 2013 IBM Corp # Copyright 2015 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 uuid import testtools from tempest_lib import base as test from tempest_lib import decorators from tempest_lib.tests import base class TestSkipBecauseDecorator(base.TestCase): def _test_skip_because_helper(self, expected_to_skip=True, **decorator_args): class TestFoo(test.BaseTestCase): _interface = 'json' @decorators.skip_because(**decorator_args) def test_bar(self): return 0 t = TestFoo('test_bar') if expected_to_skip: self.assertRaises(testtools.TestCase.skipException, t.test_bar) else: # assert that test_bar returned 0 self.assertEqual(TestFoo('test_bar').test_bar(), 0) def test_skip_because_bug(self): self._test_skip_because_helper(bug='12345') def test_skip_because_bug_and_condition_true(self): self._test_skip_because_helper(bug='12348', condition=True) def test_skip_because_bug_and_condition_false(self): self._test_skip_because_helper(expected_to_skip=False, bug='12349', condition=False) def test_skip_because_bug_without_bug_never_skips(self): """Never skip without a bug parameter.""" self._test_skip_because_helper(expected_to_skip=False, condition=True) self._test_skip_because_helper(expected_to_skip=False) def test_skip_because_invalid_bug_number(self): """Raise ValueError if with an invalid bug number""" self.assertRaises(ValueError, self._test_skip_because_helper, bug='critical_bug') class TestIdempotentIdDecorator(base.TestCase): def _test_helper(self, _id, **decorator_args): @decorators.idempotent_id(_id) def foo(): """Docstring""" pass return foo def _test_helper_without_doc(self, _id, **decorator_args): @decorators.idempotent_id(_id) def foo(): pass return foo def test_positive(self): _id = str(uuid.uuid4()) foo = self._test_helper(_id) self.assertIn('id-%s' % _id, getattr(foo, '__testtools_attrs')) self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id)) def test_positive_without_doc(self): _id = str(uuid.uuid4()) foo = self._test_helper_without_doc(_id) self.assertTrue(foo.__doc__.startswith('Test idempotent id: %s' % _id)) def test_idempotent_id_not_str(self): _id = 42 self.assertRaises(TypeError, self._test_helper, _id) def test_idempotent_id_not_valid_uuid(self): _id = '42' self.assertRaises(ValueError, self._test_helper, _id) class TestSkipUnlessAttrDecorator(base.TestCase): def _test_skip_unless_attr(self, attr, expected_to_skip=True): class TestFoo(test.BaseTestCase): expected_attr = not expected_to_skip @decorators.skip_unless_attr(attr) def test_foo(self): pass t = TestFoo('test_foo') if expected_to_skip: self.assertRaises(testtools.TestCase.skipException, t.test_foo()) else: try: t.test_foo() except Exception: raise testtools.TestCase.failureException() def test_skip_attr_does_not_exist(self): self._test_skip_unless_attr('unexpected_attr') def test_skip_attr_false(self): self._test_skip_unless_attr('expected_attr') def test_no_skip_for_attr_exist_and_true(self): self._test_skip_unless_attr('expected_attr', expected_to_skip=False) tempest-lib-1.0.0/tempest_lib/tests/test_rest_client.py000066400000000000000000001107731266406763200233570ustar00rootroot00000000000000# Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import json import httplib2 import jsonschema from oslotest import mockpatch import six from tempest_lib.common import rest_client from tempest_lib import exceptions from tempest_lib.tests import base from tempest_lib.tests import fake_auth_provider from tempest_lib.tests import fake_http class BaseRestClientTestClass(base.TestCase): url = 'fake_endpoint' def setUp(self): super(BaseRestClientTestClass, self).setUp() self.fake_auth_provider = fake_auth_provider.FakeAuthProvider() self.rest_client = rest_client.RestClient( self.fake_auth_provider, None, None) self.stubs.Set(httplib2.Http, 'request', self.fake_http.request) self.useFixture(mockpatch.PatchObject(self.rest_client, '_log_request')) class TestRestClientHTTPMethods(BaseRestClientTestClass): def setUp(self): self.fake_http = fake_http.fake_httplib2() super(TestRestClientHTTPMethods, self).setUp() self.useFixture(mockpatch.PatchObject(self.rest_client, '_error_checker')) def test_post(self): __, return_dict = self.rest_client.post(self.url, {}, {}) self.assertEqual('POST', return_dict['method']) def test_get(self): __, return_dict = self.rest_client.get(self.url) self.assertEqual('GET', return_dict['method']) def test_delete(self): __, return_dict = self.rest_client.delete(self.url) self.assertEqual('DELETE', return_dict['method']) def test_patch(self): __, return_dict = self.rest_client.patch(self.url, {}, {}) self.assertEqual('PATCH', return_dict['method']) def test_put(self): __, return_dict = self.rest_client.put(self.url, {}, {}) self.assertEqual('PUT', return_dict['method']) def test_head(self): self.useFixture(mockpatch.PatchObject(self.rest_client, 'response_checker')) __, return_dict = self.rest_client.head(self.url) self.assertEqual('HEAD', return_dict['method']) def test_copy(self): __, return_dict = self.rest_client.copy(self.url) self.assertEqual('COPY', return_dict['method']) class TestRestClientNotFoundHandling(BaseRestClientTestClass): def setUp(self): self.fake_http = fake_http.fake_httplib2(404) super(TestRestClientNotFoundHandling, self).setUp() def test_post(self): self.assertRaises(exceptions.NotFound, self.rest_client.post, self.url, {}, {}) class TestRestClientHeadersJSON(TestRestClientHTTPMethods): TYPE = "json" def _verify_headers(self, resp): self.assertEqual(self.rest_client._get_type(), self.TYPE) resp = dict((k.lower(), v) for k, v in six.iteritems(resp)) self.assertEqual(self.header_value, resp['accept']) self.assertEqual(self.header_value, resp['content-type']) def setUp(self): super(TestRestClientHeadersJSON, self).setUp() self.rest_client.TYPE = self.TYPE self.header_value = 'application/%s' % self.rest_client._get_type() def test_post(self): resp, __ = self.rest_client.post(self.url, {}) self._verify_headers(resp) def test_get(self): resp, __ = self.rest_client.get(self.url) self._verify_headers(resp) def test_delete(self): resp, __ = self.rest_client.delete(self.url) self._verify_headers(resp) def test_patch(self): resp, __ = self.rest_client.patch(self.url, {}) self._verify_headers(resp) def test_put(self): resp, __ = self.rest_client.put(self.url, {}) self._verify_headers(resp) def test_head(self): self.useFixture(mockpatch.PatchObject(self.rest_client, 'response_checker')) resp, __ = self.rest_client.head(self.url) self._verify_headers(resp) def test_copy(self): resp, __ = self.rest_client.copy(self.url) self._verify_headers(resp) class TestRestClientUpdateHeaders(BaseRestClientTestClass): def setUp(self): self.fake_http = fake_http.fake_httplib2() super(TestRestClientUpdateHeaders, self).setUp() self.useFixture(mockpatch.PatchObject(self.rest_client, '_error_checker')) self.headers = {'X-Configuration-Session': 'session_id'} def test_post_update_headers(self): __, return_dict = self.rest_client.post(self.url, {}, extra_headers=True, headers=self.headers) self.assertDictContainsSubset( {'X-Configuration-Session': 'session_id', 'Content-Type': 'application/json', 'Accept': 'application/json'}, return_dict['headers'] ) def test_get_update_headers(self): __, return_dict = self.rest_client.get(self.url, extra_headers=True, headers=self.headers) self.assertDictContainsSubset( {'X-Configuration-Session': 'session_id', 'Content-Type': 'application/json', 'Accept': 'application/json'}, return_dict['headers'] ) def test_delete_update_headers(self): __, return_dict = self.rest_client.delete(self.url, extra_headers=True, headers=self.headers) self.assertDictContainsSubset( {'X-Configuration-Session': 'session_id', 'Content-Type': 'application/json', 'Accept': 'application/json'}, return_dict['headers'] ) def test_patch_update_headers(self): __, return_dict = self.rest_client.patch(self.url, {}, extra_headers=True, headers=self.headers) self.assertDictContainsSubset( {'X-Configuration-Session': 'session_id', 'Content-Type': 'application/json', 'Accept': 'application/json'}, return_dict['headers'] ) def test_put_update_headers(self): __, return_dict = self.rest_client.put(self.url, {}, extra_headers=True, headers=self.headers) self.assertDictContainsSubset( {'X-Configuration-Session': 'session_id', 'Content-Type': 'application/json', 'Accept': 'application/json'}, return_dict['headers'] ) def test_head_update_headers(self): self.useFixture(mockpatch.PatchObject(self.rest_client, 'response_checker')) __, return_dict = self.rest_client.head(self.url, extra_headers=True, headers=self.headers) self.assertDictContainsSubset( {'X-Configuration-Session': 'session_id', 'Content-Type': 'application/json', 'Accept': 'application/json'}, return_dict['headers'] ) def test_copy_update_headers(self): __, return_dict = self.rest_client.copy(self.url, extra_headers=True, headers=self.headers) self.assertDictContainsSubset( {'X-Configuration-Session': 'session_id', 'Content-Type': 'application/json', 'Accept': 'application/json'}, return_dict['headers'] ) class TestRestClientParseRespJSON(BaseRestClientTestClass): TYPE = "json" keys = ["fake_key1", "fake_key2"] values = ["fake_value1", "fake_value2"] item_expected = dict((key, value) for (key, value) in zip(keys, values)) list_expected = {"body_list": [ {keys[0]: values[0]}, {keys[1]: values[1]}, ]} dict_expected = {"body_dict": { keys[0]: values[0], keys[1]: values[1], }} null_dict = {} def setUp(self): self.fake_http = fake_http.fake_httplib2() super(TestRestClientParseRespJSON, self).setUp() self.rest_client.TYPE = self.TYPE def test_parse_resp_body_item(self): body = self.rest_client._parse_resp(json.dumps(self.item_expected)) self.assertEqual(self.item_expected, body) def test_parse_resp_body_list(self): body = self.rest_client._parse_resp(json.dumps(self.list_expected)) self.assertEqual(self.list_expected["body_list"], body) def test_parse_resp_body_dict(self): body = self.rest_client._parse_resp(json.dumps(self.dict_expected)) self.assertEqual(self.dict_expected["body_dict"], body) def test_parse_resp_two_top_keys(self): dict_two_keys = self.dict_expected.copy() dict_two_keys.update({"second_key": ""}) body = self.rest_client._parse_resp(json.dumps(dict_two_keys)) self.assertEqual(dict_two_keys, body) def test_parse_resp_one_top_key_without_list_or_dict(self): data = {"one_top_key": "not_list_or_dict_value"} body = self.rest_client._parse_resp(json.dumps(data)) self.assertEqual(data, body) def test_parse_nullable_dict(self): body = self.rest_client._parse_resp(json.dumps(self.null_dict)) self.assertEqual(self.null_dict, body) class TestRestClientErrorCheckerJSON(base.TestCase): c_type = "application/json" def set_data(self, r_code, enc=None, r_body=None, absolute_limit=True): if enc is None: enc = self.c_type resp_dict = {'status': r_code, 'content-type': enc} resp_body = {'resp_body': 'fake_resp_body'} if absolute_limit is False: resp_dict.update({'retry-after': 120}) resp_body.update({'overLimit': {'message': 'fake_message'}}) resp = httplib2.Response(resp_dict) data = { "method": "fake_method", "url": "fake_url", "headers": "fake_headers", "body": "fake_body", "resp": resp, "resp_body": json.dumps(resp_body) } if r_body is not None: data.update({"resp_body": r_body}) return data def setUp(self): super(TestRestClientErrorCheckerJSON, self).setUp() self.rest_client = rest_client.RestClient( fake_auth_provider.FakeAuthProvider(), None, None) def test_response_less_than_400(self): self.rest_client._error_checker(**self.set_data("399")) def _test_error_checker(self, exception_type, data): e = self.assertRaises(exception_type, self.rest_client._error_checker, **data) self.assertEqual(e.resp, data['resp']) self.assertTrue(hasattr(e, 'resp_body')) return e def test_response_400(self): self._test_error_checker(exceptions.BadRequest, self.set_data("400")) def test_response_401(self): self._test_error_checker(exceptions.Unauthorized, self.set_data("401")) def test_response_403(self): self._test_error_checker(exceptions.Forbidden, self.set_data("403")) def test_response_404(self): self._test_error_checker(exceptions.NotFound, self.set_data("404")) def test_response_409(self): self._test_error_checker(exceptions.Conflict, self.set_data("409")) def test_response_410(self): self._test_error_checker(exceptions.Gone, self.set_data("410")) def test_response_413(self): self._test_error_checker(exceptions.OverLimit, self.set_data("413")) def test_response_413_without_absolute_limit(self): self._test_error_checker(exceptions.RateLimitExceeded, self.set_data("413", absolute_limit=False)) def test_response_415(self): self._test_error_checker(exceptions.InvalidContentType, self.set_data("415")) def test_response_422(self): self._test_error_checker(exceptions.UnprocessableEntity, self.set_data("422")) def test_response_500_with_text(self): # _parse_resp is expected to return 'str' self._test_error_checker(exceptions.ServerFault, self.set_data("500")) def test_response_501_with_text(self): self._test_error_checker(exceptions.NotImplemented, self.set_data("501")) def test_response_400_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' e = self._test_error_checker(exceptions.BadRequest, self.set_data("400", r_body=r_body)) if self.c_type == 'application/json': expected = {"err": "fake_resp_body"} else: expected = r_body self.assertEqual(expected, e.resp_body) def test_response_401_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' e = self._test_error_checker(exceptions.Unauthorized, self.set_data("401", r_body=r_body)) if self.c_type == 'application/json': expected = {"err": "fake_resp_body"} else: expected = r_body self.assertEqual(expected, e.resp_body) def test_response_403_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' e = self._test_error_checker(exceptions.Forbidden, self.set_data("403", r_body=r_body)) if self.c_type == 'application/json': expected = {"err": "fake_resp_body"} else: expected = r_body self.assertEqual(expected, e.resp_body) def test_response_404_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' e = self._test_error_checker(exceptions.NotFound, self.set_data("404", r_body=r_body)) if self.c_type == 'application/json': expected = {"err": "fake_resp_body"} else: expected = r_body self.assertEqual(expected, e.resp_body) def test_response_404_with_invalid_dict(self): r_body = '{"foo": "bar"]' e = self._test_error_checker(exceptions.NotFound, self.set_data("404", r_body=r_body)) expected = r_body self.assertEqual(expected, e.resp_body) def test_response_410_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' e = self._test_error_checker(exceptions.Gone, self.set_data("410", r_body=r_body)) if self.c_type == 'application/json': expected = {"err": "fake_resp_body"} else: expected = r_body self.assertEqual(expected, e.resp_body) def test_response_410_with_invalid_dict(self): r_body = '{"foo": "bar"]' e = self._test_error_checker(exceptions.Gone, self.set_data("410", r_body=r_body)) expected = r_body self.assertEqual(expected, e.resp_body) def test_response_409_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' e = self._test_error_checker(exceptions.Conflict, self.set_data("409", r_body=r_body)) if self.c_type == 'application/json': expected = {"err": "fake_resp_body"} else: expected = r_body self.assertEqual(expected, e.resp_body) def test_response_500_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' e = self._test_error_checker(exceptions.ServerFault, self.set_data("500", r_body=r_body)) if self.c_type == 'application/json': expected = {"err": "fake_resp_body"} else: expected = r_body self.assertEqual(expected, e.resp_body) def test_response_501_with_dict(self): r_body = '{"resp_body": {"err": "fake_resp_body"}}' self._test_error_checker(exceptions.NotImplemented, self.set_data("501", r_body=r_body)) def test_response_bigger_than_400(self): # Any response code, that bigger than 400, and not in # (401, 403, 404, 409, 413, 422, 500, 501) self._test_error_checker(exceptions.UnexpectedResponseCode, self.set_data("402")) class TestRestClientErrorCheckerTEXT(TestRestClientErrorCheckerJSON): c_type = "text/plain" def test_fake_content_type(self): # This test is required only in one exemplar # Any response code, that bigger than 400, and not in # (401, 403, 404, 409, 413, 422, 500, 501) self._test_error_checker(exceptions.UnexpectedContentType, self.set_data("405", enc="fake_enc")) def test_response_413_without_absolute_limit(self): # Skip this test because rest_client cannot get overLimit message # from text body. pass class TestRestClientUtils(BaseRestClientTestClass): def _is_resource_deleted(self, resource_id): if not isinstance(self.retry_pass, int): return False if self.retry_count >= self.retry_pass: return True self.retry_count = self.retry_count + 1 return False def setUp(self): self.fake_http = fake_http.fake_httplib2() super(TestRestClientUtils, self).setUp() self.retry_count = 0 self.retry_pass = None self.original_deleted_method = self.rest_client.is_resource_deleted self.rest_client.is_resource_deleted = self._is_resource_deleted def test_wait_for_resource_deletion(self): self.retry_pass = 2 # Ensure timeout long enough for loop execution to hit retry count self.rest_client.build_timeout = 500 sleep_mock = self.patch('time.sleep') self.rest_client.wait_for_resource_deletion('1234') self.assertEqual(len(sleep_mock.mock_calls), 2) def test_wait_for_resource_deletion_not_deleted(self): self.patch('time.sleep') # Set timeout to be very quick to force exception faster self.rest_client.build_timeout = 1 self.assertRaises(exceptions.TimeoutException, self.rest_client.wait_for_resource_deletion, '1234') def test_wait_for_deletion_with_unimplemented_deleted_method(self): self.rest_client.is_resource_deleted = self.original_deleted_method self.assertRaises(NotImplementedError, self.rest_client.wait_for_resource_deletion, '1234') def test_get_versions(self): self.rest_client._parse_resp = lambda x: [{'id': 'v1'}, {'id': 'v2'}] actual_resp, actual_versions = self.rest_client.get_versions() self.assertEqual(['v1', 'v2'], list(actual_versions)) def test__str__(self): def get_token(): return "deadbeef" self.fake_auth_provider.get_token = get_token self.assertIsNotNone(str(self.rest_client)) class TestProperties(BaseRestClientTestClass): def setUp(self): self.fake_http = fake_http.fake_httplib2() super(TestProperties, self).setUp() creds_dict = { 'username': 'test-user', 'user_id': 'test-user_id', 'tenant_name': 'test-tenant_name', 'tenant_id': 'test-tenant_id', 'password': 'test-password' } self.rest_client = rest_client.RestClient( fake_auth_provider.FakeAuthProvider(creds_dict=creds_dict), None, None) def test_properties(self): self.assertEqual('test-user', self.rest_client.user) self.assertEqual('test-user_id', self.rest_client.user_id) self.assertEqual('test-tenant_name', self.rest_client.tenant_name) self.assertEqual('test-tenant_id', self.rest_client.tenant_id) self.assertEqual('test-password', self.rest_client.password) self.rest_client.api_version = 'v1' expected = {'api_version': 'v1', 'endpoint_type': 'publicURL', 'region': None, 'service': None, 'skip_path': True} self.rest_client.skip_path() self.assertEqual(expected, self.rest_client.filters) self.rest_client.reset_path() self.rest_client.api_version = 'v1' expected = {'api_version': 'v1', 'endpoint_type': 'publicURL', 'region': None, 'service': None} self.assertEqual(expected, self.rest_client.filters) class TestExpectedSuccess(BaseRestClientTestClass): def setUp(self): self.fake_http = fake_http.fake_httplib2() super(TestExpectedSuccess, self).setUp() def test_expected_succes_int_match(self): expected_code = 202 read_code = 202 resp = self.rest_client.expected_success(expected_code, read_code) # Assert None resp on success self.assertFalse(resp) def test_expected_succes_int_no_match(self): expected_code = 204 read_code = 202 self.assertRaises(exceptions.InvalidHttpSuccessCode, self.rest_client.expected_success, expected_code, read_code) def test_expected_succes_list_match(self): expected_code = [202, 204] read_code = 202 resp = self.rest_client.expected_success(expected_code, read_code) # Assert None resp on success self.assertFalse(resp) def test_expected_succes_list_no_match(self): expected_code = [202, 204] read_code = 200 self.assertRaises(exceptions.InvalidHttpSuccessCode, self.rest_client.expected_success, expected_code, read_code) def test_non_success_expected_int(self): expected_code = 404 read_code = 202 self.assertRaises(AssertionError, self.rest_client.expected_success, expected_code, read_code) def test_non_success_expected_list(self): expected_code = [404, 202] read_code = 202 self.assertRaises(AssertionError, self.rest_client.expected_success, expected_code, read_code) class TestResponseBody(base.TestCase): def test_str(self): response = {'status': 200} body = {'key1': 'value1'} actual = rest_client.ResponseBody(response, body) self.assertEqual("response: %s\nBody: %s" % (response, body), str(actual)) class TestResponseBodyData(base.TestCase): def test_str(self): response = {'status': 200} data = 'data1' actual = rest_client.ResponseBodyData(response, data) self.assertEqual("response: %s\nBody: %s" % (response, data), str(actual)) class TestResponseBodyList(base.TestCase): def test_str(self): response = {'status': 200} body = ['value1', 'value2', 'value3'] actual = rest_client.ResponseBodyList(response, body) self.assertEqual("response: %s\nBody: %s" % (response, body), str(actual)) class TestJSONSchemaValidationBase(base.TestCase): class Response(dict): def __getattr__(self, attr): return self[attr] def __setattr__(self, attr, value): self[attr] = value def setUp(self): super(TestJSONSchemaValidationBase, self).setUp() self.fake_auth_provider = fake_auth_provider.FakeAuthProvider() self.rest_client = rest_client.RestClient( self.fake_auth_provider, None, None) def _test_validate_pass(self, schema, resp_body, status=200): resp = self.Response() resp.status = status self.rest_client.validate_response(schema, resp, resp_body) def _test_validate_fail(self, schema, resp_body, status=200, error_msg="HTTP response body is invalid"): resp = self.Response() resp.status = status ex = self.assertRaises(exceptions.InvalidHTTPResponseBody, self.rest_client.validate_response, schema, resp, resp_body) self.assertIn(error_msg, ex._error_string) class TestRestClientJSONSchemaValidation(TestJSONSchemaValidationBase): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'integer', }, }, 'required': ['foo'] } } def test_validate_pass_with_http_success_code(self): body = {'foo': 12} self._test_validate_pass(self.schema, body, status=200) def test_validate_pass_with_http_redirect_code(self): body = {'foo': 12} schema = copy.deepcopy(self.schema) schema['status_code'] = 300 self._test_validate_pass(schema, body, status=300) def test_validate_not_http_success_code(self): schema = { 'status_code': [200] } body = {} self._test_validate_pass(schema, body, status=400) def test_validate_multiple_allowed_type(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': ['integer', 'string'], }, }, 'required': ['foo'] } } body = {'foo': 12} self._test_validate_pass(schema, body) body = {'foo': '12'} self._test_validate_pass(schema, body) def test_validate_enable_additional_property_pass(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': {'type': 'integer'} }, 'additionalProperties': True, 'required': ['foo'] } } body = {'foo': 12, 'foo2': 'foo2value'} self._test_validate_pass(schema, body) def test_validate_disable_additional_property_pass(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': {'type': 'integer'} }, 'additionalProperties': False, 'required': ['foo'] } } body = {'foo': 12} self._test_validate_pass(schema, body) def test_validate_disable_additional_property_fail(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': {'type': 'integer'} }, 'additionalProperties': False, 'required': ['foo'] } } body = {'foo': 12, 'foo2': 'foo2value'} self._test_validate_fail(schema, body) def test_validate_wrong_status_code(self): schema = { 'status_code': [202] } body = {} resp = self.Response() resp.status = 200 ex = self.assertRaises(exceptions.InvalidHttpSuccessCode, self.rest_client.validate_response, schema, resp, body) self.assertIn("Unexpected http success status code", ex._error_string) def test_validate_wrong_attribute_type(self): body = {'foo': 1.2} self._test_validate_fail(self.schema, body) def test_validate_unexpected_response_body(self): schema = { 'status_code': [200], } body = {'foo': 12} self._test_validate_fail( schema, body, error_msg="HTTP response body should not exist") def test_validate_missing_response_body(self): body = {} self._test_validate_fail(self.schema, body) def test_validate_missing_required_attribute(self): body = {'notfoo': 12} self._test_validate_fail(self.schema, body) def test_validate_response_body_not_list(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'list_items': { 'type': 'array', 'items': {'foo': {'type': 'integer'}} } }, 'required': ['list_items'], } } body = {'foo': 12} self._test_validate_fail(schema, body) def test_validate_response_body_list_pass(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'list_items': { 'type': 'array', 'items': {'foo': {'type': 'integer'}} } }, 'required': ['list_items'], } } body = {'list_items': [{'foo': 12}, {'foo': 10}]} self._test_validate_pass(schema, body) class TestRestClientJSONHeaderSchemaValidation(TestJSONSchemaValidationBase): schema = { 'status_code': [200], 'response_header': { 'type': 'object', 'properties': { 'foo': {'type': 'integer'} }, 'required': ['foo'] } } def test_validate_header_schema_pass(self): resp_body = {} resp = self.Response() resp.status = 200 resp.foo = 12 self.rest_client.validate_response(self.schema, resp, resp_body) def test_validate_header_schema_fail(self): resp_body = {} resp = self.Response() resp.status = 200 resp.foo = 1.2 ex = self.assertRaises(exceptions.InvalidHTTPResponseHeader, self.rest_client.validate_response, self.schema, resp, resp_body) self.assertIn("HTTP response header is invalid", ex._error_string) class TestRestClientJSONSchemaFormatValidation(TestJSONSchemaValidationBase): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'format': 'email' } }, 'required': ['foo'] } } def test_validate_format_pass(self): body = {'foo': 'example@example.com'} self._test_validate_pass(self.schema, body) def test_validate_format_fail(self): body = {'foo': 'wrong_email'} self._test_validate_fail(self.schema, body) def test_validate_formats_in_oneOf_pass(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'oneOf': [ {'format': 'ipv4'}, {'format': 'ipv6'} ] } }, 'required': ['foo'] } } body = {'foo': '10.0.0.0'} self._test_validate_pass(schema, body) body = {'foo': 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329'} self._test_validate_pass(schema, body) def test_validate_formats_in_oneOf_fail_both_match(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'oneOf': [ {'format': 'ipv4'}, {'format': 'ipv4'} ] } }, 'required': ['foo'] } } body = {'foo': '10.0.0.0'} self._test_validate_fail(schema, body) def test_validate_formats_in_oneOf_fail_no_match(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'oneOf': [ {'format': 'ipv4'}, {'format': 'ipv6'} ] } }, 'required': ['foo'] } } body = {'foo': 'wrong_ip_format'} self._test_validate_fail(schema, body) def test_validate_formats_in_anyOf_pass(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'anyOf': [ {'format': 'ipv4'}, {'format': 'ipv6'} ] } }, 'required': ['foo'] } } body = {'foo': '10.0.0.0'} self._test_validate_pass(schema, body) body = {'foo': 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329'} self._test_validate_pass(schema, body) def test_validate_formats_in_anyOf_pass_both_match(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'anyOf': [ {'format': 'ipv4'}, {'format': 'ipv4'} ] } }, 'required': ['foo'] } } body = {'foo': '10.0.0.0'} self._test_validate_pass(schema, body) def test_validate_formats_in_anyOf_fail_no_match(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'anyOf': [ {'format': 'ipv4'}, {'format': 'ipv6'} ] } }, 'required': ['foo'] } } body = {'foo': 'wrong_ip_format'} self._test_validate_fail(schema, body) def test_validate_formats_pass_for_unknow_format(self): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': { 'type': 'string', 'format': 'UNKNOWN' } }, 'required': ['foo'] } } body = {'foo': 'example@example.com'} self._test_validate_pass(schema, body) class TestRestClientJSONSchemaValidatorVersion(TestJSONSchemaValidationBase): schema = { 'status_code': [200], 'response_body': { 'type': 'object', 'properties': { 'foo': {'type': 'string'} } } } def test_current_json_schema_validator_version(self): with mockpatch.PatchObject(jsonschema.Draft4Validator, "check_schema") as chk_schema: body = {'foo': 'test'} self._test_validate_pass(self.schema, body) chk_schema.mock.assert_called_once_with( self.schema['response_body']) tempest-lib-1.0.0/tempest_lib/tests/test_ssh.py000066400000000000000000000233001266406763200216260ustar00rootroot00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from io import StringIO import socket import time import mock import six import testtools from tempest_lib.common import ssh from tempest_lib import exceptions from tempest_lib.tests import base class TestSshClient(base.TestCase): SELECT_POLLIN = 1 @mock.patch('paramiko.RSAKey.from_private_key') @mock.patch('six.StringIO') def test_pkey_calls_paramiko_RSAKey(self, cs_mock, rsa_mock): cs_mock.return_value = mock.sentinel.csio pkey = 'mykey' ssh.Client('localhost', 'root', pkey=pkey) rsa_mock.assert_called_once_with(mock.sentinel.csio) cs_mock.assert_called_once_with('mykey') rsa_mock.reset_mock() cs_mock.reset_mock() pkey = mock.sentinel.pkey # Shouldn't call out to load a file from RSAKey, since # a sentinel isn't a basestring... ssh.Client('localhost', 'root', pkey=pkey) self.assertEqual(0, rsa_mock.call_count) self.assertEqual(0, cs_mock.call_count) def _set_ssh_connection_mocks(self): client_mock = mock.MagicMock() client_mock.connect.return_value = True return (self.patch('paramiko.SSHClient'), self.patch('paramiko.AutoAddPolicy'), client_mock) def test_get_ssh_connection(self): c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks() s_mock = self.patch('time.sleep') c_mock.return_value = client_mock aa_mock.return_value = mock.sentinel.aa # Test normal case for successful connection on first try client = ssh.Client('localhost', 'root', timeout=2) client._get_ssh_connection(sleep=1) aa_mock.assert_called_once_with() client_mock.set_missing_host_key_policy.assert_called_once_with( mock.sentinel.aa) expected_connect = [mock.call( 'localhost', username='root', pkey=None, key_filename=None, look_for_keys=False, timeout=10.0, password=None )] self.assertEqual(expected_connect, client_mock.connect.mock_calls) self.assertEqual(0, s_mock.call_count) def test_get_ssh_connection_two_attemps(self): c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks() c_mock.return_value = client_mock client_mock.connect.side_effect = [ socket.error, mock.MagicMock() ] client = ssh.Client('localhost', 'root', timeout=1) start_time = int(time.time()) client._get_ssh_connection(sleep=1) end_time = int(time.time()) self.assertLess((end_time - start_time), 4) self.assertGreater((end_time - start_time), 1) def test_get_ssh_connection_timeout(self): c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks() c_mock.return_value = client_mock client_mock.connect.side_effect = [ socket.error, socket.error, socket.error, ] client = ssh.Client('localhost', 'root', timeout=2) start_time = int(time.time()) with testtools.ExpectedException(exceptions.SSHTimeout): client._get_ssh_connection() end_time = int(time.time()) self.assertLess((end_time - start_time), 5) self.assertGreaterEqual((end_time - start_time), 2) @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_timeout_in_exec_command(self): chan_mock, poll_mock, _ = self._set_mocks_for_select([0, 0, 0], True) # Test for a timeout condition immediately raised client = ssh.Client('localhost', 'root', timeout=2) with testtools.ExpectedException(exceptions.TimeoutException): client.exec_command("test") chan_mock.fileno.assert_called_once_with() chan_mock.exec_command.assert_called_once_with("test") chan_mock.shutdown_write.assert_called_once_with() poll_mock.register.assert_called_once_with( chan_mock, self.SELECT_POLLIN) poll_mock.poll.assert_called_once_with(10) @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_exec_command(self): chan_mock, poll_mock, select_mock = ( self._set_mocks_for_select([[1, 0, 0]], True)) closed_prop = mock.PropertyMock(return_value=True) type(chan_mock).closed = closed_prop chan_mock.recv_exit_status.return_value = 0 chan_mock.recv.return_value = b'' chan_mock.recv_stderr.return_value = b'' client = ssh.Client('localhost', 'root', timeout=2) client.exec_command("test") chan_mock.fileno.assert_called_once_with() chan_mock.exec_command.assert_called_once_with("test") chan_mock.shutdown_write.assert_called_once_with() select_mock.assert_called_once_with() poll_mock.register.assert_called_once_with( chan_mock, self.SELECT_POLLIN) poll_mock.poll.assert_called_once_with(10) chan_mock.recv_ready.assert_called_once_with() chan_mock.recv.assert_called_once_with(1024) chan_mock.recv_stderr_ready.assert_called_once_with() chan_mock.recv_stderr.assert_called_once_with(1024) chan_mock.recv_exit_status.assert_called_once_with() closed_prop.assert_called_once_with() def _set_mocks_for_select(self, poll_data, ito_value=False): gsc_mock = self.patch('tempest_lib.common.ssh.Client.' '_get_ssh_connection') ito_mock = self.patch('tempest_lib.common.ssh.Client._is_timed_out') csp_mock = self.patch( 'tempest_lib.common.ssh.Client._can_system_poll') csp_mock.return_value = True select_mock = self.patch('select.poll', create=True) client_mock = mock.MagicMock() tran_mock = mock.MagicMock() chan_mock = mock.MagicMock() poll_mock = mock.MagicMock() select_mock.return_value = poll_mock gsc_mock.return_value = client_mock ito_mock.return_value = ito_value client_mock.get_transport.return_value = tran_mock tran_mock.open_session.return_value = chan_mock if isinstance(poll_data[0], list): poll_mock.poll.side_effect = poll_data else: poll_mock.poll.return_value = poll_data return chan_mock, poll_mock, select_mock _utf8_string = six.unichr(1071) _utf8_bytes = _utf8_string.encode("utf-8") @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_exec_good_command_output(self): chan_mock, poll_mock, _ = self._set_mocks_for_select([1, 0, 0]) closed_prop = mock.PropertyMock(return_value=True) type(chan_mock).closed = closed_prop chan_mock.recv_exit_status.return_value = 0 chan_mock.recv.side_effect = [self._utf8_bytes[0:1], self._utf8_bytes[1:], b'R', b''] chan_mock.recv_stderr.return_value = b'' client = ssh.Client('localhost', 'root', timeout=2) out_data = client.exec_command("test") self.assertEqual(self._utf8_string + 'R', out_data) @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_exec_bad_command_output(self): chan_mock, poll_mock, _ = self._set_mocks_for_select([1, 0, 0]) closed_prop = mock.PropertyMock(return_value=True) type(chan_mock).closed = closed_prop chan_mock.recv_exit_status.return_value = 1 chan_mock.recv.return_value = b'' chan_mock.recv_stderr.side_effect = [b'R', self._utf8_bytes[0:1], self._utf8_bytes[1:], b''] client = ssh.Client('localhost', 'root', timeout=2) exc = self.assertRaises(exceptions.SSHExecCommandFailed, client.exec_command, "test") self.assertIn('R' + self._utf8_string, six.text_type(exc)) def test_exec_command_no_select(self): gsc_mock = self.patch('tempest_lib.common.ssh.Client.' '_get_ssh_connection') csp_mock = self.patch( 'tempest_lib.common.ssh.Client._can_system_poll') csp_mock.return_value = False select_mock = self.patch('select.poll', create=True) client_mock = mock.MagicMock() tran_mock = mock.MagicMock() chan_mock = mock.MagicMock() # Test for proper reading of STDOUT and STDERROR gsc_mock.return_value = client_mock client_mock.get_transport.return_value = tran_mock tran_mock.open_session.return_value = chan_mock chan_mock.recv_exit_status.return_value = 0 std_out_mock = mock.MagicMock(StringIO) std_err_mock = mock.MagicMock(StringIO) chan_mock.makefile.return_value = std_out_mock chan_mock.makefile_stderr.return_value = std_err_mock client = ssh.Client('localhost', 'root', timeout=2) client.exec_command("test") chan_mock.makefile.assert_called_once_with('rb', 1024) chan_mock.makefile_stderr.assert_called_once_with('rb', 1024) std_out_mock.read.assert_called_once_with() std_err_mock.read.assert_called_once_with() self.assertFalse(select_mock.called) tempest-lib-1.0.0/tempest_lib/tests/test_tempest_lib.py000066400000000000000000000014201266406763200233370ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # 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. """ test_tempest_lib ---------------------------------- Tests for `tempest_lib` module. """ from tempest_lib.tests import base class TestTempest_lib(base.TestCase): def test_something(self): pass tempest-lib-1.0.0/test-requirements.txt000066400000000000000000000011041266406763200202060ustar00rootroot00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking<0.11,>=0.10.0 coverage>=3.6 # Apache-2.0 discover # BSD python-subunit>=0.0.18 # Apache-2.0/BSD sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD testtools>=1.4.0 # MIT mock>=1.2 # BSD ddt>=1.0.1 # MIT reno>=0.1.1 # Apache2 tempest-lib-1.0.0/tools/000077500000000000000000000000001266406763200151115ustar00rootroot00000000000000tempest-lib-1.0.0/tools/migrate_from_tempest.sh000077500000000000000000000077101266406763200216710ustar00rootroot00000000000000#!/bin/bash # # Use this script to move over a set of files from tempest master into # tempest-lib with the commit history for the files in the commit message. # This should only be done for files that haven't been migrated over already. # To use: # 1. Create a new branch in the tempest-lib repo so not to destroy your current # working branch # 2. Run the script from the repo dir and specify the file paths relative to # the root tempest dir(only code and unit tests): # # tools/migrate_from_tempest.sh tempest/file.py tempest/sub_dir function usage { echo "Usage: $0 [OPTION] file1 file2" echo "Migrate files from tempest" echo "" echo "-o, --output_dir Specify a directory relative to the repo root to move the migrated files into." echo "-u, --tempest_git_url Specify the repo to clone tempest from for the migration." } set -e output_dir="" service_client=0 while [ $# -gt 0 ]; do case "$1" in -h|--help) usage; exit;; -o|--output_dir) output_dir="$2"; shift;; -u|--tempest_git_url) tempest_git_url="$2"; shift;; -s|--service_client) service_client=1;; *) files="$files $1";; esac shift done TEMPEST_GIT_URL=${tempest_git_url:-git://git.openstack.org/openstack/tempest} tmpdir=$(mktemp -d -t tempest-migrate.XXXX) tempest_lib_dir=$(dirname "$0") function count_commits { echo echo "Have $(git log --oneline | wc -l) commits" } # Clone tempest and cd into it git clone $TEMPEST_GIT_URL $tmpdir cd $tmpdir for file in $files; do # Get the latest change-id for each file change_id=`git log -n1 --grep "Change-Id: " -- $file | grep "Change-Id: " | awk '{print $2}'` filename=`basename $file` CHANGE_LIST=`echo -e "$CHANGE_LIST\n * $filename: $change_id"` done # Move files and commit cd - file_list='' for file in $files; do filename=`basename $file` dirname=`dirname $file` if [ -n "$output_dir" ]; then dirname="$output_dir" else dirname=`echo $dirname | sed s@tempest\/@tempest_lib/\@` if [ $service_client -eq 1 ]; then # Remove /json path because tempest-lib supports JSON only without XML dirname=`echo $dirname | sed s@\/json@@` fi fi dest_file="$dirname/$filename" cp -r "$tmpdir/$file" "$dest_file" if [ $service_client -eq 1 ]; then # service_client module is not necessary in tempest-lib because rest_client can be used instead sed -i='' s/"from tempest.common import service_client"/"from tempest_lib.common import rest_client"/ $dest_file sed -i='' s/"service_client.ServiceClient"/"rest_client.RestClient"/ $dest_file sed -i='' s/"service_client.ResponseBody"/"rest_client.ResponseBody"/ $dest_file sed -i='' s/"from tempest\."/"from tempest_lib\."/ $dest_file # Replace mocked path in unit tests sed -i='' s/"tempest.common.rest_client"/"tempest_lib.common.rest_client"/ $dest_file # Remove ".json" from import line sed -i='' -e "s/^\(from tempest_lib\.services\..*\)\.json\(.*\)/\1\2/" $dest_file fi git add "$dest_file" if [[ -z "$file_list" ]]; then file_list="$filename" else tmp_file_list="$file_list, $filename" char_size=`echo $tmp_file_list | wc -c` if [ "$char_size" -lt 27 ]; then file_list="$file_list, $filename" fi fi done # Cleanup temporary tempest repo rm -rf $tmpdir # Generate a migration commit commit_message="Migrated $file_list from tempest" pre_list=$"This migrates the above files from tempest.\nThis includes tempest commits:" pre_list=`echo -e $pre_list` post_list=$"to see the commit history for these files refer to the above Change-Ids \nin the tempest repository." post_list=`echo -e $post_list` if [ $service_client -eq 1 ]; then bp_msg="Partially implements blueprint migrate-service-clients-to-tempest-lib" else bp_msg="" fi git commit -m "$commit_message" -m "$pre_list" -m "$CHANGE_LIST" -m "$post_list" -m "$bp_msg" tempest-lib-1.0.0/tox.ini000066400000000000000000000021111266406763200152570ustar00rootroot00000000000000[tox] minversion = 1.6 envlist = py34,py27,pypy,pep8 skipsdist = True [testenv] usedevelop = True install_command = pip install -U {opts} {packages} whitelist_externals = bash setenv = VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = ostestr {posargs} passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY [testenv:pep8] commands = flake8 [testenv:venv] commands = {posargs} [testenv:cover] commands = python setup.py test --coverage --coverage-package-name='tempest_lib' --testr-args='{posargs}' [testenv:docs] commands = python setup.py build_sphinx [flake8] # E125 skipped as it is invalid PEP-8. # E123 skipped because it is ignored by default in the default pep8 # E129 skipped because it is too limiting when combined with other rules show-source = True ignore = E125,E123,E129 builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,releasenotes [testenv:releasenotes] commands = sphinx-build -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html