pax_global_header00006660000000000000000000000064132341154340014512gustar00rootroot0000000000000052 comment=e3202b95e1f708a0db284e05f2b0f9a58127b65a python-magnumclient-2.8.0/000077500000000000000000000000001323411543400155235ustar00rootroot00000000000000python-magnumclient-2.8.0/.coveragerc000066400000000000000000000001451323411543400176440ustar00rootroot00000000000000[run] branch = True source = magnumclient omit = magnumclient/tests/* [report] ignore_errors = True python-magnumclient-2.8.0/.gitignore000066400000000000000000000007411323411543400175150ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg* dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox cover cover-master nosetests.xml .testrepository .venv # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject .idea # Complexity output/*.html output/*/index.html # Sphinx doc/build # pbr generates these AUTHORS ChangeLog # Editors *~ .*.swp *.DS_Store python-magnumclient-2.8.0/.gitreview000066400000000000000000000001301323411543400175230ustar00rootroot00000000000000[gerrit] host=review.openstack.org port=29418 project=openstack/python-magnumclient.git python-magnumclient-2.8.0/.mailmap000066400000000000000000000001311323411543400171370ustar00rootroot00000000000000# Format is: # # python-magnumclient-2.8.0/.testr.conf000066400000000000000000000004771323411543400176210ustar00rootroot00000000000000[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=--list python-magnumclient-2.8.0/CONTRIBUTING.rst000066400000000000000000000010471323411543400201660ustar00rootroot00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: https://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: https://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/python-magnumclientpython-magnumclient-2.8.0/LICENSE000066400000000000000000000260751323411543400165420ustar00rootroot00000000000000Apache 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. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. python-magnumclient-2.8.0/README.rst000066400000000000000000000026641323411543400172220ustar00rootroot00000000000000======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/python-magnumclient.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on Python bindings to the Magnum API ================================= .. image:: https://img.shields.io/pypi/v/python-magnumclient.svg :target: https://pypi.python.org/pypi/python-magnumclient/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/python-magnumclient.svg :target: https://pypi.python.org/pypi/python-magnumclient/ :alt: Downloads This is a client library for Magnum built on the Magnum API. It provides a Python API (the ``magnumclient`` module) and a command-line tool (``magnum``). Development takes place via the usual OpenStack processes as outlined in the `developer guide `_. * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Launchpad project`_ - release management * `Bugs`_ - issue tracking * `Source`_ .. _PyPi: https://pypi.python.org/pypi/python-magnumclient .. _Online Documentation: https://docs.openstack.org/python-magnumclient/latest/ .. _Launchpad project: https://launchpad.net/python-magnumclient .. _Bugs: https://bugs.launchpad.net/python-magnumclient .. _Source: https://git.openstack.org/cgit/openstack/python-magnumclient python-magnumclient-2.8.0/babel.cfg000066400000000000000000000000201323411543400172410ustar00rootroot00000000000000[python: **.py] python-magnumclient-2.8.0/doc/000077500000000000000000000000001323411543400162705ustar00rootroot00000000000000python-magnumclient-2.8.0/doc/source/000077500000000000000000000000001323411543400175705ustar00rootroot00000000000000python-magnumclient-2.8.0/doc/source/conf.py000066400000000000000000000052761323411543400211010ustar00rootroot00000000000000# -*- 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', 'openstackdocstheme' ] # 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'python-magnumclient' 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' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['magnumclient.'] # -- 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 = 'openstackdocs' # html_static_path = ['static'] # openstackdocstheme options repository_name = 'openstack/python-magnumclient' bug_project = 'python-magnumclient' bug_tag = '' html_last_updated_fmt = '%Y-%m-%d %H:%M' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} python-magnumclient-2.8.0/doc/source/contributing.rst000066400000000000000000000000431323411543400230260ustar00rootroot00000000000000.. include:: ../../CONTRIBUTING.rstpython-magnumclient-2.8.0/doc/source/index.rst000066400000000000000000000004301323411543400214260ustar00rootroot00000000000000Welcome to python-magnumclient's documentation! ============================================== Contents: .. toctree:: :maxdepth: 2 readme installation usage contributing Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-magnumclient-2.8.0/doc/source/installation.rst000066400000000000000000000003411323411543400230210ustar00rootroot00000000000000============ Installation ============ At the command line:: $ pip install python-magnumclient Or, if you have virtualenvwrapper installed:: $ mkvirtualenv python-magnumclient $ pip install python-magnumclientpython-magnumclient-2.8.0/doc/source/readme.rst000066400000000000000000000000361323411543400215560ustar00rootroot00000000000000.. include:: ../../README.rst python-magnumclient-2.8.0/doc/source/usage.rst000066400000000000000000000040111323411543400214220ustar00rootroot00000000000000Python bindings to the OpenStack Magnum API =========================================== This is a client for the OpenStack Magnum API. It includes a Python API (the :mod:`magnumclient` module) and a command-line script (installed as :program:`magnum`). Python API ========== To use python-magnumclient in a project, create a client instance using the keystoneauth session API:: from keystoneauth1.identity import v3 from keystoneauth1 import session from keystoneclient.v3 import client from magnumclient.client import Client magnum_endpoint = "http://magnum.example.com:9511/v1" auth = v3.Password(auth_url='http://my.keystone.com:5000/v3', username='myuser', password='mypassword', project_name='myproject', user_domain_id='default', project_domain_id='default') sess = session.Session(auth=auth) magnum = Client('1', endpoint_override=magnum_endpoint, session=sess) magnum.clusters.list() For more information on keystoneauth API, see `Using Sessions`_. .. _Using Sessions: https://docs.openstack.org/keystoneauth/latest/using-sessions.html Command-line tool ================= In order to use the CLI, you must provide your OpenStack username, password, project name, user domain ID, project domain ID, and auth endpoint. Use the corresponding configuration options (--os-username, --os-password, --os-project-name, --os-project-domain-id, --os-user-domain-id, and --os-auth-url) or set them in environment variables:: export OS_USERNAME=myuser export OS_PASSWORD=mypassword export OS_PROJECT_NAME=myproject export OS_USER_DOMAIN_ID=default export OS_PROJECT_DOMAIN_ID=default export OS_AUTH_URL=http://my.keystone.com:5000/v3 From there, all shell commands take the form:: magnum [arguments...] Run :program:`magnum help` to see a complete listing of available commands. Run :program:`magnum help ` to get detailed help for that command. python-magnumclient-2.8.0/magnumclient/000077500000000000000000000000001323411543400202065ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/__init__.py000066400000000000000000000012431323411543400223170ustar00rootroot00000000000000# -*- 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 pbr.version __version__ = pbr.version.VersionInfo( 'python-magnumclient').version_string() python-magnumclient-2.8.0/magnumclient/client.py000066400000000000000000000016061323411543400220410ustar00rootroot00000000000000# Copyright (c) 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 magnumclient.v1 import client def Client(version='1', **kwargs): """Factory function to create a new container service client.""" if version != '1': raise ValueError( "magnum only has one API version. Valid values for 'version'" " are '1'") return client.Client(**kwargs) python-magnumclient-2.8.0/magnumclient/common/000077500000000000000000000000001323411543400214765ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/common/__init__.py000066400000000000000000000000001323411543400235750ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/common/apiclient/000077500000000000000000000000001323411543400234465ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/common/apiclient/__init__.py000066400000000000000000000000001323411543400255450ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/common/apiclient/base.py000066400000000000000000000066741323411543400247470ustar00rootroot00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 Grid Dynamics # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ import copy class Resource(object): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ def __init__(self, manager, info, loaded=False): """Populate and bind to a manager. :param manager: BaseManager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ self.manager = manager self._info = info self._add_details(info) self._loaded = loaded def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) def _add_details(self, info): for (k, v) in info.items(): try: setattr(self, k, v) self._info[k] = v except AttributeError: # In this case we already defined the attribute on the class pass def __getattr__(self, k): if k not in self.__dict__: # NOTE(bcwaldon): disallow lazy-loading if already loaded once if not self.is_loaded(): self.get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] def get(self): """Support for lazy loading details. Some clients, such as novaclient have the option to lazy load the details, details which can be loaded with this function. """ # set_loaded() first ... so if we have to bail, we know we tried. self.set_loaded(True) if not hasattr(self.manager, 'get'): return new = self.manager.get(self.id) if new: self._add_details(new._info) self._add_details( {'x_request_id': self.manager.client.last_request_id}) def __eq__(self, other): if not isinstance(other, Resource): return NotImplemented # two resources of different types are not equal if not isinstance(other, self.__class__): return False if hasattr(self, 'id') and hasattr(other, 'id'): return self.id == other.id return self._info == other._info def __ne__(self, other): return not self.__eq__(other) def is_loaded(self): return self._loaded def set_loaded(self, val): self._loaded = val def to_dict(self): return copy.deepcopy(self._info) python-magnumclient-2.8.0/magnumclient/common/apiclient/exceptions.py000066400000000000000000000311051323411543400262010ustar00rootroot00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Exception definitions. """ ######################################################################## # # THIS MODULE IS DEPRECATED # # Please refer to # https://etherpad.openstack.org/p/kilo-magnumclient-library-proposals for # the discussion leading to this deprecation. # # We recommend checking out the python-openstacksdk project # (https://launchpad.net/python-openstacksdk) instead. # ######################################################################## import inspect import sys import six from magnumclient.i18n import _ class ClientException(Exception): """The base exception class for all exceptions this library raises.""" pass class ValidationError(ClientException): """Error in validation on API client side.""" pass class UnsupportedVersion(ClientException): """User is trying to use an unsupported version of the API.""" pass class CommandError(ClientException): """Error in CLI tool.""" pass class AuthorizationFailure(ClientException): """Cannot authorize API client.""" pass class ConnectionError(ClientException): """Cannot connect to API service.""" pass class ConnectionRefused(ConnectionError): """Connection refused while trying to connect to API service.""" pass class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( _("Authentication failed. Missing options: %s") % ", ".join(opt_names)) self.opt_names = opt_names class AuthSystemNotFound(AuthorizationFailure): """User has specified an AuthSystem that is not installed.""" def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__( _("AuthSystemNotFound: %r") % auth_system) self.auth_system = auth_system class EndpointException(ClientException): """Something is rotten in Service Catalog.""" pass class EndpointNotFound(EndpointException): """Could not find requested endpoint in Service Catalog.""" pass class AmbiguousEndpoints(EndpointException): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__( _("AmbiguousEndpoints: %r") % endpoints) self.endpoints = endpoints class HttpError(ClientException): """The base exception class for all HTTP exceptions.""" http_status = 0 message = _("HTTP Error") def __init__(self, message=None, details=None, response=None, request_id=None, url=None, method=None, http_status=None): self.http_status = http_status or self.http_status self.message = message or self.message self.details = details self.request_id = request_id self.response = response self.url = url self.method = method formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) if request_id: formatted_string += " (Request-ID: %s)" % request_id super(HttpError, self).__init__(formatted_string) class HTTPRedirection(HttpError): """HTTP Redirection.""" message = _("HTTP Redirection") class HTTPClientError(HttpError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = _("HTTP Client Error") class HttpServerError(HttpError): """Server-side HTTP error. Exception for cases in which the server is aware that it has erred or is incapable of performing the request. """ message = _("HTTP Server Error") class MultipleChoices(HTTPRedirection): """HTTP 300 - Multiple Choices. Indicates multiple options for the resource that the client may follow. """ http_status = 300 message = _("Multiple Choices") class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. The request cannot be fulfilled due to bad syntax. """ http_status = 400 message = _("Bad Request") class Unauthorized(HTTPClientError): """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. """ http_status = 401 message = _("Unauthorized") class PaymentRequired(HTTPClientError): """HTTP 402 - Payment Required. Reserved for future use. """ http_status = 402 message = _("Payment Required") class Forbidden(HTTPClientError): """HTTP 403 - Forbidden. The request was a valid request, but the server is refusing to respond to it. """ http_status = 403 message = _("Forbidden") class NotFound(HTTPClientError): """HTTP 404 - Not Found. The requested resource could not be found but may be available again in the future. """ http_status = 404 message = _("Not Found") class MethodNotAllowed(HTTPClientError): """HTTP 405 - Method Not Allowed. A request was made of a resource using a request method not supported by that resource. """ http_status = 405 message = _("Method Not Allowed") class NotAcceptable(HTTPClientError): """HTTP 406 - Not Acceptable. The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. """ http_status = 406 message = _("Not Acceptable") class ProxyAuthenticationRequired(HTTPClientError): """HTTP 407 - Proxy Authentication Required. The client must first authenticate itself with the proxy. """ http_status = 407 message = _("Proxy Authentication Required") class RequestTimeout(HTTPClientError): """HTTP 408 - Request Timeout. The server timed out waiting for the request. """ http_status = 408 message = _("Request Timeout") class Conflict(HTTPClientError): """HTTP 409 - Conflict. Indicates that the request could not be processed because of conflict in the request, such as an edit conflict. """ http_status = 409 message = _("Conflict") class Gone(HTTPClientError): """HTTP 410 - Gone. Indicates that the resource requested is no longer available and will not be available again. """ http_status = 410 message = _("Gone") class LengthRequired(HTTPClientError): """HTTP 411 - Length Required. The request did not specify the length of its content, which is required by the requested resource. """ http_status = 411 message = _("Length Required") class PreconditionFailed(HTTPClientError): """HTTP 412 - Precondition Failed. The server does not meet one of the preconditions that the requester put on the request. """ http_status = 412 message = _("Precondition Failed") class RequestEntityTooLarge(HTTPClientError): """HTTP 413 - Request Entity Too Large. The request is larger than the server is willing or able to process. """ http_status = 413 message = _("Request Entity Too Large") def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RequestEntityTooLarge, self).__init__(*args, **kwargs) class RequestUriTooLong(HTTPClientError): """HTTP 414 - Request-URI Too Long. The URI provided was too long for the server to process. """ http_status = 414 message = _("Request-URI Too Long") class UnsupportedMediaType(HTTPClientError): """HTTP 415 - Unsupported Media Type. The request entity has a media type which the server or resource does not support. """ http_status = 415 message = _("Unsupported Media Type") class RequestedRangeNotSatisfiable(HTTPClientError): """HTTP 416 - Requested Range Not Satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion. """ http_status = 416 message = _("Requested Range Not Satisfiable") class ExpectationFailed(HTTPClientError): """HTTP 417 - Expectation Failed. The server cannot meet the requirements of the Expect request-header field. """ http_status = 417 message = _("Expectation Failed") class UnprocessableEntity(HTTPClientError): """HTTP 422 - Unprocessable Entity. The request was well-formed but was unable to be followed due to semantic errors. """ http_status = 422 message = _("Unprocessable Entity") class InternalServerError(HttpServerError): """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. """ http_status = 500 message = _("Internal Server Error") # NotImplemented is a python keyword. class HttpNotImplemented(HttpServerError): """HTTP 501 - Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request. """ http_status = 501 message = _("Not Implemented") class BadGateway(HttpServerError): """HTTP 502 - Bad Gateway. The server was acting as a gateway or proxy and received an invalid response from the upstream server. """ http_status = 502 message = _("Bad Gateway") class ServiceUnavailable(HttpServerError): """HTTP 503 - Service Unavailable. The server is currently unavailable. """ http_status = 503 message = _("Service Unavailable") class GatewayTimeout(HttpServerError): """HTTP 504 - Gateway Timeout. The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. """ http_status = 504 message = _("Gateway Timeout") class HttpVersionNotSupported(HttpServerError): """HTTP 505 - HttpVersion Not Supported. The server does not support the HTTP protocol version used in the request. """ http_status = 505 message = _("HTTP Version Not Supported") # _code_map contains all the classes that have http_status attribute. _code_map = dict( (getattr(obj, 'http_status', None), obj) for name, obj in vars(sys.modules[__name__]).items() if inspect.isclass(obj) and getattr(obj, 'http_status', False) ) def from_response(response, method, url): """Returns an instance of :class:`HttpError` or subclass based on response. :param response: instance of `requests.Response` class :param method: HTTP method used for request :param url: URL used for request """ req_id = response.headers.get("x-openstack-request-id") # NOTE(hdd) true for older versions of nova and cinder if not req_id: req_id = response.headers.get("x-compute-request-id") kwargs = { "http_status": response.status_code, "response": response, "method": method, "url": url, "request_id": req_id, } if "retry-after" in response.headers: kwargs["retry_after"] = response.headers["retry-after"] content_type = response.headers.get("Content-Type", "") if content_type.startswith("application/json"): try: body = response.json() except ValueError: pass else: if isinstance(body, dict): error = body.get(list(body)[0]) if isinstance(error, dict): kwargs["message"] = (error.get("message") or error.get("faultstring")) kwargs["details"] = (error.get("details") or six.text_type(body)) elif content_type.startswith("text/"): kwargs["details"] = getattr(response, 'text', '') try: cls = _code_map[response.status_code] except KeyError: if 500 <= response.status_code < 600: cls = HttpServerError elif 400 <= response.status_code < 500: cls = HTTPClientError else: cls = HttpError return cls(**kwargs) python-magnumclient-2.8.0/magnumclient/common/base.py000066400000000000000000000112241323411543400227620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ import copy import six.moves.urllib.parse as urlparse from magnumclient.common.apiclient import base def getid(obj): """Wrapper to get object's ID. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: return obj.id except AttributeError: return obj class Manager(object): """Provides CRUD operations with a particular API.""" resource_class = None def __init__(self, api): self.api = api def _create(self, url, body): resp, body = self.api.json_request('POST', url, body=body) if body: return self.resource_class(self, body) def _format_body_data(self, body, response_key): if response_key: try: data = body[response_key] except KeyError: return [] else: data = body if not isinstance(data, list): data = [data] return data def _list_pagination(self, url, response_key=None, obj_class=None, limit=None): """Retrieve a list of items. The Magnum API is configured to return a maximum number of items per request, (FIXME: see Magnum's api.max_limit option). This iterates over the 'next' link (pagination) in the responses, to get the number of items specified by 'limit'. If 'limit' is None this function will continue pagination until there are no more values to be returned. :param url: a partial URL, e.g. '/nodes' :param response_key: the key to be looked up in response dictionary, e.g. 'nodes' :param obj_class: class for constructing the returned objects. :param limit: maximum number of items to return. If None returns everything. """ if obj_class is None: obj_class = self.resource_class if limit is not None: limit = int(limit) object_list = [] object_count = 0 limit_reached = False while url: resp, body = self.api.json_request('GET', url) data = self._format_body_data(body, response_key) for obj in data: object_list.append(obj_class(self, obj, loaded=True)) object_count += 1 if limit and object_count >= limit: # break the for loop limit_reached = True break # break the while loop and return if limit_reached: break url = body.get('next') if url: # NOTE(lucasagomes): We need to edit the URL to remove # the scheme and netloc url_parts = list(urlparse.urlparse(url)) url_parts[0] = url_parts[1] = '' url = urlparse.urlunparse(url_parts) return object_list def _list(self, url, response_key=None, obj_class=None, body=None): resp, body = self.api.json_request('GET', url) if obj_class is None: obj_class = self.resource_class data = self._format_body_data(body, response_key) return [obj_class(self, res, loaded=True) for res in data if res] def _update(self, url, body=None, method='PATCH', response_key=None): if body: resp, resp_body = self.api.json_request(method, url, body=body) else: resp, resp_body = self.api.raw_request(method, url) # PATCH/PUT requests may not return a body if resp_body: return self.resource_class(self, resp_body) def _delete(self, url): self.api.raw_request('DELETE', url) class Resource(base.Resource): """Represents a particular instance of an object (tenant, user, etc). This is pretty much just a bag for attributes. """ def to_dict(self): return copy.deepcopy(self._info) python-magnumclient-2.8.0/magnumclient/common/cliutils.py000066400000000000000000000374461323411543400237160ustar00rootroot00000000000000# Copyright 2012 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # W0603: Using the global statement # W0621: Redefining name %s from outer scope # pylint: disable=W0603,W0621 from __future__ import print_function import getpass import inspect import os import sys import textwrap import decorator from magnumclient.common.apiclient import exceptions from oslo_utils import encodeutils from oslo_utils import strutils import prettytable import six from six import moves from magnumclient.i18n import _ DEPRECATION_BASE = ('%sThe --%s parameter is deprecated and ' 'will be removed in a future release. Use the ' '<%s> positional parameter %s.') NAME_DEPRECATION_HELP = DEPRECATION_BASE % ('', 'name', 'name', 'instead') NAME_DEPRECATION_WARNING = DEPRECATION_BASE % ( 'WARNING: ', 'name', 'name', 'to avoid seeing this message') CLUSTER_DEPRECATION_HELP = DEPRECATION_BASE % ('', 'cluster', 'cluster', 'instead') CLUSTER_DEPRECATION_WARNING = DEPRECATION_BASE % ( 'WARNING: ', 'cluster', 'cluster', 'to avoid seeing this message') def deprecation_message(preamble, new_name): msg = ('%s This parameter is deprecated and will be removed in a future ' 'release. Use --%s instead.' % (preamble, new_name)) return msg class MissingArgs(Exception): """Supplied arguments are not sufficient for calling a function.""" def __init__(self, missing): self.missing = missing msg = _("Missing arguments: %s") % ", ".join(missing) super(MissingArgs, self).__init__(msg) class DuplicateArgs(Exception): """More than one of the same argument type was passed.""" def __init__(self, param, dupes): msg = _('Duplicate "%(param)s" arguments: %(dupes)s') % { 'param': param, 'dupes': ", ".join(dupes)} super(DuplicateArgs, self).__init__(msg) def validate_args(fn, *args, **kwargs): """Check that the supplied args are sufficient for calling a function. >>> validate_args(lambda a: None) Traceback (most recent call last): ... MissingArgs: Missing argument(s): a >>> validate_args(lambda a, b, c, d: None, 0, c=1) Traceback (most recent call last): ... MissingArgs: Missing argument(s): b, d :param fn: the function to check :param arg: the positional arguments supplied :param kwargs: the keyword arguments supplied """ argspec = inspect.getargspec(fn) num_defaults = len(argspec.defaults or []) required_args = argspec.args[:len(argspec.args) - num_defaults] def isbound(method): return getattr(method, '__self__', None) is not None if isbound(fn): required_args.pop(0) missing = [arg for arg in required_args if arg not in kwargs] missing = missing[len(args):] if missing: raise MissingArgs(missing) def validate_name_args(positional_name, optional_name): if optional_name: print(NAME_DEPRECATION_WARNING) if positional_name and optional_name: raise DuplicateArgs("", (positional_name, optional_name)) def validate_cluster_args(positional_cluster, optional_cluster): if optional_cluster: print(CLUSTER_DEPRECATION_WARNING) if positional_cluster and optional_cluster: raise DuplicateArgs("", (positional_cluster, optional_cluster)) def deprecated(message): """Decorator for marking a call as deprecated by printing a given message. Example: >>> @deprecated("Bay functions are deprecated and should be replaced by " ... "calls to cluster") ... def bay_create(args): ... pass """ @decorator.decorator def wrapper(func, *args, **kwargs): print(message) return func(*args, **kwargs) return wrapper def deprecation_map(dep_map): """Decorator for applying a map of deprecating arguments to a function. The map connects deprecating arguments and their replacements. The shell.py script uses this map to create mutually exclusive argument groups in argparse and also prints a deprecation warning telling the user to switch to the updated argument. NOTE: This decorator MUST be the outermost in the chain of argument decorators to work correctly. Example usage: >>> @deprecation_map({ "old-argument": "new-argument" }) ... @args("old-argument", required=True) ... @args("new-argument", required=True) ... def do_command_line_stuff(): ... pass """ def _decorator(func): if not hasattr(func, 'arguments'): return func func.deprecated_groups = [] for old_param, new_param in dep_map.items(): old_info, new_info = None, None required = False for (args, kwargs) in func.arguments: if old_param in args: old_info = (args, kwargs) # Old arguments shouldn't be required if they were not # previously, so prioritize old requirement if 'required' in kwargs: required = kwargs['required'] # Set to false so argparse doesn't get angry kwargs['required'] = False elif new_param in args: new_info = (args, kwargs) kwargs['required'] = False if old_info and new_info: break # Add a tuple of (old, new, required), which in turn is: # ((old_args, old_kwargs), (new_args, new_kwargs), required) func.deprecated_groups.append((old_info, new_info, required)) # Remove arguments that would be duplicated by the groups we made func.arguments.remove(old_info) func.arguments.remove(new_info) return func return _decorator def arg(*args, **kwargs): """Decorator for CLI args. Example: >>> @arg("name", help="Name of the new entity") ... def entity_create(args): ... pass """ def _decorator(func): add_arg(func, *args, **kwargs) return func return _decorator def env(*args, **kwargs): """Returns the first environment variable set. If all are empty, defaults to '' or keyword arg `default`. """ for arg in args: value = os.environ.get(arg) if value: return value return kwargs.get('default', '') def add_arg(func, *args, **kwargs): """Bind CLI arguments to a shell.py `do_foo` function.""" if not hasattr(func, 'arguments'): func.arguments = [] # NOTE(sirp): avoid dups that can occur when the module is shared across # tests. if (args, kwargs) not in func.arguments: # Because of the semantics of decorator composition if we just append # to the options list positional options will appear to be backwards. func.arguments.insert(0, (args, kwargs)) def unauthenticated(func): """Adds 'unauthenticated' attribute to decorated function. Usage: >>> @unauthenticated ... def mymethod(f): ... pass """ func.unauthenticated = True return func def isunauthenticated(func): """Checks if the function does not require authentication. Mark such functions with the `@unauthenticated` decorator. :returns: bool """ return getattr(func, 'unauthenticated', False) def print_list(objs, fields, formatters=None, sortby_index=0, mixed_case_fields=None, field_labels=None): """Print a list or objects as a table, one row per object. :param objs: iterable of :class:`Resource` :param fields: attributes that correspond to columns, in order :param formatters: `dict` of callables for field formatting :param sortby_index: index of the field for sorting table rows :param mixed_case_fields: fields corresponding to object attributes that have mixed case names (e.g., 'serverId') :param field_labels: Labels to use in the heading of the table, default to fields. """ formatters = formatters or {} mixed_case_fields = mixed_case_fields or [] field_labels = field_labels or fields if len(field_labels) != len(fields): raise ValueError(_("Field labels list %(labels)s has different number " "of elements than fields list %(fields)s"), {'labels': field_labels, 'fields': fields}) if sortby_index is None: kwargs = {} else: kwargs = {'sortby': field_labels[sortby_index]} pt = prettytable.PrettyTable(field_labels) pt.align = 'l' for o in objs: row = [] for field in fields: data = '-' if field in formatters: data = formatters[field](o) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') if data is None: data = '-' row.append(data) pt.add_row(row) if six.PY3: print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) else: print(encodeutils.safe_encode(pt.get_string(**kwargs))) def keys_and_vals_to_strs(dictionary): """Recursively convert a dictionary's keys and values to strings. :param dictionary: dictionary whose keys/vals are to be converted to strs """ def to_str(k_or_v): if isinstance(k_or_v, dict): return keys_and_vals_to_strs(k_or_v) elif isinstance(k_or_v, six.text_type): return str(k_or_v) else: return k_or_v return dict((to_str(k), to_str(v)) for k, v in dictionary.items()) def print_dict(dct, dict_property="Property", wrap=0): """Print a `dict` as a table of two columns. :param dct: `dict` to print :param dict_property: name of the first column :param wrap: wrapping for the second column """ pt = prettytable.PrettyTable([dict_property, 'Value']) pt.align = 'l' for k, v in dct.items(): # convert dict to str to check length if isinstance(v, dict): v = six.text_type(keys_and_vals_to_strs(v)) if wrap > 0: v = textwrap.fill(six.text_type(v), wrap) # if value has a newline, add in multiple rows # e.g. fault with stacktrace if v and isinstance(v, six.string_types) and r'\n' in v: lines = v.strip().split(r'\n') col1 = k for line in lines: pt.add_row([col1, line]) col1 = '' elif isinstance(v, list): val = str([str(i) for i in v]) if val is None: val = '-' pt.add_row([k, val]) else: if v is None: v = '-' pt.add_row([k, v]) if six.PY3: print(encodeutils.safe_encode(pt.get_string()).decode()) else: print(encodeutils.safe_encode(pt.get_string())) def get_password(max_password_prompts=3): """Read password from TTY.""" verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD")) pw = None if hasattr(sys.stdin, "isatty") and sys.stdin.isatty(): # Check for Ctrl-D try: for __ in moves.range(max_password_prompts): pw1 = getpass.getpass("OS Password: ") if verify: pw2 = getpass.getpass("Please verify: ") else: pw2 = pw1 if pw1 == pw2 and pw1: pw = pw1 break except EOFError: pass return pw def service_type(stype): """Adds 'service_type' attribute to decorated function. Usage: .. code-block:: python @service_type('volume') def mymethod(f): ... """ def inner(f): f.service_type = stype return f return inner def get_service_type(f): """Retrieves service type from function.""" return getattr(f, 'service_type', None) def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) def exit(msg=''): if msg: print(msg, file=sys.stderr) sys.exit(1) def _format_field_name(attr): """Format an object attribute in a human-friendly way.""" # Split at ':' and leave the extension name as-is. parts = attr.rsplit(':', 1) name = parts[-1].replace('_', ' ') # Don't title() on mixed case if name.isupper() or name.islower(): name = name.title() parts[-1] = name return ': '.join(parts) def make_field_formatter(attr, filters=None): """Given an object attribute. Return a formatted field name and a formatter suitable for passing to print_list. Optionally pass a dict mapping attribute names to a function. The function will be passed the value of the attribute and should return the string to display. """ filter_ = None if filters: filter_ = filters.get(attr) def get_field(obj): field = getattr(obj, attr, '') if field and filter_: field = filter_(field) return field name = _format_field_name(attr) formatter = get_field return name, formatter def _get_list_table_columns_and_formatters(fields, objs, exclude_fields=(), filters=None): """Check and add fields to output columns. If there is any value in fields that not an attribute of obj, CommandError will be raised. If fields has duplicate values (case sensitive), we will make them unique and ignore duplicate ones. :param fields: A list of string contains the fields to be printed. :param objs: An list of object which will be used to check if field is valid or not. Note, we don't check fields if obj is None or empty. :param exclude_fields: A tuple of string which contains the fields to be excluded. :param filters: A dictionary defines how to get value from fields, this is useful when field's value is a complex object such as dictionary. :return: columns, formatters. columns is a list of string which will be used as table header. formatters is a dictionary specifies how to display the value of the field. They can be [], {}. :raise: magnumclient.common.apiclient.exceptions.CommandError. """ if objs and isinstance(objs, list): obj = objs[0] else: obj = None fields = None columns = [] formatters = {} if fields: non_existent_fields = [] exclude_fields = set(exclude_fields) for field in fields.split(','): if not hasattr(obj, field): non_existent_fields.append(field) continue if field in exclude_fields: continue field_title, formatter = make_field_formatter(field, filters) columns.append(field_title) formatters[field_title] = formatter exclude_fields.add(field) if non_existent_fields: raise exceptions.CommandError( _("Non-existent fields are specified: %s") % non_existent_fields ) return columns, formatters python-magnumclient-2.8.0/magnumclient/common/httpclient.py000066400000000000000000000374321323411543400242370ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import json import logging import os import socket import ssl from keystoneauth1 import adapter from oslo_utils import importutils import six import six.moves.urllib.parse as urlparse from magnumclient import exceptions osprofiler_web = importutils.try_import("osprofiler.web") LOG = logging.getLogger(__name__) USER_AGENT = 'python-magnumclient' CHUNKSIZE = 1024 * 64 # 64kB API_VERSION = '/v1' DEFAULT_API_VERSION = 'latest' def _extract_error_json(body): """Return error_message from the HTTP response body.""" error_json = {} try: body_json = json.loads(body) if 'error_message' in body_json: raw_msg = body_json['error_message'] error_json = json.loads(raw_msg) elif 'error' in body_json: error_body = body_json['error'] error_json = {'faultstring': error_body['title'], 'debuginfo': error_body['message']} else: error_body = body_json['errors'][0] error_json = {'faultstring': error_body['title']} if 'detail' in error_body: error_json['debuginfo'] = error_body['detail'] elif 'description' in error_body: error_json['debuginfo'] = error_body['description'] except ValueError: return {} return error_json class HTTPClient(object): def __init__(self, endpoint, api_version=DEFAULT_API_VERSION, **kwargs): self.endpoint = endpoint self.auth_token = kwargs.get('token') self.auth_ref = kwargs.get('auth_ref') self.api_version = api_version self.connection_params = self.get_connection_params(endpoint, **kwargs) @staticmethod def get_connection_params(endpoint, **kwargs): parts = urlparse.urlparse(endpoint) # trim API version and trailing slash from endpoint path = parts.path path = path.rstrip('/').rstrip(API_VERSION) _args = (parts.hostname, parts.port, path) _kwargs = {'timeout': (float(kwargs.get('timeout')) if kwargs.get('timeout') else 600)} if parts.scheme == 'https': _class = VerifiedHTTPSConnection _kwargs['ca_file'] = kwargs.get('ca_file', None) _kwargs['cert_file'] = kwargs.get('cert_file', None) _kwargs['key_file'] = kwargs.get('key_file', None) _kwargs['insecure'] = kwargs.get('insecure', False) elif parts.scheme == 'http': _class = six.moves.http_client.HTTPConnection else: msg = 'Unsupported scheme: %s' % parts.scheme raise exceptions.EndpointException(msg) return (_class, _args, _kwargs) def get_connection(self): _class = self.connection_params[0] return _class(*self.connection_params[1][0:2], **self.connection_params[2]) def log_curl_request(self, method, url, kwargs): curl = ['curl -i -X %s' % method] for (key, value) in kwargs['headers'].items(): header = '-H \'%s: %s\'' % (key, value) curl.append(header) conn_params_fmt = [ ('key_file', '--key %s'), ('cert_file', '--cert %s'), ('ca_file', '--cacert %s'), ] for (key, fmt) in conn_params_fmt: value = self.connection_params[2].get(key) if value: curl.append(fmt % value) if self.connection_params[2].get('insecure'): curl.append('-k') if 'body' in kwargs: curl.append('-d \'%s\'' % kwargs['body']) curl.append('%s/%s' % (self.endpoint, url.lstrip(API_VERSION))) LOG.debug(' '.join(curl)) @staticmethod def log_http_response(resp, body=None): status = (resp.version / 10.0, resp.status, resp.reason) dump = ['\nHTTP/%.1f %s %s' % status] dump.extend(['%s: %s' % (k, v) for k, v in resp.getheaders()]) dump.append('') if body: dump.extend([body, '']) LOG.debug('\n'.join(dump)) def _make_connection_url(self, url): (_class, _args, _kwargs) = self.connection_params base_url = _args[2] return '%s/%s' % (base_url, url.lstrip('/')) def _http_request(self, url, method, **kwargs): """Send an http request with the specified characteristics. Wrapper around httplib.HTTP(S)Connection.request to handle tasks such as setting headers and error handling. """ # Copy the kwargs so we can reuse the original in case of redirects kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'].setdefault('User-Agent', USER_AGENT) if self.api_version: version_string = 'container-infra %s' % self.api_version kwargs['headers'].setdefault( 'OpenStack-API-Version', version_string) if self.auth_token: kwargs['headers'].setdefault('X-Auth-Token', self.auth_token) self.log_curl_request(method, url, kwargs) conn = self.get_connection() try: conn_url = self._make_connection_url(url) conn.request(method, conn_url, **kwargs) resp = conn.getresponse() except socket.gaierror as e: message = ("Error finding address for %(url)s: %(e)s" % dict(url=url, e=e)) raise exceptions.EndpointNotFound(message) except (socket.error, socket.timeout) as e: endpoint = self.endpoint message = ("Error communicating with %(endpoint)s %(e)s" % dict(endpoint=endpoint, e=e)) raise exceptions.ConnectionRefused(message) body_iter = ResponseBodyIterator(resp) # Read body into string if it isn't obviously image data body_str = None if resp.getheader('content-type', None) != 'application/octet-stream': # decoding byte to string is necessary for Python 3.4 compatibility # this issues has not been found with Python 3.4 unit tests # because the test creates a fake http response of type str # the if statement satisfies test (str) and real (bytes) behavior body_list = [ chunk.decode("utf-8") if isinstance(chunk, bytes) else chunk for chunk in body_iter ] body_str = ''.join(body_list) self.log_http_response(resp, body_str) body_iter = six.StringIO(body_str) else: self.log_http_response(resp) if 400 <= resp.status < 600: LOG.warning("Request returned failure status.") error_json = _extract_error_json(body_str) raise exceptions.from_response( resp, error_json.get('faultstring'), error_json.get('debuginfo'), method, url) elif resp.status in (301, 302, 305): # Redirected. Reissue the request to the new location. return self._http_request(resp['location'], method, **kwargs) elif resp.status == 300: raise exceptions.from_response(resp, method=method, url=url) return resp, body_iter def json_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/json') kwargs['headers'].setdefault('Accept', 'application/json') if 'body' in kwargs: kwargs['body'] = json.dumps(kwargs['body']) resp, body_iter = self._http_request(url, method, **kwargs) content_type = resp.getheader('content-type', None) if resp.status == 204 or resp.status == 205 or content_type is None: return resp, list() if 'application/json' in content_type: body = ''.join([chunk for chunk in body_iter]) try: body = json.loads(body) except ValueError: LOG.error('Could not decode response body as JSON') else: body = None return resp, body def raw_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/octet-stream') return self._http_request(url, method, **kwargs) class VerifiedHTTPSConnection(six.moves.http_client.HTTPSConnection): """httplib-compatibile connection using client-side SSL authentication :see http://code.activestate.com/recipes/ 577548-https-httplib-client-connection-with-certificate-v/ """ def __init__(self, host, port, key_file=None, cert_file=None, ca_file=None, timeout=None, insecure=False): six.moves.http_client.HTTPSConnection.__init__(self, host, port, key_file=key_file, cert_file=cert_file) self.key_file = key_file self.cert_file = cert_file if ca_file is not None: self.ca_file = ca_file else: self.ca_file = self.get_system_ca_file() self.timeout = timeout self.insecure = insecure def connect(self): """Connect to a host on a given (SSL) port. If ca_file is pointing somewhere, use it to check Server Certificate. Redefined/copied and extended from httplib.py:1105 (Python 2.6.x). This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter to ssl.wrap_socket(), which forces SSL to check server certificate against our client certificate. """ sock = socket.create_connection((self.host, self.port), self.timeout) if self._tunnel_host: self.sock = sock self._tunnel() if self.insecure is True: kwargs = {'cert_reqs': ssl.CERT_NONE} else: kwargs = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': self.ca_file} if self.cert_file: kwargs['certfile'] = self.cert_file if self.key_file: kwargs['keyfile'] = self.key_file self.sock = ssl.wrap_socket(sock, **kwargs) @staticmethod def get_system_ca_file(): """Return path to system default CA file.""" # Standard CA file locations for Debian/Ubuntu, RedHat/Fedora, # Suse, FreeBSD/OpenBSD ca_path = ['/etc/ssl/certs/ca-certificates.crt', '/etc/pki/tls/certs/ca-bundle.crt', '/etc/ssl/ca-bundle.pem', '/etc/ssl/cert.pem'] for ca in ca_path: if os.path.exists(ca): return ca return None class SessionClient(adapter.LegacyJsonAdapter): """HTTP client based on Keystone client session.""" def __init__(self, user_agent=USER_AGENT, logger=LOG, api_version=DEFAULT_API_VERSION, *args, **kwargs): self.user_agent = USER_AGENT self.api_version = api_version super(SessionClient, self).__init__(*args, **kwargs) def _http_request(self, url, method, **kwargs): if url.startswith(API_VERSION): url = url[len(API_VERSION):] kwargs.setdefault('user_agent', self.user_agent) kwargs.setdefault('auth', self.auth) kwargs.setdefault('endpoint_override', self.endpoint_override) # Copy the kwargs so we can reuse the original in case of redirects kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'].setdefault('User-Agent', self.user_agent) # NOTE(tovin07): osprofiler_web.get_trace_id_headers does not add any # headers in case if osprofiler is not initialized. if osprofiler_web: kwargs['headers'].update(osprofiler_web.get_trace_id_headers()) if self.api_version: version_string = 'container-infra %s' % self.api_version kwargs['headers'].setdefault( 'OpenStack-API-Version', version_string) endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter.setdefault('interface', self.interface) endpoint_filter.setdefault('service_type', self.service_type) endpoint_filter.setdefault('region_name', self.region_name) resp = self.session.request(url, method, raise_exc=False, **kwargs) if 400 <= resp.status_code < 600: error_json = _extract_error_json(resp.content) raise exceptions.from_response( resp, error_json.get('faultstring'), error_json.get('debuginfo'), method, url) elif resp.status_code in (301, 302, 305): # Redirected. Reissue the request to the new location. location = resp.headers.get('location') resp = self._http_request(location, method, **kwargs) elif resp.status_code == 300: raise exceptions.from_response(resp, method=method, url=url) return resp def json_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/json') kwargs['headers'].setdefault('Accept', 'application/json') if 'body' in kwargs: kwargs['data'] = json.dumps(kwargs.pop('body')) resp = self._http_request(url, method, **kwargs) body = resp.content content_type = resp.headers.get('content-type', None) status = resp.status_code if status == 204 or status == 205 or content_type is None: return resp, list() if 'application/json' in content_type: try: body = resp.json() except ValueError: LOG.error('Could not decode response body as JSON') else: body = None return resp, body def raw_request(self, method, url, **kwargs): kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('Content-Type', 'application/octet-stream') return self._http_request(url, method, **kwargs) class ResponseBodyIterator(object): """A class that acts as an iterator over an HTTP response.""" def __init__(self, resp): self.resp = resp def __iter__(self): while True: yield self.next() def __bool__(self): return hasattr(self, 'items') __nonzero__ = __bool__ # Python 2.x compatibility def next(self): chunk = self.resp.read(CHUNKSIZE) if chunk: return chunk else: raise StopIteration() def _construct_http_client(*args, **kwargs): session = kwargs.pop('session', None) auth = kwargs.pop('auth', None) if session: service_type = kwargs.pop('service_type', 'baremetal') interface = kwargs.pop('endpoint_type', None) region_name = kwargs.pop('region_name', None) return SessionClient(session=session, auth=auth, interface=interface, service_type=service_type, region_name=region_name, service_name=None, user_agent='python-magnumclient') else: return HTTPClient(*args, **kwargs) python-magnumclient-2.8.0/magnumclient/common/utils.py000066400000000000000000000216541323411543400232200ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json import os from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import serialization from cryptography import x509 from cryptography.x509.oid import NameOID from magnumclient import exceptions as exc from magnumclient.i18n import _ def common_filters(marker=None, limit=None, sort_key=None, sort_dir=None): """Generate common filters for any list request. :param marker: entity ID from which to start returning entities. :param limit: maximum number of entities to return. :param sort_key: field to use for sorting. :param sort_dir: direction of sorting: 'asc' or 'desc'. :returns: list of string filters. """ filters = [] if isinstance(limit, int): filters.append('limit=%s' % limit) if marker is not None: filters.append('marker=%s' % marker) if sort_key is not None: filters.append('sort_key=%s' % sort_key) if sort_dir is not None: filters.append('sort_dir=%s' % sort_dir) return filters def split_and_deserialize(string): """Split and try to JSON deserialize a string. Gets a string with the KEY=VALUE format, split it (using '=' as the separator) and try to JSON deserialize the VALUE. :returns: A tuple of (key, value). """ try: key, value = string.split("=", 1) except ValueError: raise exc.CommandError(_('Attributes must be a list of ' 'PATH=VALUE not "%s"') % string) try: value = json.loads(value) except ValueError: pass return (key, value) def args_array_to_patch(op, attributes): patch = [] for attr in attributes: # Sanitize if not attr.startswith('/'): attr = '/' + attr if op in ['add', 'replace']: path, value = split_and_deserialize(attr) patch.append({'op': op, 'path': path, 'value': value}) elif op == "remove": # For remove only the key is needed patch.append({'op': op, 'path': attr}) else: raise exc.CommandError(_('Unknown PATCH operation: %s') % op) return patch def handle_labels(labels): labels = format_labels(labels) if 'mesos_slave_executor_env_file' in labels: environment_variables_data = handle_json_from_file( labels['mesos_slave_executor_env_file']) labels['mesos_slave_executor_env_variables'] = json.dumps( environment_variables_data) return labels def format_labels(lbls, parse_comma=True): '''Reformat labels into dict of format expected by the API.''' if not lbls: return {} if parse_comma: # expect multiple invocations of --labels but fall back # to either , or ; delimited if only one --labels is specified if len(lbls) == 1 and lbls[0].count('=') > 1: lbls = lbls[0].replace(';', ',').split(',') labels = {} for l in lbls: try: (k, v) = l.split(('='), 1) except ValueError: raise exc.CommandError(_('labels must be a list of KEY=VALUE ' 'not %s') % l) if k not in labels: labels[k] = v else: labels[k] += ",%s" % v return labels def print_list_field(field): return lambda obj: ', '.join(getattr(obj, field)) def handle_json_from_file(json_arg): """Attempts to read JSON file by the file url. :param json_arg: May be a file name containing the JSON. :returns: A list or dictionary parsed from JSON. """ try: with open(json_arg, 'r') as f: json_arg = f.read().strip() json_arg = json.loads(json_arg) except IOError as e: err = _("Cannot get JSON from file '%(file)s'. " "Error: %(err)s") % {'err': e, 'file': json_arg} raise exc.InvalidAttribute(err) except ValueError as e: err = (_("For JSON: '%(string)s', error: '%(err)s'") % {'err': e, 'string': json_arg}) raise exc.InvalidAttribute(err) return json_arg def config_cluster(cluster, cluster_template, cfg_dir, force=False): """Return and write configuration for the given cluster.""" if cluster_template.coe == 'kubernetes': return _config_cluster_kubernetes(cluster, cluster_template, cfg_dir, force) elif (cluster_template.coe == 'swarm' or cluster_template.coe == 'swarm-mode'): return _config_cluster_swarm(cluster, cluster_template, cfg_dir, force) def _config_cluster_kubernetes(cluster, cluster_template, cfg_dir, force=False): """Return and write configuration for the given kubernetes cluster.""" cfg_file = "%s/config" % cfg_dir if cluster_template.tls_disabled: cfg = ("apiVersion: v1\n" "clusters:\n" "- cluster:\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " cluster: %(name)s\n" " user: %(name)s\n" " name: %(name)s\n" "current-context: %(name)s\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: %(name)s'\n" % {'name': cluster.name, 'api_address': cluster.api_address}) else: cfg = ("apiVersion: v1\n" "clusters:\n" "- cluster:\n" " certificate-authority: %(cfg_dir)s/ca.pem\n" " server: %(api_address)s\n" " name: %(name)s\n" "contexts:\n" "- context:\n" " cluster: %(name)s\n" " user: %(name)s\n" " name: %(name)s\n" "current-context: %(name)s\n" "kind: Config\n" "preferences: {}\n" "users:\n" "- name: %(name)s\n" " user:\n" " client-certificate: %(cfg_dir)s/cert.pem\n" " client-key: %(cfg_dir)s/key.pem\n" % {'name': cluster.name, 'api_address': cluster.api_address, 'cfg_dir': cfg_dir}) if os.path.exists(cfg_file) and not force: raise exc.CommandError("File %s exists, aborting." % cfg_file) else: f = open(cfg_file, "w") f.write(cfg) f.close() if 'csh' in os.environ['SHELL']: return "setenv KUBECONFIG %s\n" % cfg_file else: return "export KUBECONFIG=%s\n" % cfg_file def _config_cluster_swarm(cluster, cluster_template, cfg_dir, force=False): """Return and write configuration for the given swarm cluster.""" tls = "" if cluster_template.tls_disabled else True if 'csh' in os.environ['SHELL']: result = ("setenv DOCKER_HOST %(docker_host)s\n" "setenv DOCKER_CERT_PATH %(cfg_dir)s\n" "setenv DOCKER_TLS_VERIFY %(tls)s\n" % {'docker_host': cluster.api_address, 'cfg_dir': cfg_dir, 'tls': tls} ) else: result = ("export DOCKER_HOST=%(docker_host)s\n" "export DOCKER_CERT_PATH=%(cfg_dir)s\n" "export DOCKER_TLS_VERIFY=%(tls)s\n" % {'docker_host': cluster.api_address, 'cfg_dir': cfg_dir, 'tls': tls} ) return result def generate_csr_and_key(): """Return a dict with a new csr and key.""" key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend()) csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"Magnum User"), ])).sign(key, hashes.SHA256(), default_backend()) result = { 'csr': csr.public_bytes( encoding=serialization.Encoding.PEM).decode("utf-8"), 'key': key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()).decode("utf-8"), } return result python-magnumclient-2.8.0/magnumclient/exceptions.py000066400000000000000000000054341323411543400227470ustar00rootroot00000000000000# -*- 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. from magnumclient.common.apiclient import exceptions from magnumclient.common.apiclient.exceptions import * # noqa # NOTE(akurilin): This alias is left here since v.0.1.3 to support backwards # compatibility. InvalidEndpoint = EndpointException CommunicationError = ConnectionRefused HTTPBadRequest = BadRequest HTTPInternalServerError = InternalServerError HTTPNotFound = NotFound HTTPServiceUnavailable = ServiceUnavailable class AmbiguousAuthSystem(ClientException): """Could not obtain token and endpoint using provided credentials.""" pass # Alias for backwards compatibility AmbigiousAuthSystem = AmbiguousAuthSystem class InvalidAttribute(ClientException): pass def from_response(response, message=None, traceback=None, method=None, url=None): """Return an HttpError instance based on response from httplib/requests.""" error_body = {} if message: error_body['message'] = message if traceback: error_body['details'] = traceback if hasattr(response, 'status') and not hasattr(response, 'status_code'): # NOTE(akurilin): These modifications around response object give # ability to get all necessary information in method `from_response` # from common code, which expecting response object from `requests` # library instead of object from `httplib/httplib2` library. response.status_code = response.status response.headers = { 'Content-Type': response.getheader('content-type', "")} if hasattr(response, 'status_code'): # NOTE(hongbin): This allows SessionClient to handle faultstring. response.json = lambda: {'error': error_body} if (response.headers.get('Content-Type', '').startswith('text/') and not hasattr(response, 'text')): # NOTE(clif_h): There seems to be a case in the # common.apiclient.exceptions module where if the # content-type of the response is text/* then it expects # the response to have a 'text' attribute, but that # doesn't always seem to necessarily be the case. # This is to work around that problem. response.text = '' return exceptions.from_response(response, method, url) python-magnumclient-2.8.0/magnumclient/i18n.py000066400000000000000000000015151323411543400213410ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """oslo_i18n integration module for magnumclient. See https://docs.openstack.org/oslo.i18n/latest/user/usage.html . """ import oslo_i18n _translators = oslo_i18n.TranslatorFactory(domain='magnumclient') # The primary translation function using the well-known name "_" _ = _translators.primary python-magnumclient-2.8.0/magnumclient/osc/000077500000000000000000000000001323411543400207725ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/osc/__init__.py000066400000000000000000000000001323411543400230710ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/osc/plugin.py000066400000000000000000000034551323411543400226510ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 from osc_lib import utils LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '1' API_VERSION_OPTION = 'os_container_infra_api_version' API_NAME = 'container_infra' API_VERSIONS = { '1': 'magnumclient.v1.client.Client', } def make_client(instance): """Returns a magnum client.""" magnum_client = utils.get_client_class( API_NAME, instance._api_version[API_NAME], API_VERSIONS) LOG.debug('Instantiating magnum client: %s', magnum_client) client = magnum_client(session=instance.session, region_name=instance._region_name, interface=instance._interface, insecure=instance._insecure, ca_cert=instance._cacert) return client def build_option_parser(parser): """Hook to add global options""" parser.add_argument( '--os-container-infra-api-version', metavar='', default=utils.env( 'OS_CONTAINER_INFRA_API_VERSION', default=DEFAULT_API_VERSION), help='Container-Infra API version, default=' + DEFAULT_API_VERSION + ' (Env: OS_CONTAINER_INFRA_API_VERSION)') return parser python-magnumclient-2.8.0/magnumclient/osc/v1/000077500000000000000000000000001323411543400213205ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/osc/v1/__init__.py000066400000000000000000000000001323411543400234170ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/osc/v1/cluster_templates.py000066400000000000000000000357441323411543400254460ustar00rootroot00000000000000# Copyright 2016 EasyStack. 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 magnumclient.common import utils as magnum_utils from magnumclient.exceptions import InvalidAttribute from magnumclient.i18n import _ from osc_lib.command import command from osc_lib import utils as osc_utils from oslo_log import log as logging CLUSTER_TEMPLATE_ATTRIBUTES = [ 'insecure_registry', 'labels', 'updated_at', 'floating_ip_enabled', 'fixed_subnet', 'master_flavor_id', 'uuid', 'no_proxy', 'https_proxy', 'tls_disabled', 'keypair_id', 'public', 'http_proxy', 'docker_volume_size', 'server_type', 'external_network_id', 'cluster_distro', 'image_id', 'volume_driver', 'registry_enabled', 'docker_storage_driver', 'apiserver_port', 'name', 'created_at', 'network_driver', 'fixed_network', 'coe', 'flavor_id', 'master_lb_enabled', 'dns_nameserver' ] def _show_cluster_template(cluster_template): del cluster_template._info['links'] for field in cluster_template._info: if cluster_template._info[field] is None: setattr(cluster_template, field, '-') columns = CLUSTER_TEMPLATE_ATTRIBUTES return columns, osc_utils.get_item_properties(cluster_template, columns) class CreateClusterTemplate(command.ShowOne): """Create a Cluster Template.""" _description = _("Create a Cluster Template.") def get_parser(self, prog_name): parser = super(CreateClusterTemplate, self).get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_('Name of the cluster template to create.')) parser.add_argument( '--coe', required=True, metavar='', help=_('Specify the Container Orchestration Engine to use.')) parser.add_argument( '--image', required=True, metavar='', help=_('The name or UUID of the base image to customize for the ' 'Cluster.')) parser.add_argument( '--external-network', dest='external_network', required=True, metavar='', help=_('The external Neutron network name or UUID to connect to ' 'this Cluster Template.')) parser.add_argument( '--keypair', metavar='', help=_('The name or UUID of the SSH keypair to load into the ' 'Cluster nodes.')) parser.add_argument( '--fixed-network', dest='fixed_network', metavar='', help=_('The private Neutron network name to connect to this ' 'Cluster model.')) parser.add_argument( '--fixed-subnet', dest='fixed_subnet', metavar='', help=_('The private Neutron subnet name to connect to Cluster.')) parser.add_argument( '--network-driver', dest='network_driver', metavar='', help=_('The network driver name for instantiating container ' 'networks.')) parser.add_argument( '--volume-driver', dest='volume_driver', metavar='', help=_('The volume driver name for instantiating container ' 'volume.')) parser.add_argument( '--dns-nameserver', dest='dns_nameserver', metavar='', default='8.8.8.8', help=_('The DNS nameserver to use for this cluster template.')) parser.add_argument( '--flavor', metavar='', default='m1.medium', help=_('The nova flavor name or UUID to use when launching the ' 'Cluster.')) parser.add_argument( '--master-flavor', dest='master_flavor', metavar='', help=_('The nova flavor name or UUID to use when launching the ' 'master node of the Cluster.')) parser.add_argument( '--docker-volume-size', dest='docker_volume_size', metavar='', type=int, help=_('Specify the number of size in GB for the docker volume ' 'to use.')) parser.add_argument( '--docker-storage-driver', dest='docker_storage_driver', metavar='', default='devicemapper', help=_('Select a docker storage driver. Supported: devicemapper, ' 'overlay. Default: devicemapper')) parser.add_argument( '--http-proxy', dest='http_proxy', metavar='', help=_('The http_proxy address to use for nodes in Cluster.')) parser.add_argument( '--https-proxy', dest='https_proxy', metavar='', help=_('The https_proxy address to use for nodes in Cluster.')) parser.add_argument( '--no-proxy', dest='no_proxy', metavar='', help=_('The no_proxy address to use for nodes in Cluster.')) parser.add_argument( '--labels', metavar='', action='append', default=[], help=_('Arbitrary labels in the form of key=value pairs to ' 'associate with a cluster template. May be used multiple ' 'times.')) parser.add_argument( '--tls-disabled', dest='tls_disabled', action='store_true', default=False, help=_('Disable TLS in the Cluster.')) parser.add_argument( '--public', action='store_true', default=False, help=_('Make cluster template public.')) parser.add_argument( '--registry-enabled', dest='registry_enabled', action='store_true', default=False, help=_('Enable docker registry in the Cluster')) parser.add_argument( '--server-type', dest='server_type', metavar='', default='vm', help=_('Specify the server type to be used for example vm. ' 'For this release default server type will be vm.')) parser.add_argument( '--master-lb-enabled', dest='master_lb_enabled', action='store_true', default=False, help=_('Indicates whether created Clusters should have a load ' 'balancer for master nodes or not.')) parser.add_argument( '--floating-ip-enabled', dest='floating_ip_enabled', default=[], action='append_const', const=True, help=_('Indicates whether created Clusters should have a ' 'floating ip.')) parser.add_argument( '--floating-ip-disabled', dest='floating_ip_enabled', action='append_const', const=False, help=_('Disables floating ip creation on the new Cluster')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra args = { 'name': parsed_args.name, 'image_id': parsed_args.image, 'keypair_id': parsed_args.keypair, 'external_network_id': parsed_args.external_network, 'coe': parsed_args.coe, 'fixed_network': parsed_args.fixed_network, 'fixed_subnet': parsed_args.fixed_subnet, 'network_driver': parsed_args.network_driver, 'volume_driver': parsed_args.volume_driver, 'dns_nameserver': parsed_args.dns_nameserver, 'flavor_id': parsed_args.flavor, 'master_flavor_id': parsed_args.master_flavor, 'docker_volume_size': parsed_args.docker_volume_size, 'docker_storage_driver': parsed_args.docker_storage_driver, 'http_proxy': parsed_args.http_proxy, 'https_proxy': parsed_args.https_proxy, 'no_proxy': parsed_args.no_proxy, 'labels': magnum_utils.handle_labels(parsed_args.labels), 'tls_disabled': parsed_args.tls_disabled, 'public': parsed_args.public, 'registry_enabled': parsed_args.registry_enabled, 'server_type': parsed_args.server_type, 'master_lb_enabled': parsed_args.master_lb_enabled, } if len(parsed_args.floating_ip_enabled) > 1: raise InvalidAttribute('--floating-ip-enabled and ' '--floating-ip-disabled are ' 'mutually exclusive and ' 'should be specified only once.') elif len(parsed_args.floating_ip_enabled) == 1: args['floating_ip_enabled'] = parsed_args.floating_ip_enabled[0] ct = mag_client.cluster_templates.create(**args) print("Request to create cluster template %s accepted" % parsed_args.name) return _show_cluster_template(ct) class DeleteClusterTemplate(command.Command): """Delete a Cluster Template.""" _description = _("Delete a Cluster Template.") def get_parser(self, prog_name): parser = super(DeleteClusterTemplate, self).get_parser(prog_name) parser.add_argument( 'cluster-templates', metavar='', nargs='+', help=_('ID or name of the (cluster template)s to delete.')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra for cluster_template in getattr(parsed_args, 'cluster-templates'): try: mag_client.cluster_templates.delete(cluster_template) print( "Request to delete cluster template %s has been accepted." % cluster_template) except Exception as e: print("Delete for cluster template " "%(cluster_template)s failed: %(e)s" % {'cluster_template': cluster_template, 'e': e}) class ListTemplateCluster(command.Lister): """List Cluster Templates.""" _description = _("List Cluster Templates.") log = logging.getLogger(__name__ + ".ListTemplateCluster") def get_parser(self, prog_name): parser = super(ListTemplateCluster, self).get_parser(prog_name) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of cluster templates to return')) parser.add_argument( '--sort-key', metavar='', help=_('Column to sort results by')) parser.add_argument( '--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) parser.add_argument( '--fields', default=None, metavar='', help=_('Comma-separated list of fields to display. ' 'Available fields: uuid, name, coe, image_id, public, ' 'link, apiserver_port, server_type, tls_disabled, ' 'registry_enabled' ) ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra columns = ['uuid', 'name'] cts = mag_client.cluster_templates.list(limit=parsed_args.limit, sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir) return ( columns, (osc_utils.get_item_properties(ct, columns) for ct in cts) ) class ShowClusterTemplate(command.ShowOne): """Show a Cluster Template.""" _description = _("Show a Cluster Template.") log = logging.getLogger(__name__ + ".ShowClusterTemplate") def get_parser(self, prog_name): parser = super(ShowClusterTemplate, self).get_parser(prog_name) parser.add_argument( 'cluster-template', metavar='', help=_('ID or name of the cluster template to show.')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra ct = getattr(parsed_args, 'cluster-template', None) cluster_template = mag_client.cluster_templates.get(ct) return _show_cluster_template(cluster_template) class UpdateClusterTemplate(command.ShowOne): """Update a Cluster Template.""" log = logging.getLogger(__name__ + ".UpdateClusterTemplate") def get_parser(self, prog_name): parser = super(UpdateClusterTemplate, self).get_parser(prog_name) parser.add_argument( 'cluster-template', metavar='', help=_('The name or UUID of cluster template to update')) parser.add_argument( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operations: one of 'add', 'replace' or 'remove'")) parser.add_argument( 'attributes', metavar='', nargs='+', action='append', default=[], help=_( "Attributes to add/replace or remove (only PATH is necessary " "on remove)")) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra patch = magnum_utils.args_array_to_patch(parsed_args.op, parsed_args.attributes[0]) name = getattr(parsed_args, 'cluster-template', None) ct = mag_client.cluster_templates.update(name, patch) return _show_cluster_template(ct) python-magnumclient-2.8.0/magnumclient/osc/v1/clusters.py000066400000000000000000000277671323411543400235610ustar00rootroot00000000000000# Copyright 2016 EasyStack. 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 os from magnumclient.common import utils as magnum_utils from magnumclient import exceptions from magnumclient.i18n import _ from osc_lib.command import command from osc_lib import utils CLUSTER_ATTRIBUTES = [ 'status', 'cluster_template_id', 'node_addresses', 'uuid', 'stack_id', 'status_reason', 'created_at', 'updated_at', 'coe_version', 'labels', 'faults', 'keypair', 'api_address', 'master_addresses', 'create_timeout', 'node_count', 'discovery_url', 'master_count', 'container_version', 'name', 'master_flavor_id', 'flavor_id', ] class CreateCluster(command.Command): _description = _("Create a cluster") def get_parser(self, prog_name): parser = super(CreateCluster, self).get_parser(prog_name) # NOTE: All arguments are positional and, if not provided # with a default, required. parser.add_argument('--cluster-template', dest='cluster_template', required=True, metavar='', help='ID or name of the cluster template.') parser.add_argument('--discovery-url', dest='discovery_url', metavar='', help=('Specifies custom delivery url for ' 'node discovery.')) parser.add_argument('--docker-volume-size', dest='docker_volume_size', type=int, metavar='', help=('The size in GB for the docker volume to ' 'use.')) parser.add_argument('--labels', metavar='', action='append', help=_('Arbitrary labels in the form of key=value' 'pairs to associate with a cluster ' 'template. May be used multiple times.')) parser.add_argument('--keypair', default=None, metavar='', help='UUID or name of the keypair to use.') parser.add_argument('--master-count', dest='master_count', type=int, default=1, metavar='', help='The number of master nodes for the cluster.') parser.add_argument('name', metavar='', help='Name of the cluster to create.') parser.add_argument('--node-count', dest='node_count', type=int, default=1, metavar='', help='The cluster node count.') parser.add_argument('--timeout', type=int, default=60, metavar='', help=('The timeout for cluster creation time. The ' 'default is 60 minutes.')) parser.add_argument( '--master-flavor', dest='master_flavor', metavar='', help=_('The nova flavor name or UUID to use when launching the ' 'master node of the Cluster.')) parser.add_argument( '--flavor', metavar='', help=_('The nova flavor name or UUID to use when launching the ' 'Cluster.')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra args = { 'cluster_template_id': parsed_args.cluster_template, 'create_timeout': parsed_args.timeout, 'discovery_url': parsed_args.discovery_url, 'docker_volume_size': parsed_args.docker_volume_size, 'keypair': parsed_args.keypair, 'master_count': parsed_args.master_count, 'name': parsed_args.name, 'node_count': parsed_args.node_count, 'master_flavor_id': parsed_args.master_flavor, 'flavor_id': parsed_args.flavor, } if parsed_args.labels is not None: args['labels'] = magnum_utils.handle_labels(parsed_args.labels) cluster = mag_client.clusters.create(**args) print("Request to create cluster %s accepted" % cluster.uuid) class DeleteCluster(command.Command): _description = _("Delete a cluster") def get_parser(self, prog_name): parser = super(DeleteCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', nargs='+', metavar='', help='ID or name of the cluster(s) to delete.') return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra for cluster in parsed_args.cluster: mag_client.clusters.delete(cluster) print("Request to delete cluster %s has been accepted." % cluster) class ListCluster(command.Lister): _description = _("List clusters") def get_parser(self, prog_name): parser = super(ListCluster, self).get_parser(prog_name) parser.add_argument( '--limit', metavar='', type=int, help=_('Maximum number of clusters to return')) parser.add_argument( '--sort-key', metavar='', help=_('Column to sort results by')) parser.add_argument( '--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra columns = [ 'uuid', 'name', 'keypair', 'node_count', 'master_count', 'status'] clusters = mag_client.clusters.list(limit=parsed_args.limit, sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir) return ( columns, (utils.get_item_properties(c, columns) for c in clusters) ) class ShowCluster(command.ShowOne): _description = _("Show a Cluster") def get_parser(self, prog_name): parser = super(ShowCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', metavar='', help=_('ID or name of the cluster to show.') ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) columns = CLUSTER_ATTRIBUTES mag_client = self.app.client_manager.container_infra cluster = mag_client.clusters.get(parsed_args.cluster) return (columns, utils.get_item_properties(cluster, columns)) class UpdateCluster(command.Command): _description = _("Update a Cluster") def get_parser(self, prog_name): parser = super(UpdateCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', metavar='', help=_('The name or UUID of cluster to update')) parser.add_argument( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operations: one of 'add', 'replace' or 'remove'")) parser.add_argument( 'attributes', metavar='', nargs='+', action='append', default=[], help=_( "Attributes to add/replace or remove (only PATH is necessary " "on remove)")) parser.add_argument( '--rollback', action='store_true', dest='rollback', default=False, help=_('Rollback cluster on update failure.')) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra patch = magnum_utils.args_array_to_patch(parsed_args.op, parsed_args.attributes[0]) mag_client.clusters.update(parsed_args.cluster, patch) print("Request to update cluster %s has been accepted." % parsed_args.cluster) class ConfigCluster(command.Command): _description = _("Get Configuration for a Cluster") def get_parser(self, prog_name): parser = super(ConfigCluster, self).get_parser(prog_name) parser.add_argument( 'cluster', metavar='', help=_('The name or UUID of cluster to update')) parser.add_argument( '--dir', metavar='', default='.', help=_('Directory to save the certificate and config files.')) parser.add_argument( '--force', action='store_true', dest='force', default=False, help=_('Overwrite files if existing.')) return parser def take_action(self, parsed_args): """Configure native client to access cluster. You can source the output of this command to get the native client of the corresponding COE configured to access the cluster. """ self.log.debug("take_action(%s)", parsed_args) mag_client = self.app.client_manager.container_infra parsed_args.dir = os.path.abspath(parsed_args.dir) cluster = mag_client.clusters.get(parsed_args.cluster) if cluster.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE', 'ROLLBACK_COMPLETE'): raise exceptions.CommandError("cluster in status %s" % cluster.status) cluster_template = mag_client.cluster_templates.get( cluster.cluster_template_id) opts = { 'cluster_uuid': cluster.uuid, } if not cluster_template.tls_disabled: tls = magnum_utils.generate_csr_and_key() tls['ca'] = mag_client.certificates.get(**opts).pem opts['csr'] = tls['csr'] tls['cert'] = mag_client.certificates.create(**opts).pem for k in ('key', 'cert', 'ca'): fname = "%s/%s.pem" % (parsed_args.dir, k) if os.path.exists(fname) and not parsed_args.force: raise Exception("File %s exists, aborting." % fname) else: f = open(fname, "w") f.write(tls[k]) f.close() print(magnum_utils.config_cluster(cluster, cluster_template, parsed_args.dir, force=parsed_args.force)) python-magnumclient-2.8.0/magnumclient/osc/v1/mservices.py000066400000000000000000000025711323411543400236770ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 from osc_lib.command import command from osc_lib import utils def _get_client(obj, parsed_args): obj.log.debug("take_action(%s)" % parsed_args) return obj.app.client_manager.container_infra class ListService(command.Lister): """Print a list of Magnum services.""" log = logging.getLogger(__name__ + ".ListService") def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) return parser def take_action(self, parsed_args): client = _get_client(self, parsed_args) services = client.mservices.list() columns = ('id', 'host', 'binary', 'state', 'disabled', 'disabled_reason', 'created_at', 'updated_at') return (columns, (utils.get_item_properties(service, columns) for service in services)) python-magnumclient-2.8.0/magnumclient/shell.py000066400000000000000000000631261323411543400216770ustar00rootroot00000000000000# Copyright 2014 # The Cloudscaling Group, 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. ### # This code is taken from python-novaclient. Goal is minimal modification. ### """ Command-line interface to the OpenStack Magnum API. """ from __future__ import print_function import argparse import logging import os import sys from oslo_utils import encodeutils from oslo_utils import importutils from oslo_utils import strutils import six profiler = importutils.try_import("osprofiler.profiler") HAS_KEYRING = False all_errors = ValueError try: import keyring HAS_KEYRING = True try: if isinstance(keyring.get_keyring(), keyring.backend.GnomeKeyring): import gnomekeyring all_errors = (ValueError, gnomekeyring.IOError, gnomekeyring.NoKeyringDaemonError) except Exception: pass except ImportError: pass from magnumclient.common import cliutils from magnumclient import exceptions as exc from magnumclient.i18n import _ from magnumclient.v1 import client as client_v1 from magnumclient.v1 import shell as shell_v1 from magnumclient import version LATEST_API_VERSION = ('1', 'latest') DEFAULT_INTERFACE = 'public' DEFAULT_SERVICE_TYPE = 'container-infra' logger = logging.getLogger(__name__) def positive_non_zero_float(text): if text is None: return None try: value = float(text) except ValueError: msg = "%s must be a float" % text raise argparse.ArgumentTypeError(msg) if value <= 0: msg = "%s must be greater than 0" % text raise argparse.ArgumentTypeError(msg) return value class MagnumClientArgumentParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): super(MagnumClientArgumentParser, self).__init__(*args, **kwargs) def error(self, message): """error(message: string) Prints a usage message incorporating the message to stderr and exits. """ self.print_usage(sys.stderr) # FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value choose_from = ' (choose from' progparts = self.prog.partition(' ') self.exit(2, "error: %(errmsg)s\nTry '%(mainp)s help %(subp)s'" " for more information.\n" % {'errmsg': message.split(choose_from)[0], 'mainp': progparts[0], 'subp': progparts[2]}) class OpenStackMagnumShell(object): def get_base_parser(self): parser = MagnumClientArgumentParser( prog='magnum', description=__doc__.strip(), epilog='See "magnum help COMMAND" ' 'for help on a specific command.', add_help=False, formatter_class=OpenStackHelpFormatter, ) # Global arguments parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS) parser.add_argument('--version', action='version', version=version.version_info.version_string()) parser.add_argument('--debug', default=False, action='store_true', help=_("Print debugging output.")) parser.add_argument('--os-cache', default=strutils.bool_from_string( cliutils.env('OS_CACHE', default=False)), action='store_true', help=_("Use the auth token cache. Defaults to " "False if env[OS_CACHE] is not set.")) parser.add_argument('--os-region-name', metavar='', default=os.environ.get('OS_REGION_NAME'), help=_('Region name. ' 'Default=env[OS_REGION_NAME].')) # TODO(mattf) - add get_timings support to Client # parser.add_argument('--timings', # default=False, # action='store_true', # help="Print call timing info") # TODO(mattf) - use timeout # parser.add_argument('--timeout', # default=600, # metavar='', # type=positive_non_zero_float, # help="Set HTTP call timeout (in seconds)") parser.add_argument('--os-auth-url', metavar='', default=cliutils.env('OS_AUTH_URL', default=None), help=_('Defaults to env[OS_AUTH_URL].')) parser.add_argument('--os-user-id', metavar='', default=cliutils.env('OS_USER_ID', default=None), help=_('Defaults to env[OS_USER_ID].')) parser.add_argument('--os-username', metavar='', default=cliutils.env('OS_USERNAME', default=None), help=_('Defaults to env[OS_USERNAME].')) parser.add_argument('--os-user-domain-id', metavar='', default=cliutils.env('OS_USER_DOMAIN_ID', default=None), help=_('Defaults to env[OS_USER_DOMAIN_ID].')) parser.add_argument('--os-user-domain-name', metavar='', default=cliutils.env('OS_USER_DOMAIN_NAME', default=None), help=_('Defaults to env[OS_USER_DOMAIN_NAME].')) parser.add_argument('--os-project-id', metavar='', default=cliutils.env('OS_PROJECT_ID', default=None), help=_('Defaults to env[OS_PROJECT_ID].')) parser.add_argument('--os-project-name', metavar='', default=cliutils.env('OS_PROJECT_NAME', default=None), help=_('Defaults to env[OS_PROJECT_NAME].')) parser.add_argument('--os-tenant-id', metavar='', default=cliutils.env('OS_TENANT_ID', default=None), help=argparse.SUPPRESS) parser.add_argument('--os-tenant-name', metavar='', default=cliutils.env('OS_TENANT_NAME', default=None), help=argparse.SUPPRESS) parser.add_argument('--os-project-domain-id', metavar='', default=cliutils.env('OS_PROJECT_DOMAIN_ID', default=None), help=_('Defaults to env[OS_PROJECT_DOMAIN_ID].')) parser.add_argument('--os-project-domain-name', metavar='', default=cliutils.env('OS_PROJECT_DOMAIN_NAME', default=None), help=_('Defaults to ' 'env[OS_PROJECT_DOMAIN_NAME].')) parser.add_argument('--os-token', metavar='', default=cliutils.env('OS_TOKEN', default=None), help=_('Defaults to env[OS_TOKEN].')) parser.add_argument('--os-password', metavar='', default=cliutils.env('OS_PASSWORD', default=None), help=_('Defaults to env[OS_PASSWORD].')) parser.add_argument('--service-type', metavar='', help=_('Defaults to container-infra for all ' 'actions.')) parser.add_argument('--service_type', help=argparse.SUPPRESS) parser.add_argument('--endpoint-type', metavar='', default=cliutils.env('OS_ENDPOINT_TYPE', default=None), help=argparse.SUPPRESS) parser.add_argument('--os-endpoint-type', metavar='', default=cliutils.env('OS_ENDPOINT_TYPE', default=None), help=_('Defaults to env[OS_ENDPOINT_TYPE]')) parser.add_argument('--os-interface', metavar='', default=cliutils.env( 'OS_INTERFACE', default=DEFAULT_INTERFACE), help=argparse.SUPPRESS) parser.add_argument('--os-cloud', metavar='', default=cliutils.env('OS_CLOUD', default=None), help=_('Defaults to env[OS_CLOUD].')) # NOTE(dtroyer): We can't add --endpoint_type here due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. I'm leaving this here for doc purposes. # parser.add_argument('--endpoint_type', # help=argparse.SUPPRESS) parser.add_argument('--magnum-api-version', metavar='', default=cliutils.env( 'MAGNUM_API_VERSION', default='latest'), help=_('Accepts "api", ' 'defaults to env[MAGNUM_API_VERSION].')) parser.add_argument('--magnum_api_version', help=argparse.SUPPRESS) parser.add_argument('--os-cacert', metavar='', default=cliutils.env('OS_CACERT', default=None), help=_('Specify a CA bundle file to use in ' 'verifying a TLS (https) server ' 'certificate. Defaults to env[OS_CACERT].')) parser.add_argument('--os-endpoint-override', metavar='', default=cliutils.env('OS_ENDPOINT_OVERRIDE', default=None), help=_("Use this API endpoint instead of the " "Service Catalog.")) parser.add_argument('--bypass-url', metavar='', default=cliutils.env('BYPASS_URL', default=None), dest='bypass_url', help=argparse.SUPPRESS) parser.add_argument('--bypass_url', help=argparse.SUPPRESS) parser.add_argument('--insecure', default=cliutils.env('MAGNUMCLIENT_INSECURE', default=False), action='store_true', help=_("Do not verify https connections")) if profiler: parser.add_argument('--profile', metavar='HMAC_KEY', default=cliutils.env('OS_PROFILE', default=None), help='HMAC key to use for encrypting context ' 'data for performance profiling of operation. ' 'This key should be the value of the HMAC key ' 'configured for the OSprofiler middleware in ' 'magnum; it is specified in the Magnum ' 'configuration file at ' '"/etc/magnum/magnum.conf". ' 'Without the key, profiling will not be ' 'triggered even if OSprofiler is enabled on ' 'the server side.') return parser def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='') try: actions_modules = { '1': shell_v1.COMMAND_MODULES }[version] except KeyError: actions_modules = shell_v1.COMMAND_MODULES for actions_module in actions_modules: self._find_actions(subparsers, actions_module) self._find_actions(subparsers, self) self._add_bash_completion_subparser(subparsers) return parser def _add_bash_completion_subparser(self, subparsers): subparser = ( subparsers.add_parser('bash_completion', add_help=False, formatter_class=OpenStackHelpFormatter) ) self.subcommands['bash_completion'] = subparser subparser.set_defaults(func=self.do_bash_completion) def _find_actions(self, subparsers, actions_module): for attr in (a for a in dir(actions_module) if a.startswith('do_')): # I prefer to be hyphen-separated instead of underscores. command = attr[3:].replace('_', '-') callback = getattr(actions_module, attr) desc = callback.__doc__ or '' action_help = desc.strip() arguments = getattr(callback, 'arguments', []) group_args = getattr(callback, 'deprecated_groups', []) subparser = ( subparsers.add_parser(command, help=action_help, description=desc, add_help=False, formatter_class=OpenStackHelpFormatter) ) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS,) self.subcommands[command] = subparser for (old_info, new_info, req) in group_args: group = subparser.add_mutually_exclusive_group(required=req) group.add_argument(*old_info[0], **old_info[1]) group.add_argument(*new_info[0], **new_info[1]) for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) def setup_debugging(self, debug): if debug: streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" # Set up the root logger to debug so that the submodules can # print debug messages logging.basicConfig(level=logging.DEBUG, format=streamformat) else: streamformat = "%(levelname)s %(message)s" logging.basicConfig(level=logging.CRITICAL, format=streamformat) def _check_version(self, api_version): if api_version == 'latest': return LATEST_API_VERSION else: try: versions = tuple(int(i) for i in api_version.split('.')) except ValueError: versions = () if len(versions) == 1: # Default value of magnum_api_version is '1'. # If user not specify the value of api version, not passing # headers at all. magnum_api_version = None elif len(versions) == 2: magnum_api_version = api_version # In the case of '1.0' if versions[1] == 0: magnum_api_version = None else: msg = _("The requested API version %(ver)s is an unexpected " "format. Acceptable formats are 'X', 'X.Y', or the " "literal string '%(latest)s'." ) % {'ver': api_version, 'latest': 'latest'} raise exc.CommandError(msg) api_major_version = versions[0] return (api_major_version, magnum_api_version) def _ensure_auth_info(self, args): if not cliutils.isunauthenticated(args.func): if (not (args.os_token and (args.os_auth_url or args.os_endpoint_override)) and not args.os_cloud ): if not (args.os_username or args.os_user_id): raise exc.CommandError( "You must provide a username via either --os-username " "or via env[OS_USERNAME]" ) if not args.os_password: raise exc.CommandError( "You must provide a password via either " "--os-password, env[OS_PASSWORD], or prompted " "response" ) if (not args.os_project_name and not args.os_project_id): raise exc.CommandError( "You must provide a project name or project id via " "--os-project-name, --os-project-id, " "env[OS_PROJECT_NAME] or env[OS_PROJECT_ID]" ) if not args.os_auth_url: raise exc.CommandError( "You must provide an auth url via either " "--os-auth-url or via env[OS_AUTH_URL]" ) def main(self, argv): # NOTE(Christoph Jansen): With Python 3.4 argv somehow becomes a Map. # This hack fixes it. argv = list(argv) # Parse args once to find version and debug settings parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self.setup_debugging(options.debug) # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' # build available subcommands based on version (api_major_version, magnum_api_version) = ( self._check_version(options.magnum_api_version)) subcommand_parser = ( self.get_subcommand_parser(api_major_version) ) self.parser = subcommand_parser if options.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) # Short-circuit and deal with help right away. # NOTE(jamespage): args.func is not guaranteed with python >= 3.4 if not hasattr(args, 'func') or args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if not args.service_type: args.service_type = DEFAULT_SERVICE_TYPE if args.bypass_url: args.os_endpoint_override = args.bypass_url args.os_project_id = (args.os_project_id or args.os_tenant_id) args.os_project_name = (args.os_project_name or args.os_tenant_name) self._ensure_auth_info(args) try: client = { '1': client_v1, }[api_major_version] except KeyError: client = client_v1 args.os_endpoint_type = (args.os_endpoint_type or args.endpoint_type) if args.os_endpoint_type: args.os_interface = args.os_endpoint_type if args.os_interface.endswith('URL'): args.os_interface = args.os_interface[:-3] kwargs = {} if profiler: kwargs["profile"] = args.profile self.cs = client.Client( cloud=args.os_cloud, user_id=args.os_user_id, username=args.os_username, password=args.os_password, auth_token=args.os_token, project_id=args.os_project_id, project_name=args.os_project_name, user_domain_id=args.os_user_domain_id, user_domain_name=args.os_user_domain_name, project_domain_id=args.os_project_domain_id, project_domain_name=args.os_project_domain_name, auth_url=args.os_auth_url, service_type=args.service_type, region_name=args.os_region_name, magnum_url=args.os_endpoint_override, interface=args.os_interface, insecure=args.insecure, api_version=args.magnum_api_version, **kwargs ) self._check_deprecation(args.func, argv) try: args.func(self.cs, args) except (cliutils.DuplicateArgs, cliutils.MissingArgs): self.do_help(args) raise if profiler and args.profile: trace_id = profiler.get().get_base_id() print("To display trace use the command:\n\n" " osprofiler trace show --html %s " % trace_id) def _check_deprecation(self, func, argv): if not hasattr(func, 'deprecated_groups'): return for (old_info, new_info, required) in func.deprecated_groups: old_param = old_info[0][0] new_param = new_info[0][0] old_value, new_value = None, None for i in range(len(argv)): cur_arg = argv[i] if cur_arg == old_param: old_value = argv[i + 1] elif cur_arg == new_param[0]: new_value = argv[i + 1] if old_value and not new_value: print( 'WARNING: The %s parameter is deprecated and will be ' 'removed in a future release. Use the %s parameter to ' 'avoid seeing this message.' % (old_param, new_param)) def _dump_timings(self, timings): class Tyme(object): def __init__(self, url, seconds): self.url = url self.seconds = seconds results = [Tyme(url, end - start) for url, start, end in timings] total = 0.0 for tyme in results: total += tyme.seconds results.append(Tyme("Total", total)) cliutils.print_list(results, ["url", "seconds"], sortby_index=None) def do_bash_completion(self, _args): """Prints arguments for bash-completion. Prints all of the commands and options to stdout so that the magnum.bash_completion script doesn't have to hard code them. """ commands = set() options = set() for sc_str, sc in self.subcommands.items(): commands.add(sc_str) for option in sc._optionals._option_string_actions.keys(): options.add(option) commands.remove('bash-completion') commands.remove('bash_completion') print(' '.join(commands | options)) @cliutils.arg('command', metavar='', nargs='?', help=_('Display help for .')) def do_help(self, args): """Display help about this program or one of its subcommands.""" # NOTE(jamespage): args.command is not guaranteed with python >= 3.4 command = getattr(args, 'command', '') if command: if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError("'%s' is not a valid subcommand" % args.command) else: self.parser.print_help() # I'm picky about my shell help. class OpenStackHelpFormatter(argparse.HelpFormatter): def start_section(self, heading): # Title-case the headings heading = '%s%s' % (heading[0].upper(), heading[1:]) super(OpenStackHelpFormatter, self).start_section(heading) def main(): try: OpenStackMagnumShell().main(map(encodeutils.safe_decode, sys.argv[1:])) except Exception as e: logger.debug(e, exc_info=1) print("ERROR: %s" % encodeutils.safe_encode(six.text_type(e)), file=sys.stderr) sys.exit(1) if __name__ == "__main__": main() python-magnumclient-2.8.0/magnumclient/tests/000077500000000000000000000000001323411543400213505ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/__init__.py000066400000000000000000000000001323411543400234470ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/base.py000066400000000000000000000036361323411543400226440ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Copyright 2010-2011 OpenStack Foundation # 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. import os import fixtures import testtools _TRUE_VALUES = ('true', '1', 'yes') class TestCase(testtools.TestCase): """Test case base class for all unit tests.""" def setUp(self): """Run before each test method to initialize test environment.""" super(TestCase, self).setUp() test_timeout = os.environ.get('OS_TEST_TIMEOUT', 60) try: test_timeout = int(test_timeout) except ValueError: # If timeout value is invalid, set a default timeout. test_timeout = 60 if test_timeout <= 0: test_timeout = 60 self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) self.useFixture(fixtures.NestedTempfile()) self.useFixture(fixtures.TempHomeDir()) if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES: stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES: stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) self.log_fixture = self.useFixture(fixtures.FakeLogger()) python-magnumclient-2.8.0/magnumclient/tests/osc/000077500000000000000000000000001323411543400221345ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/osc/__init__.py000066400000000000000000000000001323411543400242330ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/osc/unit/000077500000000000000000000000001323411543400231135ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/osc/unit/__init__.py000066400000000000000000000000001323411543400252120ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/osc/unit/osc_fakes.py000066400000000000000000000153531323411543400254310ustar00rootroot00000000000000# Copyright 2013 Nebula 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 json import mock import sys from keystoneauth1 import fixture import requests import six AUTH_TOKEN = "foobar" AUTH_URL = "http://0.0.0.0" USERNAME = "itchy" PASSWORD = "scratchy" PROJECT_NAME = "poochie" REGION_NAME = "richie" INTERFACE = "catchy" VERSION = "3" TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN, user_name=USERNAME) _s = TEST_RESPONSE_DICT.add_service('identity', name='keystone') _s.add_endpoint(AUTH_URL + ':5000/v2.0') _s = TEST_RESPONSE_DICT.add_service('network', name='neutron') _s.add_endpoint(AUTH_URL + ':9696') _s = TEST_RESPONSE_DICT.add_service('compute', name='nova') _s.add_endpoint(AUTH_URL + ':8774/v2.1') _s = TEST_RESPONSE_DICT.add_service('image', name='glance') _s.add_endpoint(AUTH_URL + ':9292') _s = TEST_RESPONSE_DICT.add_service('object', name='swift') _s.add_endpoint(AUTH_URL + ':8080/v1') TEST_RESPONSE_DICT_V3 = fixture.V3Token(user_name=USERNAME) TEST_RESPONSE_DICT_V3.set_project_scope() TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) def to_unicode_dict(catalog_dict): """Converts dict to unicode dict """ if isinstance(catalog_dict, dict): return {to_unicode_dict(key): to_unicode_dict(value) for key, value in catalog_dict.items()} elif isinstance(catalog_dict, list): return [to_unicode_dict(element) for element in catalog_dict] elif isinstance(catalog_dict, str): return catalog_dict + u"" else: return catalog_dict class FakeStdout(object): def __init__(self): self.content = [] def write(self, text): self.content.append(text) def make_string(self): result = '' for line in self.content: result = result + line return result class FakeLog(object): def __init__(self): self.messages = {} def debug(self, msg): self.messages['debug'] = msg def info(self, msg): self.messages['info'] = msg def warning(self, msg): self.messages['warning'] = msg def error(self, msg): self.messages['error'] = msg def critical(self, msg): self.messages['critical'] = msg class FakeApp(object): def __init__(self, _stdout, _log): self.stdout = _stdout self.client_manager = None self.stdin = sys.stdin self.stdout = _stdout or sys.stdout self.stderr = sys.stderr self.log = _log class FakeOptions(object): def __init__(self, **kwargs): self.os_beta_command = False class FakeClient(object): def __init__(self, **kwargs): self.endpoint = kwargs['endpoint'] self.token = kwargs['token'] class FakeClientManager(object): _api_version = { 'image': '2', } def __init__(self): self.compute = None self.identity = None self.image = None self.object_store = None self.volume = None self.network = None self.session = None self.auth_ref = None self.auth_plugin_name = None self.network_endpoint_enabled = True def get_configuration(self): return { 'auth': { 'username': USERNAME, 'password': PASSWORD, 'token': AUTH_TOKEN, }, 'region': REGION_NAME, 'identity_api_version': VERSION, } def is_network_endpoint_enabled(self): return self.network_endpoint_enabled class FakeModule(object): def __init__(self, name, version): self.name = name self.__version__ = version # Workaround for openstacksdk case self.version = mock.Mock() self.version.__version__ = version class FakeResource(object): def __init__(self, manager=None, info=None, loaded=False, methods=None): """Set attributes and methods for a resource. :param manager: The resource manager :param Dictionary info: A dictionary with all attributes :param bool loaded: True if the resource is loaded in memory :param Dictionary methods: A dictionary with all methods """ info = info or {} methods = methods or {} self.__name__ = type(self).__name__ self.manager = manager self._info = info self._add_details(info) self._add_methods(methods) self._loaded = loaded def _add_details(self, info): for (k, v) in info.items(): setattr(self, k, v) def _add_methods(self, methods): """Fake methods with MagicMock objects. For each <@key, @value> pairs in methods, add an callable MagicMock object named @key as an attribute, and set the mock's return_value to @value. When users access the attribute with (), @value will be returned, which looks like a function call. """ for (name, ret) in methods.items(): method = mock.Mock(return_value=ret) setattr(self, name, method) def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) def keys(self): return self._info.keys() def to_dict(self): return self._info @property def info(self): return self._info def __getitem__(self, item): return self._info.get(item) def get(self, item, default=None): return self._info.get(item, default) class FakeResponse(requests.Response): def __init__(self, headers=None, status_code=200, data=None, encoding=None): super(FakeResponse, self).__init__() headers = headers or {} self.status_code = status_code self.headers.update(headers) self._content = json.dumps(data) if not isinstance(self._content, six.binary_type): self._content = self._content.encode() class FakeModel(dict): def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(key) python-magnumclient-2.8.0/magnumclient/tests/osc/unit/osc_utils.py000066400000000000000000000047731323411543400255040ustar00rootroot00000000000000# Copyright 2012-2013 OpenStack Foundation # Copyright 2013 Nebula 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 fixtures import os import testtools from openstackclient.tests.unit import fakes class ParserException(Exception): pass class TestCase(testtools.TestCase): def setUp(self): testtools.TestCase.setUp(self) 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)) def assertNotCalled(self, m, msg=None): """Assert a function was not called""" if m.called: if not msg: msg = 'method %s should not have been called' % m self.fail(msg) class TestCommand(TestCase): """Test cliff command classes""" def setUp(self): super(TestCommand, self).setUp() # Build up a fake app self.fake_stdout = fakes.FakeStdout() self.fake_log = fakes.FakeLog() self.app = fakes.FakeApp(self.fake_stdout, self.fake_log) self.app.client_manager = fakes.FakeClientManager() self.app.options = fakes.FakeOptions() def check_parser(self, cmd, args, verify_args): cmd_parser = cmd.get_parser('check_parser') try: parsed_args = cmd_parser.parse_args(args) except SystemExit: raise ParserException("Argument parse failed") for av in verify_args: attr, value = av if attr: self.assertIn(attr, parsed_args) self.assertEqual(value, getattr(parsed_args, attr)) return parsed_args python-magnumclient-2.8.0/magnumclient/tests/osc/unit/v1/000077500000000000000000000000001323411543400234415ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/osc/unit/v1/__init__.py000066400000000000000000000000001323411543400255400ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/osc/unit/v1/fakes.py000066400000000000000000000147701323411543400251150ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 copy import datetime import uuid from magnumclient.tests.osc.unit import osc_fakes from magnumclient.tests.osc.unit import osc_utils class FakeBaseModel(object): def __repr__(self): return "<" + self.__class__.model_name + "%s>" % self._info class FakeBaseModelManager(object): def list(self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False): pass def get(self, id): pass def create(self, **kwargs): pass def delete(self, id): pass def update(self, id, patch): pass class MagnumFakeContainerInfra(object): def __init__(self): self.cluster_templates = FakeBaseModelManager() self.clusters = FakeBaseModelManager() self.mservices = FakeBaseModelManager() class MagnumFakeClientManager(osc_fakes.FakeClientManager): def __init__(self): super(MagnumFakeClientManager, self).__init__() self.container_infra = MagnumFakeContainerInfra() class MagnumParseException(Exception): """The base exception class for all exceptions this library raises.""" def __init__(self, message=None, details=None): self.message = message or "Argument parse exception" self.details = details or None def __str__(self): return self.message class TestMagnumClientOSCV1(osc_utils.TestCase): def setUp(self): super(TestMagnumClientOSCV1, self).setUp() self.fake_stdout = osc_fakes.FakeStdout() self.fake_log = osc_fakes.FakeLog() self.app = osc_fakes.FakeApp(self.fake_stdout, self.fake_log) self.namespace = argparse.Namespace() self.app.client_manager = MagnumFakeClientManager() def check_parser(self, cmd, args, verify_args): cmd_parser = cmd.get_parser('check_parser') try: parsed_args = cmd_parser.parse_args(args) except SystemExit: raise MagnumParseException() for av in verify_args: attr, value = av if attr: self.assertIn(attr, parsed_args) self.assertEqual(value, getattr(parsed_args, attr)) return parsed_args class FakeClusterTemplate(object): """Fake one or more ClusterTemplate.""" @staticmethod def create_one_cluster_template(attrs=None): """Create a fake ClusterTemplate. :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with flavor_id, image_id, and so on """ attrs = attrs or {} # set default attributes. ct_info = { 'links': [], 'insecure_registry': None, 'labels': None, 'updated_at': None, 'floating_ip_enabled': True, 'fixed_subnet': None, 'master_flavor_id': None, 'uuid': uuid.uuid4().hex, 'no_proxy': None, 'https_proxy': None, 'tls_disabled': False, 'keypair_id': None, 'public': False, 'http_proxy': None, 'docker_volume_size': None, 'server_type': 'vm', 'external_network_id': 'public', 'cluster_distro': 'fedora-atomic', 'image_id': 'fedora-atomic-latest', 'volume_driver': None, 'registry_enabled': False, 'docker_storage_driver': 'devicemapper', 'apiserver_port': None, 'name': 'fake-ct-' + uuid.uuid4().hex, 'created_at': datetime.datetime.now(), 'network_driver': 'flannel', 'fixed_network': None, 'coe': 'kubernetes', 'flavor_id': 'm1.medium', 'master_lb_enabled': False, 'dns_nameserver': '8.8.8.8' } # Overwrite default attributes. ct_info.update(attrs) ct = osc_fakes.FakeResource(info=copy.deepcopy(ct_info), loaded=True) return ct @staticmethod def create_cluster_templates(attrs=None, count=2): """Create multiple fake cluster templates. :param Dictionary attrs: A dictionary with all attributes :param int count: The number of cluster templates to fake :return: A list of FakeResource objects faking the cluster templates """ cts = [] for i in range(0, count): cts.append(FakeClusterTemplate.create_one_cluster_template(attrs)) return cts class FakeCluster(object): """Fake one or more Cluster.""" @staticmethod def create_one_cluster(attrs=None): """Create a fake Cluster. :param Dictionary attrs: A dictionary with all attributes :return: A FakeResource object, with flavor_id, image_id, and so on """ attrs = attrs or {} # set default attributes. cluster_info = { 'status': 'CREATE_IN_PROGRESS', 'cluster_template_id': 'fake-ct', 'node_addresses': [], 'uuid': '3a369884-b6ba-484f-a206-919b4b718aff', 'stack_id': 'c4554582-77bd-4734-8f1a-72c3c40e5fb4', 'status_reason': None, 'labels': {}, 'created_at': '2017-03-16T18:40:39+00:00', 'updated_at': '2017-03-16T18:40:45+00:00', 'coe_version': None, 'faults': None, 'keypair': 'fakekey', 'api_address': None, 'master_addresses': [], 'create_timeout': 60, 'node_count': 1, 'discovery_url': 'https://fake.cluster', 'master_count': 1, 'container_version': None, 'name': 'fake-cluster', 'master_flavor_id': None, 'flavor_id': 'm1.medium', } # Overwrite default attributes. cluster_info.update(attrs) cluster = osc_fakes.FakeResource(info=copy.deepcopy(cluster_info), loaded=True) return cluster python-magnumclient-2.8.0/magnumclient/tests/osc/unit/v1/test_cluster_templates.py000066400000000000000000000320241323411543400306120ustar00rootroot00000000000000# Copyright 2016 Easystack. 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 mock from mock import call from magnumclient.exceptions import InvalidAttribute from magnumclient.osc.v1 import cluster_templates as osc_ct from magnumclient.tests.osc.unit.v1 import fakes as magnum_fakes from osc_lib import exceptions as osc_exceptions class TestClusterTemplate(magnum_fakes.TestMagnumClientOSCV1): default_create_args = { 'coe': 'kubernetes', 'dns_nameserver': '8.8.8.8', 'docker_storage_driver': 'devicemapper', 'docker_volume_size': None, 'external_network_id': 'public', 'fixed_network': None, 'fixed_subnet': None, 'flavor_id': 'm1.medium', 'http_proxy': None, 'https_proxy': None, 'image_id': 'fedora-atomic-latest', 'keypair_id': None, 'labels': {}, 'master_flavor_id': None, 'master_lb_enabled': False, 'name': 'fake-ct-1', 'network_driver': None, 'no_proxy': None, 'public': False, 'registry_enabled': False, 'server_type': 'vm', 'tls_disabled': False, 'volume_driver': None } def setUp(self): super(TestClusterTemplate, self).setUp() self.cluster_templates_mock = ( self.app.client_manager.container_infra.cluster_templates) class TestClusterTemplateCreate(TestClusterTemplate): def setUp(self): super(TestClusterTemplateCreate, self).setUp() attr = dict() attr['name'] = 'fake-ct-1' self.new_ct = ( magnum_fakes.FakeClusterTemplate.create_one_cluster_template(attr)) self.cluster_templates_mock.create = mock.Mock() self.cluster_templates_mock.create.return_value = self.new_ct self.cluster_templates_mock.get = mock.Mock() self.cluster_templates_mock.get.return_value = copy.deepcopy( self.new_ct) self.cluster_templates_mock.update = mock.Mock() self.cluster_templates_mock.update.return_value = self.new_ct # Get the command object to test self.cmd = osc_ct.CreateClusterTemplate(self.app, None) self.data = tuple(map(lambda x: getattr(self.new_ct, x), osc_ct.CLUSTER_TEMPLATE_ATTRIBUTES)) def test_cluster_template_create_required_args_pass(self): """Verifies required arguments.""" arglist = [ '--coe', self.new_ct.coe, '--external-network', self.new_ct.external_network_id, '--image', self.new_ct.image_id, self.new_ct.name ] verifylist = [ ('coe', self.new_ct.coe), ('external_network', self.new_ct.external_network_id), ('image', self.new_ct.image_id), ('name', self.new_ct.name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.cluster_templates_mock.create.assert_called_with( **self.default_create_args) def test_cluster_template_create_missing_required_arg(self): """Verifies missing required arguments.""" arglist = [ '--external-network', self.new_ct.external_network_id, '--image', self.new_ct.image_id ] verifylist = [ ('external_network', self.new_ct.external_network_id), ('image', self.new_ct.image_id) ] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) # Verify all required args are checked and not just coe arglist.append('--coe') arglist.append(self.new_ct.coe) verifylist.append(('coe', self.new_ct.coe)) arglist.remove('--image') arglist.remove(self.new_ct.image_id) verifylist.remove(('image', self.new_ct.image_id)) self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) arglist.remove('--external-network') arglist.remove(self.new_ct.external_network_id) verifylist.remove( ('external_network', self.new_ct.external_network_id)) self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) def test_cluster_template_create_floating_ips(self): """Verifies floating ip parameters.""" arglist = [ '--coe', self.new_ct.coe, '--external-network', self.new_ct.external_network_id, '--image', self.new_ct.image_id, '--floating-ip-enabled', self.new_ct.name ] verifylist = [ ('coe', self.new_ct.coe), ('external_network', self.new_ct.external_network_id), ('image', self.new_ct.image_id), ('floating_ip_enabled', [True]), ('name', self.new_ct.name) ] self.default_create_args['floating_ip_enabled'] = True parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.default_create_args.pop('floating_ip_enabled') arglist.append('--floating-ip-disabled') verifylist.remove(('floating_ip_enabled', [True])) verifylist.append(('floating_ip_enabled', [True, False])) parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises(InvalidAttribute, self.cmd.take_action, parsed_args) class TestClusterTemplateDelete(TestClusterTemplate): def setUp(self): super(TestClusterTemplateDelete, self).setUp() self.cluster_templates_mock.delete = mock.Mock() self.cluster_templates_mock.delete.return_value = None # Get the command object to test self.cmd = osc_ct.DeleteClusterTemplate(self.app, None) def test_cluster_template_delete_one(self): arglist = ['foo'] verifylist = [('cluster-templates', ['foo'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.cluster_templates_mock.delete.assert_called_with('foo') def test_cluster_template_delete_multiple(self): arglist = ['foo', 'bar'] verifylist = [('cluster-templates', ['foo', 'bar'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.cluster_templates_mock.delete.assert_has_calls([call('foo'), call('bar')]) def test_cluster_template_delete_bad_uuid(self): self.cluster_templates_mock.delete.side_effect = ( osc_exceptions.NotFound(404)) arglist = ['foo'] verifylist = [('cluster-templates', ['foo'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) returns = self.cmd.take_action(parsed_args) self.assertEqual(returns, None) def test_cluster_template_delete_no_uuid(self): arglist = [] verifylist = [('cluster-templates', [])] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) class TestClusterTemplateList(TestClusterTemplate): attr = dict() attr['name'] = 'fake-ct-1' _cluster_template = ( magnum_fakes.FakeClusterTemplate.create_one_cluster_template(attr)) attr['name'] = 'fake-ct-2' _cluster_template2 = ( magnum_fakes.FakeClusterTemplate.create_one_cluster_template(attr)) columns = [ 'uuid', 'name' ] datalist = ( (_cluster_template.uuid, _cluster_template.name), (_cluster_template2.uuid, _cluster_template2.name) ) def setUp(self): super(TestClusterTemplateList, self).setUp() self.cluster_templates_mock.list = mock.Mock() self.cluster_templates_mock.list.return_value = [ self._cluster_template, self._cluster_template2 ] # Get the command object to test self.cmd = osc_ct.ListTemplateCluster(self.app, None) def test_cluster_template_list_no_options(self): arglist = [] verifylist = [ ('limit', None), ('sort_key', None), ('sort_dir', None), ('fields', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.cluster_templates_mock.list.assert_called_with( limit=None, sort_dir=None, sort_key=None, ) self.assertEqual(self.columns, columns) index = 0 for d in data: self.assertEqual(self.datalist[index], d) index += 1 def test_cluster_template_list_options(self): arglist = [ '--limit', '1', '--sort-key', 'key', '--sort-dir', 'asc', '--fields', 'fields' ] verifylist = [ ('limit', 1), ('sort_key', 'key'), ('sort_dir', 'asc'), ('fields', 'fields'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.cluster_templates_mock.list.assert_called_with( limit=1, sort_dir='asc', sort_key='key', ) def test_cluster_template_list_bad_sort_dir_fail(self): arglist = [ '--sort-dir', 'foo' ] verifylist = [ ('limit', None), ('sort_key', None), ('sort_dir', 'foo'), ('fields', None), ] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) class TestClusterTemplateShow(TestClusterTemplate): attr = dict() attr['name'] = 'fake-ct-1' _cluster_template = ( magnum_fakes.FakeClusterTemplate.create_one_cluster_template(attr)) columns = osc_ct.CLUSTER_TEMPLATE_ATTRIBUTES def setUp(self): super(TestClusterTemplateShow, self).setUp() datalist = map(lambda x: getattr(self._cluster_template, x), self.columns) self.show_data_list = tuple(map(lambda x: x if x is not None else '-', datalist)) self.cluster_templates_mock.get = mock.Mock() self.cluster_templates_mock.get.return_value = self._cluster_template # Get the command object to test self.cmd = osc_ct.ShowClusterTemplate(self.app, None) def test_cluster_template_show(self): arglist = ['fake-ct-1'] verifylist = [('cluster-template', 'fake-ct-1')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.cluster_templates_mock.get.assert_called_with('fake-ct-1') self.assertEqual(self.columns, columns) self.assertEqual(self.show_data_list, data) def test_cluster_template_show_no_ct_fail(self): arglist = [] verifylist = [] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) class TestClusterTemplateUpdate(TestClusterTemplate): def setUp(self): super(TestClusterTemplateUpdate, self).setUp() attr = dict() attr['name'] = 'fake-ct-1' ct = magnum_fakes.FakeClusterTemplate.create_one_cluster_template(attr) self.cluster_templates_mock.update = mock.Mock() self.cluster_templates_mock.update.return_value = ct # Get the command object to test self.cmd = osc_ct.UpdateClusterTemplate(self.app, None) def test_cluster_template_update_pass(self): arglist = ['foo', 'remove', 'bar'] verifylist = [ ('cluster-template', 'foo'), ('op', 'remove'), ('attributes', [['bar']]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.cluster_templates_mock.update.assert_called_with( 'foo', [{'op': 'remove', 'path': '/bar'}] ) def test_cluster_template_update_bad_op(self): arglist = ['foo', 'bar', 'snafu'] verifylist = [ ('cluster-template', 'foo'), ('op', 'bar'), ('attributes', ['snafu']) ] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) python-magnumclient-2.8.0/magnumclient/tests/osc/unit/v1/test_clusters.py000066400000000000000000000237541323411543400267310ustar00rootroot00000000000000# Copyright 2016 Easystack. 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 mock from mock import call from magnumclient.osc.v1 import clusters as osc_clusters from magnumclient.tests.osc.unit.v1 import fakes as magnum_fakes class TestCluster(magnum_fakes.TestMagnumClientOSCV1): def setUp(self): super(TestCluster, self).setUp() self.clusters_mock = self.app.client_manager.container_infra.clusters class TestClusterCreate(TestCluster): def setUp(self): super(TestClusterCreate, self).setUp() attr = dict() attr['name'] = 'fake-cluster-1' self._cluster = magnum_fakes.FakeCluster.create_one_cluster(attr) self._default_args = { 'cluster_template_id': 'fake-ct', 'create_timeout': 60, 'discovery_url': None, 'docker_volume_size': None, 'flavor_id': None, 'keypair': None, 'master_count': 1, 'name': 'fake-cluster-1', 'node_count': 1, 'master_flavor_id': None, } self.clusters_mock.create = mock.Mock() self.clusters_mock.create.return_value = self._cluster self.clusters_mock.get = mock.Mock() self.clusters_mock.get.return_value = copy.deepcopy(self._cluster) self.clusters_mock.update = mock.Mock() self.clusters_mock.update.return_value = self._cluster # Get the command object to test self.cmd = osc_clusters.CreateCluster(self.app, None) self.data = tuple(map(lambda x: getattr(self._cluster, x), osc_clusters.CLUSTER_ATTRIBUTES)) def test_cluster_create_required_args_pass(self): """Verifies required arguments.""" arglist = [ '--cluster-template', self._cluster.cluster_template_id, self._cluster.name ] verifylist = [ ('cluster_template', self._cluster.cluster_template_id), ('name', self._cluster.name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.clusters_mock.create.assert_called_with(**self._default_args) def test_cluster_create_missing_required_arg(self): """Verifies missing required arguments.""" arglist = [ self._cluster.name ] verifylist = [ ('name', self._cluster.name) ] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) def test_cluster_create_with_labels(self): """Verifies labels are properly parsed when given as argument.""" expected_args = self._default_args expected_args['labels'] = { 'arg1': 'value1', 'arg2': 'value2' } arglist = [ '--cluster-template', self._cluster.cluster_template_id, '--labels', 'arg1=value1', '--labels', 'arg2=value2', self._cluster.name ] verifylist = [ ('cluster_template', self._cluster.cluster_template_id), ('labels', ['arg1=value1', 'arg2=value2']), ('name', self._cluster.name) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.clusters_mock.create.assert_called_with(**expected_args) class TestClusterDelete(TestCluster): def setUp(self): super(TestClusterDelete, self).setUp() self.clusters_mock.delete = mock.Mock() self.clusters_mock.delete.return_value = None # Get the command object to test self.cmd = osc_clusters.DeleteCluster(self.app, None) def test_cluster_delete_one(self): arglist = ['foo'] verifylist = [('cluster', ['foo'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.clusters_mock.delete.assert_called_with('foo') def test_cluster_delete_multiple(self): arglist = ['foo', 'bar'] verifylist = [('cluster', ['foo', 'bar'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.clusters_mock.delete.assert_has_calls([call('foo'), call('bar')]) def test_cluster_delete_bad_uuid(self): arglist = ['foo'] verifylist = [('cluster', ['foo'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) returns = self.cmd.take_action(parsed_args) self.assertEqual(returns, None) def test_cluster_delete_no_uuid(self): arglist = [] verifylist = [('cluster', [])] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) class TestClusterList(TestCluster): attr = dict() attr['name'] = 'fake-cluster-1' _cluster = magnum_fakes.FakeCluster.create_one_cluster(attr) columns = [ 'uuid', 'name', 'keypair', 'node_count', 'master_count', 'status' ] datalist = ( ( _cluster.uuid, _cluster.name, _cluster.keypair, _cluster.node_count, _cluster.master_count, _cluster.status ), ) def setUp(self): super(TestClusterList, self).setUp() self.clusters_mock.list = mock.Mock() self.clusters_mock.list.return_value = [self._cluster] # Get the command object to test self.cmd = osc_clusters.ListCluster(self.app, None) def test_cluster_list_no_options(self): arglist = [] verifylist = [ ('limit', None), ('sort_key', None), ('sort_dir', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.clusters_mock.list.assert_called_with( limit=None, sort_dir=None, sort_key=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_cluster_list_options(self): arglist = [ '--limit', '1', '--sort-key', 'key', '--sort-dir', 'asc' ] verifylist = [ ('limit', 1), ('sort_key', 'key'), ('sort_dir', 'asc') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.clusters_mock.list.assert_called_with( limit=1, sort_dir='asc', sort_key='key', ) def test_cluster_list_bad_sort_dir_fail(self): arglist = [ '--sort-dir', 'foo' ] verifylist = [ ('limit', None), ('sort_key', None), ('sort_dir', 'foo'), ('fields', None), ] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) class TestClusterUpdate(TestCluster): def setUp(self): super(TestClusterUpdate, self).setUp() self.clusters_mock.update = mock.Mock() self.clusters_mock.update.return_value = None # Get the command object to test self.cmd = osc_clusters.UpdateCluster(self.app, None) def test_cluster_update_pass(self): arglist = ['foo', 'remove', 'bar'] verifylist = [ ('cluster', 'foo'), ('op', 'remove'), ('attributes', [['bar']]), ('rollback', False) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.clusters_mock.update.assert_called_with( 'foo', [{'op': 'remove', 'path': '/bar'}] ) def test_cluster_update_bad_op(self): arglist = ['foo', 'bar', 'snafu'] verifylist = [ ('cluster', 'foo'), ('op', 'bar'), ('attributes', ['snafu']), ('rollback', False) ] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) class TestClusterShow(TestCluster): def setUp(self): super(TestClusterShow, self).setUp() attr = dict() attr['name'] = 'fake-cluster-1' self._cluster = magnum_fakes.FakeCluster.create_one_cluster(attr) self.clusters_mock.get = mock.Mock() self.clusters_mock.get.return_value = self._cluster # Get the command object to test self.cmd = osc_clusters.ShowCluster(self.app, None) self.data = tuple(map(lambda x: getattr(self._cluster, x), osc_clusters.CLUSTER_ATTRIBUTES)) def test_cluster_show_pass(self): arglist = ['fake-cluster'] verifylist = [ ('cluster', 'fake-cluster') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.clusters_mock.get.assert_called_with('fake-cluster') self.assertEqual(osc_clusters.CLUSTER_ATTRIBUTES, columns) self.assertEqual(self.data, data) def test_cluster_show_no_cluster_fail(self): arglist = [] verifylist = [] self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) python-magnumclient-2.8.0/magnumclient/tests/osc/unit/v1/test_mservices.py000066400000000000000000000033031323411543400270510ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.osc.v1 import mservices from magnumclient.tests.osc.unit.v1 import fakes class TestServiceList(fakes.TestMagnumClientOSCV1): columns = ('id', 'host', 'binary', 'state', 'disabled', 'disabled_reason', 'created_at', 'updated_at') def setUp(self): super(TestServiceList, self).setUp() self.mservices_mock = self.app.client_manager.container_infra.mservices self.mservices_mock.list = mock.Mock() fake_service = mock.Mock( Binary='magnum-conductor', Host='Host1', Status='enabled', State='up', Updated_at=None, Disabled_Reason=None, ) fake_service.name = 'test_service' self.mservices_mock.list.return_value = [fake_service] # Get the command object to test self.cmd = mservices.ListService(self.app, None) def test_service_list(self): arglist = [] parsed_args = self.check_parser(self.cmd, arglist, []) columns, data = self.cmd.take_action(parsed_args) self.mservices_mock.list.assert_called_with() self.assertEqual(self.columns, columns) python-magnumclient-2.8.0/magnumclient/tests/test_client.py000066400000000000000000000026601323411543400242430ustar00rootroot00000000000000# Copyright (c) 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 mock import testtools from magnumclient import client class ClientTest(testtools.TestCase): @mock.patch('magnumclient.v1.client.Client') def test_no_version_argument(self, mock_magnum_client): client.Client(auth_token='mytoken', magnum_url='http://myurl/') mock_magnum_client.assert_called_with( auth_token='mytoken', magnum_url='http://myurl/') @mock.patch('magnumclient.v1.client.Client') def test_valid_version_argument(self, mock_magnum_client): client.Client(version='1', magnum_url='http://myurl/') mock_magnum_client.assert_called_with(magnum_url='http://myurl/') @mock.patch('magnumclient.v1.client.Client') def test_invalid_version_argument(self, mock_magnum_client): self.assertRaises( ValueError, client.Client, version='2', magnum_url='http://myurl/') python-magnumclient-2.8.0/magnumclient/tests/test_httpclient.py000066400000000000000000000446331323411543400251510ustar00rootroot00000000000000# Copyright 2015 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json import mock import six import socket from magnumclient.common.apiclient.exceptions import GatewayTimeout from magnumclient.common.apiclient.exceptions import MultipleChoices from magnumclient.common import httpclient as http from magnumclient import exceptions as exc from magnumclient.tests import utils NORMAL_ERROR = 0 ERROR_DICT = 1 ERROR_LIST_WITH_DETAIL = 2 ERROR_LIST_WITH_DESC = 3 def _get_error_body(faultstring=None, debuginfo=None, err_type=NORMAL_ERROR): if err_type == NORMAL_ERROR: error_body = { 'faultstring': faultstring, 'debuginfo': debuginfo } raw_error_body = json.dumps(error_body) body = {'error_message': raw_error_body} elif err_type == ERROR_DICT: body = {'error': {'title': faultstring, 'message': debuginfo}} elif err_type == ERROR_LIST_WITH_DETAIL: main_body = {'title': faultstring, 'detail': debuginfo} body = {'errors': [main_body]} elif err_type == ERROR_LIST_WITH_DESC: main_body = {'title': faultstring, 'description': debuginfo} body = {'errors': [main_body]} raw_body = json.dumps(body) return raw_body HTTP_CLASS = six.moves.http_client.HTTPConnection HTTPS_CLASS = http.VerifiedHTTPSConnection DEFAULT_TIMEOUT = 600 class HttpClientTest(utils.BaseTestCase): def test_url_generation_trailing_slash_in_base(self): client = http.HTTPClient('http://localhost/') url = client._make_connection_url('/v1/resources') self.assertEqual('/v1/resources', url) def test_url_generation_without_trailing_slash_in_base(self): client = http.HTTPClient('http://localhost') url = client._make_connection_url('/v1/resources') self.assertEqual('/v1/resources', url) def test_url_generation_prefix_slash_in_path(self): client = http.HTTPClient('http://localhost/') url = client._make_connection_url('/v1/resources') self.assertEqual('/v1/resources', url) def test_url_generation_without_prefix_slash_in_path(self): client = http.HTTPClient('http://localhost') url = client._make_connection_url('v1/resources') self.assertEqual('/v1/resources', url) def test_server_exception_empty_body(self): error_body = _get_error_body() fake_resp = utils.FakeResponse({'content-type': 'application/json'}, six.StringIO(error_body), version=1, status=500) client = http.HTTPClient('http://localhost/') client.get_connection = ( lambda *a, **kw: utils.FakeConnection(fake_resp)) error = self.assertRaises(exc.InternalServerError, client.json_request, 'GET', '/v1/resources') self.assertEqual('Internal Server Error (HTTP 500)', str(error)) def test_server_exception_msg_only(self): error_msg = 'test error msg' error_body = _get_error_body(error_msg, err_type=ERROR_DICT) fake_resp = utils.FakeResponse({'content-type': 'application/json'}, six.StringIO(error_body), version=1, status=500) client = http.HTTPClient('http://localhost/') client.get_connection = ( lambda *a, **kw: utils.FakeConnection(fake_resp)) error = self.assertRaises(exc.InternalServerError, client.json_request, 'GET', '/v1/resources') self.assertEqual(error_msg + ' (HTTP 500)', str(error)) def test_server_exception_msg_and_traceback(self): error_msg = 'another test error' error_trace = ("\"Traceback (most recent call last):\\n\\n " "File \\\"/usr/local/lib/python2.7/...") error_body = _get_error_body(error_msg, error_trace, ERROR_LIST_WITH_DESC) fake_resp = utils.FakeResponse({'content-type': 'application/json'}, six.StringIO(error_body), version=1, status=500) client = http.HTTPClient('http://localhost/') client.get_connection = ( lambda *a, **kw: utils.FakeConnection(fake_resp)) error = self.assertRaises(exc.InternalServerError, client.json_request, 'GET', '/v1/resources') self.assertEqual( '%(error)s (HTTP 500)\n%(trace)s' % {'error': error_msg, 'trace': error_trace}, "%(error)s\n%(details)s" % {'error': str(error), 'details': str(error.details)}) def test_server_exception_address(self): endpoint = 'https://magnum-host:6385' client = http.HTTPClient(endpoint, token='foobar', insecure=True, ca_file='/path/to/ca_file') client.get_connection = ( lambda *a, **kw: utils.FakeConnection(exc=socket.gaierror)) self.assertRaises(exc.EndpointNotFound, client.json_request, 'GET', '/v1/resources', body='farboo') def test_server_exception_socket(self): client = http.HTTPClient('http://localhost/', token='foobar') client.get_connection = ( lambda *a, **kw: utils.FakeConnection(exc=socket.error)) self.assertRaises(exc.ConnectionRefused, client.json_request, 'GET', '/v1/resources') def test_server_exception_endpoint(self): endpoint = 'https://magnum-host:6385' client = http.HTTPClient(endpoint, token='foobar', insecure=True, ca_file='/path/to/ca_file') client.get_connection = ( lambda *a, **kw: utils.FakeConnection(exc=socket.gaierror)) self.assertRaises(exc.EndpointNotFound, client.json_request, 'GET', '/v1/resources', body='farboo') def test_get_connection(self): endpoint = 'https://magnum-host:6385' client = http.HTTPClient(endpoint) conn = client.get_connection() self.assertTrue(conn, http.VerifiedHTTPSConnection) def test_get_connection_exception(self): endpoint = 'http://magnum-host:6385/' expected = (HTTP_CLASS, ('magnum-host', 6385, ''), {'timeout': DEFAULT_TIMEOUT}) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_ssl(self): endpoint = 'https://magnum-host:6385' expected = (HTTPS_CLASS, ('magnum-host', 6385, ''), { 'timeout': DEFAULT_TIMEOUT, 'ca_file': None, 'cert_file': None, 'key_file': None, 'insecure': False, }) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_ssl_params(self): endpoint = 'https://magnum-host:6385' ssl_args = { 'ca_file': '/path/to/ca_file', 'cert_file': '/path/to/cert_file', 'key_file': '/path/to/key_file', 'insecure': True, } expected_kwargs = {'timeout': DEFAULT_TIMEOUT} expected_kwargs.update(ssl_args) expected = (HTTPS_CLASS, ('magnum-host', 6385, ''), expected_kwargs) params = http.HTTPClient.get_connection_params(endpoint, **ssl_args) self.assertEqual(expected, params) def test_get_connection_params_with_timeout(self): endpoint = 'http://magnum-host:6385' expected = (HTTP_CLASS, ('magnum-host', 6385, ''), {'timeout': 300.0}) params = http.HTTPClient.get_connection_params(endpoint, timeout=300) self.assertEqual(expected, params) def test_get_connection_params_with_version(self): endpoint = 'http://magnum-host:6385/v1' expected = (HTTP_CLASS, ('magnum-host', 6385, ''), {'timeout': DEFAULT_TIMEOUT}) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_version_trailing_slash(self): endpoint = 'http://magnum-host:6385/v1/' expected = (HTTP_CLASS, ('magnum-host', 6385, ''), {'timeout': DEFAULT_TIMEOUT}) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_subpath(self): endpoint = 'http://magnum-host:6385/magnum' expected = (HTTP_CLASS, ('magnum-host', 6385, '/magnum'), {'timeout': DEFAULT_TIMEOUT}) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_subpath_trailing_slash(self): endpoint = 'http://magnum-host:6385/magnum/' expected = (HTTP_CLASS, ('magnum-host', 6385, '/magnum'), {'timeout': DEFAULT_TIMEOUT}) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_subpath_version(self): endpoint = 'http://magnum-host:6385/magnum/v1' expected = (HTTP_CLASS, ('magnum-host', 6385, '/magnum'), {'timeout': DEFAULT_TIMEOUT}) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_subpath_version_trailing_slash(self): endpoint = 'http://magnum-host:6385/magnum/v1/' expected = (HTTP_CLASS, ('magnum-host', 6385, '/magnum'), {'timeout': DEFAULT_TIMEOUT}) params = http.HTTPClient.get_connection_params(endpoint) self.assertEqual(expected, params) def test_get_connection_params_with_unsupported_scheme(self): endpoint = 'foo://magnum-host:6385/magnum/v1/' self.assertRaises(exc.EndpointException, http.HTTPClient.get_connection_params, endpoint) def test_401_unauthorized_exception(self): error_body = _get_error_body(err_type=ERROR_LIST_WITH_DETAIL) fake_resp = utils.FakeResponse({'content-type': 'text/plain'}, six.StringIO(error_body), version=1, status=401) client = http.HTTPClient('http://localhost/') client.get_connection = (lambda *a, **kw: utils.FakeConnection(fake_resp)) self.assertRaises(exc.Unauthorized, client.json_request, 'GET', '/v1/resources') def test_server_redirect_exception(self): fake_redirect_resp = utils.FakeResponse( {'content-type': 'application/octet-stream'}, 'foo', version=1, status=301) fake_resp = utils.FakeResponse( {'content-type': 'application/octet-stream'}, 'bar', version=1, status=300) client = http.HTTPClient('http://localhost/') conn = utils.FakeConnection(fake_redirect_resp, redirect_resp=fake_resp) client.get_connection = (lambda *a, **kw: conn) self.assertRaises(MultipleChoices, client.json_request, 'GET', '/v1/resources') def test_server_body_undecode_json(self): err = "foo" fake_resp = utils.FakeResponse( {'content-type': 'application/json'}, six.StringIO(err), version=1, status=200) client = http.HTTPClient('http://localhost/') conn = utils.FakeConnection(fake_resp) client.get_connection = (lambda *a, **kw: conn) resp, body = client.json_request('GET', '/v1/resources') self.assertEqual(resp, fake_resp) self.assertEqual(err, body) def test_server_success_body_app(self): fake_resp = utils.FakeResponse( {'content-type': 'application/octet-stream'}, 'bar', version=1, status=200) client = http.HTTPClient('http://localhost/') conn = utils.FakeConnection(fake_resp) client.get_connection = (lambda *a, **kw: conn) resp, body = client.json_request('GET', '/v1/resources') self.assertEqual(resp, fake_resp) self.assertIsNone(body) def test_server_success_body_none(self): fake_resp = utils.FakeResponse( {'content-type': None}, six.StringIO('bar'), version=1, status=200) client = http.HTTPClient('http://localhost/') conn = utils.FakeConnection(fake_resp) client.get_connection = (lambda *a, **kw: conn) resp, body = client.json_request('GET', '/v1/resources') self.assertEqual(resp, fake_resp) self.assertIsInstance(body, list) def test_server_success_body_json(self): err = _get_error_body() fake_resp = utils.FakeResponse( {'content-type': 'application/json'}, six.StringIO(err), version=1, status=200) client = http.HTTPClient('http://localhost/') conn = utils.FakeConnection(fake_resp) client.get_connection = (lambda *a, **kw: conn) resp, body = client.json_request('GET', '/v1/resources') self.assertEqual(resp, fake_resp) self.assertEqual(json.dumps(body), err) def test_raw_request(self): fake_resp = utils.FakeResponse( {'content-type': 'application/octet-stream'}, 'bar', version=1, status=200) client = http.HTTPClient('http://localhost/') conn = utils.FakeConnection(fake_resp) client.get_connection = (lambda *a, **kw: conn) resp, body = client.raw_request('GET', '/v1/resources') self.assertEqual(resp, fake_resp) self.assertIsInstance(body, http.ResponseBodyIterator) class SessionClientTest(utils.BaseTestCase): def test_server_exception_msg_and_traceback(self): error_msg = 'another test error' error_trace = ("\"Traceback (most recent call last):\\n\\n " "File \\\"/usr/local/lib/python2.7/...") error_body = _get_error_body(error_msg, error_trace) fake_session = utils.FakeSession({'Content-Type': 'application/json'}, error_body, 500) client = http.SessionClient(session=fake_session) error = self.assertRaises(exc.InternalServerError, client.json_request, 'GET', '/v1/resources') self.assertEqual( '%(error)s (HTTP 500)\n%(trace)s' % {'error': error_msg, 'trace': error_trace}, "%(error)s\n%(details)s" % {'error': str(error), 'details': str(error.details)}) def test_server_exception_empty_body(self): error_body = _get_error_body() fake_session = utils.FakeSession({'Content-Type': 'application/json'}, error_body, 500) client = http.SessionClient(session=fake_session) error = self.assertRaises(exc.InternalServerError, client.json_request, 'GET', '/v1/resources') self.assertEqual('Internal Server Error (HTTP 500)', str(error)) def test_bypass_url(self): fake_response = utils.FakeSessionResponse( {}, content="", status_code=201) fake_session = mock.MagicMock() fake_session.request.side_effect = [fake_response] client = http.SessionClient( session=fake_session, endpoint_override='http://magnum') client.json_request('GET', '/v1/bays') self.assertEqual( fake_session.request.call_args[1]['endpoint_override'], 'http://magnum' ) def test_exception(self): fake_response = utils.FakeSessionResponse( {}, content="", status_code=504) fake_session = mock.MagicMock() fake_session.request.side_effect = [fake_response] client = http.SessionClient( session=fake_session, endpoint_override='http://magnum') self.assertRaises(GatewayTimeout, client.json_request, 'GET', '/v1/resources') def test_construct_http_client_return_httpclient(self): client = http._construct_http_client('http://localhost/') self.assertIsInstance(client, http.HTTPClient) def test_construct_http_client_return_sessionclient(self): fake_session = mock.MagicMock() client = http._construct_http_client(session=fake_session) self.assertIsInstance(client, http.SessionClient) def test_raw_request(self): fake_response = utils.FakeSessionResponse( {'content-type': 'application/octet-stream'}, content="", status_code=200) fake_session = mock.MagicMock() fake_session.request.side_effect = [fake_response] client = http.SessionClient( session=fake_session, endpoint_override='http://magnum') resp = client.raw_request('GET', '/v1/bays') self.assertEqual( fake_session.request.call_args[1]['headers']['Content-Type'], 'application/octet-stream' ) self.assertEqual(fake_response, resp) python-magnumclient-2.8.0/magnumclient/tests/test_magnumclient.py000066400000000000000000000014231323411543400254440ustar00rootroot00000000000000# -*- 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_magnumclient ---------------------------------- Tests for `magnumclient` module. """ from magnumclient.tests import base class TestMagnumclient(base.TestCase): def test_something(self): pass python-magnumclient-2.8.0/magnumclient/tests/test_shell.py000066400000000000000000000321101323411543400240650ustar00rootroot00000000000000# 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 re import sys import argparse import fixtures from keystoneauth1 import fixture import mock import six from testtools import matchers from magnumclient import exceptions import magnumclient.shell from magnumclient.tests import utils FAKE_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_PROJECT_NAME': 'project_name', 'OS_AUTH_URL': 'http://no.where/v2.0'} FAKE_ENV2 = {'OS_USER_ID': 'user_id', 'OS_PASSWORD': 'password', 'OS_PROJECT_ID': 'project_id', 'OS_AUTH_URL': 'http://no.where/v2.0'} FAKE_ENV3 = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_PROJECT_ID': 'project_id', 'OS_AUTH_URL': 'http://no.where/v2.0'} FAKE_ENV4 = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_PROJECT_ID': 'project_id', 'OS_USER_DOMAIN_NAME': 'Default', 'OS_PROJECT_DOMAIN_NAME': 'Default', 'OS_AUTH_URL': 'http://no.where/v3'} def _create_ver_list(versions): return {'versions': {'values': versions}} class ParserTest(utils.TestCase): def setUp(self): super(ParserTest, self).setUp() self.parser = magnumclient.shell.MagnumClientArgumentParser() def test_ambiguous_option(self): self.parser.add_argument('--tic') self.parser.add_argument('--tac') try: self.parser.parse_args(['--t']) except SystemExit as err: self.assertEqual(2, err.code) else: self.fail('SystemExit not raised') class ShellTest(utils.TestCase): AUTH_URL = utils.FAKE_ENV['OS_AUTH_URL'] _msg_no_tenant_project = ("You must provide a project name or project id" " via --os-project-name, --os-project-id," " env[OS_PROJECT_NAME] or env[OS_PROJECT_ID]") def setUp(self): super(ShellTest, self).setUp() self.nc_util = mock.patch( 'magnumclient.common.cliutils.isunauthenticated').start() self.nc_util.return_value = False def test_positive_non_zero_float(self): output = magnumclient.shell.positive_non_zero_float(None) self.assertIsNone(output) output = magnumclient.shell.positive_non_zero_float(5) self.assertEqual(5, output) output = magnumclient.shell.positive_non_zero_float(5.0) self.assertEqual(5.0, output) self.assertRaises(argparse.ArgumentTypeError, magnumclient.shell.positive_non_zero_float, "Invalid") self.assertRaises(argparse.ArgumentTypeError, magnumclient.shell.positive_non_zero_float, -1) self.assertRaises(argparse.ArgumentTypeError, magnumclient.shell.positive_non_zero_float, 0) def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') def test_help(self): required = [ '.*?^usage: ', '.*?^See "magnum help COMMAND" for help on a specific command', ] stdout, stderr = self.shell('help') for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_help_on_subcommand(self): required = [ '.*?^usage: magnum bay-create', '.*?^Create a bay.', '.*?^Optional arguments:', ] stdout, stderr = self.shell('help bay-create') for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_help_no_options(self): required = [ '.*?^usage: ', '.*?^See "magnum help COMMAND" for help on a specific command', ] stdout, stderr = self.shell('') for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_bash_completion(self): stdout, stderr = self.shell('bash-completion') # just check we have some output required = [ '.*help', '.*bay-show', '.*--bay'] for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_no_username(self): required = ('You must provide a username via' ' either --os-username or via env[OS_USERNAME]') self.make_env(exclude='OS_USERNAME') try: self.shell('bay-list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') def test_no_user_id(self): required = ('You must provide a username via' ' either --os-username or via env[OS_USERNAME]') self.make_env(exclude='OS_USER_ID', fake_env=FAKE_ENV2) try: self.shell('bay-list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') def test_no_project_name(self): required = self._msg_no_tenant_project self.make_env(exclude='OS_PROJECT_NAME') try: self.shell('bay-list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') def test_no_project_id(self): required = self._msg_no_tenant_project self.make_env(exclude='OS_PROJECT_ID', fake_env=FAKE_ENV3) try: self.shell('bay-list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') def test_no_password(self): required = ('You must provide a password via either --os-password, ' 'env[OS_PASSWORD], or prompted response') self.make_env(exclude='OS_PASSWORD', fake_env=FAKE_ENV3) try: self.shell('bay-list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') def test_no_auth_url(self): required = ("You must provide an auth url via either " "--os-auth-url or via env[OS_AUTH_URL]") self.make_env(exclude='OS_AUTH_URL') try: self.shell('bay-list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') # FIXME(madhuri) Remove this harcoded v1 Client class. # In future, when a new version of API will # introduce, this needs to be dynamic then. @mock.patch('magnumclient.v1.client.Client') def test_service_type(self, mock_client): self.make_env() self.shell('bay-list') _, client_kwargs = mock_client.call_args_list[0] self.assertEqual('container-infra', client_kwargs['service_type']) @mock.patch('magnumclient.v1.bays_shell.do_bay_list') @mock.patch('magnumclient.v1.client.ksa_session') def test_insecure(self, mock_session, mock_bay_list): self.make_env() self.shell('--insecure bay-list') _, session_kwargs = mock_session.Session.call_args_list[0] self.assertEqual(False, session_kwargs['verify']) @mock.patch('sys.argv', ['magnum']) @mock.patch('sys.stdout', six.StringIO()) @mock.patch('sys.stderr', six.StringIO()) def test_main_noargs(self): # Ensure that main works with no command-line arguments try: magnumclient.shell.main() except SystemExit: self.fail('Unexpected SystemExit') # We expect the normal usage as a result self.assertIn('Command-line interface to the OpenStack Magnum API', sys.stdout.getvalue()) def _expected_client_kwargs(self): return { 'password': 'password', 'auth_token': None, 'auth_url': self.AUTH_URL, 'profile': None, 'cloud': None, 'interface': 'public', 'insecure': False, 'magnum_url': None, 'project_id': None, 'project_name': 'project_name', 'project_domain_id': None, 'project_domain_name': None, 'region_name': None, 'service_type': 'container-infra', 'user_id': None, 'username': 'username', 'user_domain_id': None, 'user_domain_name': None, 'api_version': 'latest' } @mock.patch('magnumclient.v1.client.Client') def _test_main_region(self, command, expected_region_name, mock_client): self.shell(command) expected_args = self._expected_client_kwargs() expected_args['region_name'] = expected_region_name mock_client.assert_called_once_with(**expected_args) def test_main_option_region(self): self.make_env() self._test_main_region('--os-region-name=myregion bay-list', 'myregion') def test_main_env_region(self): fake_env = dict(utils.FAKE_ENV, OS_REGION_NAME='myregion') self.make_env(fake_env=fake_env) self._test_main_region('bay-list', 'myregion') def test_main_no_region(self): self.make_env() self._test_main_region('bay-list', None) @mock.patch('magnumclient.v1.client.Client') def test_main_endpoint_public(self, mock_client): self.make_env() self.shell('--endpoint-type publicURL bay-list') expected_args = self._expected_client_kwargs() expected_args['interface'] = 'public' mock_client.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.client.Client') def test_main_endpoint_internal(self, mock_client): self.make_env() self.shell('--endpoint-type internalURL bay-list') expected_args = self._expected_client_kwargs() expected_args['interface'] = 'internal' mock_client.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.client.Client') def test_main_os_cloud(self, mock_client): expected_cloud = 'default' self.shell('--os-cloud %s bay-list' % expected_cloud) expected_args = self._expected_client_kwargs() expected_args['cloud'] = expected_cloud expected_args['username'] = None expected_args['password'] = None expected_args['project_name'] = None expected_args['auth_url'] = None mock_client.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.client.Client') def test_main_env_os_cloud(self, mock_client): expected_cloud = 'default' self.make_env(fake_env={'OS_CLOUD': expected_cloud}) self.shell('bay-list') expected_args = self._expected_client_kwargs() expected_args['cloud'] = expected_cloud expected_args['username'] = None expected_args['password'] = None expected_args['project_name'] = None expected_args['auth_url'] = None mock_client.assert_called_once_with(**expected_args) class ShellTestKeystoneV3(ShellTest): AUTH_URL = 'http://no.where/v3' def make_env(self, exclude=None, fake_env=FAKE_ENV): if 'OS_AUTH_URL' in fake_env: fake_env.update({'OS_AUTH_URL': self.AUTH_URL}) env = dict((k, v) for k, v in fake_env.items() if k != exclude) self.useFixture(fixtures.MonkeyPatch('os.environ', env)) def register_keystone_discovery_fixture(self, mreq): v3_url = "http://no.where/v3" v3_version = fixture.V3Discovery(v3_url) mreq.register_uri( 'GET', v3_url, json=_create_ver_list([v3_version]), status_code=200) @mock.patch('magnumclient.v1.client.Client') def test_main_endpoint_public(self, mock_client): self.make_env(fake_env=FAKE_ENV4) self.shell('--endpoint-type publicURL bay-list') expected_args = self._expected_client_kwargs() expected_args['interface'] = 'public' expected_args['project_id'] = 'project_id' expected_args['project_name'] = None expected_args['project_domain_name'] = 'Default' expected_args['user_domain_name'] = 'Default' mock_client.assert_called_once_with(**expected_args) python-magnumclient-2.8.0/magnumclient/tests/test_utils.py000066400000000000000000000234401323411543400241240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Copyright 2013 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections import mock from oslo_serialization import jsonutils as json import six import six.moves.builtins as __builtin__ import tempfile from magnumclient.common import cliutils from magnumclient.common import utils from magnumclient import exceptions as exc from magnumclient.tests import utils as test_utils class CommonFiltersTest(test_utils.BaseTestCase): def test_limit(self): result = utils.common_filters(limit=42) self.assertEqual(['limit=42'], result) def test_limit_0(self): result = utils.common_filters(limit=0) self.assertEqual(['limit=0'], result) def test_limit_negative_number(self): result = utils.common_filters(limit=-2) self.assertEqual(['limit=-2'], result) def test_other(self): for key in ('marker', 'sort_key', 'sort_dir'): result = utils.common_filters(**{key: 'test'}) self.assertEqual(['%s=test' % key], result) class SplitAndDeserializeTest(test_utils.BaseTestCase): def test_split_and_deserialize(self): ret = utils.split_and_deserialize('str=foo') self.assertEqual(('str', 'foo'), ret) ret = utils.split_and_deserialize('int=1') self.assertEqual(('int', 1), ret) ret = utils.split_and_deserialize('bool=false') self.assertEqual(('bool', False), ret) ret = utils.split_and_deserialize('list=[1, "foo", 2]') self.assertEqual(('list', [1, "foo", 2]), ret) ret = utils.split_and_deserialize('dict={"foo": 1}') self.assertEqual(('dict', {"foo": 1}), ret) ret = utils.split_and_deserialize('str_int="1"') self.assertEqual(('str_int', "1"), ret) def test_split_and_deserialize_fail(self): self.assertRaises(exc.CommandError, utils.split_and_deserialize, 'foo:bar') class ArgsArrayToPatchTest(test_utils.BaseTestCase): def test_args_array_to_patch(self): my_args = { 'attributes': ['str=foo', 'int=1', 'bool=true', 'list=[1, 2, 3]', 'dict={"foo": "bar"}'], 'op': 'add', } patch = utils.args_array_to_patch(my_args['op'], my_args['attributes']) self.assertEqual([{'op': 'add', 'value': 'foo', 'path': '/str'}, {'op': 'add', 'value': 1, 'path': '/int'}, {'op': 'add', 'value': True, 'path': '/bool'}, {'op': 'add', 'value': [1, 2, 3], 'path': '/list'}, {'op': 'add', 'value': {"foo": "bar"}, 'path': '/dict'}], patch) def test_args_array_to_patch_format_error(self): my_args = { 'attributes': ['foobar'], 'op': 'add', } self.assertRaises(exc.CommandError, utils.args_array_to_patch, my_args['op'], my_args['attributes']) def test_args_array_to_patch_remove(self): my_args = { 'attributes': ['/foo', 'extra/bar'], 'op': 'remove', } patch = utils.args_array_to_patch(my_args['op'], my_args['attributes']) self.assertEqual([{'op': 'remove', 'path': '/foo'}, {'op': 'remove', 'path': '/extra/bar'}], patch) def test_args_array_to_patch_invalid_op(self): my_args = { 'attributes': ['/foo', 'extra/bar'], 'op': 'invalid', } self.assertRaises(exc.CommandError, utils.args_array_to_patch, my_args['op'], my_args['attributes']) class FormatLabelsTest(test_utils.BaseTestCase): def test_format_label_none(self): self.assertEqual({}, utils.format_labels(None)) def test_format_labels(self): l = utils.format_labels([ 'K1=V1,K2=V2,' 'K3=V3,K4=V4,' 'K5=V5']) self.assertEqual({'K1': 'V1', 'K2': 'V2', 'K3': 'V3', 'K4': 'V4', 'K5': 'V5' }, l) def test_format_labels_semicolon(self): l = utils.format_labels([ 'K1=V1;K2=V2;' 'K3=V3;K4=V4;' 'K5=V5']) self.assertEqual({'K1': 'V1', 'K2': 'V2', 'K3': 'V3', 'K4': 'V4', 'K5': 'V5' }, l) def test_format_labels_mix_commas_semicolon(self): l = utils.format_labels([ 'K1=V1,K2=V2,' 'K3=V3;K4=V4,' 'K5=V5']) self.assertEqual({'K1': 'V1', 'K2': 'V2', 'K3': 'V3', 'K4': 'V4', 'K5': 'V5' }, l) def test_format_labels_split(self): l = utils.format_labels([ 'K1=V1,' 'K2=V22222222222222222222222222222' '222222222222222222222222222,' 'K3=3.3.3.3']) self.assertEqual({'K1': 'V1', 'K2': 'V22222222222222222222222222222' '222222222222222222222222222', 'K3': '3.3.3.3'}, l) def test_format_labels_multiple(self): l = utils.format_labels([ 'K1=V1', 'K2=V22222222222222222222222222222' '222222222222222222222222222', 'K3=3.3.3.3']) self.assertEqual({'K1': 'V1', 'K2': 'V22222222222222222222222222222' '222222222222222222222222222', 'K3': '3.3.3.3'}, l) def test_format_labels_multiple_colon_values(self): l = utils.format_labels([ 'K1=V1', 'K2=V2,V22,V222,V2222', 'K3=3.3.3.3']) self.assertEqual({'K1': 'V1', 'K2': 'V2,V22,V222,V2222', 'K3': '3.3.3.3'}, l) def test_format_labels_parse_comma_false(self): l = utils.format_labels( ['K1=V1,K2=2.2.2.2,K=V'], parse_comma=False) self.assertEqual({'K1': 'V1,K2=2.2.2.2,K=V'}, l) def test_format_labels_multiple_values_per_labels(self): l = utils.format_labels([ 'K1=V1', 'K1=V2']) self.assertEqual({'K1': 'V1,V2'}, l) def test_format_label_special_label(self): labels = ['K1=V1,K22.2.2.2'] l = utils.format_labels( labels, parse_comma=True) self.assertEqual({'K1': 'V1,K22.2.2.2'}, l) def test_format_multiple_bad_label(self): labels = ['K1=V1', 'K22.2.2.2'] ex = self.assertRaises(exc.CommandError, utils.format_labels, labels) self.assertEqual('labels must be a list of KEY=VALUE ' 'not K22.2.2.2', str(ex)) class CliUtilsTest(test_utils.BaseTestCase): def test_keys_and_vals_to_strs(self): dict_in = {six.u('a'): six.u('1'), six.u('b'): {six.u('x'): 1, 'y': six.u('2'), six.u('z'): six.u('3')}, 'c': 7} dict_exp = collections.OrderedDict([ ('a', '1'), ('b', collections.OrderedDict([ ('x', 1), ('y', '2'), ('z', '3')])), ('c', 7)]) dict_out = cliutils.keys_and_vals_to_strs(dict_in) dict_act = collections.OrderedDict([ ('a', dict_out['a']), ('b', collections.OrderedDict(sorted(dict_out['b'].items()))), ('c', dict_out['c'])]) self.assertEqual(six.text_type(dict_exp), six.text_type(dict_act)) class HandleJsonFromFileTest(test_utils.BaseTestCase): def test_handle_json_from_file_bad_json(self): contents = 'foo' with tempfile.NamedTemporaryFile(mode='w') as f: f.write(contents) f.flush() self.assertRaisesRegex(exc.InvalidAttribute, 'For JSON', utils.handle_json_from_file, f.name) def test_handle_json_from_file_valid_file(self): contents = '{"step": "upgrade", "interface": "deploy"}' with tempfile.NamedTemporaryFile(mode='w') as f: f.write(contents) f.flush() steps = utils.handle_json_from_file(f.name) self.assertEqual(json.loads(contents), steps) @mock.patch.object(__builtin__, 'open', autospec=True) def test_handle_json_from_file_open_fail(self, mock_open): mock_file_object = mock.MagicMock() mock_file_handle = mock.MagicMock() mock_file_handle.__enter__.return_value = mock_file_object mock_open.return_value = mock_file_handle mock_file_object.read.side_effect = IOError with tempfile.NamedTemporaryFile(mode='w') as f: self.assertRaisesRegex(exc.InvalidAttribute, "from file", utils.handle_json_from_file, f.name) mock_open.assert_called_once_with(f.name, 'r') mock_file_object.read.assert_called_once_with() python-magnumclient-2.8.0/magnumclient/tests/utils.py000066400000000000000000000137631323411543400230740ustar00rootroot00000000000000# Copyright 2012 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import datetime import os import sys import fixtures import six import testtools from magnumclient.common import httpclient as http from magnumclient import shell FAKE_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_PROJECT_NAME': 'project_name', 'OS_AUTH_URL': 'http://no.where/v2.0'} class BaseTestCase(testtools.TestCase): def setUp(self): super(BaseTestCase, self).setUp() self.useFixture(fixtures.FakeLogger()) class FakeAPI(object): def __init__(self, responses): self.responses = responses self.calls = [] def _request(self, method, url, headers=None, body=None): call = (method, url, headers or {}, body) self.calls.append(call) return self.responses[url][method] def raw_request(self, *args, **kwargs): response = self._request(*args, **kwargs) body_iter = http.ResponseBodyIterator(six.StringIO(response[1])) return FakeResponse(response[0]), body_iter def json_request(self, *args, **kwargs): response = self._request(*args, **kwargs) return FakeResponse(response[0]), response[1] class FakeConnection(object): def __init__(self, response=None, **kwargs): self._response = six.moves.queue.Queue() self._response.put(response) self._last_request = None self._exc = kwargs['exc'] if 'exc' in kwargs else None if 'redirect_resp' in kwargs: self._response.put(kwargs['redirect_resp']) def request(self, method, conn_url, **kwargs): self._last_request = (method, conn_url, kwargs) if self._exc: raise self._exc def setresponse(self, response): self._response = response def getresponse(self): return self._response.get() class FakeResponse(object): def __init__(self, headers, body=None, version=None, status=None, reason=None): """Fake object to help testing. :param headers: dict representing HTTP response headers :param body: file-like object """ self.headers = headers self.body = body self.version = version self.status = status self.reason = reason def __getitem__(self, key): if key is 'location': return 'fake_url' else: return None 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) class FakeServiceCatalog(object): def url_for(self, endpoint_type, service_type, attr=None, filter_value=None): if attr == 'region' and filter_value: return 'http://regionhost:6385/v1/f14b41234' else: return 'http://localhost:6385/v1/f14b41234' class FakeKeystone(object): service_catalog = FakeServiceCatalog() timestamp = datetime.datetime.utcnow() + datetime.timedelta(days=5) def __init__(self, auth_token): self.auth_token = auth_token self.auth_ref = { 'token': {'expires': FakeKeystone.timestamp.strftime( '%Y-%m-%dT%H:%M:%S.%f'), 'id': 'd1a541311782870742235'} } class TestCase(testtools.TestCase): TEST_REQUEST_BASE = { 'verify': True, } def setUp(self): super(TestCase, self).setUp() 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)) def make_env(self, exclude=None, fake_env=FAKE_ENV): env = dict((k, v) for k, v in fake_env.items() if k != exclude) self.useFixture(fixtures.MonkeyPatch('os.environ', env)) def shell(self, argstr, exitcodes=(0,)): orig = sys.stdout orig_stderr = sys.stderr try: sys.stdout = six.StringIO() sys.stderr = six.StringIO() _shell = shell.OpenStackMagnumShell() _shell.main(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertIn(exc_value.code, exitcodes) finally: stdout = sys.stdout.getvalue() sys.stdout.close() sys.stdout = orig stderr = sys.stderr.getvalue() sys.stderr.close() sys.stderr = orig_stderr return (stdout, stderr) class FakeSessionResponse(object): def __init__(self, headers, content=None, status_code=None): self.headers = headers self.content = content self.status_code = status_code class FakeSession(object): def __init__(self, headers, content=None, status_code=None): self.headers = headers self.content = content self.status_code = status_code def request(self, url, method, **kwargs): return FakeSessionResponse(self.headers, self.content, self.status_code) python-magnumclient-2.8.0/magnumclient/tests/v1/000077500000000000000000000000001323411543400216765ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/v1/__init__.py000066400000000000000000000000001323411543400237750ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/tests/v1/shell_test_base.py000066400000000000000000000064611323411543400254170ustar00rootroot00000000000000# 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 re import mock from testtools import matchers from magnumclient.tests import utils FAKE_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_PROJECT_NAME': 'project_name', 'OS_AUTH_URL': 'http://no.where/v2.0', 'BYPASS_URL': 'http://magnum'} class TestCommandLineArgument(utils.TestCase): _unrecognized_arg_error = [ '.*?^usage: ', '.*?^error: unrecognized arguments:', ".*?^Try 'magnum help ' for more information.", ] _mandatory_group_arg_error = [ '.*?^usage: ', '.*?^error: one of the arguments', ".*?^Try 'magnum help ", ] _too_many_group_arg_error = [ '.*?^usage: ', '.*?^error: (argument \-\-[a-z\-]*: not allowed with argument )', ".*?^Try 'magnum help ", ] _mandatory_arg_error = [ '.*?^usage: ', '.*?^error: (the following arguments|argument)', ".*?^Try 'magnum help ", ] _duplicate_arg_error = [ '.*?^usage: ', '.*?^error: (Duplicate "<.*>" arguments:)', ".*?^Try 'magnum help ", ] _deprecated_warning = [ '.*(WARNING: The \-\-[a-z\-]* parameter is deprecated)+', ('.*(Use the [\<\-a-z\-\>]* (positional )*parameter to avoid seeing ' 'this message)+') ] _few_argument_error = [ '.*?^usage: magnum ', '.*?^error: (the following arguments|too few arguments)', ".*?^Try 'magnum help ", ] _invalid_value_error = [ '.*?^usage: ', '.*?^error: argument .*: invalid .* value:', ".*?^Try 'magnum help ", ] _bay_status_error = [ '.*?^Bay status for', ] def setUp(self): super(TestCommandLineArgument, self).setUp() self.make_env(fake_env=FAKE_ENV) session_client = mock.patch( 'magnumclient.common.httpclient.SessionClient') session_client.start() loader = mock.patch('keystoneauth1.loading.get_plugin_loader') loader.start() session = mock.patch('keystoneauth1.session.Session') session.start() self.addCleanup(session_client.stop) self.addCleanup(loader.stop) self.addCleanup(session.stop) def _test_arg_success(self, command, keyword=None): stdout, stderr = self.shell(command) if keyword: self.assertIn(keyword, (stdout + stderr)) def _test_arg_failure(self, command, error_msg): stdout, stderr = self.shell(command, (2,)) for line in error_msg: self.assertThat((stdout + stderr), matchers.MatchesRegex(line, re.DOTALL | re.MULTILINE)) python-magnumclient-2.8.0/magnumclient/tests/v1/test_baymodels.py000066400000000000000000000307551323411543400253000ustar00rootroot00000000000000# 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 testtools from testtools import matchers from magnumclient import exceptions from magnumclient.tests import utils from magnumclient.v1 import baymodels BAYMODEL1 = {'id': 123, 'uuid': '66666666-7777-8888-9999-000000000001', 'name': 'baymodel1', 'image_id': 'baymodel1-image', 'master_flavor_id': 'm1.tiny', 'flavor_id': 'm1.small', 'keypair_id': 'keypair1', 'external_network_id': 'd1f02cfb-d27f-4068-9332-84d907cb0e21', 'fixed_network': 'private', 'fixed_subnet': 'private-subnet', 'network_driver': 'libnetwork', 'volume_driver': 'rexray', 'dns_nameserver': '8.8.1.1', 'docker_volume_size': '71', 'docker_storage_driver': 'devicemapper', 'coe': 'swarm', 'http_proxy': 'http_proxy', 'https_proxy': 'https_proxy', 'no_proxy': 'no_proxy', 'labels': 'key1=val1,key11=val11', 'tls_disabled': False, 'public': False, 'registry_enabled': False, 'master_lb_enabled': True, 'floating_ip_enabled': True, } BAYMODEL2 = {'id': 124, 'uuid': '66666666-7777-8888-9999-000000000002', 'name': 'baymodel2', 'image_id': 'baymodel2-image', 'flavor_id': 'm2.small', 'master_flavor_id': 'm2.tiny', 'keypair_id': 'keypair2', 'external_network_id': 'd1f02cfb-d27f-4068-9332-84d907cb0e22', 'fixed_network': 'private2', 'network_driver': 'flannel', 'volume_driver': 'cinder', 'dns_nameserver': '8.8.1.2', 'docker_volume_size': '50', 'docker_storage_driver': 'overlay', 'coe': 'kubernetes', 'labels': 'key2=val2,key22=val22', 'tls_disabled': True, 'public': True, 'registry_enabled': True} CREATE_BAYMODEL = copy.deepcopy(BAYMODEL1) del CREATE_BAYMODEL['id'] del CREATE_BAYMODEL['uuid'] UPDATED_BAYMODEL = copy.deepcopy(BAYMODEL1) NEW_NAME = 'newbay' UPDATED_BAYMODEL['name'] = NEW_NAME fake_responses = { '/v1/baymodels': { 'GET': ( {}, {'baymodels': [BAYMODEL1, BAYMODEL2]}, ), 'POST': ( {}, CREATE_BAYMODEL, ), }, '/v1/baymodels/%s' % BAYMODEL1['id']: { 'GET': ( {}, BAYMODEL1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_BAYMODEL, ), }, '/v1/baymodels/%s' % BAYMODEL1['name']: { 'GET': ( {}, BAYMODEL1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_BAYMODEL, ), }, '/v1/baymodels/detail': { 'GET': ( {}, {'baymodels': [BAYMODEL1, BAYMODEL2]}, ), }, '/v1/baymodels/?limit=2': { 'GET': ( {}, {'baymodels': [BAYMODEL1, BAYMODEL2]}, ), }, '/v1/baymodels/?marker=%s' % BAYMODEL2['uuid']: { 'GET': ( {}, {'baymodels': [BAYMODEL1, BAYMODEL2]}, ), }, '/v1/baymodels/?limit=2&marker=%s' % BAYMODEL2['uuid']: { 'GET': ( {}, {'baymodels': [BAYMODEL1, BAYMODEL2]}, ), }, '/v1/baymodels/?sort_dir=asc': { 'GET': ( {}, {'baymodels': [BAYMODEL1, BAYMODEL2]}, ), }, '/v1/baymodels/?sort_key=uuid': { 'GET': ( {}, {'baymodels': [BAYMODEL1, BAYMODEL2]}, ), }, '/v1/baymodels/?sort_key=uuid&sort_dir=desc': { 'GET': ( {}, {'baymodels': [BAYMODEL2, BAYMODEL1]}, ), }, } class BayModelManagerTest(testtools.TestCase): def setUp(self): super(BayModelManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = baymodels.BayModelManager(self.api) def test_baymodel_list(self): baymodels = self.mgr.list() expect = [ ('GET', '/v1/baymodels', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(baymodels, matchers.HasLength(2)) def _test_baymodel_list_with_filters( self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False, expect=[]): baymodels_filter = self.mgr.list(limit=limit, marker=marker, sort_key=sort_key, sort_dir=sort_dir, detail=detail) self.assertEqual(expect, self.api.calls) self.assertThat(baymodels_filter, matchers.HasLength(2)) def test_baymodel_list_with_detail(self): expect = [ ('GET', '/v1/baymodels/detail', {}, None), ] self._test_baymodel_list_with_filters( detail=True, expect=expect) def test_baymodel_list_with_limit(self): expect = [ ('GET', '/v1/baymodels/?limit=2', {}, None), ] self._test_baymodel_list_with_filters( limit=2, expect=expect) def test_baymodel_list_with_marker(self): expect = [ ('GET', '/v1/baymodels/?marker=%s' % BAYMODEL2['uuid'], {}, None), ] self._test_baymodel_list_with_filters( marker=BAYMODEL2['uuid'], expect=expect) def test_baymodel_list_with_marker_limit(self): expect = [ ('GET', '/v1/baymodels/?limit=2&marker=%s' % BAYMODEL2['uuid'], {}, None), ] self._test_baymodel_list_with_filters( limit=2, marker=BAYMODEL2['uuid'], expect=expect) def test_baymodel_list_with_sort_dir(self): expect = [ ('GET', '/v1/baymodels/?sort_dir=asc', {}, None), ] self._test_baymodel_list_with_filters( sort_dir='asc', expect=expect) def test_baymodel_list_with_sort_key(self): expect = [ ('GET', '/v1/baymodels/?sort_key=uuid', {}, None), ] self._test_baymodel_list_with_filters( sort_key='uuid', expect=expect) def test_baymodel_list_with_sort_key_dir(self): expect = [ ('GET', '/v1/baymodels/?sort_key=uuid&sort_dir=desc', {}, None), ] self._test_baymodel_list_with_filters( sort_key='uuid', sort_dir='desc', expect=expect) def test_baymodel_show_by_id(self): baymodel = self.mgr.get(BAYMODEL1['id']) expect = [ ('GET', '/v1/baymodels/%s' % BAYMODEL1['id'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(BAYMODEL1['name'], baymodel.name) self.assertEqual(BAYMODEL1['image_id'], baymodel.image_id) self.assertEqual(BAYMODEL1['docker_volume_size'], baymodel.docker_volume_size) self.assertEqual(BAYMODEL1['docker_storage_driver'], baymodel.docker_storage_driver) self.assertEqual(BAYMODEL1['fixed_network'], baymodel.fixed_network) self.assertEqual(BAYMODEL1['fixed_subnet'], baymodel.fixed_subnet) self.assertEqual(BAYMODEL1['coe'], baymodel.coe) self.assertEqual(BAYMODEL1['http_proxy'], baymodel.http_proxy) self.assertEqual(BAYMODEL1['https_proxy'], baymodel.https_proxy) self.assertEqual(BAYMODEL1['no_proxy'], baymodel.no_proxy) self.assertEqual(BAYMODEL1['network_driver'], baymodel.network_driver) self.assertEqual(BAYMODEL1['volume_driver'], baymodel.volume_driver) self.assertEqual(BAYMODEL1['labels'], baymodel.labels) self.assertEqual(BAYMODEL1['tls_disabled'], baymodel.tls_disabled) self.assertEqual(BAYMODEL1['public'], baymodel.public) self.assertEqual(BAYMODEL1['registry_enabled'], baymodel.registry_enabled) self.assertEqual(BAYMODEL1['master_lb_enabled'], baymodel.master_lb_enabled) self.assertEqual(BAYMODEL1['floating_ip_enabled'], baymodel.floating_ip_enabled) def test_baymodel_show_by_name(self): baymodel = self.mgr.get(BAYMODEL1['name']) expect = [ ('GET', '/v1/baymodels/%s' % BAYMODEL1['name'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(BAYMODEL1['name'], baymodel.name) self.assertEqual(BAYMODEL1['image_id'], baymodel.image_id) self.assertEqual(BAYMODEL1['docker_volume_size'], baymodel.docker_volume_size) self.assertEqual(BAYMODEL1['docker_storage_driver'], baymodel.docker_storage_driver) self.assertEqual(BAYMODEL1['fixed_network'], baymodel.fixed_network) self.assertEqual(BAYMODEL1['fixed_subnet'], baymodel.fixed_subnet) self.assertEqual(BAYMODEL1['coe'], baymodel.coe) self.assertEqual(BAYMODEL1['http_proxy'], baymodel.http_proxy) self.assertEqual(BAYMODEL1['https_proxy'], baymodel.https_proxy) self.assertEqual(BAYMODEL1['no_proxy'], baymodel.no_proxy) self.assertEqual(BAYMODEL1['network_driver'], baymodel.network_driver) self.assertEqual(BAYMODEL1['volume_driver'], baymodel.volume_driver) self.assertEqual(BAYMODEL1['labels'], baymodel.labels) self.assertEqual(BAYMODEL1['tls_disabled'], baymodel.tls_disabled) self.assertEqual(BAYMODEL1['public'], baymodel.public) self.assertEqual(BAYMODEL1['registry_enabled'], baymodel.registry_enabled) self.assertEqual(BAYMODEL1['master_lb_enabled'], baymodel.master_lb_enabled) self.assertEqual(BAYMODEL1['floating_ip_enabled'], baymodel.floating_ip_enabled) def test_baymodel_create(self): baymodel = self.mgr.create(**CREATE_BAYMODEL) expect = [ ('POST', '/v1/baymodels', {}, CREATE_BAYMODEL), ] self.assertEqual(expect, self.api.calls) self.assertTrue(baymodel) self.assertEqual(BAYMODEL1['docker_volume_size'], baymodel.docker_volume_size) self.assertEqual(BAYMODEL1['docker_storage_driver'], baymodel.docker_storage_driver) def test_baymodel_create_fail(self): CREATE_BAYMODEL_FAIL = copy.deepcopy(CREATE_BAYMODEL) CREATE_BAYMODEL_FAIL["wrong_key"] = "wrong" self.assertRaisesRegex(exceptions.InvalidAttribute, ("Key must be in %s" % ','.join(baymodels.CREATION_ATTRIBUTES)), self.mgr.create, **CREATE_BAYMODEL_FAIL) self.assertEqual([], self.api.calls) def test_baymodel_delete_by_id(self): baymodel = self.mgr.delete(BAYMODEL1['id']) expect = [ ('DELETE', '/v1/baymodels/%s' % BAYMODEL1['id'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(baymodel) def test_baymodel_delete_by_name(self): baymodel = self.mgr.delete(BAYMODEL1['name']) expect = [ ('DELETE', '/v1/baymodels/%s' % BAYMODEL1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(baymodel) def test_baymodel_update(self): patch = {'op': 'replace', 'value': NEW_NAME, 'path': '/name'} baymodel = self.mgr.update(id=BAYMODEL1['id'], patch=patch) expect = [ ('PATCH', '/v1/baymodels/%s' % BAYMODEL1['id'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_NAME, baymodel.name) python-magnumclient-2.8.0/magnumclient/tests/v1/test_baymodels_shell.py000066400000000000000000000763231323411543400264700ustar00rootroot00000000000000# 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 mock from magnumclient.common.apiclient import exceptions from magnumclient.tests.v1 import shell_test_base from magnumclient.v1.baymodels import BayModel class FakeBayModel(BayModel): def __init__(self, manager=None, info={}, **kwargs): BayModel.__init__(self, manager=manager, info=info) self.apiserver_port = kwargs.get('apiserver_port', None) self.uuid = kwargs.get('uuid', 'x') self.links = kwargs.get('links', []) self.server_type = kwargs.get('server_type', 'vm') self.image_id = kwargs.get('image_id', 'x') self.tls_disabled = kwargs.get('tls_disabled', False) self.registry_enabled = kwargs.get('registry_enabled', False) self.coe = kwargs.get('coe', 'x') self.public = kwargs.get('public', False) self.name = kwargs.get('name', 'x') class ShellTest(shell_test_base.TestCommandLineArgument): def _get_expected_args_list(self, limit=None, sort_dir=None, sort_key=None, detail=False): expected_args = {} expected_args['limit'] = limit expected_args['sort_dir'] = sort_dir expected_args['sort_key'] = sort_key expected_args['detail'] = detail return expected_args def _get_expected_args(self, image_id, external_network_id, coe, master_flavor_id=None, name=None, keypair_id=None, fixed_network=None, fixed_subnet=None, network_driver=None, volume_driver=None, dns_nameserver='8.8.8.8', flavor_id='m1.medium', docker_storage_driver='devicemapper', docker_volume_size=None, http_proxy=None, https_proxy=None, no_proxy=None, labels={}, tls_disabled=False, public=False, master_lb_enabled=False, server_type='vm', registry_enabled=False, floating_ip_enabled=None): expected_args = {} expected_args['image_id'] = image_id expected_args['external_network_id'] = external_network_id expected_args['coe'] = coe expected_args['master_flavor_id'] = master_flavor_id expected_args['name'] = name expected_args['keypair_id'] = keypair_id expected_args['fixed_network'] = fixed_network expected_args['fixed_subnet'] = fixed_subnet expected_args['network_driver'] = network_driver expected_args['volume_driver'] = volume_driver expected_args['dns_nameserver'] = dns_nameserver expected_args['flavor_id'] = flavor_id expected_args['docker_volume_size'] = docker_volume_size expected_args['docker_storage_driver'] = docker_storage_driver expected_args['http_proxy'] = http_proxy expected_args['https_proxy'] = https_proxy expected_args['no_proxy'] = no_proxy expected_args['labels'] = labels expected_args['tls_disabled'] = tls_disabled expected_args['public'] = public expected_args['master_lb_enabled'] = master_lb_enabled expected_args['server_type'] = server_type expected_args['registry_enabled'] = registry_enabled return expected_args @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--image-id test_image ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--coe swarm ' '--dns-nameserver test_dns ' '--flavor-id test_flavor ' '--fixed-network private ' '--fixed-subnet private-subnet ' '--volume-driver test_volume ' '--network-driver test_driver ' '--labels key=val ' '--master-flavor-id test_flavor ' '--docker-volume-size 10 ' '--docker-storage-driver devicemapper ' '--public ' '--server-type vm ' '--master-lb-enabled ' '--floating-ip-enabled ') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', dns_nameserver='test_dns', public=True, flavor_id='test_flavor', master_flavor_id='test_flavor', fixed_network='private', fixed_subnet='private-subnet', server_type='vm', network_driver='test_driver', volume_driver='test_volume', docker_storage_driver='devicemapper', docker_volume_size=10, master_lb_enabled=True, labels={'key': 'val'}) expected_args['floating_ip_enabled'] = True mock_create.assert_called_with(**expected_args) self._test_arg_success('baymodel-create ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe kubernetes ' '--name test ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='kubernetes', external_network_id='test_net', server_type='vm') mock_create.assert_called_with(**expected_args) self._test_arg_success('baymodel-create ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe kubernetes ' '--name test ' '--server-type vm ' '--floating-ip-disabled ') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='kubernetes', external_network_id='test_net', server_type='vm', floating_ip_enabled=False) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_success_no_servertype(self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--image-id test_image ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--coe swarm ' '--dns-nameserver test_dns ' '--flavor-id test_flavor ' '--fixed-network public ' '--network-driver test_driver ' '--labels key=val ' '--master-flavor-id test_flavor ' '--docker-volume-size 10 ' '--docker-storage-driver devicemapper ' '--public ') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', dns_nameserver='test_dns', public=True, flavor_id='test_flavor', master_flavor_id='test_flavor', fixed_network='public', network_driver='test_driver', docker_storage_driver='devicemapper', docker_volume_size=10, labels={'key': 'val'}) mock_create.assert_called_with(**expected_args) self._test_arg_success('baymodel-create ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe kubernetes ' '--name test ') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='kubernetes', external_network_id='test_net') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_success_with_registry_enabled( self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--network-driver test_driver ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--registry-enabled') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', network_driver='test_driver', registry_enabled=True) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_public_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --network-driver test_driver ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--public ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', public=True, server_type='vm', network_driver='test_driver') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_success_with_master_flavor(self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--image-id test_image ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--coe swarm ' '--dns-nameserver test_dns ' '--master-flavor-id test_flavor') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', dns_nameserver='test_dns', master_flavor_id='test_flavor') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_docker_vol_size_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --docker-volume-size 4514 ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', docker_volume_size=4514) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_docker_storage_driver_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--docker-storage-driver devicemapper ' '--coe swarm' ) expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', docker_storage_driver='devicemapper') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_fixed_network_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', fixed_network='private', external_network_id='test_net', server_type='vm') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_network_driver_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --network-driver test_driver ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', network_driver='test_driver') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_volume_driver_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --volume-driver test_volume ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', volume_driver='test_volume') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_http_proxy_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--http-proxy http_proxy ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', fixed_network='private', server_type='vm', http_proxy='http_proxy') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_https_proxy_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--https-proxy https_proxy ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', fixed_network='private', server_type='vm', https_proxy='https_proxy') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_no_proxy_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--no-proxy no_proxy ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', fixed_network='private', server_type='vm', no_proxy='no_proxy') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_labels_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--labels key=val ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', labels={'key': 'val'}) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_separate_labels_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--labels key1=val1 ' '--labels key2=val2 ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', labels={'key1': 'val1', 'key2': 'val2'}) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_combined_labels_success(self, mock_create): self._test_arg_success('baymodel-create ' '--name test ' '--labels key1=val1,key2=val2 ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', labels={'key1': 'val1', 'key2': 'val2'}) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.create') def test_baymodel_create_failure_few_arg(self, mock_create): self._test_arg_failure('baymodel-create ' '--name test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('baymodel-create ' '--image-id test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('baymodel-create ' '--keypair-id test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('baymodel-create ' '--external-network-id test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('baymodel-create ' '--coe test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('baymodel-create ' '--server-type test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('baymodel-create', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') def test_baymodel_show_success(self, mock_show): self._test_arg_success('baymodel-show xxx') mock_show.assert_called_once_with('xxx') @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') def test_baymodel_show_failure_no_arg(self, mock_show): self._test_arg_failure('baymodel-show', self._few_argument_error) mock_show.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.delete') def test_baymodel_delete_success(self, mock_delete): self._test_arg_success('baymodel-delete xxx') mock_delete.assert_called_once_with('xxx') @mock.patch('magnumclient.v1.baymodels.BayModelManager.delete') def test_baymodel_delete_multiple_id_success(self, mock_delete): self._test_arg_success('baymodel-delete xxx xyz') calls = [mock.call('xxx'), mock.call('xyz')] mock_delete.assert_has_calls(calls) @mock.patch('magnumclient.v1.baymodels.BayModelManager.delete') def test_baymodel_delete_failure_no_arg(self, mock_delete): self._test_arg_failure('baymodel-delete', self._few_argument_error) mock_delete.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.update') def test_baymodel_update_success(self, mock_update): self._test_arg_success('baymodel-update test add test=test') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}] mock_update.assert_called_once_with('test', patch) @mock.patch('magnumclient.v1.baymodels.BayModelManager.update') def test_baymodel_update_success_many_attribute(self, mock_update): self._test_arg_success('baymodel-update test ' 'add test=test test1=test1') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}, {'op': 'add', 'path': '/test1', 'value': 'test1'}] mock_update.assert_called_once_with('test', patch) @mock.patch('magnumclient.v1.baymodels.BayModelManager.update') def test_baymodel_update_failure_wrong_op(self, mock_update): _error_msg = [ '.*?^usage: magnum baymodel-update ', '.*?^error: argument : invalid choice: ', ".*?^Try 'magnum help baymodel-update' for more information." ] self._test_arg_failure('baymodel-update test wrong test=test', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.update') def test_baymodel_update_failure_few_args(self, mock_update): _error_msg = [ '.*?^usage: magnum baymodel-update ', '.*?^error: (the following arguments|too few arguments)', ".*?^Try 'magnum help baymodel-update' for more information." ] self._test_arg_failure('baymodel-update', _error_msg) mock_update.assert_not_called() self._test_arg_failure('baymodel-update test', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.list') def test_baymodel_list_success(self, mock_list): self._test_arg_success('baymodel-list') expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.list') def test_baymodel_list_success_with_arg(self, mock_list): self._test_arg_success('baymodel-list ' '--limit 1 ' '--sort-dir asc ' '--sort-key uuid') expected_args = self._get_expected_args_list(1, 'asc', 'uuid') mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.list') def test_baymodel_list_success_detailed(self, mock_list): self._test_arg_success('baymodel-list ' '--detail') expected_args = self._get_expected_args_list(detail=True) mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.list') def test_baymodel_list_ignored_duplicated_field(self, mock_list): mock_list.return_value = [FakeBayModel()] self._test_arg_success('baymodel-list --fields coe,coe,coe,name,name', keyword='\n| uuid | name | Coe |\n') # Output should be # +------+------+-----+ # | uuid | name | Coe | # +------+------+-----+ # | x | x | x | # +------+------+-----+ expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.list') def test_baymodel_list_failure_with_invalid_field(self, mock_list): mock_list.return_value = [FakeBayModel()] _error_msg = [".*?^Non-existent fields are specified: ['xxx','zzz']"] self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'baymodel-list --fields xxx,coe,zzz', _error_msg) expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.list') def test_baymodel_list_failure_invalid_arg(self, mock_list): _error_msg = [ '.*?^usage: magnum baymodel-list ', '.*?^error: argument --sort-dir: invalid choice: ', ".*?^Try 'magnum help baymodel-list' for more information." ] self._test_arg_failure('baymodel-list --sort-dir aaa', _error_msg) mock_list.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.list') def test_baymodel_list_failure(self, mock_list): self._test_arg_failure('baymodel-list --wrong', self._unrecognized_arg_error) mock_list.assert_not_called() python-magnumclient-2.8.0/magnumclient/tests/v1/test_bays.py000066400000000000000000000220051323411543400242440ustar00rootroot00000000000000# 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 testtools from testtools import matchers from magnumclient import exceptions from magnumclient.tests import utils from magnumclient.v1 import bays BAY1 = {'id': 123, 'uuid': '66666666-7777-8888-9999-000000000001', 'name': 'bay1', 'baymodel_id': 'e74c40e0-d825-11e2-a28f-0800200c9a61', 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a51', 'api_address': '172.17.2.1', 'node_addresses': ['172.17.2.3'], 'node_count': 2, 'master_count': 1, } BAY2 = {'id': 124, 'uuid': '66666666-7777-8888-9999-000000000002', 'name': 'bay2', 'baymodel_id': 'e74c40e0-d825-11e2-a28f-0800200c9a62', 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', 'api_address': '172.17.2.2', 'node_addresses': ['172.17.2.4'], 'node_count': 2, 'master_count': 1, } CREATE_BAY = copy.deepcopy(BAY1) del CREATE_BAY['id'] del CREATE_BAY['uuid'] del CREATE_BAY['stack_id'] del CREATE_BAY['api_address'] del CREATE_BAY['node_addresses'] UPDATED_BAY = copy.deepcopy(BAY1) NEW_NAME = 'newbay' UPDATED_BAY['name'] = NEW_NAME fake_responses = { '/v1/bays': { 'GET': ( {}, {'bays': [BAY1, BAY2]}, ), 'POST': ( {}, CREATE_BAY, ), }, '/v1/bays/%s' % BAY1['id']: { 'GET': ( {}, BAY1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_BAY, ), }, '/v1/bays/%s/?rollback=True' % BAY1['id']: { 'PATCH': ( {}, UPDATED_BAY, ), }, '/v1/bays/%s' % BAY1['name']: { 'GET': ( {}, BAY1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_BAY, ), }, '/v1/bays/?limit=2': { 'GET': ( {}, {'bays': [BAY1, BAY2]}, ), }, '/v1/bays/?marker=%s' % BAY2['uuid']: { 'GET': ( {}, {'bays': [BAY1, BAY2]}, ), }, '/v1/bays/?limit=2&marker=%s' % BAY2['uuid']: { 'GET': ( {}, {'bays': [BAY1, BAY2]}, ), }, '/v1/bays/?sort_dir=asc': { 'GET': ( {}, {'bays': [BAY1, BAY2]}, ), }, '/v1/bays/?sort_key=uuid': { 'GET': ( {}, {'bays': [BAY1, BAY2]}, ), }, '/v1/bays/?sort_key=uuid&sort_dir=desc': { 'GET': ( {}, {'bays': [BAY2, BAY1]}, ), }, } class BayManagerTest(testtools.TestCase): def setUp(self): super(BayManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = bays.BayManager(self.api) def test_bay_list(self): bays = self.mgr.list() expect = [ ('GET', '/v1/bays', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(bays, matchers.HasLength(2)) def _test_bay_list_with_filters(self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False, expect=[]): bays_filter = self.mgr.list(limit=limit, marker=marker, sort_key=sort_key, sort_dir=sort_dir, detail=detail) self.assertEqual(expect, self.api.calls) self.assertThat(bays_filter, matchers.HasLength(2)) def test_bay_list_with_limit(self): expect = [ ('GET', '/v1/bays/?limit=2', {}, None), ] self._test_bay_list_with_filters( limit=2, expect=expect) def test_bay_list_with_marker(self): expect = [ ('GET', '/v1/bays/?marker=%s' % BAY2['uuid'], {}, None), ] self._test_bay_list_with_filters( marker=BAY2['uuid'], expect=expect) def test_bay_list_with_marker_limit(self): expect = [ ('GET', '/v1/bays/?limit=2&marker=%s' % BAY2['uuid'], {}, None), ] self._test_bay_list_with_filters( limit=2, marker=BAY2['uuid'], expect=expect) def test_bay_list_with_sort_dir(self): expect = [ ('GET', '/v1/bays/?sort_dir=asc', {}, None), ] self._test_bay_list_with_filters( sort_dir='asc', expect=expect) def test_bay_list_with_sort_key(self): expect = [ ('GET', '/v1/bays/?sort_key=uuid', {}, None), ] self._test_bay_list_with_filters( sort_key='uuid', expect=expect) def test_bay_list_with_sort_key_dir(self): expect = [ ('GET', '/v1/bays/?sort_key=uuid&sort_dir=desc', {}, None), ] self._test_bay_list_with_filters( sort_key='uuid', sort_dir='desc', expect=expect) def test_bay_show_by_id(self): bay = self.mgr.get(BAY1['id']) expect = [ ('GET', '/v1/bays/%s' % BAY1['id'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(BAY1['name'], bay.name) self.assertEqual(BAY1['baymodel_id'], bay.baymodel_id) def test_bay_show_by_name(self): bay = self.mgr.get(BAY1['name']) expect = [ ('GET', '/v1/bays/%s' % BAY1['name'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(BAY1['name'], bay.name) self.assertEqual(BAY1['baymodel_id'], bay.baymodel_id) def test_bay_create(self): bay = self.mgr.create(**CREATE_BAY) expect = [ ('POST', '/v1/bays', {}, CREATE_BAY), ] self.assertEqual(expect, self.api.calls) self.assertTrue(bay) def test_bay_create_with_discovery_url(self): bay_with_discovery = dict() bay_with_discovery.update(CREATE_BAY) bay_with_discovery['discovery_url'] = 'discovery_url' bay = self.mgr.create(**bay_with_discovery) expect = [ ('POST', '/v1/bays', {}, bay_with_discovery), ] self.assertEqual(expect, self.api.calls) self.assertTrue(bay) def test_bay_create_with_bay_create_timeout(self): bay_with_timeout = dict() bay_with_timeout.update(CREATE_BAY) bay_with_timeout['bay_create_timeout'] = '15' bay = self.mgr.create(**bay_with_timeout) expect = [ ('POST', '/v1/bays', {}, bay_with_timeout), ] self.assertEqual(expect, self.api.calls) self.assertTrue(bay) def test_bay_create_fail(self): CREATE_BAY_FAIL = copy.deepcopy(CREATE_BAY) CREATE_BAY_FAIL["wrong_key"] = "wrong" self.assertRaisesRegex(exceptions.InvalidAttribute, ("Key must be in %s" % ','.join(bays.CREATION_ATTRIBUTES)), self.mgr.create, **CREATE_BAY_FAIL) self.assertEqual([], self.api.calls) def test_bay_delete_by_id(self): bay = self.mgr.delete(BAY1['id']) expect = [ ('DELETE', '/v1/bays/%s' % BAY1['id'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(bay) def test_bay_delete_by_name(self): bay = self.mgr.delete(BAY1['name']) expect = [ ('DELETE', '/v1/bays/%s' % BAY1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(bay) def test_bay_update(self): patch = {'op': 'replace', 'value': NEW_NAME, 'path': '/name'} bay = self.mgr.update(id=BAY1['id'], patch=patch) expect = [ ('PATCH', '/v1/bays/%s' % BAY1['id'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_NAME, bay.name) def test_bay_update_with_rollback(self): patch = {'op': 'replace', 'value': NEW_NAME, 'path': '/name'} bay = self.mgr.update(id=BAY1['id'], patch=patch, rollback=True) expect = [ ('PATCH', '/v1/bays/%s/?rollback=True' % BAY1['id'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_NAME, bay.name) python-magnumclient-2.8.0/magnumclient/tests/v1/test_bays_shell.py000066400000000000000000000453711323411543400254460ustar00rootroot00000000000000# 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 mock from magnumclient import exceptions from magnumclient.tests.v1 import shell_test_base from magnumclient.tests.v1 import test_baymodels_shell from magnumclient.v1.bays import Bay class FakeBay(Bay): def __init__(self, manager=None, info={}, **kwargs): Bay.__init__(self, manager=manager, info=info) self.uuid = kwargs.get('uuid', 'x') self.name = kwargs.get('name', 'x') self.baymodel_id = kwargs.get('baymodel_id', 'x') self.stack_id = kwargs.get('stack_id', 'x') self.status = kwargs.get('status', 'x') self.master_count = kwargs.get('master_count', 1) self.node_count = kwargs.get('node_count', 1) self.links = kwargs.get('links', []) self.bay_create_timeout = kwargs.get('bay_create_timeout', 60) class FakeCert(object): def __init__(self, pem): self.pem = pem class ShellTest(shell_test_base.TestCommandLineArgument): def _get_expected_args_list(self, marker=None, limit=None, sort_dir=None, sort_key=None): expected_args = {} expected_args['marker'] = marker expected_args['limit'] = limit expected_args['sort_dir'] = sort_dir expected_args['sort_key'] = sort_key return expected_args def _get_expected_args_create(self, baymodel_id, name=None, master_count=1, node_count=1, bay_create_timeout=60, discovery_url=None): expected_args = {} expected_args['name'] = name expected_args['baymodel_id'] = baymodel_id expected_args['master_count'] = master_count expected_args['node_count'] = node_count expected_args['bay_create_timeout'] = bay_create_timeout expected_args['discovery_url'] = discovery_url return expected_args @mock.patch('magnumclient.v1.bays.BayManager.list') def test_bay_list_success(self, mock_list): self._test_arg_success('bay-list') expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.bays.BayManager.list') def test_bay_list_success_with_arg(self, mock_list): self._test_arg_success('bay-list ' '--marker some_uuid ' '--limit 1 ' '--sort-dir asc ' '--sort-key uuid') expected_args = self._get_expected_args_list('some_uuid', 1, 'asc', 'uuid') mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.bays.BayManager.list') def test_bay_list_ignored_duplicated_field(self, mock_list): mock_list.return_value = [FakeBay()] self._test_arg_success('bay-list --fields status,status,status,name', keyword=('\n| uuid | name | node_count | ' 'master_count | status |\n')) # Output should be # +------+------+------------+--------------+--------+ # | uuid | name | node_count | master_count | status | # +------+------+------------+--------------+--------+ # | x | x | x | x | x | # +------+------+------------+--------------+--------+ expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.bays.BayManager.list') def test_bay_list_failure_with_invalid_field(self, mock_list): mock_list.return_value = [FakeBay()] _error_msg = [".*?^Non-existent fields are specified: ['xxx','zzz']"] self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'bay-list --fields xxx,stack_id,zzz,status', _error_msg) expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.bays.BayManager.list') def test_bay_list_failure_invalid_arg(self, mock_list): _error_msg = [ '.*?^usage: magnum bay-list ', '.*?^error: argument --sort-dir: invalid choice: ', ".*?^Try 'magnum help bay-list' for more information." ] self._test_arg_failure('bay-list --sort-dir aaa', _error_msg) mock_list.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.list') def test_bay_list_failure(self, mock_list): self._test_arg_failure('bay-list --wrong', self._unrecognized_arg_error) mock_list.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_success(self, mock_create, mock_get): mock_baymodel = mock.MagicMock() mock_baymodel.uuid = 'xxx' mock_get.return_value = mock_baymodel self._test_arg_success('bay-create --name test --baymodel xxx ' '--node-count 123 --timeout 15') expected_args = self._get_expected_args_create('xxx', name='test', node_count=123, bay_create_timeout=15) mock_create.assert_called_with(**expected_args) self._test_arg_success('bay-create --baymodel xxx') expected_args = self._get_expected_args_create('xxx') mock_create.assert_called_with(**expected_args) self._test_arg_success('bay-create --name test --baymodel xxx') expected_args = self._get_expected_args_create('xxx', name='test') mock_create.assert_called_with(**expected_args) self._test_arg_success('bay-create --baymodel xxx --node-count 123') expected_args = self._get_expected_args_create('xxx', node_count=123) self._test_arg_success('bay-create --baymodel xxx --node-count 123 ' '--master-count 123') expected_args = self._get_expected_args_create('xxx', master_count=123, node_count=123) mock_create.assert_called_with(**expected_args) self._test_arg_success('bay-create --baymodel xxx ' '--timeout 15') expected_args = self._get_expected_args_create('xxx', bay_create_timeout=15) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') @mock.patch('magnumclient.v1.bays.BayManager.get') def test_bay_show_baymodel_metadata(self, mock_bay, mock_baymodel): mock_bay.return_value = mock.MagicMock(baymodel_id=0) mock_baymodel.return_value = test_baymodels_shell.FakeBayModel( info={'links': 0, 'uuid': 0, 'id': 0, 'name': ''}) self._test_arg_success('bay-show --long x') mock_bay.assert_called_once_with('x') mock_baymodel.assert_called_once_with(0) @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_success_only_baymodel_arg(self, mock_create, mock_get): mock_baymodel = mock.MagicMock() mock_baymodel.uuid = 'xxx' mock_get.return_value = mock_baymodel self._test_arg_success('bay-create --baymodel xxx') expected_args = self._get_expected_args_create('xxx') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_failure_only_name(self, mock_create): self._test_arg_failure('bay-create --name test', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_failure_only_node_count(self, mock_create): self._test_arg_failure('bay-create --node-count 1', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_failure_invalid_node_count(self, mock_create): self._test_arg_failure('bay-create --baymodel xxx --node-count test', self._invalid_value_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_failure_only_bay_create_timeout(self, mock_create): self._test_arg_failure('bay-create --timeout 15', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_failure_no_arg(self, mock_create): self._test_arg_failure('bay-create', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.create') def test_bay_create_failure_invalid_master_count(self, mock_create): self._test_arg_failure('bay-create --baymodel xxx --master-count test', self._invalid_value_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.delete') def test_bay_delete_success(self, mock_delete): self._test_arg_success('bay-delete xxx') mock_delete.assert_called_once_with('xxx') @mock.patch('magnumclient.v1.bays.BayManager.delete') def test_bay_delete_multiple_id_success(self, mock_delete): self._test_arg_success('bay-delete xxx xyz') calls = [mock.call('xxx'), mock.call('xyz')] mock_delete.assert_has_calls(calls) @mock.patch('magnumclient.v1.bays.BayManager.delete') def test_bay_delete_failure_no_arg(self, mock_delete): self._test_arg_failure('bay-delete', self._few_argument_error) mock_delete.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.get') def test_bay_show_success(self, mock_show): self._test_arg_success('bay-show xxx') mock_show.assert_called_once_with('xxx') @mock.patch('magnumclient.v1.bays.BayManager.get') def test_bay_show_failure_no_arg(self, mock_show): self._test_arg_failure('bay-show', self._few_argument_error) mock_show.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.update') def test_bay_update_success(self, mock_update): self._test_arg_success('bay-update test add test=test') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}] mock_update.assert_called_once_with('test', patch, False) @mock.patch('magnumclient.v1.bays.BayManager.update') def test_bay_update_success_many_attribute(self, mock_update): self._test_arg_success('bay-update test add test=test test1=test1') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}, {'op': 'add', 'path': '/test1', 'value': 'test1'}] mock_update.assert_called_once_with('test', patch, False) @mock.patch('magnumclient.v1.bays.BayManager.update') def test_bay_update_success_rollback(self, mock_update): self._test_arg_success('bay-update test add test=test --rollback') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}] mock_update.assert_called_once_with('test', patch, True) @mock.patch('magnumclient.v1.bays.BayManager.update') def test_bay_update_rollback_old_api_version(self, mock_update): self.assertRaises( exceptions.CommandError, self.shell, '--magnum-api-version 1.2 bay-update ' 'test add test=test --rollback') mock_update.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.update') def test_bay_update_failure_wrong_op(self, mock_update): _error_msg = [ '.*?^usage: magnum bay-update ', '.*?^error: argument : invalid choice: ', ".*?^Try 'magnum help bay-update' for more information." ] self._test_arg_failure('bay-update test wrong test=test', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.update') def test_bay_update_failure_wrong_attribute(self, mock_update): _error_msg = [ '.*?^ERROR: Attributes must be a list of PATH=VALUE' ] self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'bay-update test add test', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.update') def test_bay_update_failure_few_args(self, mock_update): _error_msg = [ '.*?^usage: magnum bay-update ', '.*?^error: (the following arguments|too few arguments)', ".*?^Try 'magnum help bay-update' for more information." ] self._test_arg_failure('bay-update', _error_msg) mock_update.assert_not_called() self._test_arg_failure('bay-update test', _error_msg) mock_update.assert_not_called() self._test_arg_failure('bay-update test add', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') @mock.patch('magnumclient.v1.bays.BayManager.get') def test_bay_config_success(self, mock_bay, mock_baymodel): mock_bay.return_value = FakeBay(status='UPDATE_COMPLETE') self._test_arg_success('bay-config xxx') mock_bay.assert_called_with('xxx') mock_bay.return_value = FakeBay(status='CREATE_COMPLETE') self._test_arg_success('bay-config xxx') mock_bay.assert_called_with('xxx') self._test_arg_success('bay-config --dir /tmp xxx') mock_bay.assert_called_with('xxx') self._test_arg_success('bay-config --force xxx') mock_bay.assert_called_with('xxx') self._test_arg_success('bay-config --dir /tmp --force xxx') mock_bay.assert_called_with('xxx') @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') @mock.patch('magnumclient.v1.bays.BayManager.get') def test_bay_config_failure_wrong_status(self, mock_bay, mock_baymodel): mock_bay.return_value = FakeBay(status='CREATE_IN_PROGRESS') self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'bay-config xxx', ['.*?^Bay in status: ']) @mock.patch('magnumclient.v1.bays.BayManager.get') def test_bay_config_failure_no_arg(self, mock_bay): self._test_arg_failure('bay-config', self._few_argument_error) mock_bay.assert_not_called() @mock.patch('magnumclient.v1.bays.BayManager.get') def test_bay_config_failure_wrong_arg(self, mock_bay): self._test_arg_failure('bay-config xxx yyy', self._unrecognized_arg_error) mock_bay.assert_not_called() @mock.patch('os.path.exists') @mock.patch('magnumclient.v1.certificates.CertificateManager.create') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') @mock.patch('magnumclient.v1.baymodels.BayModelManager.get') @mock.patch('magnumclient.v1.bays.BayManager.get') def _test_bay_config_success(self, mock_bay, mock_bm, mock_cert_get, mock_cert_create, mock_exists, coe, shell, tls_disable): cert = FakeCert(pem='foo bar') mock_exists.return_value = False mock_bay.return_value = FakeBay(status='CREATE_COMPLETE', info={'name': 'Kluster', 'api_address': '127.0.0.1'}, baymodel_id='fake_bm', uuid='fake_bay') mock_cert_get.return_value = cert mock_cert_create.return_value = cert mock_bm.return_value = test_baymodels_shell. \ FakeBayModel(coe=coe, name='fake_bm', tls_disabled=tls_disable) with mock.patch.dict('os.environ', {'SHELL': shell}): self._test_arg_success('bay-config test_bay') self.assertTrue(mock_exists.called) mock_bay.assert_called_once_with('test_bay') mock_bm.assert_called_once_with('fake_bm') if not tls_disable: mock_cert_create.assert_called_once_with(cluster_uuid='fake_bay', csr=mock.ANY) mock_cert_get.assert_called_once_with(cluster_uuid='fake_bay') def test_bay_config_swarm_success_with_tls_csh(self): self._test_bay_config_success(coe='swarm', shell='csh', tls_disable=False) def test_bay_config_swarm_success_with_tls_non_csh(self): self._test_bay_config_success(coe='swarm', shell='zsh', tls_disable=False) def test_bay_config_swarm_success_without_tls_csh(self): self._test_bay_config_success(coe='swarm', shell='csh', tls_disable=True) def test_bay_config_swarm_success_without_tls_non_csh(self): self._test_bay_config_success(coe='swarm', shell='zsh', tls_disable=True) def test_bay_config_k8s_success_with_tls_csh(self): self._test_bay_config_success(coe='kubernetes', shell='csh', tls_disable=False) def test_bay_config_k8s_success_with_tls_non_csh(self): self._test_bay_config_success(coe='kubernetes', shell='zsh', tls_disable=False) def test_bay_config_k8s_success_without_tls_csh(self): self._test_bay_config_success(coe='kubernetes', shell='csh', tls_disable=True) def test_bay_config_k8s_success_without_tls_non_csh(self): self._test_bay_config_success(coe='kubernetes', shell='zsh', tls_disable=True) python-magnumclient-2.8.0/magnumclient/tests/v1/test_certificates.py000066400000000000000000000070361323411543400257620ustar00rootroot00000000000000# 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 testtools from magnumclient import exceptions from magnumclient.tests import utils from magnumclient.v1 import certificates CERT1 = { 'cluster_uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a53', 'pem': 'fake-pem' } CERT2 = { 'cluster_uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a53', 'pem': 'fake-pem', 'csr': 'fake-csr', } CREATE_CERT = {'cluster_uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a53', 'csr': 'fake-csr'} CREATE_BACKWARDS_CERT = { 'bay_uuid': '5d12f6fd-a196-4bf0-ae4c-1f639a523a53', 'csr': 'fake-csr' } fake_responses = { '/v1/certificates': { 'POST': ( {}, CERT2, ) }, '/v1/certificates/%s' % CERT1['cluster_uuid']: { 'GET': ( {}, CERT1 ), 'PATCH': ( {}, None, ) } } class CertificateManagerTest(testtools.TestCase): def setUp(self): super(CertificateManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = certificates.CertificateManager(self.api) def test_cert_show_by_id(self): cert = self.mgr.get(CERT1['cluster_uuid']) expect = [ ('GET', '/v1/certificates/%s' % CERT1['cluster_uuid'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(CERT1['cluster_uuid'], cert.cluster_uuid) self.assertEqual(CERT1['pem'], cert.pem) def test_cert_create(self): cert = self.mgr.create(**CREATE_CERT) expect = [ ('POST', '/v1/certificates', {}, CREATE_CERT), ] self.assertEqual(expect, self.api.calls) self.assertEqual(CERT2['cluster_uuid'], cert.cluster_uuid) self.assertEqual(CERT2['pem'], cert.pem) self.assertEqual(CERT2['csr'], cert.csr) def test_cert_create_backwards_compatibility(self): # Using a CREATION_ATTRIBUTE of bay_uuid and expecting a # cluster_uuid in return cert = self.mgr.create(**CREATE_BACKWARDS_CERT) expect = [ ('POST', '/v1/certificates', {}, CREATE_CERT), ] self.assertEqual(expect, self.api.calls) self.assertEqual(CERT2['cluster_uuid'], cert.cluster_uuid) self.assertEqual(CERT2['csr'], cert.csr) def test_create_fail(self): create_cert_fail = copy.deepcopy(CREATE_CERT) create_cert_fail["wrong_key"] = "wrong" self.assertRaisesRegex(exceptions.InvalidAttribute, ("Key must be in %s" % ','.join(certificates.CREATION_ATTRIBUTES)), self.mgr.create, **create_cert_fail) self.assertEqual([], self.api.calls) def test_rotate_ca(self): self.mgr.rotate_ca(cluster_uuid=CERT1['cluster_uuid']) expect = [ ('PATCH', '/v1/certificates/%s' % CERT1['cluster_uuid'], {}, None) ] self.assertEqual(expect, self.api.calls) python-magnumclient-2.8.0/magnumclient/tests/v1/test_certificates_shell.py000066400000000000000000000223001323411543400271400ustar00rootroot00000000000000# 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 mock from magnumclient.common import cliutils from magnumclient.tests.v1 import shell_test_base from magnumclient.v1 import certificates_shell class ShellTest(shell_test_base.TestCommandLineArgument): @mock.patch('magnumclient.v1.bays.BayManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') def test_ca_show_success(self, mock_cert_get, mock_bay_get): mockbay = mock.MagicMock() mockbay.status = "CREATE_COMPLETE" mockbay.uuid = "xxx" mock_bay_get.return_value = mockbay self._test_arg_success('ca-show --bay xxx') expected_args = {} expected_args['cluster_uuid'] = mockbay.uuid mock_cert_get.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') def test_cluster_ca_show_success(self, mock_cert_get, mock_cluster_get): mockcluster = mock.MagicMock() mockcluster.status = "CREATE_COMPLETE" mockcluster.uuid = "xxx" mock_cluster_get.return_value = mockcluster self._test_arg_success('ca-show xxx') expected_args = {} expected_args['cluster_uuid'] = mockcluster.uuid mock_cert_get.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') def test_positional_cluster_bay_ca_show_success(self, mock_cert_get, mock_cluster_get): mockcluster = mock.MagicMock() mockcluster.status = "CREATE_COMPLETE" mockcluster.uuid = "xxx" mock_cluster_get.return_value = mockcluster self._test_arg_success('ca-show xxx --bay not-found') expected_args = {} expected_args['cluster_uuid'] = mockcluster.uuid mock_cert_get.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') def test_cluster_bay_ca_show_success(self, mock_cert_get, mock_cluster_get): mockcluster = mock.MagicMock() mockcluster.status = "CREATE_COMPLETE" mockcluster.uuid = "xxx" mock_cluster_get.return_value = mockcluster self._test_arg_success('ca-show --cluster xxx --bay not-found') expected_args = {} expected_args['cluster_uuid'] = mockcluster.uuid mock_cert_get.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') def test_ca_show_failure_duplicate_arg(self, mock_cert_get, mock_cluster_get): self.assertRaises(cliutils.DuplicateArgs, self._test_arg_failure, 'ca-show foo --cluster foo', self._duplicate_arg_error) mock_cert_get.assert_not_called() mock_cluster_get.assert_not_called() @mock.patch('os.path.isfile') @mock.patch('magnumclient.v1.bays.BayManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.create') def test_ca_sign_success( self, mock_cert_create, mock_bay_get, mock_isfile): mock_isfile.return_value = True mockbay = mock.MagicMock() mockbay.status = "CREATE_COMPLETE" mockbay.uuid = "xxx" mock_bay_get.return_value = mockbay fake_csr = 'fake-csr' mock_file = mock.mock_open(read_data=fake_csr) with mock.patch.object(certificates_shell, 'open', mock_file): self._test_arg_success('ca-sign ' '--csr path/csr.pem ' '--bay xxx') expected_args = {} expected_args['cluster_uuid'] = mockbay.uuid expected_args['csr'] = fake_csr mock_cert_create.assert_called_once_with(**expected_args) @mock.patch('os.path.isfile') @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.create') def test_cluster_ca_sign_success( self, mock_cert_create, mock_cluster_get, mock_isfile): mock_isfile.return_value = True mockcluster = mock.MagicMock() mockcluster.status = "CREATE_COMPLETE" mockcluster.uuid = "xxx" mock_cluster_get.return_value = mockcluster fake_csr = 'fake-csr' mock_file = mock.mock_open(read_data=fake_csr) with mock.patch.object(certificates_shell, 'open', mock_file): self._test_arg_success('ca-sign ' '--csr path/csr.pem ' '--cluster xxx') expected_args = {} expected_args['cluster_uuid'] = mockcluster.uuid expected_args['csr'] = fake_csr mock_cert_create.assert_called_once_with(**expected_args) @mock.patch('os.path.isfile') @mock.patch('magnumclient.v1.bays.BayManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.create') def test_ca_sign_with_not_csr( self, mock_cert_create, mock_bay_get, mock_isfile): mock_isfile.return_value = False mockbay = mock.MagicMock() mockbay.status = "CREATE_COMPLETE" mock_bay_get.return_value = mockbay fake_csr = 'fake-csr' mock_file = mock.mock_open(read_data=fake_csr) with mock.patch.object(certificates_shell, 'open', mock_file): self._test_arg_success('ca-sign ' '--csr path/csr.pem ' '--bay xxx') mock_isfile.assert_called_once_with('path/csr.pem') mock_file.assert_not_called() mock_cert_create.assert_not_called() @mock.patch('os.path.isfile') @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.create') def test_cluster_ca_sign_with_not_csr( self, mock_cert_create, mock_cluster_get, mock_isfile): mock_isfile.return_value = False mockcluster = mock.MagicMock() mockcluster.status = "CREATE_COMPLETE" mock_cluster_get.return_value = mockcluster fake_csr = 'fake-csr' mock_file = mock.mock_open(read_data=fake_csr) with mock.patch.object(certificates_shell, 'open', mock_file): self._test_arg_success('ca-sign ' '--csr path/csr.pem ' '--cluster xxx') mock_isfile.assert_called_once_with('path/csr.pem') mock_file.assert_not_called() mock_cert_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') def test_ca_show_failure_with_invalid_field(self, mock_cert_get, mock_cluster_get): self.assertRaises(cliutils.MissingArgs, self._test_arg_failure, 'ca-show', self._few_argument_error) mock_cert_get.assert_not_called() mock_cluster_get.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.rotate_ca') def test_ca_rotate(self, mock_rotate_ca, mock_cluster_get): mockcluster = mock.MagicMock() mockcluster.status = "CREATE_COMPLETE" mockcluster.uuid = "xxx" mock_cluster_get.return_value = mockcluster mock_rotate_ca.return_value = None self._test_arg_success('ca-rotate ' '--cluster xxx') expected_args = {} expected_args['cluster_uuid'] = mockcluster.uuid mock_rotate_ca.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.get') @mock.patch('magnumclient.v1.certificates.CertificateManager.rotate_ca') def test_ca_rotate_no_cluster_arg(self, mock_rotate_ca, mock_cluster_get): _error_msg = [ (".*(error: argument --cluster is required|" # py27 compatibility "error: the following arguments are required: --cluster).*"), ".*Try 'magnum help ca-rotate' for more information.*" ] self._test_arg_failure('ca-rotate', _error_msg) mock_rotate_ca.assert_not_called() mock_cluster_get.assert_not_called() python-magnumclient-2.8.0/magnumclient/tests/v1/test_client.py000066400000000000000000000207171323411543400245740ustar00rootroot00000000000000# Copyright (c) 2015 Thales Services SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import testtools from keystoneauth1.exceptions import catalog from magnumclient.v1 import client class ClientInitializeTest(testtools.TestCase): def _load_session_kwargs(self): return { 'username': None, 'project_id': None, 'project_name': None, 'auth_url': None, 'password': None, 'auth_type': 'password', 'insecure': False, 'user_domain_id': None, 'user_domain_name': None, 'project_domain_id': None, 'project_domain_name': None, 'auth_token': None, 'timeout': 600, } def _load_service_type_kwargs(self): return { 'interface': 'public', 'region_name': None, 'service_name': None, 'service_type': 'container-infra', } def _session_client_kwargs(self, session): kwargs = self._load_service_type_kwargs() kwargs['endpoint_override'] = None kwargs['session'] = session kwargs['api_version'] = None return kwargs @mock.patch('magnumclient.common.httpclient.SessionClient') @mock.patch('magnumclient.v1.client._load_session') @mock.patch('magnumclient.v1.client._load_service_type', return_value='container-infra') def test_init_with_session(self, mock_load_service_type, mock_load_session, mock_http_client): session = mock.Mock() client.Client(session=session) mock_load_session.assert_not_called() mock_load_service_type.assert_called_once_with( session, **self._load_service_type_kwargs() ) mock_http_client.assert_called_once_with( **self._session_client_kwargs(session) ) def _test_init_with_secret(self, init_func, mock_load_service_type, mock_load_session, mock_http_client,): expected_password = 'expected_password' session = mock.Mock() mock_load_session.return_value = session init_func(expected_password) load_session_args = self._load_session_kwargs() load_session_args['password'] = expected_password mock_load_session.assert_called_once_with( **load_session_args ) mock_load_service_type.assert_called_once_with( session, **self._load_service_type_kwargs() ) mock_http_client.assert_called_once_with( **self._session_client_kwargs(session) ) @mock.patch('magnumclient.common.httpclient.SessionClient') @mock.patch('magnumclient.v1.client._load_session') @mock.patch('magnumclient.v1.client._load_service_type', return_value='container-infra') def test_init_with_password(self, mock_load_service_type, mock_load_session, mock_http_client): self._test_init_with_secret( lambda x: client.Client(password=x), mock_load_service_type, mock_load_session, mock_http_client ) @mock.patch('magnumclient.common.httpclient.SessionClient') @mock.patch('magnumclient.v1.client._load_session') @mock.patch('magnumclient.v1.client._load_service_type', return_value='container-infra') def test_init_with_api_key(self, mock_load_service_type, mock_load_session, mock_http_client): self._test_init_with_secret( lambda x: client.Client(api_key=x), mock_load_service_type, mock_load_session, mock_http_client ) @mock.patch('magnumclient.common.httpclient.HTTPClient') def test_init_with_auth_token(self, mock_http_client,): expected_token = 'expected_password' expected_magnum_url = 'expected_magnum_url' expected_api_version = 'expected_api_version' expected_insecure = False expected_timeout = 600 expected_kwargs = {'expected_key': 'expected_value'} client.Client(auth_token=expected_token, magnum_url=expected_magnum_url, api_version=expected_api_version, timeout=expected_timeout, insecure=expected_insecure, **expected_kwargs) mock_http_client.assert_called_once_with( expected_magnum_url, token=expected_token, api_version=expected_api_version, timeout=expected_timeout, insecure=expected_insecure, **expected_kwargs) def _test_init_with_interface(self, init_func, mock_load_service_type, mock_load_session, mock_http_client): expected_interface = 'admin' session = mock.Mock() mock_load_session.return_value = session init_func(expected_interface) mock_load_session.assert_called_once_with( **self._load_session_kwargs() ) expected_kwargs = self._load_service_type_kwargs() expected_kwargs['interface'] = expected_interface mock_load_service_type.assert_called_once_with( session, **expected_kwargs ) expected_kwargs = self._session_client_kwargs(session) expected_kwargs['interface'] = expected_interface mock_http_client.assert_called_once_with( **expected_kwargs ) @mock.patch('magnumclient.common.httpclient.SessionClient') @mock.patch('magnumclient.v1.client._load_session') @mock.patch('magnumclient.v1.client._load_service_type', return_value='container-infra') def test_init_with_interface(self, mock_load_service_type, mock_load_session, mock_http_client): self._test_init_with_interface( lambda x: client.Client(interface=x), mock_load_service_type, mock_load_session, mock_http_client ) @mock.patch('magnumclient.common.httpclient.SessionClient') @mock.patch('magnumclient.v1.client._load_session') @mock.patch('magnumclient.v1.client._load_service_type', return_value='container-infra') def test_init_with_endpoint_type(self, mock_load_service_type, mock_load_session, mock_http_client): self._test_init_with_interface( lambda x: client.Client(interface='public', endpoint_type=('%sURL' % x)), mock_load_service_type, mock_load_session, mock_http_client ) @mock.patch('magnumclient.common.httpclient.SessionClient') @mock.patch('magnumclient.v1.client._load_session') def test_init_with_legacy_service_type(self, mock_load_session, mock_http_client): session = mock.Mock() mock_load_session.return_value = session session.get_endpoint.side_effect = [ catalog.EndpointNotFound(), mock.Mock() ] client.Client(username='myuser', auth_url='authurl') expected_kwargs = self._session_client_kwargs(session) expected_kwargs['service_type'] = 'container' mock_http_client.assert_called_once_with( **expected_kwargs ) python-magnumclient-2.8.0/magnumclient/tests/v1/test_clusters.py000066400000000000000000000263341323411543400251630ustar00rootroot00000000000000# 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 testtools from testtools import matchers from magnumclient import exceptions from magnumclient.tests import utils from magnumclient.v1 import clusters CLUSTER1 = {'id': 123, 'uuid': '66666666-7777-8888-9999-000000000001', 'name': 'cluster1', 'cluster_template_id': 'e74c40e0-d825-11e2-a28f-0800200c9a61', 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a51', 'api_address': '172.17.2.1', 'node_addresses': ['172.17.2.3'], 'node_count': 2, 'master_count': 1, } CLUSTER2 = {'id': 124, 'uuid': '66666666-7777-8888-9999-000000000002', 'name': 'cluster2', 'cluster_template_id': 'e74c40e0-d825-11e2-a28f-0800200c9a62', 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', 'api_address': '172.17.2.2', 'node_addresses': ['172.17.2.4'], 'node_count': 2, 'master_count': 1, } CREATE_CLUSTER = copy.deepcopy(CLUSTER1) del CREATE_CLUSTER['id'] del CREATE_CLUSTER['uuid'] del CREATE_CLUSTER['stack_id'] del CREATE_CLUSTER['api_address'] del CREATE_CLUSTER['node_addresses'] UPDATED_CLUSTER = copy.deepcopy(CLUSTER1) NEW_NAME = 'newcluster' UPDATED_CLUSTER['name'] = NEW_NAME fake_responses = { '/v1/clusters': { 'GET': ( {}, {'clusters': [CLUSTER1, CLUSTER2]}, ), 'POST': ( {}, CREATE_CLUSTER, ), }, '/v1/clusters/%s' % CLUSTER1['id']: { 'GET': ( {}, CLUSTER1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_CLUSTER, ), }, '/v1/clusters/%s/?rollback=True' % CLUSTER1['id']: { 'PATCH': ( {}, UPDATED_CLUSTER, ), }, '/v1/clusters/%s' % CLUSTER1['name']: { 'GET': ( {}, CLUSTER1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_CLUSTER, ), }, '/v1/clusters/?limit=2': { 'GET': ( {}, {'clusters': [CLUSTER1, CLUSTER2]}, ), }, '/v1/clusters/?marker=%s' % CLUSTER2['uuid']: { 'GET': ( {}, {'clusters': [CLUSTER1, CLUSTER2]}, ), }, '/v1/clusters/?limit=2&marker=%s' % CLUSTER2['uuid']: { 'GET': ( {}, {'clusters': [CLUSTER1, CLUSTER2]}, ), }, '/v1/clusters/?sort_dir=asc': { 'GET': ( {}, {'clusters': [CLUSTER1, CLUSTER2]}, ), }, '/v1/clusters/?sort_key=uuid': { 'GET': ( {}, {'clusters': [CLUSTER1, CLUSTER2]}, ), }, '/v1/clusters/?sort_key=uuid&sort_dir=desc': { 'GET': ( {}, {'clusters': [CLUSTER2, CLUSTER1]}, ), }, } class ClusterManagerTest(testtools.TestCase): def setUp(self): super(ClusterManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = clusters.ClusterManager(self.api) def test_cluster_list(self): clusters = self.mgr.list() expect = [ ('GET', '/v1/clusters', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(clusters, matchers.HasLength(2)) def _test_cluster_list_with_filters(self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False, expect=[]): clusters_filter = self.mgr.list(limit=limit, marker=marker, sort_key=sort_key, sort_dir=sort_dir, detail=detail) self.assertEqual(expect, self.api.calls) self.assertThat(clusters_filter, matchers.HasLength(2)) def test_cluster_list_with_limit(self): expect = [ ('GET', '/v1/clusters/?limit=2', {}, None), ] self._test_cluster_list_with_filters( limit=2, expect=expect) def test_cluster_list_with_marker(self): expect = [ ('GET', '/v1/clusters/?marker=%s' % CLUSTER2['uuid'], {}, None), ] self._test_cluster_list_with_filters( marker=CLUSTER2['uuid'], expect=expect) def test_cluster_list_with_marker_limit(self): expect = [ ('GET', '/v1/clusters/?limit=2&marker=%s' % CLUSTER2['uuid'], {}, None), ] self._test_cluster_list_with_filters( limit=2, marker=CLUSTER2['uuid'], expect=expect) def test_cluster_list_with_sort_dir(self): expect = [ ('GET', '/v1/clusters/?sort_dir=asc', {}, None), ] self._test_cluster_list_with_filters( sort_dir='asc', expect=expect) def test_cluster_list_with_sort_key(self): expect = [ ('GET', '/v1/clusters/?sort_key=uuid', {}, None), ] self._test_cluster_list_with_filters( sort_key='uuid', expect=expect) def test_cluster_list_with_sort_key_dir(self): expect = [ ('GET', '/v1/clusters/?sort_key=uuid&sort_dir=desc', {}, None), ] self._test_cluster_list_with_filters( sort_key='uuid', sort_dir='desc', expect=expect) def test_cluster_show_by_id(self): cluster = self.mgr.get(CLUSTER1['id']) expect = [ ('GET', '/v1/clusters/%s' % CLUSTER1['id'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(CLUSTER1['name'], cluster.name) self.assertEqual(CLUSTER1['cluster_template_id'], cluster.cluster_template_id) def test_cluster_show_by_name(self): cluster = self.mgr.get(CLUSTER1['name']) expect = [ ('GET', '/v1/clusters/%s' % CLUSTER1['name'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(CLUSTER1['name'], cluster.name) self.assertEqual(CLUSTER1['cluster_template_id'], cluster.cluster_template_id) def test_cluster_create(self): cluster = self.mgr.create(**CREATE_CLUSTER) expect = [ ('POST', '/v1/clusters', {}, CREATE_CLUSTER), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster) def test_cluster_create_with_keypair(self): cluster_with_keypair = dict() cluster_with_keypair.update(CREATE_CLUSTER) cluster_with_keypair['keypair'] = 'test_key' cluster = self.mgr.create(**cluster_with_keypair) expect = [ ('POST', '/v1/clusters', {}, cluster_with_keypair), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster) def test_cluster_create_with_docker_volume_size(self): cluster_with_volume_size = dict() cluster_with_volume_size.update(CREATE_CLUSTER) cluster_with_volume_size['docker_volume_size'] = 20 cluster = self.mgr.create(**cluster_with_volume_size) expect = [ ('POST', '/v1/clusters', {}, cluster_with_volume_size), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster) def test_cluster_create_with_labels(self): cluster_with_labels = dict() cluster_with_labels.update(CREATE_CLUSTER) cluster_with_labels['labels'] = "key=val" cluster = self.mgr.create(**cluster_with_labels) expect = [ ('POST', '/v1/clusters', {}, cluster_with_labels), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster) def test_cluster_create_with_discovery_url(self): cluster_with_discovery = dict() cluster_with_discovery.update(CREATE_CLUSTER) cluster_with_discovery['discovery_url'] = 'discovery_url' cluster = self.mgr.create(**cluster_with_discovery) expect = [ ('POST', '/v1/clusters', {}, cluster_with_discovery), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster) def test_cluster_create_with_cluster_create_timeout(self): cluster_with_timeout = dict() cluster_with_timeout.update(CREATE_CLUSTER) cluster_with_timeout['create_timeout'] = '15' cluster = self.mgr.create(**cluster_with_timeout) expect = [ ('POST', '/v1/clusters', {}, cluster_with_timeout), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster) def test_cluster_create_fail(self): CREATE_CLUSTER_FAIL = copy.deepcopy(CREATE_CLUSTER) CREATE_CLUSTER_FAIL["wrong_key"] = "wrong" self.assertRaisesRegex(exceptions.InvalidAttribute, ("Key must be in %s" % ','.join(clusters.CREATION_ATTRIBUTES)), self.mgr.create, **CREATE_CLUSTER_FAIL) self.assertEqual([], self.api.calls) def test_cluster_delete_by_id(self): cluster = self.mgr.delete(CLUSTER1['id']) expect = [ ('DELETE', '/v1/clusters/%s' % CLUSTER1['id'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(cluster) def test_cluster_delete_by_name(self): cluster = self.mgr.delete(CLUSTER1['name']) expect = [ ('DELETE', '/v1/clusters/%s' % CLUSTER1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(cluster) def test_cluster_update(self): patch = {'op': 'replace', 'value': NEW_NAME, 'path': '/name'} cluster = self.mgr.update(id=CLUSTER1['id'], patch=patch) expect = [ ('PATCH', '/v1/clusters/%s' % CLUSTER1['id'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_NAME, cluster.name) def test_cluster_update_with_rollback(self): patch = {'op': 'replace', 'value': NEW_NAME, 'path': '/name'} cluster = self.mgr.update(id=CLUSTER1['id'], patch=patch, rollback=True) expect = [ ('PATCH', '/v1/clusters/%s/?rollback=True' % CLUSTER1['id'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_NAME, cluster.name) python-magnumclient-2.8.0/magnumclient/tests/v1/test_clusters_shell.py000066400000000000000000000630521323411543400263500ustar00rootroot00000000000000# 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 mock from magnumclient.common import cliutils from magnumclient import exceptions from magnumclient.tests.v1 import shell_test_base from magnumclient.tests.v1 import test_clustertemplates_shell from magnumclient.v1.clusters import Cluster class FakeCluster(Cluster): def __init__(self, manager=None, info={}, **kwargs): Cluster.__init__(self, manager=manager, info=info) self.uuid = kwargs.get('uuid', 'x') self.keypair = kwargs.get('keypair', 'x') self.docker_volume_size = kwargs.get('docker_volume_size', 3) self.labels = kwargs.get('labels', 'key=val') self.name = kwargs.get('name', 'x') self.cluster_template_id = kwargs.get('cluster_template_id', 'x') self.stack_id = kwargs.get('stack_id', 'x') self.status = kwargs.get('status', 'x') self.master_count = kwargs.get('master_count', 1) self.node_count = kwargs.get('node_count', 1) self.links = kwargs.get('links', []) self.create_timeout = kwargs.get('create_timeout', 60) class FakeCert(object): def __init__(self, pem): self.pem = pem class ShellTest(shell_test_base.TestCommandLineArgument): def _get_expected_args_list(self, marker=None, limit=None, sort_dir=None, sort_key=None): expected_args = {} expected_args['marker'] = marker expected_args['limit'] = limit expected_args['sort_dir'] = sort_dir expected_args['sort_key'] = sort_key return expected_args def _get_expected_args_create(self, cluster_template_id, name=None, master_count=1, node_count=1, create_timeout=60, keypair=None, docker_volume_size=None, labels=None, discovery_url=None): expected_args = {} expected_args['name'] = name expected_args['cluster_template_id'] = cluster_template_id expected_args['master_count'] = master_count expected_args['node_count'] = node_count expected_args['create_timeout'] = create_timeout expected_args['discovery_url'] = discovery_url expected_args['keypair'] = keypair if docker_volume_size is not None: expected_args['docker_volume_size'] = docker_volume_size if labels is not None: expected_args['labels'] = labels return expected_args @mock.patch('magnumclient.v1.clusters.ClusterManager.list') def test_cluster_list_success(self, mock_list): self._test_arg_success('cluster-list') expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.list') def test_cluster_list_success_with_arg(self, mock_list): self._test_arg_success('cluster-list ' '--marker some_uuid ' '--limit 1 ' '--sort-dir asc ' '--sort-key uuid') expected_args = self._get_expected_args_list('some_uuid', 1, 'asc', 'uuid') mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.list') def test_cluster_list_ignored_duplicated_field(self, mock_list): mock_list.return_value = [FakeCluster()] self._test_arg_success( 'cluster-list --fields status,status,status,name', keyword=('\n| uuid | name | keypair | node_count | master_count | ' 'status |\n')) expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.list') def test_cluster_list_failure_with_invalid_field(self, mock_list): mock_list.return_value = [FakeCluster()] _error_msg = [".*?^Non-existent fields are specified: ['xxx','zzz']"] self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'cluster-list --fields xxx,stack_id,zzz,status', _error_msg) expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.clusters.ClusterManager.list') def test_cluster_list_failure_invalid_arg(self, mock_list): _error_msg = [ '.*?^usage: magnum cluster-list ', '.*?^error: argument --sort-dir: invalid choice: ', ".*?^Try 'magnum help cluster-list' for more information." ] self._test_arg_failure('cluster-list --sort-dir aaa', _error_msg) mock_list.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.list') def test_cluster_list_failure(self, mock_list): self._test_arg_failure('cluster-list --wrong', self._unrecognized_arg_error) mock_list.assert_not_called() @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_success(self, mock_create, mock_get): mock_ct = mock.MagicMock() mock_ct.uuid = 'xxx' mock_get.return_value = mock_ct self._test_arg_success('cluster-create test ' '--cluster-template xxx ' '--node-count 123 --timeout 15') expected_args = self._get_expected_args_create('xxx', name='test', node_count=123, create_timeout=15) mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-create --cluster-template xxx') expected_args = self._get_expected_args_create('xxx') mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-create --cluster-template xxx ' '--keypair x') expected_args = self._get_expected_args_create('xxx', keypair='x') mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-create --cluster-template xxx ' '--docker-volume-size 20') expected_args = self._get_expected_args_create('xxx', docker_volume_size=20) self._test_arg_success('cluster-create --cluster-template xxx ' '--labels key=val') expected_args = self._get_expected_args_create('xxx', labels={'key': 'val'}) mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-create test ' '--cluster-template xxx') expected_args = self._get_expected_args_create('xxx', name='test') mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-create --cluster-template xxx ' '--node-count 123') expected_args = self._get_expected_args_create('xxx', node_count=123) mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-create --cluster-template xxx ' '--node-count 123 --master-count 123') expected_args = self._get_expected_args_create('xxx', master_count=123, node_count=123) mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-create --cluster-template xxx ' '--timeout 15') expected_args = self._get_expected_args_create('xxx', create_timeout=15) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_deprecation_warnings(self, mock_create, mock_get): self._test_arg_failure('cluster-create --cluster-template xxx ' '--keypair-id x', self._deprecated_warning) self.assertTrue(mock_create.called) self.assertTrue(mock_get.called) self._test_arg_failure('cluster-create --cluster-template xxx ' '--name foo ', self._deprecated_warning) self.assertTrue(mock_create.called) self.assertTrue(mock_get.called) @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_deprecation_errors(self, mock_create, mock_get): self._test_arg_failure('cluster-create --cluster-template xxx ' '--keypair-id x --keypair x', self._too_many_group_arg_error) self.assertFalse(mock_create.called) self.assertFalse(mock_get.called) @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def test_cluster_show_clustertemplate_metadata(self, mock_cluster, mock_clustertemplate): mock_cluster.return_value = mock.MagicMock(cluster_template_id=0) mock_clustertemplate.return_value = \ test_clustertemplates_shell.FakeClusterTemplate(info={'links': 0, 'uuid': 0, 'id': 0, 'name': ''}) self._test_arg_success('cluster-show --long x') mock_cluster.assert_called_once_with('x') mock_clustertemplate.assert_called_once_with(0) @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def _test_cluster_create_success(self, cmd, expected_args, expected_kwargs, mock_create, mock_get): mock_ct = mock.MagicMock() mock_ct.uuid = 'xxx' mock_get.return_value = mock_ct self._test_arg_success(cmd) expected = self._get_expected_args_create(*expected_args, **expected_kwargs) mock_create.assert_called_with(**expected) def test_cluster_create_success_only_clustertemplate_arg(self): self._test_cluster_create_success( 'cluster-create --cluster-template xxx', ['xxx'], {}) @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_success_only_positional_name(self, mock_create, mock_get): self._test_cluster_create_success( 'cluster-create foo --cluster-template xxx', ['xxx'], {'name': 'foo'}) @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_success_only_optional_name(self, mock_create, mock_get): self._test_cluster_create_success( 'cluster-create --name foo --cluster-template xxx', ['xxx'], {'name': 'foo'}) @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_only_name(self, mock_create): self._test_arg_failure('cluster-create --name test', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_only_keypair(self, mock_create): self._test_arg_failure('cluster-create --keypair test', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_only_docker_volume_size(self, mock_create): self._test_arg_failure('cluster-create --docker_volume_size 20', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_only_labels(self, mock_create): self._test_arg_failure('cluster-create --labels key=val', self._mandatory_arg_error) @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_only_node_count(self, mock_create): self._test_arg_failure('cluster-create --node-count 1', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_invalid_node_count(self, mock_create): self._test_arg_failure('cluster-create --cluster-template xxx ' '--node-count test', self._invalid_value_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_only_cluster_create_timeout(self, mock_create): self._test_arg_failure('cluster-create --timeout 15', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_no_arg(self, mock_create): self._test_arg_failure('cluster-create', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_invalid_master_count(self, mock_create): self._test_arg_failure('cluster-create --cluster-template xxx ' '--master-count test', self._invalid_value_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.create') def test_cluster_create_failure_duplicate_name(self, mock_create): self.assertRaises(cliutils.DuplicateArgs, self._test_arg_failure, 'cluster-create foo --name bar ' '--cluster-template xxx', self._duplicate_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.delete') def test_cluster_delete_success(self, mock_delete): self._test_arg_success('cluster-delete xxx') mock_delete.assert_called_once_with('xxx') @mock.patch('magnumclient.v1.clusters.ClusterManager.delete') def test_cluster_delete_multiple_id_success(self, mock_delete): self._test_arg_success('cluster-delete xxx xyz') calls = [mock.call('xxx'), mock.call('xyz')] mock_delete.assert_has_calls(calls) @mock.patch('magnumclient.v1.clusters.ClusterManager.delete') def test_cluster_delete_failure_no_arg(self, mock_delete): self._test_arg_failure('cluster-delete', self._few_argument_error) mock_delete.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def test_cluster_show_success(self, mock_show): self._test_arg_success('cluster-show xxx') mock_show.assert_called_once_with('xxx') @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def test_cluster_show_failure_no_arg(self, mock_show): self._test_arg_failure('cluster-show', self._few_argument_error) mock_show.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.update') def test_cluster_update_success(self, mock_update): self._test_arg_success('cluster-update test add test=test') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}] mock_update.assert_called_once_with('test', patch, False) @mock.patch('magnumclient.v1.clusters.ClusterManager.update') def test_cluster_update_success_many_attribute(self, mock_update): self._test_arg_success('cluster-update test add test=test test1=test1') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}, {'op': 'add', 'path': '/test1', 'value': 'test1'}] mock_update.assert_called_once_with('test', patch, False) @mock.patch('magnumclient.v1.clusters.ClusterManager.update') def test_cluster_update_success_rollback(self, mock_update): self._test_arg_success('cluster-update test add test=test --rollback') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}] mock_update.assert_called_once_with('test', patch, True) @mock.patch('magnumclient.v1.clusters.ClusterManager.update') def test_cluster_update_rollback_old_api_version(self, mock_update): self.assertRaises( exceptions.CommandError, self.shell, '--magnum-api-version 1.2 cluster-update ' 'test add test=test --rollback') mock_update.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.update') def test_cluster_update_failure_wrong_op(self, mock_update): _error_msg = [ '.*?^usage: magnum cluster-update ', '.*?^error: argument : invalid choice: ', ".*?^Try 'magnum help cluster-update' for more information." ] self._test_arg_failure('cluster-update test wrong test=test', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.update') def test_cluster_update_failure_wrong_attribute(self, mock_update): _error_msg = [ '.*?^ERROR: Attributes must be a list of PATH=VALUE' ] self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'cluster-update test add test', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.update') def test_cluster_update_failure_few_args(self, mock_update): _error_msg = [ '.*?^usage: magnum cluster-update ', '.*?^error: (the following arguments|too few arguments)', ".*?^Try 'magnum help cluster-update' for more information." ] self._test_arg_failure('cluster-update', _error_msg) mock_update.assert_not_called() self._test_arg_failure('cluster-update test', _error_msg) mock_update.assert_not_called() self._test_arg_failure('cluster-update test add', _error_msg) mock_update.assert_not_called() @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def test_cluster_config_success(self, mock_cluster, mock_clustertemplate): mock_cluster.return_value = FakeCluster(status='UPDATE_COMPLETE') self._test_arg_success('cluster-config xxx') mock_cluster.assert_called_with('xxx') mock_cluster.return_value = FakeCluster(status='CREATE_COMPLETE') self._test_arg_success('cluster-config xxx') mock_cluster.assert_called_with('xxx') self._test_arg_success('cluster-config --dir /tmp xxx') mock_cluster.assert_called_with('xxx') self._test_arg_success('cluster-config --force xxx') mock_cluster.assert_called_with('xxx') self._test_arg_success('cluster-config --dir /tmp --force xxx') mock_cluster.assert_called_with('xxx') @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def test_cluster_config_failure_wrong_status(self, mock_cluster, mock_clustertemplate): mock_cluster.return_value = FakeCluster(status='CREATE_IN_PROGRESS') self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'cluster-config xxx', ['.*?^Cluster in status: ']) @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def test_cluster_config_failure_no_arg(self, mock_cluster): self._test_arg_failure('cluster-config', self._few_argument_error) mock_cluster.assert_not_called() @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def test_cluster_config_failure_wrong_arg(self, mock_cluster): self._test_arg_failure('cluster-config xxx yyy', self._unrecognized_arg_error) mock_cluster.assert_not_called() @mock.patch('os.path.exists') @mock.patch('magnumclient.v1.certificates.CertificateManager.create') @mock.patch('magnumclient.v1.certificates.CertificateManager.get') @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') @mock.patch('magnumclient.v1.clusters.ClusterManager.get') def _test_cluster_config_success(self, mock_cluster, mock_ct, mock_cert_get, mock_cert_create, mock_exists, coe, shell, tls_disable): cert = FakeCert(pem='foo bar') mock_exists.return_value = False mock_cluster.return_value = FakeCluster(status='CREATE_COMPLETE', info={ 'name': 'Kluster', 'api_address': '10.0.0.1'}, cluster_template_id='fake_ct', uuid='fake_cluster') mock_cert_get.return_value = cert mock_cert_create.return_value = cert mock_ct.return_value = test_clustertemplates_shell.\ FakeClusterTemplate(coe=coe, name='fake_ct', tls_disabled=tls_disable) with mock.patch.dict('os.environ', {'SHELL': shell}): self._test_arg_success('cluster-config test_cluster') self.assertTrue(mock_exists.called) mock_cluster.assert_called_once_with('test_cluster') mock_ct.assert_called_once_with('fake_ct') if not tls_disable: mock_cert_create.assert_called_once_with( cluster_uuid='fake_cluster', csr=mock.ANY) mock_cert_get.assert_called_once_with(cluster_uuid='fake_cluster') def test_cluster_config_swarm_success_with_tls_csh(self): self._test_cluster_config_success(coe='swarm', shell='csh', tls_disable=False) def test_cluster_config_swarm_success_with_tls_non_csh(self): self._test_cluster_config_success(coe='swarm', shell='zsh', tls_disable=False) def test_cluster_config_swarm_success_without_tls_csh(self): self._test_cluster_config_success(coe='swarm', shell='csh', tls_disable=True) def test_cluster_config_swarm_success_without_tls_non_csh(self): self._test_cluster_config_success(coe='swarm', shell='zsh', tls_disable=True) def test_cluster_config_k8s_success_with_tls_csh(self): self._test_cluster_config_success(coe='kubernetes', shell='csh', tls_disable=False) def test_cluster_config_k8s_success_with_tls_non_csh(self): self._test_cluster_config_success(coe='kubernetes', shell='zsh', tls_disable=False) def test_cluster_config_k8s_success_without_tls_csh(self): self._test_cluster_config_success(coe='kubernetes', shell='csh', tls_disable=True) def test_cluster_config_k8s_success_without_tls_non_csh(self): self._test_cluster_config_success(coe='kubernetes', shell='zsh', tls_disable=True) python-magnumclient-2.8.0/magnumclient/tests/v1/test_clustertemplates.py000066400000000000000000000402441323411543400267130ustar00rootroot00000000000000# 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 testtools from testtools import matchers from magnumclient import exceptions from magnumclient.tests import utils from magnumclient.v1 import cluster_templates CLUSTERTEMPLATE1 = { 'id': 123, 'uuid': '66666666-7777-8888-9999-000000000001', 'name': 'clustertemplate1', 'image_id': 'clustertemplate1-image', 'master_flavor_id': 'm1.tiny', 'flavor_id': 'm1.small', 'external_network_id': 'd1f02cfb-d27f-4068-9332-84d907cb0e21', 'fixed_network': 'private', 'fixed_subnet': 'private-subnet', 'network_driver': 'libnetwork', 'volume_driver': 'rexray', 'dns_nameserver': '8.8.1.1', 'docker_volume_size': '71', 'docker_storage_driver': 'devicemapper', 'coe': 'swarm', 'http_proxy': 'http_proxy', 'https_proxy': 'https_proxy', 'no_proxy': 'no_proxy', 'labels': 'key1=val1,key11=val11', 'tls_disabled': False, 'public': False, 'registry_enabled': False, 'master_lb_enabled': True, 'floating_ip_enabled': True } CLUSTERTEMPLATE2 = { 'id': 124, 'uuid': '66666666-7777-8888-9999-000000000002', 'name': 'clustertemplate2', 'image_id': 'clustertemplate2-image', 'flavor_id': 'm2.small', 'master_flavor_id': 'm2.tiny', 'external_network_id': 'd1f02cfb-d27f-4068-9332-84d907cb0e22', 'fixed_network': 'private2', 'network_driver': 'flannel', 'volume_driver': 'cinder', 'dns_nameserver': '8.8.1.2', 'docker_volume_size': '71', 'docker_storage_driver': 'overlay', 'coe': 'kubernetes', 'labels': 'key2=val2,key22=val22', 'tls_disabled': True, 'public': True, 'registry_enabled': True} CREATE_CLUSTERTEMPLATE = copy.deepcopy(CLUSTERTEMPLATE1) del CREATE_CLUSTERTEMPLATE['id'] del CREATE_CLUSTERTEMPLATE['uuid'] UPDATED_CLUSTERTEMPLATE = copy.deepcopy(CLUSTERTEMPLATE1) NEW_NAME = 'newcluster' UPDATED_CLUSTERTEMPLATE['name'] = NEW_NAME fake_responses = { '/v1/clustertemplates': { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE1, CLUSTERTEMPLATE2]}, ), 'POST': ( {}, CREATE_CLUSTERTEMPLATE, ), }, '/v1/clustertemplates/%s' % CLUSTERTEMPLATE1['id']: { 'GET': ( {}, CLUSTERTEMPLATE1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_CLUSTERTEMPLATE, ), }, '/v1/clustertemplates/%s' % CLUSTERTEMPLATE1['name']: { 'GET': ( {}, CLUSTERTEMPLATE1 ), 'DELETE': ( {}, None, ), 'PATCH': ( {}, UPDATED_CLUSTERTEMPLATE, ), }, '/v1/clustertemplates/detail': { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE1, CLUSTERTEMPLATE2]}, ), }, '/v1/clustertemplates/?limit=2': { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE1, CLUSTERTEMPLATE2]}, ), }, '/v1/clustertemplates/?marker=%s' % CLUSTERTEMPLATE2['uuid']: { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE1, CLUSTERTEMPLATE2]}, ), }, '/v1/clustertemplates/?limit=2&marker=%s' % CLUSTERTEMPLATE2['uuid']: { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE1, CLUSTERTEMPLATE2]}, ), }, '/v1/clustertemplates/?sort_dir=asc': { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE1, CLUSTERTEMPLATE2]}, ), }, '/v1/clustertemplates/?sort_key=uuid': { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE1, CLUSTERTEMPLATE2]}, ), }, '/v1/clustertemplates/?sort_key=uuid&sort_dir=desc': { 'GET': ( {}, {'clustertemplates': [CLUSTERTEMPLATE2, CLUSTERTEMPLATE1]}, ), }, } class ClusterTemplateManagerTest(testtools.TestCase): def setUp(self): super(ClusterTemplateManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = cluster_templates.ClusterTemplateManager(self.api) def test_clustertemplate_list(self): clustertemplates = self.mgr.list() expect = [ ('GET', '/v1/clustertemplates', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(clustertemplates, matchers.HasLength(2)) def _test_clustertemplate_list_with_filters( self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False, expect=[]): clustertemplates_filter = self.mgr.list(limit=limit, marker=marker, sort_key=sort_key, sort_dir=sort_dir, detail=detail) self.assertEqual(expect, self.api.calls) self.assertThat(clustertemplates_filter, matchers.HasLength(2)) def test_clustertemplate_list_with_detail(self): expect = [ ('GET', '/v1/clustertemplates/detail', {}, None), ] self._test_clustertemplate_list_with_filters( detail=True, expect=expect) def test_clustertemplate_list_with_limit(self): expect = [ ('GET', '/v1/clustertemplates/?limit=2', {}, None), ] self._test_clustertemplate_list_with_filters( limit=2, expect=expect) def test_clustertemplate_list_with_marker(self): expect = [ ('GET', '/v1/clustertemplates/?marker=%s' % CLUSTERTEMPLATE2['uuid'], {}, None), ] self._test_clustertemplate_list_with_filters( marker=CLUSTERTEMPLATE2['uuid'], expect=expect) def test_clustertemplate_list_with_marker_limit(self): expect = [ ('GET', '/v1/clustertemplates/?limit=2&marker=%s' % CLUSTERTEMPLATE2['uuid'], {}, None), ] self._test_clustertemplate_list_with_filters( limit=2, marker=CLUSTERTEMPLATE2['uuid'], expect=expect) def test_clustertemplate_list_with_sort_dir(self): expect = [ ('GET', '/v1/clustertemplates/?sort_dir=asc', {}, None), ] self._test_clustertemplate_list_with_filters( sort_dir='asc', expect=expect) def test_clustertemplate_list_with_sort_key(self): expect = [ ('GET', '/v1/clustertemplates/?sort_key=uuid', {}, None), ] self._test_clustertemplate_list_with_filters( sort_key='uuid', expect=expect) def test_clustertemplate_list_with_sort_key_dir(self): expect = [ ('GET', '/v1/clustertemplates/?sort_key=uuid&sort_dir=desc', {}, None), ] self._test_clustertemplate_list_with_filters( sort_key='uuid', sort_dir='desc', expect=expect) def test_clustertemplate_show_by_id(self): cluster_template = self.mgr.get(CLUSTERTEMPLATE1['id']) expect = [ ('GET', '/v1/clustertemplates/%s' % CLUSTERTEMPLATE1['id'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(CLUSTERTEMPLATE1['name'], cluster_template.name) self.assertEqual(CLUSTERTEMPLATE1['image_id'], cluster_template.image_id) self.assertEqual(CLUSTERTEMPLATE1['docker_volume_size'], cluster_template.docker_volume_size) self.assertEqual(CLUSTERTEMPLATE1['docker_storage_driver'], cluster_template.docker_storage_driver) self.assertEqual(CLUSTERTEMPLATE1['fixed_network'], cluster_template.fixed_network) self.assertEqual(CLUSTERTEMPLATE1['fixed_subnet'], cluster_template.fixed_subnet) self.assertEqual(CLUSTERTEMPLATE1['coe'], cluster_template.coe) self.assertEqual(CLUSTERTEMPLATE1['http_proxy'], cluster_template.http_proxy) self.assertEqual(CLUSTERTEMPLATE1['https_proxy'], cluster_template.https_proxy) self.assertEqual(CLUSTERTEMPLATE1['no_proxy'], cluster_template.no_proxy) self.assertEqual(CLUSTERTEMPLATE1['network_driver'], cluster_template.network_driver) self.assertEqual(CLUSTERTEMPLATE1['volume_driver'], cluster_template.volume_driver) self.assertEqual(CLUSTERTEMPLATE1['labels'], cluster_template.labels) self.assertEqual(CLUSTERTEMPLATE1['tls_disabled'], cluster_template.tls_disabled) self.assertEqual(CLUSTERTEMPLATE1['public'], cluster_template.public) self.assertEqual(CLUSTERTEMPLATE1['registry_enabled'], cluster_template.registry_enabled) self.assertEqual(CLUSTERTEMPLATE1['master_lb_enabled'], cluster_template.master_lb_enabled) self.assertEqual(CLUSTERTEMPLATE1['floating_ip_enabled'], cluster_template.floating_ip_enabled) def test_clustertemplate_show_by_name(self): cluster_template = self.mgr.get(CLUSTERTEMPLATE1['name']) expect = [ ('GET', '/v1/clustertemplates/%s' % CLUSTERTEMPLATE1['name'], {}, None) ] self.assertEqual(expect, self.api.calls) self.assertEqual(CLUSTERTEMPLATE1['name'], cluster_template.name) self.assertEqual(CLUSTERTEMPLATE1['image_id'], cluster_template.image_id) self.assertEqual(CLUSTERTEMPLATE1['docker_volume_size'], cluster_template.docker_volume_size) self.assertEqual(CLUSTERTEMPLATE1['docker_storage_driver'], cluster_template.docker_storage_driver) self.assertEqual(CLUSTERTEMPLATE1['fixed_network'], cluster_template.fixed_network) self.assertEqual(CLUSTERTEMPLATE1['fixed_subnet'], cluster_template.fixed_subnet) self.assertEqual(CLUSTERTEMPLATE1['coe'], cluster_template.coe) self.assertEqual(CLUSTERTEMPLATE1['http_proxy'], cluster_template.http_proxy) self.assertEqual(CLUSTERTEMPLATE1['https_proxy'], cluster_template.https_proxy) self.assertEqual(CLUSTERTEMPLATE1['no_proxy'], cluster_template.no_proxy) self.assertEqual(CLUSTERTEMPLATE1['network_driver'], cluster_template.network_driver) self.assertEqual(CLUSTERTEMPLATE1['volume_driver'], cluster_template.volume_driver) self.assertEqual(CLUSTERTEMPLATE1['labels'], cluster_template.labels) self.assertEqual(CLUSTERTEMPLATE1['tls_disabled'], cluster_template.tls_disabled) self.assertEqual(CLUSTERTEMPLATE1['public'], cluster_template.public) self.assertEqual(CLUSTERTEMPLATE1['registry_enabled'], cluster_template.registry_enabled) self.assertEqual(CLUSTERTEMPLATE1['master_lb_enabled'], cluster_template.master_lb_enabled) self.assertEqual(CLUSTERTEMPLATE1['floating_ip_enabled'], cluster_template.floating_ip_enabled) def test_clustertemplate_create(self): cluster_template = self.mgr.create(**CREATE_CLUSTERTEMPLATE) expect = [ ('POST', '/v1/clustertemplates', {}, CREATE_CLUSTERTEMPLATE), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster_template) self.assertEqual(CLUSTERTEMPLATE1['docker_volume_size'], cluster_template.docker_volume_size) self.assertEqual(CLUSTERTEMPLATE1['docker_storage_driver'], cluster_template.docker_storage_driver) def test_clustertemplate_create_with_keypair(self): cluster_template_with_keypair = dict() cluster_template_with_keypair.update(CREATE_CLUSTERTEMPLATE) cluster_template_with_keypair['keypair_id'] = 'test_key' cluster_template = self.mgr.create(**cluster_template_with_keypair) expect = [ ('POST', '/v1/clustertemplates', {}, cluster_template_with_keypair), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster_template) self.assertEqual(CLUSTERTEMPLATE1['docker_volume_size'], cluster_template.docker_volume_size) self.assertEqual(CLUSTERTEMPLATE1['docker_storage_driver'], cluster_template.docker_storage_driver) def test_clustertemplate_create_with_docker_volume_size(self): cluster_template_with_docker_volume_size = dict() cluster_template_with_docker_volume_size.update(CREATE_CLUSTERTEMPLATE) cluster_template_with_docker_volume_size['docker_volume_size'] = 11 cluster_template = self.mgr.create( **cluster_template_with_docker_volume_size) expect = [ ('POST', '/v1/clustertemplates', {}, cluster_template_with_docker_volume_size), ] self.assertEqual(expect, self.api.calls) self.assertTrue(cluster_template) self.assertEqual(CLUSTERTEMPLATE1['docker_volume_size'], cluster_template.docker_volume_size) self.assertEqual(CLUSTERTEMPLATE1['docker_storage_driver'], cluster_template.docker_storage_driver) def test_clustertemplate_create_fail(self): CREATE_CLUSTERTEMPLATE_FAIL = copy.deepcopy(CREATE_CLUSTERTEMPLATE) CREATE_CLUSTERTEMPLATE_FAIL["wrong_key"] = "wrong" self.assertRaisesRegex( exceptions.InvalidAttribute, ("Key must be in %s" % ','.join(cluster_templates.CREATION_ATTRIBUTES)), self.mgr.create, **CREATE_CLUSTERTEMPLATE_FAIL) self.assertEqual([], self.api.calls) def test_clustertemplate_delete_by_id(self): cluster_template = self.mgr.delete(CLUSTERTEMPLATE1['id']) expect = [ ('DELETE', '/v1/clustertemplates/%s' % CLUSTERTEMPLATE1['id'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(cluster_template) def test_clustertemplate_delete_by_name(self): cluster_template = self.mgr.delete(CLUSTERTEMPLATE1['name']) expect = [ ('DELETE', '/v1/clustertemplates/%s' % CLUSTERTEMPLATE1['name'], {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(cluster_template) def test_clustertemplate_update(self): patch = {'op': 'replace', 'value': NEW_NAME, 'path': '/name'} cluster_template = self.mgr.update(id=CLUSTERTEMPLATE1['id'], patch=patch) expect = [ ('PATCH', '/v1/clustertemplates/%s' % CLUSTERTEMPLATE1['id'], {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_NAME, cluster_template.name) python-magnumclient-2.8.0/magnumclient/tests/v1/test_clustertemplates_shell.py000066400000000000000000001157411323411543400301070ustar00rootroot00000000000000# 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 mock from magnumclient.common.apiclient import exceptions from magnumclient.tests.v1 import shell_test_base from magnumclient.v1.cluster_templates import ClusterTemplate class FakeClusterTemplate(ClusterTemplate): def __init__(self, manager=None, info={}, **kwargs): ClusterTemplate.__init__(self, manager=manager, info=info) self.apiserver_port = kwargs.get('apiserver_port', None) self.uuid = kwargs.get('uuid', 'x') self.links = kwargs.get('links', []) self.server_type = kwargs.get('server_type', 'vm') self.image_id = kwargs.get('image', 'x') self.tls_disabled = kwargs.get('tls_disabled', False) self.registry_enabled = kwargs.get('registry_enabled', False) self.coe = kwargs.get('coe', 'x') self.public = kwargs.get('public', False) self.name = kwargs.get('name', 'x') class ShellTest(shell_test_base.TestCommandLineArgument): def _get_expected_args_list(self, limit=None, sort_dir=None, sort_key=None, detail=False): expected_args = {} expected_args['limit'] = limit expected_args['sort_dir'] = sort_dir expected_args['sort_key'] = sort_key expected_args['detail'] = detail return expected_args def _get_expected_args(self, image_id, external_network_id, coe, master_flavor_id=None, name=None, keypair_id=None, fixed_network=None, fixed_subnet=None, network_driver=None, volume_driver=None, dns_nameserver='8.8.8.8', flavor_id='m1.medium', docker_storage_driver='devicemapper', docker_volume_size=None, http_proxy=None, https_proxy=None, no_proxy=None, labels={}, tls_disabled=False, public=False, master_lb_enabled=False, server_type='vm', registry_enabled=False, insecure_registry=None): expected_args = {} expected_args['image_id'] = image_id expected_args['external_network_id'] = external_network_id expected_args['coe'] = coe expected_args['master_flavor_id'] = master_flavor_id expected_args['name'] = name expected_args['keypair_id'] = keypair_id expected_args['fixed_network'] = fixed_network expected_args['fixed_subnet'] = fixed_subnet expected_args['network_driver'] = network_driver expected_args['volume_driver'] = volume_driver expected_args['dns_nameserver'] = dns_nameserver expected_args['flavor_id'] = flavor_id expected_args['docker_volume_size'] = docker_volume_size expected_args['docker_storage_driver'] = docker_storage_driver expected_args['http_proxy'] = http_proxy expected_args['https_proxy'] = https_proxy expected_args['no_proxy'] = no_proxy expected_args['labels'] = labels expected_args['tls_disabled'] = tls_disabled expected_args['public'] = public expected_args['master_lb_enabled'] = master_lb_enabled expected_args['server_type'] = server_type expected_args['registry_enabled'] = registry_enabled expected_args['insecure_registry'] = insecure_registry return expected_args @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--image-id test_image ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--coe swarm ' '--dns-nameserver test_dns ' '--flavor-id test_flavor ' '--fixed-network private ' '--fixed-subnet private-subnet ' '--volume-driver test_volume ' '--network-driver test_driver ' '--labels key=val ' '--master-flavor-id test_flavor ' '--docker-volume-size 10 ' '--docker-storage-driver devicemapper ' '--public ' '--server-type vm ' '--master-lb-enabled ') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', dns_nameserver='test_dns', public=True, flavor_id='test_flavor', master_flavor_id='test_flavor', fixed_network='private', fixed_subnet='private-subnet', server_type='vm', network_driver='test_driver', volume_driver='test_volume', docker_storage_driver='devicemapper', docker_volume_size=10, master_lb_enabled=True, labels={'key': 'val'}) mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-template-create ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe kubernetes ' '--name test ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='kubernetes', external_network_id='test_net', server_type='vm') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_success_no_servertype(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--image-id test_image ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--coe swarm ' '--dns-nameserver test_dns ' '--flavor-id test_flavor ' '--fixed-network public ' '--network-driver test_driver ' '--labels key=val ' '--master-flavor-id test_flavor ' '--docker-volume-size 10 ' '--docker-storage-driver devicemapper ' '--public ') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', dns_nameserver='test_dns', public=True, flavor_id='test_flavor', master_flavor_id='test_flavor', fixed_network='public', network_driver='test_driver', docker_storage_driver='devicemapper', docker_volume_size=10, labels={'key': 'val'}) mock_create.assert_called_with(**expected_args) self._test_arg_success('cluster-template-create ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe kubernetes ' '--name test ') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='kubernetes', external_network_id='test_net') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_success_with_registry_enabled( self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--network-driver test_driver ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--registry-enabled') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', network_driver='test_driver', registry_enabled=True) mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_public_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --network-driver test_driver ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--public ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', public=True, server_type='vm', network_driver='test_driver') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_success_with_master_flavor(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--image-id test_image ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--coe swarm ' '--dns-nameserver test_dns ' '--master-flavor-id test_flavor') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', dns_nameserver='test_dns', master_flavor_id='test_flavor') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_docker_vol_size_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --docker-volume-size 4514 ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', docker_volume_size=4514) mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_docker_storage_driver_success( self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--docker-storage-driver devicemapper ' '--coe swarm' ) expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', docker_storage_driver='devicemapper') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_fixed_network_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', fixed_network='private', external_network_id='test_net', server_type='vm') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_network_driver_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --network-driver test_driver ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', network_driver='test_driver') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_volume_driver_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --volume-driver test_volume ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', volume_driver='test_volume') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_http_proxy_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--http-proxy http_proxy ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', fixed_network='private', server_type='vm', http_proxy='http_proxy') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_https_proxy_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--https-proxy https_proxy ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', fixed_network='private', server_type='vm', https_proxy='https_proxy') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_no_proxy_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test --fixed-network private ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--no-proxy no_proxy ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', fixed_network='private', server_type='vm', no_proxy='no_proxy') mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_labels_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--labels key=val ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', labels={'key': 'val'}) mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_separate_labels_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--labels key1=val1 ' '--labels key2=val2 ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', labels={'key1': 'val1', 'key2': 'val2'}) mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_combined_labels_success(self, mock_create): self._test_arg_success('cluster-template-create ' '--name test ' '--labels key1=val1,key2=val2 ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', labels={'key1': 'val1', 'key2': 'val2'}) mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_success_only_positional_name(self, mock_create): self._test_arg_success('cluster-template-create ' 'test ' '--labels key1=val1,key2=val2 ' '--keypair-id test_keypair ' '--external-network-id test_net ' '--image-id test_image ' '--coe swarm ' '--server-type vm') expected_args = \ self._get_expected_args(name='test', image_id='test_image', keypair_id='test_keypair', coe='swarm', external_network_id='test_net', server_type='vm', labels={'key1': 'val1', 'key2': 'val2'}) mock_create.assert_called_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_failure_duplicate_name(self, mock_create): self._test_arg_failure('cluster-template-create ' 'foo --name test', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_failure_few_arg(self, mock_create): self._test_arg_failure('cluster-template-create ' '--name test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create ' '--image-id test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create ' '--keypair-id test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create ' '--external-network-id test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create ' '--coe test', self._mandatory_group_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create ' '--coe test ' '--external-network test ', self._mandatory_group_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create ' '--coe test ' '--image test ', self._mandatory_group_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create ' '--server-type test', self._mandatory_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_deprecation_errors(self, mock_create): required_args = ('cluster-template-create ' '--coe test --external-network public --image test ') self._test_arg_failure('cluster-template-create --coe test ' '--external-network-id test ' '--external-network test ', self._too_many_group_arg_error) mock_create.assert_not_called() self._test_arg_failure('cluster-template-create --coe test ' '--image-id test ' '--image test ', self._too_many_group_arg_error) mock_create.assert_not_called() self._test_arg_failure(required_args + '--flavor test --flavor-id test', self._too_many_group_arg_error) mock_create.assert_not_called() self._test_arg_failure(required_args + '--master-flavor test --master-flavor-id test', self._too_many_group_arg_error) mock_create.assert_not_called() self._test_arg_failure(required_args + '--keypair test --keypair-id test', self._too_many_group_arg_error) mock_create.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.create') def test_cluster_template_create_deprecation_warnings(self, mock_create): required_args = ('cluster-template-create ' '--coe test --external-network public --image test ') self._test_arg_failure('cluster-template-create ' '--coe test ' '--external-network-id test ' '--image test ', self._deprecated_warning) expected_args = \ self._get_expected_args(image_id='test', coe='test', external_network_id='test') mock_create.assert_called_with(**expected_args) self._test_arg_failure('cluster-template-create ' '--coe test ' '--external-network test ' '--image-id test ', self._deprecated_warning) expected_args = \ self._get_expected_args(image_id='test', coe='test', external_network_id='test') mock_create.assert_called_with(**expected_args) self._test_arg_failure('cluster-template-create ' '--coe test ' '--external-network-id test ' '--image-id test ', self._deprecated_warning) expected_args = \ self._get_expected_args(image_id='test', coe='test', external_network_id='test') mock_create.assert_called_with(**expected_args) self._test_arg_failure(required_args + '--keypair-id test', self._deprecated_warning) expected_args = \ self._get_expected_args(image_id='test', coe='test', keypair_id='test', external_network_id='public') mock_create.assert_called_with(**expected_args) self._test_arg_failure(required_args + '--flavor-id test', self._deprecated_warning) expected_args = \ self._get_expected_args(image_id='test', coe='test', flavor_id='test', external_network_id='public') mock_create.assert_called_with(**expected_args) self._test_arg_failure(required_args + '--master-flavor-id test', self._deprecated_warning) expected_args = \ self._get_expected_args(image_id='test', coe='test', master_flavor_id='test', external_network_id='public') mock_create.assert_called_with(**expected_args) self._test_arg_failure(required_args + '--name foo', self._deprecated_warning) expected_args = \ self._get_expected_args(image_id='test', coe='test', name='foo', external_network_id='public') mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') def test_cluster_template_show_success(self, mock_show): self._test_arg_success('cluster-template-show xxx') mock_show.assert_called_once_with('xxx') @mock.patch('magnumclient.v1.cluster_templates.ClusterTemplateManager.get') def test_cluster_template_show_failure_no_arg(self, mock_show): self._test_arg_failure('cluster-template-show', self._few_argument_error) mock_show.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.delete') def test_cluster_template_delete_success(self, mock_delete): self._test_arg_success('cluster-template-delete xxx') mock_delete.assert_called_once_with('xxx') @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.delete') def test_cluster_template_delete_multiple_id_success(self, mock_delete): self._test_arg_success('cluster-template-delete xxx xyz') calls = [mock.call('xxx'), mock.call('xyz')] mock_delete.assert_has_calls(calls) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.delete') def test_cluster_template_delete_failure_no_arg(self, mock_delete): self._test_arg_failure('cluster-template-delete', self._few_argument_error) mock_delete.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.update') def test_cluster_template_update_success(self, mock_update): self._test_arg_success('cluster-template-update test add test=test') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}] mock_update.assert_called_once_with('test', patch) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.update') def test_cluster_template_update_success_many_attribute(self, mock_update): self._test_arg_success('cluster-template-update test ' 'add test=test test1=test1') patch = [{'op': 'add', 'path': '/test', 'value': 'test'}, {'op': 'add', 'path': '/test1', 'value': 'test1'}] mock_update.assert_called_once_with('test', patch) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.update') def test_cluster_template_update_failure_wrong_op(self, mock_update): _error_msg = [ '.*?^usage: magnum cluster-template-update ', '.*?^error: argument : invalid choice: ', ".*?^Try 'magnum help cluster-template-update' " "for more information." ] self._test_arg_failure('cluster-template-update test wrong test=test', _error_msg) mock_update.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.update') def test_cluster_template_update_failure_few_args(self, mock_update): _error_msg = [ '.*?^usage: magnum cluster-template-update ', '.*?^error: (the following arguments|too few arguments)', ".*?^Try 'magnum help cluster-template-update' " "for more information." ] self._test_arg_failure('cluster-template-update', _error_msg) mock_update.assert_not_called() self._test_arg_failure('cluster-template-update test', _error_msg) mock_update.assert_not_called() self._test_arg_failure('cluster-template-update test add', _error_msg) mock_update.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.list') def test_cluster_template_list_success(self, mock_list): self._test_arg_success('cluster-template-list') expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.list') def test_cluster_template_list_success_with_arg(self, mock_list): self._test_arg_success('cluster-template-list ' '--limit 1 ' '--sort-dir asc ' '--sort-key uuid') expected_args = self._get_expected_args_list(1, 'asc', 'uuid') mock_list.assert_called_once_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.list') def test_cluster_template_list_success_detailed(self, mock_list): self._test_arg_success('cluster-template-list ' '--detail') expected_args = self._get_expected_args_list(detail=True) mock_list.assert_called_once_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.list') def test_cluster_template_list_ignored_duplicated_field(self, mock_list): mock_list.return_value = [FakeClusterTemplate()] self._test_arg_success( 'cluster-template-list --fields coe,coe,coe,name,name', keyword='\n| uuid | name | Coe |\n') # Output should be # +------+------+-----+ # | uuid | name | Coe | # +------+------+-----+ # | x | x | x | # +------+------+-----+ expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.list') def test_cluster_template_list_failure_with_invalid_field(self, mock_list): mock_list.return_value = [FakeClusterTemplate()] _error_msg = [".*?^Non-existent fields are specified: ['xxx','zzz']"] self.assertRaises(exceptions.CommandError, self._test_arg_failure, 'cluster-template-list --fields xxx,coe,zzz', _error_msg) expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.list') def test_cluster_template_list_failure_invalid_arg(self, mock_list): _error_msg = [ '.*?^usage: magnum cluster-template-list ', '.*?^error: argument --sort-dir: invalid choice: ', ".*?^Try 'magnum help cluster-template-list' for more information." ] self._test_arg_failure('cluster-template-list --sort-dir aaa', _error_msg) mock_list.assert_not_called() @mock.patch( 'magnumclient.v1.cluster_templates.ClusterTemplateManager.list') def test_cluster_template_list_failure(self, mock_list): self._test_arg_failure('cluster-template-list --wrong', self._unrecognized_arg_error) mock_list.assert_not_called() python-magnumclient-2.8.0/magnumclient/tests/v1/test_mservices.py000066400000000000000000000110451323411543400253100ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 testtools import matchers from magnumclient.tests import utils from magnumclient.v1 import mservices SERVICE1 = {'id': 123, 'host': 'fake-host1', 'binary': 'fake-bin1', 'state': 'up', } SERVICE2 = {'id': 124, 'host': 'fake-host2', 'binary': 'fake-bin2', 'state': 'down', } fake_responses = { '/v1/mservices': { 'GET': ( {}, {'mservices': [SERVICE1, SERVICE2]}, ), }, '/v1/mservices/?limit=2': { 'GET': ( {}, {'mservices': [SERVICE1, SERVICE2]}, ), }, '/v1/mservices/?marker=%s' % SERVICE2['id']: { 'GET': ( {}, {'mservices': [SERVICE1, SERVICE2]}, ), }, '/v1/mservices/?limit=2&marker=%s' % SERVICE2['id']: { 'GET': ( {}, {'mservices': [SERVICE2, SERVICE1]}, ), }, '/v1/mservices/?sort_dir=asc': { 'GET': ( {}, {'mservices': [SERVICE1, SERVICE2]}, ), }, '/v1/mservices/?sort_key=id': { 'GET': ( {}, {'mservices': [SERVICE1, SERVICE2]}, ), }, '/v1/mservices/?sort_key=id&sort_dir=desc': { 'GET': ( {}, {'mservices': [SERVICE2, SERVICE1]}, ), }, } class MServiceManagerTest(testtools.TestCase): def setUp(self): super(MServiceManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = mservices.MServiceManager(self.api) def test_coe_service_list(self): mservices = self.mgr.list() expect = [ ('GET', '/v1/mservices', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(mservices, matchers.HasLength(2)) def _test_coe_service_list_with_filters( self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False, expect=[]): mservices_filter = self.mgr.list(limit=limit, marker=marker, sort_key=sort_key, sort_dir=sort_dir, detail=detail) self.assertEqual(expect, self.api.calls) self.assertThat(mservices_filter, matchers.HasLength(2)) def test_coe_service_list_with_limit(self): expect = [ ('GET', '/v1/mservices/?limit=2', {}, None), ] self._test_coe_service_list_with_filters( limit=2, expect=expect) def test_coe_service_list_with_marker(self): expect = [ ('GET', '/v1/mservices/?marker=%s' % SERVICE2['id'], {}, None), ] self._test_coe_service_list_with_filters( marker=SERVICE2['id'], expect=expect) def test_coe_service_list_with_marker_limit(self): expect = [ ('GET', '/v1/mservices/?limit=2&marker=%s' % SERVICE2['id'], {}, None), ] self._test_coe_service_list_with_filters( limit=2, marker=SERVICE2['id'], expect=expect) def test_coe_service_list_with_sort_dir(self): expect = [ ('GET', '/v1/mservices/?sort_dir=asc', {}, None), ] self._test_coe_service_list_with_filters( sort_dir='asc', expect=expect) def test_coe_service_list_with_sort_key(self): expect = [ ('GET', '/v1/mservices/?sort_key=id', {}, None), ] self._test_coe_service_list_with_filters( sort_key='id', expect=expect) def test_coe_service_list_with_sort_key_dir(self): expect = [ ('GET', '/v1/mservices/?sort_key=id&sort_dir=desc', {}, None), ] self._test_coe_service_list_with_filters( sort_key='id', sort_dir='desc', expect=expect) python-magnumclient-2.8.0/magnumclient/tests/v1/test_mservices_shell.py000066400000000000000000000023431323411543400265000ustar00rootroot00000000000000# 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 mock from magnumclient.tests.v1 import shell_test_base class ShellTest(shell_test_base.TestCommandLineArgument): @mock.patch('magnumclient.v1.mservices.MServiceManager.list') def test_magnum_service_list_success(self, mock_list): self._test_arg_success('service-list') mock_list.assert_called_once_with() @mock.patch('magnumclient.v1.mservices.MServiceManager.list') def test_magnum_service_list_failure(self, mock_list): self._test_arg_failure('service-list --wrong', self._unrecognized_arg_error) mock_list.assert_not_called() python-magnumclient-2.8.0/magnumclient/tests/v1/test_quotas.py000066400000000000000000000102741323411543400246270ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 testtools from testtools import matchers from magnumclient.tests import utils from magnumclient.v1 import quotas QUOTA1 = { 'id': 123, 'resource': "Cluster", 'hard_limit': 5, 'project_id': 'abc' } QUOTA2 = { 'id': 124, 'resource': "Cluster", 'hard_limit': 10, 'project_id': 'bcd' } CREATE_QUOTA = copy.deepcopy(QUOTA1) del CREATE_QUOTA['id'] UPDATED_QUOTA = copy.deepcopy(QUOTA2) NEW_HARD_LIMIT = 20 UPDATED_QUOTA['hard_limit'] = NEW_HARD_LIMIT fake_responses = { '/v1/quotas?all_tenants=True': { 'GET': ( {}, {'quotas': [QUOTA1, QUOTA2]}, ), }, '/v1/quotas': { 'GET': ( {}, {'quotas': [QUOTA1]}, ), 'POST': ( {}, QUOTA1, ), }, '/v1/quotas/%(id)s/%(res)s' % {'id': QUOTA2['project_id'], 'res': QUOTA2['resource']}: { 'GET': ( {}, QUOTA2, ), 'PATCH': ( {}, UPDATED_QUOTA, ), 'DELETE': ( {}, None, ), }, } class QuotasManagerTest(testtools.TestCase): def setUp(self): super(QuotasManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = quotas.QuotasManager(self.api) def test_list_quotas(self): quotas = self.mgr.list() expect = [ ('GET', '/v1/quotas', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(quotas, matchers.HasLength(1)) def test_list_quotas_all(self): quotas = self.mgr.list(all_tenants=True) expect = [ ('GET', '/v1/quotas?all_tenants=True', {}, None), ] self.assertEqual(expect, self.api.calls) self.assertThat(quotas, matchers.HasLength(2)) def test_show_project_resource_quota(self): expect = [ ('GET', '/v1/quotas/%(id)s/%(res)s' % {'id': QUOTA2['project_id'], 'res': QUOTA2['resource']}, {}, None), ] quotas = self.mgr.get(QUOTA2['project_id'], QUOTA2['resource']) self.assertEqual(expect, self.api.calls) expected_quotas = QUOTA2 self.assertEqual(expected_quotas, quotas._info) def test_quota_create(self): quota = self.mgr.create(**CREATE_QUOTA) expect = [ ('POST', '/v1/quotas', {}, CREATE_QUOTA), ] self.assertEqual(expect, self.api.calls) self.assertEqual(QUOTA1, quota._info) def test_quota_update(self): patch = { 'resource': "Cluster", 'hard_limit': NEW_HARD_LIMIT, 'project_id': 'bcd' } quota = self.mgr.update(id=QUOTA2['project_id'], resource=QUOTA2['resource'], patch=patch) expect = [ ('PATCH', '/v1/quotas/%(id)s/%(res)s' % { 'id': QUOTA2['project_id'], 'res': QUOTA2['resource']}, {}, patch), ] self.assertEqual(expect, self.api.calls) self.assertEqual(NEW_HARD_LIMIT, quota.hard_limit) def test_quota_delete(self): quota = self.mgr.delete(QUOTA2['project_id'], QUOTA2['resource']) expect = [ ('DELETE', '/v1/quotas/%(id)s/%(res)s' % {'id': QUOTA2['project_id'], 'res': QUOTA2['resource']}, {}, None), ] self.assertEqual(expect, self.api.calls) self.assertIsNone(quota) python-magnumclient-2.8.0/magnumclient/tests/v1/test_quotas_shell.py000066400000000000000000000123661323411543400260220ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.tests.v1 import shell_test_base class ShellTest(shell_test_base.TestCommandLineArgument): def _get_expected_args_list(self, marker=None, limit=None, sort_dir=None, sort_key=None, all_tenants=False): expected_args = {} expected_args['marker'] = marker expected_args['limit'] = limit expected_args['sort_dir'] = sort_dir expected_args['sort_key'] = sort_key expected_args['all_tenants'] = False return expected_args def _get_expected_args_create(self, project_id, resource, hard_limit): expected_args = {} expected_args['project_id'] = project_id expected_args['resource'] = resource expected_args['hard_limit'] = hard_limit return expected_args @mock.patch('magnumclient.v1.quotas.QuotasManager.list') def test_quotas_list_success(self, mock_list): self._test_arg_success('quotas-list') expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch('magnumclient.v1.quotas.QuotasManager.list') def test_quotas_list_failure(self, mock_list): self._test_arg_failure('quotas-list --wrong', self._unrecognized_arg_error) mock_list.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.create') def test_quotas_create_success(self, mock_create): self._test_arg_success('quotas-create --project-id abc ' '--resource Cluster ' '--hard-limit 15') expected_args = self._get_expected_args_create('abc', 'Cluster', 15) mock_create.assert_called_with(**expected_args) @mock.patch('magnumclient.v1.quotas.QuotasManager.create') def test_quotas_create_failure_only_project_id(self, mock_create): self._test_arg_failure('quotas-create --project-id abc', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.create') def test_quotas_create_failure_only_resource(self, mock_create): self._test_arg_failure('quotas-create --resource Cluster', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.create') def test_quotas_create_failure_only_hard_limit(self, mock_create): self._test_arg_failure('quotas-create --hard-limit 10', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.create') def test_quotas_create_failure_no_arg(self, mock_create): self._test_arg_failure('quotas-create', self._mandatory_arg_error) mock_create.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.delete') def test_quotas_delete_success(self, mock_delete): self._test_arg_success( 'quotas-delete --project-id xxx --resource Cluster') mock_delete.assert_called_once_with('xxx', 'Cluster') @mock.patch('magnumclient.v1.quotas.QuotasManager.delete') def test_quotas_delete_failure_no_project_id(self, mock_delete): self._test_arg_failure('quotas-delete --resource Cluster', self._mandatory_arg_error) mock_delete.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.delete') def test_quotas_delete_failure_no_resource(self, mock_delete): self._test_arg_failure('quotas-delete --project-id xxx', self._mandatory_arg_error) mock_delete.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.get') def test_quotas_show_success(self, mock_show): self._test_arg_success('quotas-show --project-id abc ' '--resource Cluster') mock_show.assert_called_once_with('abc', 'Cluster') @mock.patch('magnumclient.v1.quotas.QuotasManager.get') def test_quotas_show_failure_no_arg(self, mock_show): self._test_arg_failure('quotas-show', self._mandatory_arg_error) mock_show.assert_not_called() @mock.patch('magnumclient.v1.quotas.QuotasManager.update') def test_quotas_update_success(self, mock_update): self._test_arg_success('quotas-update --project-id abc ' '--resource Cluster ' '--hard-limit 20') patch = {'project_id': 'abc', 'resource': 'Cluster', 'hard_limit': 20} mock_update.assert_called_once_with('abc', 'Cluster', patch) python-magnumclient-2.8.0/magnumclient/tests/v1/test_stats.py000066400000000000000000000055741323411543400244600ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.tests import utils from magnumclient.v1 import stats CLUSTER1 = {'id': 123, 'uuid': '66666666-7777-8888-9999-000000000001', 'name': 'cluster1', 'cluster_template_id': 'e74c40e0-d825-11e2-a28f-0800200c9a61', 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a51', 'api_address': '172.17.2.1', 'node_addresses': ['172.17.2.3'], 'node_count': 2, 'master_count': 1, 'project_id': 'abc' } CLUSTER2 = {'id': 124, 'uuid': '66666666-7777-8888-9999-000000000002', 'name': 'cluster2', 'cluster_template_id': 'e74c40e0-d825-11e2-a28f-0800200c9a62', 'stack_id': '5d12f6fd-a196-4bf0-ae4c-1f639a523a52', 'api_address': '172.17.2.2', 'node_addresses': ['172.17.2.4'], 'node_count': 2, 'master_count': 1, 'project_id': 'bcd' } nc = 'node_count' mc = 'master_count' C1 = CLUSTER1 C2 = CLUSTER2 fake_responses = { '/v1/stats': { 'GET': ( {}, {'clusters': 2, 'nodes': C1[nc] + C1[mc] + C2[nc] + C2[mc]}, ) }, '/v1/stats?project_id=%s' % C2['project_id']: { 'GET': ( {}, {'clusters': 1, 'nodes': C2[nc] + C2[mc]}, ) }, } class StatsManagerTest(testtools.TestCase): def setUp(self): super(StatsManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) self.mgr = stats.StatsManager(self.api) def test_stats(self): stats = self.mgr.list() expect = [ ('GET', '/v1/stats', {}, None), ] self.assertEqual(expect, self.api.calls) expected_stats = {'clusters': 2, 'nodes': C1[nc] + C1[mc] + C2[nc] + C2[mc]} self.assertEqual(expected_stats, stats._info) def test_stats_with_project_id(self): expect = [ ('GET', '/v1/stats?project_id=%s' % CLUSTER2['project_id'], {}, None), ] stats = self.mgr.list(project_id=CLUSTER2['project_id']) self.assertEqual(expect, self.api.calls) expected_stats = {'clusters': 1, 'nodes': C2[nc] + C2[mc]} self.assertEqual(expected_stats, stats._info) python-magnumclient-2.8.0/magnumclient/tests/v1/test_stats_shell.py000066400000000000000000000037461323411543400256460ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.tests.v1 import shell_test_base from magnumclient.v1.stats import Stats class FakeStats(Stats): def __init__(self, manager=None, info={}, **kwargs): Stats.__init__(self, manager=manager, info=info) self.clusters = kwargs.get('clusters', 0) self.nodes = kwargs.get('nodes', 0) class ShellTest(shell_test_base.TestCommandLineArgument): def _get_expected_args_list(self, project_id=None): expected_args = {} expected_args['project_id'] = project_id return expected_args @mock.patch( 'magnumclient.v1.stats.StatsManager.list') def test_stats_get_success(self, mock_list): self._test_arg_success('stats-list') expected_args = self._get_expected_args_list() mock_list.assert_called_once_with(**expected_args) @mock.patch( 'magnumclient.v1.stats.StatsManager.list') def test_stats_get_success_with_arg(self, mock_list): self._test_arg_success('stats-list ' '--project-id 111 ') expected_args = self._get_expected_args_list('111') mock_list.assert_called_once_with(**expected_args) @mock.patch( 'magnumclient.v1.stats.StatsManager.list') def test_stats_get_failure(self, mock_list): self._test_arg_failure('stats-list --wrong', self._unrecognized_arg_error) mock_list.assert_not_called() python-magnumclient-2.8.0/magnumclient/v1/000077500000000000000000000000001323411543400205345ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/v1/__init__.py000066400000000000000000000000001323411543400226330ustar00rootroot00000000000000python-magnumclient-2.8.0/magnumclient/v1/basemodels.py000066400000000000000000000106551323411543400232330ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.common import base from magnumclient.common import utils from magnumclient import exceptions CREATION_ATTRIBUTES = ['name', 'image_id', 'flavor_id', 'master_flavor_id', 'keypair_id', 'external_network_id', 'fixed_network', 'fixed_subnet', 'dns_nameserver', 'docker_volume_size', 'labels', 'coe', 'http_proxy', 'https_proxy', 'no_proxy', 'network_driver', 'tls_disabled', 'public', 'registry_enabled', 'volume_driver', 'server_type', 'docker_storage_driver', 'master_lb_enabled', 'floating_ip_enabled'] OUTPUT_ATTRIBUTES = CREATION_ATTRIBUTES + ['apiserver_port', 'created_at', 'insecure_registry', 'links', 'updated_at', 'cluster_distro', 'uuid'] class BaseModel(base.Resource): # model_name needs to be overridden by any derived class. # model_name should be capitalized and singular, e.g. "Cluster" model_name = '' def __repr__(self): return "<" + self.__class__.model_name + "%s>" % self._info class BaseModelManager(base.Manager): # api_name needs to be overridden by any derived class. # api_name should be pluralized and lowercase, e.g. "clustertemplates", as # it shows up in the URL path: "/v1/{api_name}" api_name = '' @classmethod def _path(cls, id=None): return '/v1/' + cls.api_name + \ '/%s' % id if id else '/v1/' + cls.api_name def list(self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False): """Retrieve a list of baymodels. :param marker: Optional, the UUID of a baymodel, eg the last baymodel from a previous result set. Return the next result set. :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of baymodels to return. 2) limit == 0, return the entire list of baymodels. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Magnum API (see Magnum's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about baymodels. :returns: A list of baymodels. """ if limit is not None: limit = int(limit) filters = utils.common_filters(marker, limit, sort_key, sort_dir) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), self.__class__.api_name) else: return self._list_pagination(self._path(path), self.__class__.api_name, limit=limit) def get(self, id): try: return self._list(self._path(id))[0] except IndexError: return None def create(self, **kwargs): new = {} for (key, value) in kwargs.items(): if key in CREATION_ATTRIBUTES: new[key] = value else: raise exceptions.InvalidAttribute( "Key must be in %s" % ",".join(CREATION_ATTRIBUTES)) return self._create(self._path(), new) def delete(self, id): return self._delete(self._path(id)) def update(self, id, patch): return self._update(self._path(id), patch) python-magnumclient-2.8.0/magnumclient/v1/baseunit.py000066400000000000000000000075231323411543400227270ustar00rootroot00000000000000# 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 magnumclient.common import base from magnumclient.common import utils from magnumclient import exceptions # Derived classes may append their own custom attributes to this default list CREATION_ATTRIBUTES = ['name', 'node_count', 'discovery_url', 'master_count'] class BaseTemplate(base.Resource): # template_name must be overridden by any derived class. # template_name should be an uppercase plural, e.g. "Clusters" template_name = '' def __repr__(self): return "<" + self.__class__.template_name + " %s>" % self._info class BaseTemplateManager(base.Manager): # template_name must be overridden by any derived class. # template_name should be a lowercase plural, e.g. "clusters" template_name = '' @classmethod def _path(cls, id=None): return '/v1/' + cls.template_name + \ '/%s' % id if id else '/v1/' + cls.template_name def list(self, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False): """Retrieve a list of bays. :param marker: Optional, the UUID of a bay, eg the last bay from a previous result set. Return the next result set. :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of bays to return. 2) limit == 0, return the entire list of bays. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Magnum API (see Magnum's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about bays. :returns: A list of bays. """ if limit is not None: limit = int(limit) filters = utils.common_filters(marker, limit, sort_key, sort_dir) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), self.__class__.template_name) else: return self._list_pagination(self._path(path), self.__class__.template_name, limit=limit) def get(self, id): try: return self._list(self._path(id))[0] except IndexError: return None def create(self, **kwargs): new = {} for (key, value) in kwargs.items(): if key in CREATION_ATTRIBUTES: new[key] = value else: raise exceptions.InvalidAttribute( "Key must be in %s" % ",".join(CREATION_ATTRIBUTES)) return self._create(self._path(), new) def delete(self, id): return self._delete(self._path(id)) def update(self, id, patch, rollback=False): url = self._path(id) if rollback: url += '/?rollback=True' return self._update(url, patch) python-magnumclient-2.8.0/magnumclient/v1/baymodels.py000066400000000000000000000015161323411543400230700ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.v1 import basemodels CREATION_ATTRIBUTES = basemodels.CREATION_ATTRIBUTES class BayModel(basemodels.BaseModel): model_name = "BayModel" class BayModelManager(basemodels.BaseModelManager): api_name = "baymodels" resource_class = BayModel python-magnumclient-2.8.0/magnumclient/v1/baymodels_shell.py000066400000000000000000000250361323411543400242620ustar00rootroot00000000000000# 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 magnumclient.common import cliutils as utils from magnumclient.common import utils as magnum_utils from magnumclient.exceptions import InvalidAttribute from magnumclient.i18n import _ from magnumclient.v1 import basemodels DEPRECATION_MESSAGE = ( 'WARNING: Baymodel commands are deprecated and will be removed in a future' ' release.\nUse cluster commands to avoid seeing this message.') def _show_baymodel(baymodel): del baymodel._info['links'] utils.print_dict(baymodel._info) @utils.arg('--name', metavar='', help=_('Name of the baymodel to create.')) @utils.arg('--image-id', required=True, metavar='', help=_('The name or UUID of the base image to customize for' ' the bay.')) @utils.arg('--keypair-id', required=True, metavar='', help=_('The name of the SSH keypair to load into the' ' Bay nodes.')) @utils.arg('--external-network-id', required=True, metavar='', help=_('The external Neutron network ID to connect to this bay' ' model.')) @utils.arg('--coe', required=True, metavar='', help=_('Specify the Container Orchestration Engine to use.')) @utils.arg('--fixed-network', metavar='', help=_('The private Neutron network name to connect to this bay' ' model.')) @utils.arg('--fixed-subnet', metavar='', help=_('The private Neutron subnet name to connect to bay.')) @utils.arg('--network-driver', metavar='', help=_('The network driver name for instantiating container' ' networks.')) @utils.arg('--volume-driver', metavar='', help=_('The volume driver name for instantiating container' ' volume.')) @utils.arg('--dns-nameserver', metavar='', default='8.8.8.8', help=_('The DNS nameserver to use for this baymodel.')) @utils.arg('--flavor-id', metavar='', default='m1.medium', help=_('The nova flavor id to use when launching the bay.')) @utils.arg('--master-flavor-id', metavar='', help=_('The nova flavor id to use when launching the master node ' 'of the bay.')) @utils.arg('--docker-volume-size', metavar='', type=int, help=_('Specify the number of size in GB ' 'for the docker volume to use.')) @utils.arg('--docker-storage-driver', metavar='', default='devicemapper', help=_('Select a docker storage driver. Supported: devicemapper, ' 'overlay. Default: devicemapper')) @utils.arg('--http-proxy', metavar='', help=_('The http_proxy address to use for nodes in bay.')) @utils.arg('--https-proxy', metavar='', help=_('The https_proxy address to use for nodes in bay.')) @utils.arg('--no-proxy', metavar='', help=_('The no_proxy address to use for nodes in bay.')) @utils.arg('--labels', metavar='', action='append', default=[], help=_('Arbitrary labels in the form of key=value pairs ' 'to associate with a baymodel. ' 'May be used multiple times.')) @utils.arg('--tls-disabled', action='store_true', default=False, help=_('Disable TLS in the Bay.')) @utils.arg('--public', action='store_true', default=False, help=_('Make baymodel public.')) @utils.arg('--registry-enabled', action='store_true', default=False, help=_('Enable docker registry in the Bay')) @utils.arg('--server-type', metavar='', default='vm', help=_('Specify the server type to be used ' 'for example vm. For this release ' 'default server type will be vm.')) @utils.arg('--master-lb-enabled', action='store_true', default=False, help=_('Indicates whether created bays should have a load balancer ' 'for master nodes or not.')) @utils.arg('--floating-ip-enabled', action='append_const', dest='floating_ip_enabled', const=True, default=[], help=_('Indicates whether created bays should have a floating ip' 'or not.')) @utils.arg('--floating-ip-disabled', action='append_const', dest='floatin_ip_disabled', const=False, default=[], help=_('Disables floating ip creation on the new Cluster')) @utils.deprecated(DEPRECATION_MESSAGE) def do_baymodel_create(cs, args): """Create a baymodel. (Deprecated in favor of cluster-template-create.) """ opts = {} opts['name'] = args.name opts['flavor_id'] = args.flavor_id opts['master_flavor_id'] = args.master_flavor_id opts['image_id'] = args.image_id opts['keypair_id'] = args.keypair_id opts['external_network_id'] = args.external_network_id opts['fixed_network'] = args.fixed_network opts['fixed_subnet'] = args.fixed_subnet opts['network_driver'] = args.network_driver opts['volume_driver'] = args.volume_driver opts['dns_nameserver'] = args.dns_nameserver opts['docker_volume_size'] = args.docker_volume_size opts['docker_storage_driver'] = args.docker_storage_driver opts['coe'] = args.coe opts['http_proxy'] = args.http_proxy opts['https_proxy'] = args.https_proxy opts['no_proxy'] = args.no_proxy opts['labels'] = magnum_utils.handle_labels(args.labels) opts['tls_disabled'] = args.tls_disabled opts['public'] = args.public opts['registry_enabled'] = args.registry_enabled opts['server_type'] = args.server_type opts['master_lb_enabled'] = args.master_lb_enabled if len(args.floating_ip_enabled) > 1: raise InvalidAttribute('--floating-ip-enabled and ' '--floating-ip-disabled are ' 'mutually exclusive and ' 'should be specified only once.') elif len(args.floating_ip_enabled) == 1: opts['floating_ip_enabled'] = args.floating_ip_enabled[0] baymodel = cs.baymodels.create(**opts) _show_baymodel(baymodel) @utils.arg('baymodels', metavar='', nargs='+', help=_('ID or name of the (baymodel)s to delete.')) @utils.deprecated(DEPRECATION_MESSAGE) def do_baymodel_delete(cs, args): """Delete specified baymodel. (Deprecated in favor of cluster-template-delete.) """ for baymodel in args.baymodels: try: cs.baymodels.delete(baymodel) print("Request to delete baymodel %s has been accepted." % baymodel) except Exception as e: print("Delete for baymodel %(baymodel)s failed: %(e)s" % {'baymodel': baymodel, 'e': e}) @utils.arg('baymodel', metavar='', help=_('ID or name of the baymodel to show.')) @utils.deprecated(DEPRECATION_MESSAGE) def do_baymodel_show(cs, args): """Show details about the given baymodel. (Deprecated in favor of cluster-template-show.) """ baymodel = cs.baymodels.get(args.baymodel) _show_baymodel(baymodel) @utils.arg('--limit', metavar='', type=int, help=_('Maximum number of baymodels to return')) @utils.arg('--sort-key', metavar='', help=_('Column to sort results by')) @utils.arg('--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) @utils.arg('--fields', default=None, metavar='', help=_('Comma-separated list of fields to display. ' 'Available fields: uuid, name, coe, image_id, public, link, ' 'apiserver_port, server_type, tls_disabled, registry_enabled' ) ) @utils.arg('--detail', action='store_true', default=False, help=_('Show detailed information about the baymodels.') ) @utils.deprecated(DEPRECATION_MESSAGE) def do_baymodel_list(cs, args): """Print a list of baymodels. (Deprecated in favor of cluster-template-list.) """ nodes = cs.baymodels.list(limit=args.limit, sort_key=args.sort_key, sort_dir=args.sort_dir, detail=args.detail) if args.detail: columns = basemodels.OUTPUT_ATTRIBUTES else: columns = ['uuid', 'name'] columns += utils._get_list_table_columns_and_formatters( args.fields, nodes, exclude_fields=(c.lower() for c in columns))[0] utils.print_list(nodes, columns, {'versions': magnum_utils.print_list_field('versions')}, sortby_index=None) @utils.arg('baymodel', metavar='', help=_("UUID or name of baymodel")) @utils.arg( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operations: 'add', 'replace' or 'remove'")) @utils.arg( 'attributes', metavar='', nargs='+', action='append', default=[], help=_("Attributes to add/replace or remove " "(only PATH is necessary on remove)")) @utils.deprecated(DEPRECATION_MESSAGE) def do_baymodel_update(cs, args): """Updates one or more baymodel attributes. (Deprecated in favor of cluster-template-update.) """ patch = magnum_utils.args_array_to_patch(args.op, args.attributes[0]) baymodel = cs.baymodels.update(args.baymodel, patch) _show_baymodel(baymodel) python-magnumclient-2.8.0/magnumclient/v1/bays.py000066400000000000000000000017211323411543400220450ustar00rootroot00000000000000# 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 magnumclient.v1 import baseunit CREATION_ATTRIBUTES = baseunit.CREATION_ATTRIBUTES CREATION_ATTRIBUTES.append('baymodel_id') CREATION_ATTRIBUTES.append('bay_create_timeout') class Bay(baseunit.BaseTemplate): template_name = "Bays" class BayManager(baseunit.BaseTemplateManager): resource_class = Bay template_name = 'bays' python-magnumclient-2.8.0/magnumclient/v1/bays_shell.py000066400000000000000000000210561323411543400232370ustar00rootroot00000000000000# 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 magnumclient.common import cliutils as utils from magnumclient.common import utils as magnum_utils from magnumclient import exceptions from magnumclient.i18n import _ import os DEPRECATION_MESSAGE = ( 'WARNING: Bay commands are deprecated and will be removed in a future ' 'release.\nUse cluster commands to avoid seeing this message.') def _show_bay(bay): del bay._info['links'] utils.print_dict(bay._info) @utils.arg('--marker', metavar='', default=None, help=_('The last bay UUID of the previous page; ' 'displays list of bays after "marker".')) @utils.arg('--limit', metavar='', type=int, help=_('Maximum number of bays to return.')) @utils.arg('--sort-key', metavar='', help=_('Column to sort results by.')) @utils.arg('--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) @utils.arg('--fields', default=None, metavar='', help=_('Comma-separated list of fields to display. ' 'Available fields: uuid, name, baymodel_id, stack_id, ' 'status, master_count, node_count, links, bay_create_timeout' ) ) @utils.deprecated(DEPRECATION_MESSAGE) def do_bay_list(cs, args): """Print a list of available bays. (Deprecated in favor of cluster-list.) """ bays = cs.bays.list(marker=args.marker, limit=args.limit, sort_key=args.sort_key, sort_dir=args.sort_dir) columns = ['uuid', 'name', 'node_count', 'master_count', 'status'] columns += utils._get_list_table_columns_and_formatters( args.fields, bays, exclude_fields=(c.lower() for c in columns))[0] utils.print_list(bays, columns, {'versions': magnum_utils.print_list_field('versions')}, sortby_index=None) @utils.deprecated(DEPRECATION_MESSAGE) @utils.arg('--name', metavar='', help=_('Name of the bay to create.')) @utils.arg('--baymodel', required=True, metavar='', help=_('ID or name of the baymodel.')) @utils.arg('--node-count', metavar='', type=int, default=1, help=_('The bay node count.')) @utils.arg('--master-count', metavar='', type=int, default=1, help=_('The number of master nodes for the bay.')) @utils.arg('--discovery-url', metavar='', help=_('Specifies custom discovery url for node discovery.')) @utils.arg('--timeout', metavar='', type=int, default=60, help=_('The timeout for bay creation in minutes. The default ' 'is 60 minutes.')) def do_bay_create(cs, args): """Create a bay. (Deprecated in favor of cluster-create.) """ baymodel = cs.baymodels.get(args.baymodel) opts = {} opts['name'] = args.name opts['baymodel_id'] = baymodel.uuid opts['node_count'] = args.node_count opts['master_count'] = args.master_count opts['discovery_url'] = args.discovery_url opts['bay_create_timeout'] = args.timeout try: bay = cs.bays.create(**opts) # support for non-async in 1.1 if args.magnum_api_version and args.magnum_api_version == '1.1': _show_bay(bay) else: uuid = str(bay._info['uuid']) print("Request to create bay %s has been accepted." % uuid) except Exception as e: print("Create for bay %s failed: %s" % (opts['name'], e)) @utils.arg('bay', metavar='', nargs='+', help=_('ID or name of the (bay)s to delete.')) @utils.deprecated(DEPRECATION_MESSAGE) def do_bay_delete(cs, args): """Delete specified bay. (Deprecated in favor of cluster-delete.) """ for id in args.bay: try: cs.bays.delete(id) print("Request to delete bay %s has been accepted." % id) except Exception as e: print("Delete for bay %(bay)s failed: %(e)s" % {'bay': id, 'e': e}) @utils.arg('bay', metavar='', help=_('ID or name of the bay to show.')) @utils.arg('--long', action='store_true', default=False, help=_('Display extra associated Baymodel info.')) @utils.deprecated(DEPRECATION_MESSAGE) def do_bay_show(cs, args): """Show details about the given bay. (Deprecated in favor of cluster-show.) """ bay = cs.bays.get(args.bay) if args.long: baymodel = cs.baymodels.get(bay.baymodel_id) del baymodel._info['links'], baymodel._info['uuid'] for key in baymodel._info: if 'baymodel_' + key not in bay._info: bay._info['baymodel_' + key] = baymodel._info[key] _show_bay(bay) @utils.arg('bay', metavar='', help=_("UUID or name of bay")) @utils.arg('--rollback', action='store_true', default=False, help=_('Rollback bay on update failure.')) @utils.arg( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operations: 'add', 'replace' or 'remove'")) @utils.arg( 'attributes', metavar='', nargs='+', action='append', default=[], help=_("Attributes to add/replace or remove " "(only PATH is necessary on remove)")) @utils.deprecated(DEPRECATION_MESSAGE) def do_bay_update(cs, args): """Update information about the given bay. (Deprecated in favor of cluster-update.) """ if args.rollback and args.magnum_api_version and \ args.magnum_api_version in ('1.0', '1.1', '1.2'): raise exceptions.CommandError( "Rollback is not supported in API v%s. " "Please use API v1.3+." % args.magnum_api_version) patch = magnum_utils.args_array_to_patch(args.op, args.attributes[0]) bay = cs.bays.update(args.bay, patch, args.rollback) if args.magnum_api_version and args.magnum_api_version == '1.1': _show_bay(bay) else: print("Request to update bay %s has been accepted." % args.bay) @utils.arg('bay', metavar='', help=_('ID or name of the bay to retrieve config.')) @utils.arg('--dir', metavar='', default='.', help=_('Directory to save the certificate and config files.')) @utils.arg('--force', action='store_true', default=False, help=_('Overwrite files if existing.')) @utils.deprecated(DEPRECATION_MESSAGE) def do_bay_config(cs, args): """Configure native client to access bay. You can source the output of this command to get the native client of the corresponding COE configured to access the bay. Example: eval $(magnum bay-config ). (Deprecated in favor of cluster-config.) """ args.dir = os.path.abspath(args.dir) bay = cs.bays.get(args.bay) if bay.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE'): raise exceptions.CommandError("Bay in status %s" % bay.status) baymodel = cs.baymodels.get(bay.baymodel_id) opts = { 'cluster_uuid': bay.uuid, } if not baymodel.tls_disabled: tls = magnum_utils.generate_csr_and_key() tls['ca'] = cs.certificates.get(**opts).pem opts['csr'] = tls['csr'] tls['cert'] = cs.certificates.create(**opts).pem for k in ('key', 'cert', 'ca'): fname = "%s/%s.pem" % (args.dir, k) if os.path.exists(fname) and not args.force: raise Exception("File %s exists, aborting." % fname) else: f = open(fname, "w") f.write(tls[k]) f.close() print(magnum_utils.config_cluster(bay, baymodel, cfg_dir=args.dir, force=args.force)) python-magnumclient-2.8.0/magnumclient/v1/certificates.py000066400000000000000000000033231323411543400235540ustar00rootroot00000000000000# Copyright 2015 Rackspace, 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. from magnumclient.common import base from magnumclient import exceptions CREATION_ATTRIBUTES = ['cluster_uuid', 'csr'] class Certificate(base.Resource): def __repr__(self): return "" % self._info class CertificateManager(base.Manager): resource_class = Certificate @staticmethod def _path(id=None): return '/v1/certificates/%s' % id if id else '/v1/certificates' def get(self, cluster_uuid): try: return self._list(self._path(cluster_uuid))[0] except IndexError: return None def create(self, **kwargs): new = {} for (key, value) in kwargs.items(): if key in CREATION_ATTRIBUTES: new[key] = value elif key == 'bay_uuid': new['cluster_uuid'] = value else: raise exceptions.InvalidAttribute( "Key must be in %s" % ",".join(CREATION_ATTRIBUTES)) return self._create(self._path(), new) def rotate_ca(self, **kwargs): return self._update(self._path(id=kwargs['cluster_uuid'])) python-magnumclient-2.8.0/magnumclient/v1/certificates_shell.py000066400000000000000000000063061323411543400247470ustar00rootroot00000000000000# 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 os.path from magnumclient.common import cliutils as utils from magnumclient.i18n import _ DEPRECATION_MESSAGE = ( 'WARNING: The bay parameter is deprecated and will be removed in a future ' 'release.\nUse the cluster parameter to avoid seeing this message.') def _show_cert(certificate): print(certificate.pem) def _get_target_uuid(cs, args): target = None if args.cluster: target = cs.clusters.get(args.cluster) elif args.bay: print(DEPRECATION_MESSAGE) target = cs.bays.get(args.bay) else: raise utils.MissingArgs(['--cluster or --bay']) return target.uuid @utils.arg('--bay', required=False, metavar='', help=_('ID or name of the bay.')) @utils.arg('postional_cluster', metavar='', nargs='?', default=None, help=_('ID or name of the cluster.')) @utils.arg('--cluster', metavar='', default=None, help=(_('ID or name of the cluster. %s') % utils.CLUSTER_DEPRECATION_HELP)) def do_ca_show(cs, args): """Show details about the CA certificate for a bay or cluster.""" utils.validate_cluster_args(args.postional_cluster, args.cluster) args.cluster = args.postional_cluster or args.cluster opts = { 'cluster_uuid': _get_target_uuid(cs, args) } cert = cs.certificates.get(**opts) _show_cert(cert) @utils.arg('--csr', metavar='', help=_('File path of the csr file to send to Magnum' ' to get signed.')) @utils.arg('--bay', required=False, metavar='', help=_('ID or name of the bay.')) @utils.arg('--cluster', required=False, metavar='', help=_('ID or name of the cluster.')) def do_ca_sign(cs, args): """Generate the CA certificate for a bay or cluster.""" opts = { 'cluster_uuid': _get_target_uuid(cs, args) } if args.csr is None or not os.path.isfile(args.csr): print('A CSR must be provided.') return with open(args.csr, 'r') as f: opts['csr'] = f.read() cert = cs.certificates.create(**opts) _show_cert(cert) @utils.arg('--cluster', required=True, metavar='', help=_('ID or name of the cluster.')) def do_ca_rotate(cs, args): """Rotate the CA certificate for a bay or cluster to revoke access.""" cluster = cs.clusters.get(args.cluster) opts = { 'cluster_uuid': cluster.uuid } cs.certificates.rotate_ca(**opts) python-magnumclient-2.8.0/magnumclient/v1/client.py000066400000000000000000000200111323411543400223560ustar00rootroot00000000000000# Copyright 2014 # The Cloudscaling Group, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from keystoneauth1.exceptions import catalog from keystoneauth1 import session as ksa_session import os_client_config from oslo_utils import importutils from magnumclient.common import httpclient from magnumclient.v1 import baymodels from magnumclient.v1 import bays from magnumclient.v1 import certificates from magnumclient.v1 import cluster_templates from magnumclient.v1 import clusters from magnumclient.v1 import mservices from magnumclient.v1 import quotas from magnumclient.v1 import stats profiler = importutils.try_import("osprofiler.profiler") DEFAULT_SERVICE_TYPE = 'container-infra' LEGACY_DEFAULT_SERVICE_TYPE = 'container' def _load_session(cloud=None, insecure=False, timeout=None, **kwargs): cloud_config = os_client_config.OpenStackConfig() cloud_config = cloud_config.get_one_cloud( cloud=cloud, verify=not insecure, **kwargs) verify, cert = cloud_config.get_requests_verify_args() auth = cloud_config.get_auth() session = ksa_session.Session( auth=auth, verify=verify, cert=cert, timeout=timeout) return session def _load_service_type(session, service_type=None, service_name=None, interface=None, region_name=None): try: # Trigger an auth error so that we can throw the exception # we always have session.get_endpoint( service_type=service_type, service_name=service_name, interface=interface, region_name=region_name) except catalog.EndpointNotFound: service_type = LEGACY_DEFAULT_SERVICE_TYPE try: session.get_endpoint( service_type=service_type, service_name=service_name, interface=interface, region_name=region_name) except Exception as e: raise RuntimeError(str(e)) except Exception as e: raise RuntimeError(str(e)) return service_type def _load_session_client(session=None, endpoint_override=None, username=None, project_id=None, project_name=None, auth_url=None, password=None, auth_type=None, insecure=None, user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, auth_token=None, timeout=None, service_type=None, service_name=None, interface=None, region_name=None, api_version=None, **kwargs): if not session: session = _load_session( username=username, project_id=project_id, project_name=project_name, auth_url=auth_url, password=password, auth_type=auth_type, insecure=insecure, user_domain_id=user_domain_id, user_domain_name=user_domain_name, project_domain_id=project_domain_id, project_domain_name=project_domain_name, auth_token=auth_token, timeout=timeout, **kwargs ) if not endpoint_override: service_type = _load_service_type( session, service_type=service_type, service_name=service_name, interface=interface, region_name=region_name, ) return httpclient.SessionClient( service_type=service_type, service_name=service_name, interface=interface, region_name=region_name, session=session, endpoint_override=endpoint_override, api_version=api_version, ) class Client(object): def __init__(self, username=None, api_key=None, project_id=None, project_name=None, auth_url=None, magnum_url=None, endpoint_type=None, endpoint_override=None, service_type=DEFAULT_SERVICE_TYPE, region_name=None, input_auth_token=None, session=None, password=None, auth_type='password', interface=None, service_name=None, insecure=False, user_domain_id=None, user_domain_name=None, project_domain_id=None, project_domain_name=None, auth_token=None, timeout=600, api_version=None, **kwargs): # We have to keep the api_key are for backwards compat, but let's # remove it from the rest of our code since it's not a keystone # concept if not password: password = api_key # Backwards compat for people passing in input_auth_token if input_auth_token: auth_token = input_auth_token # Backwards compat for people passing in endpoint_type if endpoint_type: interface = endpoint_type # osc sometimes give 'None' value if not interface: interface = 'public' if interface.endswith('URL'): interface = interface[:-3] # fix (yolanda): os-cloud-config is using endpoint_override # instead of magnum_url if magnum_url and not endpoint_override: endpoint_override = magnum_url if endpoint_override and auth_token: self.http_client = httpclient.HTTPClient( endpoint_override, token=auth_token, api_version=api_version, timeout=timeout, insecure=insecure, **kwargs ) else: self.http_client = _load_session_client( session=session, endpoint_override=endpoint_override, username=username, project_id=project_id, project_name=project_name, auth_url=auth_url, password=password, auth_type=auth_type, insecure=insecure, user_domain_id=user_domain_id, user_domain_name=user_domain_name, project_domain_id=project_domain_id, project_domain_name=project_domain_name, auth_token=auth_token, timeout=timeout, service_type=service_type, service_name=service_name, interface=interface, region_name=region_name, api_version=api_version, **kwargs ) self.bays = bays.BayManager(self.http_client) self.clusters = clusters.ClusterManager(self.http_client) self.certificates = certificates.CertificateManager(self.http_client) self.baymodels = baymodels.BayModelManager(self.http_client) self.cluster_templates = \ cluster_templates.ClusterTemplateManager(self.http_client) self.mservices = mservices.MServiceManager(self.http_client) profile = kwargs.pop("profile", None) if profiler and profile: # Initialize the root of the future trace: the created trace ID # will be used as the very first parent to which all related # traces will be bound to. The given HMAC key must correspond to # the one set in magnum-api magnum.conf, otherwise the latter # will fail to check the request signature and will skip # initialization of osprofiler on the server side. profiler.init(profile) self.stats = stats.StatsManager(self.http_client) self.quotas = quotas.QuotasManager(self.http_client) python-magnumclient-2.8.0/magnumclient/v1/cluster_templates.py000066400000000000000000000016411323411543400246470ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.v1 import basemodels CREATION_ATTRIBUTES = basemodels.CREATION_ATTRIBUTES CREATION_ATTRIBUTES.append('insecure_registry') class ClusterTemplate(basemodels.BaseModel): model_name = "ClusterTemplate" class ClusterTemplateManager(basemodels.BaseModelManager): api_name = "clustertemplates" resource_class = ClusterTemplate python-magnumclient-2.8.0/magnumclient/v1/cluster_templates_shell.py000066400000000000000000000314461323411543400260440ustar00rootroot00000000000000# 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 magnumclient.common import cliutils as utils from magnumclient.common import utils as magnum_utils from magnumclient.exceptions import InvalidAttribute from magnumclient.i18n import _ from magnumclient.v1 import basemodels # Maps old parameter names to their new names and whether they are required DEPRECATING_PARAMS = { "--external-network-id": "--external-network", "--flavor-id": "--flavor", "--image-id": "--image", "--keypair-id": "--keypair", "--master-flavor-id": "--master-flavor", } def _show_cluster_template(cluster_template): del cluster_template._info['links'] utils.print_dict(cluster_template._info) @utils.deprecation_map(DEPRECATING_PARAMS) @utils.arg('positional_name', metavar='', nargs='?', default=None, help=_('Name of the cluster template to create.')) @utils.arg('--name', metavar='', default=None, help=(_('Name of the cluster template to create. %s') % utils.NAME_DEPRECATION_HELP)) @utils.arg('--image-id', dest='image', required=True, metavar='', help=utils.deprecation_message( 'The name or UUID of the base image to customize for the ' 'Cluster.', 'image')) @utils.arg('--image', dest='image', required=True, metavar='', help=_('The name or UUID of the base image to customize for the ' 'Cluster.')) @utils.arg('--keypair-id', dest='keypair', metavar='', help=utils.deprecation_message( 'The name of the SSH keypair to load into the ' 'Cluster nodes.', 'keypair')) @utils.arg('--keypair', dest='keypair', metavar='', help=_('The name of the SSH keypair to load into the ' 'Cluster nodes.')) @utils.arg('--external-network-id', dest='external_network', required=True, metavar='', help=utils.deprecation_message( 'The external Neutron network name or UUID to connect to ' 'this Cluster Template.', 'external-network')) @utils.arg('--external-network', dest='external_network', required=True, metavar='', help=_('The external Neutron network name or UUID to connect to ' 'this Cluster Template.')) @utils.arg('--coe', required=True, metavar='', help=_('Specify the Container Orchestration Engine to use.')) @utils.arg('--fixed-network', metavar='', help=_('The private Neutron network name to connect to this Cluster' ' model.')) @utils.arg('--fixed-subnet', metavar='', help=_('The private Neutron subnet name to connect to Cluster.')) @utils.arg('--network-driver', metavar='', help=_('The network driver name for instantiating container' ' networks.')) @utils.arg('--volume-driver', metavar='', help=_('The volume driver name for instantiating container' ' volume.')) @utils.arg('--dns-nameserver', metavar='', default='8.8.8.8', help=_('The DNS nameserver to use for this cluster template.')) @utils.arg('--flavor-id', dest='flavor', metavar='', default='m1.medium', help=utils.deprecation_message( 'The nova flavor name or UUID to use when launching the ' 'Cluster.', 'flavor')) @utils.arg('--flavor', dest='flavor', metavar='', default='m1.medium', help=_('The nova flavor name or UUID to use when launching the ' 'Cluster.')) @utils.arg('--master-flavor-id', dest='master_flavor', metavar='', help=utils.deprecation_message( 'The nova flavor name or UUID to use when launching the master' ' node of the Cluster.', 'master-flavor')) @utils.arg('--master-flavor', dest='master_flavor', metavar='', help=_('The nova flavor name or UUID to use when launching the' ' master node of the Cluster.')) @utils.arg('--docker-volume-size', metavar='', type=int, help=_('Specify the number of size in GB ' 'for the docker volume to use.')) @utils.arg('--docker-storage-driver', metavar='', default='devicemapper', help=_('Select a docker storage driver. Supported: devicemapper, ' 'overlay. Default: devicemapper')) @utils.arg('--http-proxy', metavar='', help=_('The http_proxy address to use for nodes in Cluster.')) @utils.arg('--https-proxy', metavar='', help=_('The https_proxy address to use for nodes in Cluster.')) @utils.arg('--no-proxy', metavar='', help=_('The no_proxy address to use for nodes in Cluster.')) @utils.arg('--labels', metavar='', action='append', default=[], help=_('Arbitrary labels in the form of key=value pairs ' 'to associate with a cluster template. ' 'May be used multiple times.')) @utils.arg('--tls-disabled', action='store_true', default=False, help=_('Disable TLS in the Cluster.')) @utils.arg('--public', action='store_true', default=False, help=_('Make cluster template public.')) @utils.arg('--registry-enabled', action='store_true', default=False, help=_('Enable docker registry in the Cluster')) @utils.arg('--server-type', metavar='', default='vm', help=_('Specify the server type to be used ' 'for example vm. For this release ' 'default server type will be vm.')) @utils.arg('--master-lb-enabled', action='store_true', default=False, help=_('Indicates whether created Clusters should have a load ' 'balancer for master nodes or not.')) @utils.arg('--floating-ip-enabled', action='append_const', const=True, default=[], dest='floating_ip_enabled', help=_('Indicates whether created Clusters should have a ' 'floating ip.')) @utils.arg('--floating-ip-disabled', action='append_const', const=False, default=[], dest='floating_ip_enabled', help=_('Disables floating ip creation on the new Cluster')) @utils.arg('--insecure-registry', metavar='', help='url of docker registry') def do_cluster_template_create(cs, args): """Create a cluster template.""" args.command = 'cluster-template-create' utils.validate_name_args(args.positional_name, args.name) opts = {} opts['name'] = args.positional_name or args.name opts['flavor_id'] = args.flavor opts['master_flavor_id'] = args.master_flavor opts['image_id'] = args.image opts['keypair_id'] = args.keypair opts['external_network_id'] = args.external_network opts['fixed_network'] = args.fixed_network opts['fixed_subnet'] = args.fixed_subnet opts['network_driver'] = args.network_driver opts['volume_driver'] = args.volume_driver opts['dns_nameserver'] = args.dns_nameserver opts['docker_volume_size'] = args.docker_volume_size opts['docker_storage_driver'] = args.docker_storage_driver opts['coe'] = args.coe opts['http_proxy'] = args.http_proxy opts['https_proxy'] = args.https_proxy opts['no_proxy'] = args.no_proxy opts['labels'] = magnum_utils.handle_labels(args.labels) opts['tls_disabled'] = args.tls_disabled opts['public'] = args.public opts['registry_enabled'] = args.registry_enabled opts['server_type'] = args.server_type opts['master_lb_enabled'] = args.master_lb_enabled opts['insecure_registry'] = args.insecure_registry if len(args.floating_ip_enabled) > 1: raise InvalidAttribute('--floating-ip-enabled and ' '--floating-ip-disabled are ' 'mutually exclusive and ' 'should be specified only once.') elif len(args.floating_ip_enabled) == 1: opts['floating_ip_enabled'] = args.floating_ip_enabled[0] cluster_template = cs.cluster_templates.create(**opts) _show_cluster_template(cluster_template) @utils.arg('cluster_templates', metavar='', nargs='+', help=_('ID or name of the (cluster template)s to delete.')) def do_cluster_template_delete(cs, args): """Delete specified cluster template.""" for cluster_template in args.cluster_templates: try: cs.cluster_templates.delete(cluster_template) print("Request to delete cluster template %s has been accepted." % cluster_template) except Exception as e: print("Delete for cluster template " "%(cluster_template)s failed: %(e)s" % {'cluster_template': cluster_template, 'e': e}) @utils.arg('cluster_template', metavar='', help=_('ID or name of the cluster template to show.')) def do_cluster_template_show(cs, args): """Show details about the given cluster template.""" cluster_template = cs.cluster_templates.get(args.cluster_template) _show_cluster_template(cluster_template) @utils.arg('--limit', metavar='', type=int, help=_('Maximum number of cluster templates to return')) @utils.arg('--sort-key', metavar='', help=_('Column to sort results by')) @utils.arg('--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) @utils.arg('--fields', default=None, metavar='', help=_('Comma-separated list of fields to display. ' 'Available fields: uuid, name, coe, image_id, public, link, ' 'apiserver_port, server_type, tls_disabled, registry_enabled' ) ) @utils.arg('--detail', action='store_true', default=False, help=_('Show detailed information about the cluster templates.') ) def do_cluster_template_list(cs, args): """Print a list of cluster templates.""" nodes = cs.cluster_templates.list(limit=args.limit, sort_key=args.sort_key, sort_dir=args.sort_dir, detail=args.detail) if args.detail: columns = basemodels.OUTPUT_ATTRIBUTES else: columns = ['uuid', 'name'] columns += utils._get_list_table_columns_and_formatters( args.fields, nodes, exclude_fields=(c.lower() for c in columns))[0] utils.print_list(nodes, columns, {'versions': magnum_utils.print_list_field('versions')}, sortby_index=None) @utils.arg('cluster_template', metavar='', help=_("UUID or name of cluster template")) @utils.arg( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operations: 'add', 'replace' or 'remove'")) @utils.arg( 'attributes', metavar='', nargs='+', action='append', default=[], help=_("Attributes to add/replace or remove " "(only PATH is necessary on remove)")) def do_cluster_template_update(cs, args): """Updates one or more cluster template attributes.""" patch = magnum_utils.args_array_to_patch(args.op, args.attributes[0]) cluster_template = cs.cluster_templates.update(args.cluster_template, patch) _show_cluster_template(cluster_template) python-magnumclient-2.8.0/magnumclient/v1/clusters.py000066400000000000000000000022741323411543400227570ustar00rootroot00000000000000# 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 magnumclient.v1 import baseunit CREATION_ATTRIBUTES = baseunit.CREATION_ATTRIBUTES CREATION_ATTRIBUTES.append('cluster_template_id') CREATION_ATTRIBUTES.append('create_timeout') CREATION_ATTRIBUTES.append('keypair') CREATION_ATTRIBUTES.append('docker_volume_size') CREATION_ATTRIBUTES.append('labels') CREATION_ATTRIBUTES.append('master_flavor_id') CREATION_ATTRIBUTES.append('flavor_id') class Cluster(baseunit.BaseTemplate): template_name = "Clusters" class ClusterManager(baseunit.BaseTemplateManager): resource_class = Cluster template_name = 'clusters' python-magnumclient-2.8.0/magnumclient/v1/clusters_shell.py000066400000000000000000000243371323411543400241520ustar00rootroot00000000000000# 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 os from magnumclient.common import cliutils as utils from magnumclient.common import utils as magnum_utils from magnumclient import exceptions from magnumclient.i18n import _ # Maps old parameter names to their new names and whether they are required # e.g. keypair-id to keypair DEPRECATING_PARAMS = { "--keypair-id": "--keypair", } def _show_cluster(cluster): del cluster._info['links'] utils.print_dict(cluster._info) @utils.arg('--marker', metavar='', default=None, help=_('The last cluster UUID of the previous page; ' 'displays list of clusters after "marker".')) @utils.arg('--limit', metavar='', type=int, help=_('Maximum number of clusters to return.')) @utils.arg('--sort-key', metavar='', help=_('Column to sort results by.')) @utils.arg('--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) @utils.arg('--fields', default=None, metavar='', help=_('Comma-separated list of fields to display. ' 'Available fields: uuid, name, cluster_template_id, ' 'stack_id, status, master_count, node_count, links, ' 'create_timeout' ) ) def do_cluster_list(cs, args): """Print a list of available clusters.""" clusters = cs.clusters.list(marker=args.marker, limit=args.limit, sort_key=args.sort_key, sort_dir=args.sort_dir) columns = [ 'uuid', 'name', 'keypair', 'node_count', 'master_count', 'status' ] columns += utils._get_list_table_columns_and_formatters( args.fields, clusters, exclude_fields=(c.lower() for c in columns))[0] utils.print_list(clusters, columns, {'versions': magnum_utils.print_list_field('versions')}, sortby_index=None) @utils.deprecation_map(DEPRECATING_PARAMS) @utils.arg('positional_name', metavar='', nargs='?', default=None, help=_('Name of the cluster to create.')) @utils.arg('--name', metavar='', default=None, help=(_('Name of the cluster to create. %s') % utils.NAME_DEPRECATION_HELP)) @utils.arg('--cluster-template', required=True, metavar='', help=_('ID or name of the cluster template.')) @utils.arg('--keypair-id', dest='keypair', metavar='', default=None, help=utils.deprecation_message( 'Name of the keypair to use for this cluster.', 'keypair')) @utils.arg('--keypair', dest='keypair', metavar='', default=None, help=_('Name of the keypair to use for this cluster.')) @utils.arg('--docker-volume-size', metavar='', type=int, help=_('The size in GB for the docker volume to use')) @utils.arg('--labels', metavar='', action='append', help=_('Arbitrary labels in the form of key=value pairs ' 'to associate with a cluster. ' 'May be used multiple times.')) @utils.arg('--node-count', metavar='', type=int, default=1, help=_('The cluster node count.')) @utils.arg('--master-count', metavar='', type=int, default=1, help=_('The number of master nodes for the cluster.')) @utils.arg('--discovery-url', metavar='', help=_('Specifies custom discovery url for node discovery.')) @utils.arg('--timeout', metavar='', type=int, default=60, help=_('The timeout for cluster creation in minutes. The default ' 'is 60 minutes.')) def do_cluster_create(cs, args): """Create a cluster.""" args.command = 'cluster-create' utils.validate_name_args(args.positional_name, args.name) cluster_template = cs.cluster_templates.get(args.cluster_template) opts = dict() opts['name'] = args.positional_name or args.name opts['cluster_template_id'] = cluster_template.uuid opts['keypair'] = args.keypair if args.docker_volume_size is not None: opts['docker_volume_size'] = args.docker_volume_size if args.labels is not None: opts['labels'] = magnum_utils.handle_labels(args.labels) opts['node_count'] = args.node_count opts['master_count'] = args.master_count opts['discovery_url'] = args.discovery_url opts['create_timeout'] = args.timeout try: cluster = cs.clusters.create(**opts) # support for non-async in 1.1 if args.magnum_api_version and args.magnum_api_version == '1.1': _show_cluster(cluster) else: uuid = str(cluster._info['uuid']) print("Request to create cluster %s has been accepted." % uuid) except Exception as e: print("Create for cluster %s failed: %s" % (opts['name'], e)) @utils.arg('cluster', metavar='', nargs='+', help=_('ID or name of the (cluster)s to delete.')) def do_cluster_delete(cs, args): """Delete specified cluster.""" for id in args.cluster: try: cs.clusters.delete(id) print("Request to delete cluster %s has been accepted." % id) except Exception as e: print("Delete for cluster %(cluster)s failed: %(e)s" % {'cluster': id, 'e': e}) @utils.arg('cluster', metavar='', help=_('ID or name of the cluster to show.')) @utils.arg('--long', action='store_true', default=False, help=_('Display extra associated cluster template info.')) def do_cluster_show(cs, args): """Show details about the given cluster.""" cluster = cs.clusters.get(args.cluster) if args.long: cluster_template = \ cs.cluster_templates.get(cluster.cluster_template_id) del cluster_template._info['links'], cluster_template._info['uuid'] for key in cluster_template._info: if 'clustertemplate_' + key not in cluster._info: cluster._info['clustertemplate_' + key] = \ cluster_template._info[key] _show_cluster(cluster) @utils.arg('cluster', metavar='', help=_("UUID or name of cluster")) @utils.arg('--rollback', action='store_true', default=False, help=_('Rollback cluster on update failure.')) @utils.arg( 'op', metavar='', choices=['add', 'replace', 'remove'], help=_("Operations: 'add', 'replace' or 'remove'")) @utils.arg( 'attributes', metavar='', nargs='+', action='append', default=[], help=_("Attributes to add/replace or remove " "(only PATH is necessary on remove)")) def do_cluster_update(cs, args): """Update information about the given cluster.""" if args.rollback and args.magnum_api_version and \ args.magnum_api_version in ('1.0', '1.1', '1.2'): raise exceptions.CommandError( "Rollback is not supported in API v%s. " "Please use API v1.3+." % args.magnum_api_version) patch = magnum_utils.args_array_to_patch(args.op, args.attributes[0]) try: cluster = cs.clusters.update(args.cluster, patch, args.rollback) except Exception as e: print("ERROR: %s" % e.details) return if args.magnum_api_version and args.magnum_api_version == '1.1': _show_cluster(cluster) else: print("Request to update cluster %s has been accepted." % args.cluster) @utils.arg('cluster', metavar='', help=_('ID or name of the cluster to retrieve config.')) @utils.arg('--dir', metavar='', default='.', help=_('Directory to save the certificate and config files.')) @utils.arg('--force', action='store_true', default=False, help=_('Overwrite files if existing.')) def do_cluster_config(cs, args): """Configure native client to access cluster. You can source the output of this command to get the native client of the corresponding COE configured to access the cluster. Example: eval $(magnum cluster-config ). """ args.dir = os.path.abspath(args.dir) cluster = cs.clusters.get(args.cluster) if cluster.status not in ('CREATE_COMPLETE', 'UPDATE_COMPLETE', 'ROLLBACK_COMPLETE'): raise exceptions.CommandError("cluster in status %s" % cluster.status) cluster_template = cs.cluster_templates.get(cluster.cluster_template_id) opts = { 'cluster_uuid': cluster.uuid, } if not cluster_template.tls_disabled: tls = magnum_utils.generate_csr_and_key() tls['ca'] = cs.certificates.get(**opts).pem opts['csr'] = tls['csr'] tls['cert'] = cs.certificates.create(**opts).pem for k in ('key', 'cert', 'ca'): fname = "%s/%s.pem" % (args.dir, k) if os.path.exists(fname) and not args.force: raise Exception("File %s exists, aborting." % fname) else: f = open(fname, "w") f.write(tls[k]) f.close() print(magnum_utils.config_cluster(cluster, cluster_template, cfg_dir=args.dir, force=args.force)) python-magnumclient-2.8.0/magnumclient/v1/mservices.py000066400000000000000000000047461323411543400231210ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.common import base from magnumclient.common import utils class MService(base.Resource): def __repr__(self): return "" % self._info class MServiceManager(base.Manager): resource_class = MService @staticmethod def _path(id=None): return '/v1/mservices/%s' % id if id else '/v1/mservices' def list(self, marker=None, limit=None, sort_key=None, sort_dir=None, detail=False): """Retrieve list of magnum services. :param marker: Optional, the ID of a magnum service, eg the last services from a previous result set. Return the next result set. :param limit: The maximum number of results to return per request, if: 1) limit > 0, the maximum number of services to return. 2) limit == 0, return the entire list of services. 3) limit param is NOT specified (None), the number of items returned respect the maximum imposed by the Magnum API (see Magnum's api.max_limit option). :param sort_key: Optional, field used for sorting. :param sort_dir: Optional, direction of sorting, either 'asc' (the default) or 'desc'. :param detail: Optional, boolean whether to return detailed information about services. :returns: A list of services. """ if limit is not None: limit = int(limit) filters = utils.common_filters(marker, limit, sort_key, sort_dir) path = '' if detail: path += 'detail' if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(self._path(path), "mservices") else: return self._list_pagination(self._path(path), "mservices", limit=limit) python-magnumclient-2.8.0/magnumclient/v1/mservices_shell.py000066400000000000000000000021021323411543400242700ustar00rootroot00000000000000# 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 magnumclient.common import cliutils as utils from magnumclient.common import utils as magnum_utils def do_service_list(cs, args): """Print a list of magnum services.""" mservices = cs.mservices.list() columns = ('id', 'host', 'binary', 'state', 'disabled', 'disabled_reason', 'created_at', 'updated_at') utils.print_list(mservices, columns, {'versions': magnum_utils.print_list_field('versions')}) python-magnumclient-2.8.0/magnumclient/v1/quotas.py000066400000000000000000000046221323411543400224260ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.common import utils from magnumclient import exceptions from magnumclient.v1 import basemodels CREATION_ATTRIBUTES = ['project_id', 'resource', 'hard_limit'] class Quotas(basemodels.BaseModel): model_name = "Quotas" class QuotasManager(basemodels.BaseModelManager): api_name = "quotas" resource_class = Quotas @staticmethod def _path(id=None, resource=None): if not id: return '/v1/quotas' return '/v1/quotas/%(id)s/%(res)s' % {'id': id, 'res': resource} def list(self, limit=None, marker=None, sort_key=None, sort_dir=None, all_tenants=False): if limit is not None: limit = int(limit) filters = utils.common_filters(marker, limit, sort_key, sort_dir) if all_tenants: filters.append('all_tenants=True') path = self._path() if filters: path += '?' + '&'.join(filters) if limit is None: return self._list(path, self.api_name) else: return self._list_pagination(path, self.api_name, limit=limit) def get(self, id, resource): try: return self._list(self._path(id, resource))[0] except IndexError: return None def create(self, **kwargs): new = {} for (key, value) in kwargs.items(): if key in CREATION_ATTRIBUTES: new[key] = value else: raise exceptions.InvalidAttribute( "Key must be in %s" % ",".join(CREATION_ATTRIBUTES)) return self._create(self._path(), new) def delete(self, id, resource): return self._delete(self._path(id, resource)) def update(self, id, resource, patch): url = self._path(id, resource) return self._update(url, patch) python-magnumclient-2.8.0/magnumclient/v1/quotas_shell.py000066400000000000000000000115151323411543400236140ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.common import cliutils as utils from magnumclient.common import utils as magnum_utils from magnumclient.i18n import _ def _show_quota(quota): utils.print_dict(quota._info) @utils.arg('--marker', metavar='', default=None, help=_('The last quota UUID of the previous page; ' 'displays list of quotas after "marker".')) @utils.arg('--limit', metavar='', type=int, help=_('Maximum number of quotas to return.')) @utils.arg('--sort-key', metavar='', help=_('Column to sort results by.')) @utils.arg('--sort-dir', metavar='', choices=['desc', 'asc'], help=_('Direction to sort. "asc" or "desc".')) @utils.arg('--all-tenants', action='store_true', default=False, help=_('Flag to indicate list all tenant quotas.')) def do_quotas_list(cs, args): """Print a list of available quotas.""" quotas = cs.quotas.list(marker=args.marker, limit=args.limit, sort_key=args.sort_key, sort_dir=args.sort_dir, all_tenants=args.all_tenants) columns = ['project_id', 'resource', 'hard_limit'] utils.print_list(quotas, columns, {'versions': magnum_utils.print_list_field('versions')}, sortby_index=None) @utils.arg('--project-id', required=True, metavar='', help=_('Project Id.')) @utils.arg('--resource', required=True, metavar='', help=_('Resource name.')) @utils.arg('--hard-limit', metavar='', type=int, default=1, help=_('Max resource limit.')) def do_quotas_create(cs, args): """Create a quota.""" opts = dict() opts['project_id'] = args.project_id opts['resource'] = args.resource opts['hard_limit'] = args.hard_limit try: quota = cs.quotas.create(**opts) _show_quota(quota) except Exception as e: print("Create quota for project_id %(id)s resource %(res)s failed: " "%(e)s" % {'id': args.project_id, 'res': args.resource, 'e': e.details}) @utils.arg('--project-id', required=True, metavar='', help=_('Project ID.')) @utils.arg('--resource', required=True, metavar='', help=_('Resource name')) def do_quotas_delete(cs, args): """Delete specified resource quota.""" try: cs.quotas.delete(args.project_id, args.resource) print("Request to delete quota for project id %(id)s and resource " "%(res)s has been accepted." % { 'id': args.project_id, 'res': args.resource}) except Exception as e: print("Quota delete failed for project id %(id)s and resource " "%(res)s :%(e)s" % {'id': args.project_id, 'res': args.resource, 'e': e.details}) @utils.arg('--project-id', required=True, metavar='', help=_('Project ID.')) @utils.arg('--resource', required=True, metavar='', help=_('Resource name')) def do_quotas_show(cs, args): """Show details about the given project resource quota.""" quota = cs.quotas.get(args.project_id, args.resource) _show_quota(quota) @utils.arg('--project-id', required=True, metavar='', help=_('Project Id.')) @utils.arg('--resource', required=True, metavar='', help=_('Resource name.')) @utils.arg('--hard-limit', metavar='', type=int, default=1, help=_('Max resource limit.')) def do_quotas_update(cs, args): """Update information about the given project resource quota.""" patch = dict() patch['project_id'] = args.project_id patch['resource'] = args.resource patch['hard_limit'] = args.hard_limit quota = cs.quotas.update(args.project_id, args.resource, patch) _show_quota(quota) python-magnumclient-2.8.0/magnumclient/v1/shell.py000066400000000000000000000021521323411543400222150ustar00rootroot00000000000000# Copyright 2014 # The Cloudscaling Group, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from magnumclient.v1 import baymodels_shell from magnumclient.v1 import bays_shell from magnumclient.v1 import certificates_shell from magnumclient.v1 import cluster_templates_shell from magnumclient.v1 import clusters_shell from magnumclient.v1 import mservices_shell from magnumclient.v1 import quotas_shell from magnumclient.v1 import stats_shell COMMAND_MODULES = [ baymodels_shell, bays_shell, certificates_shell, clusters_shell, cluster_templates_shell, mservices_shell, stats_shell, quotas_shell, ] python-magnumclient-2.8.0/magnumclient/v1/stats.py000066400000000000000000000020171323411543400222440ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.common import base class Stats(base.Resource): def __repr__(self): return "" % self._info class StatsManager(base.Manager): resource_class = Stats @staticmethod def _path(id=None): return '/v1/stats?project_id=%s' % id if id else '/v1/stats' def list(self, project_id=None): try: return self._list(self._path(project_id))[0] except IndexError: return None python-magnumclient-2.8.0/magnumclient/v1/stats_shell.py000066400000000000000000000017241323411543400234370ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 magnumclient.common import cliutils as utils from magnumclient.i18n import _ @utils.arg('--project-id', required=False, metavar='', help=_('Project ID')) def do_stats_list(cs, args): """Show stats for the given project_id""" opts = { 'project_id': args.project_id } stats = cs.stats.list(**opts) utils.print_dict(stats._info) python-magnumclient-2.8.0/magnumclient/version.py000066400000000000000000000012421323411543400222440ustar00rootroot00000000000000# Copyright 2014 # The Cloudscaling Group, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy # of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from pbr import version version_info = version.VersionInfo('python-magnumclient') python-magnumclient-2.8.0/requirements.txt000066400000000000000000000012551323411543400210120ustar00rootroot00000000000000# 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!=2.1.0,>=2.0.0 # Apache-2.0 Babel!=2.4.0,>=2.3.4 # BSD six>=1.10.0 # MIT keystoneauth1>=3.3.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 os-client-config>=1.28.0 # Apache-2.0 osc-lib>=1.8.0 # Apache-2.0 PrettyTable<0.8,>=0.7.1 # BSD cryptography!=2.0,>=1.9 # BSD/Apache-2.0 decorator>=3.4.0 # BSD python-magnumclient-2.8.0/setup.cfg000066400000000000000000000044631323411543400173530ustar00rootroot00000000000000[metadata] name = python-magnumclient summary = Client library for Magnum API description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://docs.openstack.org/python-magnumclient/latest/ 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.5 [files] packages = magnumclient [entry_points] console_scripts = magnum = magnumclient.shell:main openstack.cli.extension = container_infra = magnumclient.osc.plugin openstack.container_infra.v1 = coe_cluster_template_create = magnumclient.osc.v1.cluster_templates:CreateClusterTemplate coe_cluster_template_delete = magnumclient.osc.v1.cluster_templates:DeleteClusterTemplate coe_cluster_template_list = magnumclient.osc.v1.cluster_templates:ListTemplateCluster coe_cluster_template_show = magnumclient.osc.v1.cluster_templates:ShowClusterTemplate coe_cluster_template_update = magnumclient.osc.v1.cluster_templates:UpdateClusterTemplate coe_cluster_create = magnumclient.osc.v1.clusters:CreateCluster coe_cluster_list = magnumclient.osc.v1.clusters:ListCluster coe_cluster_delete = magnumclient.osc.v1.clusters:DeleteCluster coe_cluster_show = magnumclient.osc.v1.clusters:ShowCluster coe_cluster_update = magnumclient.osc.v1.clusters:UpdateCluster coe_cluster_config = magnumclient.osc.v1.clusters:ConfigCluster coe_service_list = magnumclient.osc.v1.mservices:ListService [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 [upload_sphinx] upload-dir = doc/build/html [compile_catalog] directory = magnumclient/locale domain = magnumclient [update_catalog] domain = magnumclient output_dir = magnumclient/locale input_file = magnumclient/locale/magnumclient.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = magnumclient/locale/magnumclient.pot [wheel] universal = 1 [pbr] autodoc_index_modules = True warnerrors = True python-magnumclient-2.8.0/setup.py000066400000000000000000000020061323411543400172330ustar00rootroot00000000000000# 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>=2.0.0'], pbr=True) python-magnumclient-2.8.0/test-requirements.txt000066400000000000000000000011741323411543400217670ustar00rootroot00000000000000# 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.13.0,<0.14,>=0.12.0 # Apache-2.0 bandit>=1.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD python-openstackclient>=3.12.0 # Apache-2.0 sphinx!=1.6.6,>=1.6.2 # BSD openstackdocstheme>=1.18.1 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD testtools>=2.2.0 # MIT mock>=2.0.0 # BSD python-magnumclient-2.8.0/tools/000077500000000000000000000000001323411543400166635ustar00rootroot00000000000000python-magnumclient-2.8.0/tools/cover.sh000077500000000000000000000044241323411543400203440ustar00rootroot00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. ALLOWED_EXTRA_MISSING=0 show_diff () { head -1 $1 diff -U 0 $1 $2 | sed 1,2d } if ! git diff --exit-code || ! git diff --cached --exit-code then echo "There are uncommitted changes!" echo "Please clean git working directory and try again" exit 1 fi # Checkout master and save coverage report git checkout HEAD^ baseline_report=$(mktemp -t magnumclient_coverageXXXXXXX) find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --testr-args="$*" coverage report > $baseline_report mv cover cover-master cat $baseline_report baseline_missing=$(awk 'END { print $3 }' $baseline_report) # Checkout back and save coverage report git checkout - current_report=$(mktemp -t magnumclient_coverageXXXXXXX) find . -type f -name "*.pyc" -delete && python setup.py testr --coverage --testr-args="$*" coverage report > $current_report current_missing=$(awk 'END { print $3 }' $current_report) # Show coverage details allowed_missing=$((baseline_missing+ALLOWED_EXTRA_MISSING)) echo "Allowed to introduce missing lines : ${ALLOWED_EXTRA_MISSING}" echo "Missing lines in master : ${baseline_missing}" echo "Missing lines in proposed change : ${current_missing}" if [ $allowed_missing -ge $current_missing ]; then if [ $baseline_missing -lt $current_missing ]; then show_diff $baseline_report $current_report echo "We believe you can test your code with 100% coverage!" else echo "Thank you! You are awesome! Keep writing unit tests! :)" fi exit_code=0 else show_diff $baseline_report $current_report echo "Please write more unit tests, we must maintain our test coverage :( " exit_code=1 fi rm $baseline_report $current_report exit $exit_code python-magnumclient-2.8.0/tools/magnum.bash_completion000066400000000000000000000021131323411543400232340ustar00rootroot00000000000000_magnum_opts="" # lazy init _magnum_flags="" # lazy init _magnum_opts_exp="" # lazy init _magnum() { local cur prev nbc cflags COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [ "x$_magnum_opts" == "x" ] ; then nbc="`magnum bash-completion | sed -e "s/ *-h */ /" -e "s/ *-i */ /"`" _magnum_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/ */ /g"`" _magnum_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/ */ /g"`" _magnum_opts_exp="`echo "$_magnum_opts" | tr ' ' '|'`" fi if [[ " ${COMP_WORDS[@]} " =~ " "($_magnum_opts_exp)" " && "$prev" != "help" ]] ; then COMPLETION_CACHE=~/.magnumclient/*/*-cache cflags="$_magnum_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) else COMPREPLY=($(compgen -W "${_magnum_opts}" -- ${cur})) fi return 0 } complete -F _magnum magnum python-magnumclient-2.8.0/tox.ini000066400000000000000000000033411323411543400170370ustar00rootroot00000000000000[tox] minversion = 1.6 envlist = py35,py27,pypy,pep8 skipsdist = True [testenv] usedevelop = True install_command = pip install {opts} {packages} whitelist_externals = find setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = find . -type f -name "*.py[c|o]" -delete python setup.py testr --slowest --testr-args='{posargs}' [testenv:bandit] deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/test-requirements.txt commands = bandit -r magnumclient -x tests -n5 -ll [testenv:pypy] deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} setuptools<3.2 -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt [testenv:debug] commands = oslo_debug_helper -t magnumclient/tests {posargs} [testenv:debug-py27] basepython = python2.7 commands = oslo_debug_helper -t magnumclient/tests {posargs} [testenv:debug-py35] basepython = python3.5 commands = oslo_debug_helper -t magnumclient/tests {posargs} [testenv:pep8] commands = flake8 # Run security linter bandit -r magnumclient -x tests -n5 -ll [testenv:venv] commands = {posargs} [testenv:cover] commands = {toxinidir}/tools/cover.sh {posargs} [flake8] # E123, E125 skipped as they are invalid PEP-8. show-source = True ignore = E123,E125 builtins = _ exclude=.venv,.git,.tox,dist,doc,,*lib/python*,*egg,build [hacking] import_exceptions = magnumclient._i18n