python-novaclient-9.1.1/0000775000175000017500000000000013165151230016326 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/test-requirements.txt0000664000175000017500000000157213165151100022570 0ustar jenkinsjenkins00000000000000# 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 keyring>=5.5.1 # MIT/PSF mock>=2.0 # BSD python-keystoneclient>=3.8.0 # Apache-2.0 python-cinderclient>=3.0.0 # Apache-2.0 python-glanceclient>=2.7.0 # Apache-2.0 python-neutronclient>=6.3.0 # Apache-2.0 requests-mock>=1.1 # Apache-2.0 sphinx>=1.6.2 # BSD os-client-config>=1.28.0 # Apache-2.0 openstackdocstheme>=1.11.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>=1.4.0 # MIT tempest>=16.1.0 # Apache-2.0 # releasenotes reno!=2.3.1,>=1.8.0 # Apache-2.0 python-novaclient-9.1.1/requirements.txt0000664000175000017500000000100413165151100021601 0ustar jenkinsjenkins00000000000000# 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 keystoneauth1>=3.0.1 # Apache-2.0 iso8601>=0.1.11 # MIT oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0 oslo.serialization!=2.19.1,>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 PrettyTable<0.8,>=0.7.1 # BSD simplejson>=2.2.0 # MIT six>=1.9.0 # MIT Babel!=2.4.0,>=2.3.4 # BSD python-novaclient-9.1.1/LICENSE0000664000175000017500000002707313165151077017355 0ustar jenkinsjenkins00000000000000Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1) Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1) All rights reserved. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. --- License for python-novaclient versions prior to 2.1 --- All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. python-novaclient-9.1.1/novaclient/0000775000175000017500000000000013165151230020470 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/extension.py0000664000175000017500000000256213165151077023074 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import base from novaclient import utils class Extension(base.HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') def __init__(self, name, module): self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in self.module.__dict__.items(): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) elif utils.safe_issubclass(attr_value, base.Manager): self.manager_class = attr_value def __repr__(self): return "" % self.name python-novaclient-9.1.1/novaclient/shell.py0000664000175000017500000011426513165151077022173 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Command-line interface to the OpenStack Nova API. """ from __future__ import print_function import argparse import getpass import logging import sys from keystoneauth1 import loading from oslo_utils import encodeutils from oslo_utils import importutils from oslo_utils import strutils import six osprofiler_profiler = importutils.try_import("osprofiler.profiler") HAS_KEYRING = False all_errors = ValueError try: import keyring HAS_KEYRING = True except ImportError: pass import novaclient from novaclient import api_versions from novaclient import client from novaclient import exceptions as exc import novaclient.extension from novaclient.i18n import _ from novaclient import utils DEFAULT_MAJOR_OS_COMPUTE_API_VERSION = "2.0" # The default behaviour of nova client CLI is that CLI negotiates with server # to find out the most recent version between client and server, and # '2.latest' means to that. This value never be changed until we decided to # change the default behaviour of nova client CLI. DEFAULT_OS_COMPUTE_API_VERSION = '2.latest' DEFAULT_NOVA_ENDPOINT_TYPE = 'publicURL' DEFAULT_NOVA_SERVICE_TYPE = "compute" HINT_HELP_MSG = (" [hint: use '--os-compute-api-version' flag to show help " "message for proper version]") logger = logging.getLogger(__name__) class DeprecatedAction(argparse.Action): """An argparse action for deprecated options. This class is an ``argparse.Action`` subclass that allows command line options to be explicitly deprecated. It modifies the help text for the option to indicate that it's deprecated (unless help has been suppressed using ``argparse.SUPPRESS``), and provides a means to specify an alternate option to use using the ``use`` keyword argument to ``argparse.ArgumentParser.add_argument()``. The original action may be specified with the ``real_action`` keyword argument, which has the same interpretation as the ``action`` argument to ``argparse.ArgumentParser.add_argument()``, with the addition of the special "nothing" action which completely ignores the option (other than emitting the deprecation warning). Note that the deprecation warning is only emitted once per specific option string. Note: If the ``real_action`` keyword argument specifies an unknown action, no warning will be emitted unless the action is used, due to limitations with the method used to resolve the action names. """ def __init__(self, option_strings, dest, help=None, real_action=None, use=None, **kwargs): """Initialize a ``DeprecatedAction`` instance. :param option_strings: The recognized option strings. :param dest: The attribute that will be set. :param help: Help text. This will be updated to indicate the deprecation, and if ``use`` is provided, that text will be included as well. :param real_action: The actual action to invoke. This is interpreted the same way as the ``action`` parameter. :param use: Text explaining which option to use instead. """ # Update the help text if not help: if use: help = _('Deprecated; %(use)s') % {'use': use} else: help = _('Deprecated') elif help != argparse.SUPPRESS: if use: help = _('%(help)s (Deprecated; %(use)s)') % { 'help': help, 'use': use, } else: help = _('%(help)s (Deprecated)') % {'help': help} # Initialize ourself appropriately super(DeprecatedAction, self).__init__( option_strings, dest, help=help, **kwargs) # 'emitted' tracks which warnings we've emitted self.emitted = set() self.use = use # Select the appropriate action if real_action == 'nothing': # NOTE(Vek): "nothing" is distinct from a real_action=None # argument. When real_action=None, the argparse default # action of "store" is used; when real_action='nothing', # however, we explicitly inhibit doing anything with the # option self.real_action_args = False self.real_action = None elif real_action is None or isinstance(real_action, six.string_types): # Specified by string (or None); we have to have a parser # to look up the actual action, so defer to later self.real_action_args = (option_strings, dest, help, kwargs) self.real_action = real_action else: self.real_action_args = False self.real_action = real_action( option_strings, dest, help=help, **kwargs) def _get_action(self, parser): """Retrieve the action callable. This internal method is used to retrieve the callable implementing the action. If ``real_action`` was specified as ``None`` or one of the standard string names, an internal method of the ``argparse.ArgumentParser`` instance is used to resolve it into an actual action class, which is then instantiated. This is cached, in case the action is called multiple times. :param parser: The ``argparse.ArgumentParser`` instance. :returns: The action callable. """ # If a lookup is needed, look up the action in the parser if self.real_action_args is not False: option_strings, dest, help, kwargs = self.real_action_args action_class = parser._registry_get('action', self.real_action) # Did we find the action class? if action_class is None: print(_('WARNING: Programming error: Unknown real action ' '"%s"') % self.real_action, file=sys.stderr) self.real_action = None else: # OK, instantiate the action class self.real_action = action_class( option_strings, dest, help=help, **kwargs) # It's been resolved, no further need to look it up self.real_action_args = False return self.real_action def __call__(self, parser, namespace, values, option_string): """Implement the action. Emits the deprecation warning message (only once for any given option string), then calls the real action (if any). :param parser: The ``argparse.ArgumentParser`` instance. :param namespace: The ``argparse.Namespace`` object which should have an attribute set. :param values: Any arguments provided to the option. :param option_string: The option string that was used. """ action = self._get_action(parser) # Only emit the deprecation warning once per option if option_string not in self.emitted: if self.use: print(_('WARNING: Option "%(option)s" is deprecated; ' '%(use)s') % { 'option': option_string, 'use': self.use, }, file=sys.stderr) else: print(_('WARNING: Option "%(option)s" is deprecated') % {'option': option_string}, file=sys.stderr) self.emitted.add(option_string) if action: action(parser, namespace, values, option_string) class SecretsHelper(object): def __init__(self, args, client): self.args = args self.client = client self.key = None self._password = None def _validate_string(self, text): if text is None or len(text) == 0: return False return True def _make_key(self): if self.key is not None: return self.key keys = [ self.client.auth_url, self.client.projectid, self.client.user, self.client.region_name, self.client.endpoint_type, self.client.service_type, self.client.service_name, ] for (index, key) in enumerate(keys): if key is None: keys[index] = '?' else: keys[index] = str(keys[index]) self.key = "/".join(keys) return self.key def _prompt_password(self, verify=True): pw = None if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): # Check for Ctl-D try: while True: pw1 = getpass.getpass('OS Password: ') if verify: pw2 = getpass.getpass('Please verify: ') else: pw2 = pw1 if pw1 == pw2 and self._validate_string(pw1): pw = pw1 break except EOFError: pass return pw def save(self, auth_token, management_url, tenant_id): if not HAS_KEYRING or not self.args.os_cache: return if (auth_token == self.auth_token and management_url == self.management_url): # Nothing changed.... return if not all([management_url, auth_token, tenant_id]): raise ValueError(_("Unable to save empty management url/auth " "token")) value = "|".join([str(auth_token), str(management_url), str(tenant_id)]) keyring.set_password("novaclient_auth", self._make_key(), value) @property def password(self): # Cache password so we prompt user at most once if self._password: pass elif self._validate_string(self.args.os_password): self._password = self.args.os_password else: verify_pass = strutils.bool_from_string( utils.env("OS_VERIFY_PASSWORD", default=False), True) self._password = self._prompt_password(verify_pass) if not self._password: raise exc.CommandError( 'Expecting a password provided via either ' '--os-password, env[OS_PASSWORD], or ' 'prompted response') return self._password @property def management_url(self): if not HAS_KEYRING or not self.args.os_cache: return None management_url = None try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: _token, management_url, _tenant_id = block.split('|', 2) except all_errors: pass return management_url @property def auth_token(self): # Now is where it gets complicated since we # want to look into the keyring module, if it # exists and see if anything was provided in that # file that we can use. if not HAS_KEYRING or not self.args.os_cache: return None token = None try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: token, _management_url, _tenant_id = block.split('|', 2) except all_errors: pass return token @property def tenant_id(self): if not HAS_KEYRING or not self.args.os_cache: return None tenant_id = None try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: _token, _management_url, tenant_id = block.split('|', 2) except all_errors: pass return tenant_id class NovaClientArgumentParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): super(NovaClientArgumentParser, 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]}) def _get_option_tuples(self, option_string): """returns (action, option, value) candidates for an option prefix Returns [first candidate] if all candidates refers to current and deprecated forms of the same options: "nova boot ... --key KEY" parsing succeed because --key could only match --key-name, --key_name which are current/deprecated forms of the same option. """ option_tuples = (super(NovaClientArgumentParser, self) ._get_option_tuples(option_string)) if len(option_tuples) > 1: normalizeds = [option.replace('_', '-') for action, option, value in option_tuples] if len(set(normalizeds)) == 1: return option_tuples[:1] return option_tuples class OpenStackComputeShell(object): times = [] def __init__(self): self.client_logger = None def _append_global_identity_args(self, parser, argv): # Register the CLI arguments that have moved to the session object. loading.register_session_argparse_arguments(parser) # Peek into argv to see if os-auth-token or os-token were given, # in which case, the token auth plugin is what the user wants # else, we'll default to password default_auth_plugin = 'password' if 'os-token' in argv: default_auth_plugin = 'token' loading.register_auth_argparse_arguments( parser, argv, default=default_auth_plugin) parser.set_defaults(insecure=strutils.bool_from_string( utils.env('NOVACLIENT_INSECURE', default=False))) parser.set_defaults(os_auth_url=utils.env('OS_AUTH_URL', 'NOVA_URL')) parser.set_defaults(os_username=utils.env('OS_USERNAME', 'NOVA_USERNAME')) parser.set_defaults(os_password=utils.env('OS_PASSWORD', 'NOVA_PASSWORD')) parser.set_defaults(os_project_name=utils.env( 'OS_PROJECT_NAME', 'OS_TENANT_NAME', 'NOVA_PROJECT_ID')) parser.set_defaults(os_project_id=utils.env( 'OS_PROJECT_ID', 'OS_TENANT_ID')) def get_base_parser(self, argv): parser = NovaClientArgumentParser( prog='nova', description=__doc__.strip(), epilog='See "nova 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=novaclient.__version__) parser.add_argument( '--debug', default=False, action='store_true', help=_("Print debugging output.")) parser.add_argument( '--os-cache', default=strutils.bool_from_string( utils.env('OS_CACHE', default=False), True), action='store_true', help=_("Use the auth token cache. Defaults to False if " "env[OS_CACHE] is not set.")) parser.add_argument( '--timings', default=False, action='store_true', help=_("Print call timing info.")) parser.add_argument( '--os-region-name', metavar='', default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'), help=_('Defaults to env[OS_REGION_NAME].')) parser.add_argument( '--service-type', metavar='', help=_('Defaults to compute for most actions.')) parser.add_argument( '--service-name', metavar='', default=utils.env('NOVA_SERVICE_NAME'), help=_('Defaults to env[NOVA_SERVICE_NAME].')) parser.add_argument( '--os-endpoint-type', metavar='', dest='endpoint_type', default=utils.env( 'NOVA_ENDPOINT_TYPE', default=utils.env( 'OS_ENDPOINT_TYPE', default=DEFAULT_NOVA_ENDPOINT_TYPE)), help=_('Defaults to env[NOVA_ENDPOINT_TYPE], ' 'env[OS_ENDPOINT_TYPE] or ') + DEFAULT_NOVA_ENDPOINT_TYPE + '.') parser.add_argument( '--os-compute-api-version', metavar='', default=utils.env('OS_COMPUTE_API_VERSION', default=DEFAULT_OS_COMPUTE_API_VERSION), help=_('Accepts X, X.Y (where X is major and Y is minor part) or ' '"X.latest", defaults to env[OS_COMPUTE_API_VERSION].')) parser.add_argument( '--endpoint-override', metavar='', dest='endpoint_override', default=utils.env('NOVACLIENT_ENDPOINT_OVERRIDE', 'NOVACLIENT_BYPASS_URL'), help=_("Use this API endpoint instead of the Service Catalog. " "Defaults to env[NOVACLIENT_ENDPOINT_OVERRIDE].")) parser.add_argument( '--bypass-url', action=DeprecatedAction, use=_('use "%s"; this option will be removed after Pike OpenStack ' 'release.') % '--os-endpoint-override', dest='endpoint_override', help=argparse.SUPPRESS) if osprofiler_profiler: parser.add_argument('--profile', metavar='HMAC_KEY', default=utils.env('OS_PROFILE'), 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 ' 'nova; it is specified in the Nova ' 'configuration file at "/etc/nova/nova.conf". ' 'Without the key, profiling will not be ' 'triggered even if OSprofiler is enabled on ' 'the server side.') self._append_global_identity_args(parser, argv) return parser def get_subcommand_parser(self, version, do_help=False, argv=None): parser = self.get_base_parser(argv) self.subcommands = {} subparsers = parser.add_subparsers(metavar='') actions_module = importutils.import_module( "novaclient.v%s.shell" % version.ver_major) self._find_actions(subparsers, actions_module, version, do_help) self._find_actions(subparsers, self, version, do_help) for extension in self.extensions: self._find_actions(subparsers, extension.module, version, do_help) 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, version, do_help): msg = _(" (Supported by API versions '%(start)s' - '%(end)s')") 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 '' if hasattr(callback, "versioned"): additional_msg = "" subs = api_versions.get_substitutions(callback) if do_help: additional_msg = msg % { 'start': subs[0].start_version.get_string(), 'end': subs[-1].end_version.get_string()} if version.is_latest(): additional_msg += HINT_HELP_MSG subs = [versioned_method for versioned_method in subs if version.matches(versioned_method.start_version, versioned_method.end_version)] if subs: # use the "latest" substitution callback = subs[-1].func else: # there is no proper versioned method continue desc = callback.__doc__ or desc desc += additional_msg action_help = desc.strip() arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser( command, help=action_help, description=desc, add_help=False, formatter_class=OpenStackHelpFormatter) subparser.add_argument( '-h', '--help', action='help', help=argparse.SUPPRESS, ) self.subcommands[command] = subparser for (args, kwargs) in arguments: start_version = kwargs.get("start_version", None) if start_version: start_version = api_versions.APIVersion(start_version) end_version = kwargs.get("end_version", None) if end_version: end_version = api_versions.APIVersion(end_version) else: end_version = api_versions.APIVersion( "%s.latest" % start_version.ver_major) if do_help: kwargs["help"] = kwargs.get("help", "") + (msg % { "start": start_version.get_string(), "end": end_version.get_string()}) if not version.matches(start_version, end_version): continue kw = kwargs.copy() kw.pop("start_version", None) kw.pop("end_version", None) subparser.add_argument(*args, **kw) subparser.set_defaults(func=callback) def setup_debugging(self, debug): if not debug: return streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" # Set up the root logger to debug so that the submodules can # print debug messages logging.basicConfig(level=logging.DEBUG, format=streamformat) logging.getLogger('iso8601').setLevel(logging.WARNING) self.client_logger = logging.getLogger(client.__name__) ch = logging.StreamHandler() self.client_logger.setLevel(logging.DEBUG) self.client_logger.addHandler(ch) def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser(argv) (args, args_list) = parser.parse_known_args(argv) self.setup_debugging(args.debug) self.extensions = [] do_help = args.help or not args_list or args_list[0] == 'help' # bash-completion should not require authentication skip_auth = do_help or ( 'bash-completion' in argv) if not args.os_compute_api_version: api_version = api_versions.get_api_version( DEFAULT_MAJOR_OS_COMPUTE_API_VERSION) else: api_version = api_versions.get_api_version( args.os_compute_api_version) os_username = args.os_username os_user_id = args.os_user_id os_password = None # Fetched and set later as needed os_project_name = getattr( args, 'os_project_name', getattr(args, 'os_tenant_name', None)) os_project_id = getattr( args, 'os_project_id', getattr(args, 'os_tenant_id', None)) os_auth_url = args.os_auth_url os_region_name = args.os_region_name if "v2.0" not in os_auth_url: # NOTE(andreykurilin): assume that keystone V3 is used and try to # be more user-friendly, i.e provide default values for domains if (not args.os_project_domain_id and not args.os_project_domain_name): setattr(args, "os_project_domain_id", "default") if not args.os_user_domain_id and not args.os_user_domain_name: setattr(args, "os_user_domain_id", "default") os_project_domain_id = args.os_project_domain_id os_project_domain_name = args.os_project_domain_name os_user_domain_id = args.os_project_domain_id os_user_domain_name = args.os_project_domain_name endpoint_type = args.endpoint_type insecure = args.insecure service_type = args.service_type service_name = args.service_name endpoint_override = args.endpoint_override os_cache = args.os_cache cacert = args.os_cacert cert = args.os_cert timeout = args.timeout keystone_session = None keystone_auth = None # We may have either, both or none of these. # If we have both, we don't need USERNAME, PASSWORD etc. # Fill in the blanks from the SecretsHelper if possible. # Finally, authenticate unless we have both. # Note if we don't auth we probably don't have a tenant ID so we can't # cache the token. auth_token = getattr(args, 'os_token', None) if not endpoint_type: endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE # This allow users to use endpoint_type as (internal, public or admin) # just like other openstack clients (glance, cinder etc) if endpoint_type in ['internal', 'public', 'admin']: endpoint_type += 'URL' if not service_type: # Note(alex_xu): We need discover version first, so if there isn't # service type specified, we use default nova service type. service_type = DEFAULT_NOVA_SERVICE_TYPE # We should always auth unless we have a token and we're passing a # specific endpoint # Expired tokens are handled by client.py:_cs_request must_auth = not (auth_token and endpoint_override) # FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if must_auth and not skip_auth: if not os_username and not os_user_id: raise exc.CommandError( _("You must provide a username " "or user ID via --os-username, --os-user-id, " "env[OS_USERNAME] or env[OS_USER_ID]")) if not any([os_project_name, 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_ID]" " or env[OS_PROJECT_NAME]. You may" " use os-project and os-tenant" " interchangeably.")) if not os_auth_url: raise exc.CommandError( _("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL].")) with utils.record_time(self.times, args.timings, 'auth_url', args.os_auth_url): keystone_session = ( loading.load_session_from_argparse_arguments(args)) keystone_auth = ( loading.load_auth_from_argparse_arguments(args)) if (not skip_auth and not any([os_project_name, 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_ID]" " or env[OS_PROJECT_NAME]. You may" " use os-project and os-tenant" " interchangeably.")) if not os_auth_url and not skip_auth: raise exc.CommandError( _("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL]")) additional_kwargs = {} if osprofiler_profiler: additional_kwargs["profile"] = args.profile # This client is just used to discover api version. Version API needn't # microversion, so we just pass version 2 at here. self.cs = client.Client( api_versions.APIVersion("2.0"), os_username, os_password, project_id=os_project_id, project_name=os_project_name, user_id=os_user_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_token=auth_token, timings=args.timings, endpoint_override=endpoint_override, os_cache=os_cache, http_log_debug=args.debug, cacert=cacert, cert=cert, timeout=timeout, session=keystone_session, auth=keystone_auth, logger=self.client_logger, project_domain_id=os_project_domain_id, project_domain_name=os_project_domain_name, user_domain_id=os_user_domain_id, user_domain_name=os_user_domain_name, **additional_kwargs) if not skip_auth: if not api_version.is_latest(): if api_version > api_versions.APIVersion("2.0"): if not api_version.matches(novaclient.API_MIN_VERSION, novaclient.API_MAX_VERSION): raise exc.CommandError( _("The specified version isn't supported by " "client. The valid version range is '%(min)s' " "to '%(max)s'") % { "min": novaclient.API_MIN_VERSION.get_string(), "max": novaclient.API_MAX_VERSION.get_string()} ) api_version = api_versions.discover_version(self.cs, api_version) # build available subcommands based on version self.extensions = client.discover_extensions(api_version) self._run_extension_hooks('__pre_parse_args__') subcommand_parser = self.get_subcommand_parser( api_version, do_help=do_help, argv=argv) self.parser = subcommand_parser if args.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) self._run_extension_hooks('__post_parse_args__', args) # Short-circuit and deal with help right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 if not args.service_type: service_type = (utils.get_service_type(args.func) or DEFAULT_NOVA_SERVICE_TYPE) if utils.isunauthenticated(args.func): # NOTE(alex_xu): We need authentication for discover microversion. # But the subcommands may needn't it. If the subcommand needn't, # we clear the session arguments. keystone_session = None keystone_auth = None # Recreate client object with discovered version. self.cs = client.Client( api_version, os_username, os_password, project_id=os_project_id, project_name=os_project_name, user_id=os_user_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_token=auth_token, timings=args.timings, endpoint_override=endpoint_override, os_cache=os_cache, http_log_debug=args.debug, cacert=cacert, cert=cert, timeout=timeout, session=keystone_session, auth=keystone_auth, project_domain_id=os_project_domain_id, project_domain_name=os_project_domain_name, user_domain_id=os_user_domain_id, user_domain_name=os_user_domain_name) # Now check for the password/token of which pieces of the # identifying keyring key can come from the underlying client if must_auth: helper = SecretsHelper(args, self.cs.client) self.cs.client.keyring_saver = helper tenant_id = helper.tenant_id # Allow commandline to override cache if not auth_token: auth_token = helper.auth_token endpoint_override = endpoint_override or helper.management_url if tenant_id and auth_token and endpoint_override: self.cs.client.tenant_id = tenant_id self.cs.client.auth_token = auth_token self.cs.client.management_url = endpoint_override self.cs.client.password_func = lambda: helper.password else: # We're missing something, so auth with user/pass and save # the result in our helper. self.cs.client.password = helper.password args.func(self.cs, args) if osprofiler_profiler and args.profile: trace_id = osprofiler_profiler.get().get_base_id() print("To display trace use the command:\n\n" " osprofiler trace show --html %s " % trace_id) if args.timings: self._dump_timings(self.times + self.cs.get_timings()) def _dump_timings(self, timings): class Tyme(object): def __init__(self, url, seconds): self.url = url self.seconds = seconds results = [Tyme(url, end - start) for url, start, end in timings] total = 0.0 for tyme in results: total += tyme.seconds results.append(Tyme("Total", total)) utils.print_list(results, ["url", "seconds"], sortby_index=None) def _run_extension_hooks(self, hook_type, *args, **kwargs): """Run hooks for all registered extensions.""" for extension in self.extensions: extension.run_hooks(hook_type, *args, **kwargs) def do_bash_completion(self, _args): """ Prints all of the commands and options to stdout so that the nova.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)) @utils.arg( 'command', metavar='', nargs='?', help=_('Display help for .')) def do_help(self, args): """ Display help about this program or one of its subcommands. """ if args.command: if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError(_("'%s' is not a valid subcommand") % args.command) else: self.parser.print_help() # I'm picky about my shell help. class OpenStackHelpFormatter(argparse.HelpFormatter): def __init__(self, prog, indent_increment=2, max_help_position=32, width=None): super(OpenStackHelpFormatter, self).__init__(prog, indent_increment, max_help_position, width) 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: argv = [encodeutils.safe_decode(a) for a in sys.argv[1:]] OpenStackComputeShell().main(argv) except Exception as exc: logger.debug(exc, exc_info=1) if six.PY2: message = encodeutils.safe_encode(six.text_type(exc)) else: message = encodeutils.exception_to_unicode(exc) print("ERROR (%(type)s): %(msg)s" % { 'type': exc.__class__.__name__, 'msg': message}, file=sys.stderr) sys.exit(1) except KeyboardInterrupt: print(_("... terminating nova client"), file=sys.stderr) sys.exit(130) if __name__ == "__main__": main() python-novaclient-9.1.1/novaclient/i18n.py0000664000175000017500000000150613165151100021617 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """oslo_i18n integration module for novaclient. See http://docs.openstack.org/developer/oslo.i18n/usage.html . """ import oslo_i18n _translators = oslo_i18n.TranslatorFactory(domain='novaclient') # The primary translation function using the well-known name "_" _ = _translators.primary python-novaclient-9.1.1/novaclient/crypto.py0000664000175000017500000000241513165151077022375 0ustar jenkinsjenkins00000000000000# Copyright 2013 Nebula, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import base64 import subprocess class DecryptionFailure(Exception): pass def decrypt_password(private_key, password): """Base64 decodes password and unencrypts it with private key. Requires openssl binary available in the path. """ unencoded = base64.b64decode(password) cmd = ['openssl', 'rsautl', '-decrypt', '-inkey', private_key] proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(unencoded) proc.stdin.close() if proc.returncode: raise DecryptionFailure(err) return out python-novaclient-9.1.1/novaclient/tests/0000775000175000017500000000000013165151230021632 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/__init__.py0000664000175000017500000000000013165151077023742 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/unit/0000775000175000017500000000000013165151230022611 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/unit/test_utils.py0000664000175000017500000004071313165151077025400 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys import mock from oslo_utils import encodeutils import six from six.moves.urllib import parse from novaclient import base from novaclient import exceptions from novaclient.tests.unit import fakes from novaclient.tests.unit import utils as test_utils from novaclient import utils UUID = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0' class FakeResource(object): NAME_ATTR = 'name' request_ids = fakes.FAKE_REQUEST_ID_LIST def __init__(self, _id, properties): self.id = _id try: self.name = properties['name'] except KeyError: pass def append_request_ids(self, resp): pass class FakeManager(base.ManagerWithFind): resource_class = FakeResource resources = [ FakeResource('1234', {'name': 'entity_one'}), FakeResource('12345', {'name': 'UPPER'}), FakeResource('123456', {'name': 'lower'}), FakeResource('1234567', {'name': 'Mixed'}), FakeResource('12345678', {'name': 'mixed'}), FakeResource(UUID, {'name': 'entity_two'}), FakeResource('5678', {'name': '9876'}), FakeResource('01234', {'name': 'entity_three'}) ] is_alphanum_id_allowed = None def __init__(self, alphanum_id_allowed=False): self.is_alphanum_id_allowed = alphanum_id_allowed def get(self, resource_id): for resource in self.resources: if resource.id == str(resource_id): return resource raise exceptions.NotFound(resource_id) def list(self): return base.ListWithMeta(self.resources, fakes.FAKE_REQUEST_ID_LIST) class FakeDisplayResource(object): NAME_ATTR = 'display_name' def __init__(self, _id, properties): self.id = _id try: self.display_name = properties['display_name'] except KeyError: pass def append_request_ids(self, resp): pass class FakeDisplayManager(FakeManager): resource_class = FakeDisplayResource resources = [ FakeDisplayResource('4242', {'display_name': 'entity_three'}), ] class FindResourceTestCase(test_utils.TestCase): def setUp(self): super(FindResourceTestCase, self).setUp() self.manager = FakeManager(None) def test_find_none(self): """Test a few non-valid inputs.""" self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, 'asdf') self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, None) self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, {}) def test_find_by_integer_id(self): output = utils.find_resource(self.manager, 1234) self.assertEqual(output, self.manager.get('1234')) def test_find_by_str_id(self): output = utils.find_resource(self.manager, '1234') self.assertEqual(output, self.manager.get('1234')) def test_find_by_uuid(self): output = utils.find_resource(self.manager, UUID) self.assertEqual(output, self.manager.get(UUID)) def test_find_by_str_name(self): output = utils.find_resource(self.manager, 'entity_one') self.assertEqual(output, self.manager.get('1234')) def test_find_by_str_upper_name(self): output = utils.find_resource(self.manager, 'UPPER') self.assertEqual(output, self.manager.get('12345')) def test_find_by_str_lower_name(self): output = utils.find_resource(self.manager, 'lower') self.assertEqual(output, self.manager.get('123456')) def test_find_by_str_mix_name(self): output = utils.find_resource(self.manager, 'Mixed') self.assertEqual(output, self.manager.get('1234567')) def test_find_by_str_lower_name_mixed(self): output = utils.find_resource(self.manager, 'mixed') self.assertEqual(output, self.manager.get('12345678')) def test_find_by_str_display_name(self): display_manager = FakeDisplayManager(None) output = utils.find_resource(display_manager, 'entity_three') self.assertEqual(output, display_manager.get('4242')) def test_find_in_alphanum_allowed_manager_by_str_id_(self): alphanum_manager = FakeManager(True) output = utils.find_resource(alphanum_manager, '01234') self.assertEqual(output, alphanum_manager.get('01234')) def test_find_without_wrapping_exception(self): alphanum_manager = FakeManager(True) self.assertRaises(exceptions.NotFound, utils.find_resource, alphanum_manager, 'not_exist', wrap_exception=False) res = alphanum_manager.resources[0] alphanum_manager.resources.append(res) self.assertRaises(exceptions.NoUniqueMatch, utils.find_resource, alphanum_manager, res.name, wrap_exception=False) class _FakeResult(object): def __init__(self, name, value): self.name = name self.value = value class PrintResultTestCase(test_utils.TestCase): @mock.patch('sys.stdout', six.StringIO()) def test_print_dict(self): dict = {'key': 'value'} utils.print_dict(dict) self.assertEqual('+----------+-------+\n' '| Property | Value |\n' '+----------+-------+\n' '| key | value |\n' '+----------+-------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_wrap(self): dict = {'key1': 'not wrapped', 'key2': 'this will be wrapped'} utils.print_dict(dict, wrap=16) self.assertEqual('+----------+--------------+\n' '| Property | Value |\n' '+----------+--------------+\n' '| key1 | not wrapped |\n' '| key2 | this will be |\n' '| | wrapped |\n' '+----------+--------------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_str(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 2), _FakeResult("k2", 3)] utils.print_list(objs, ["Name", "Value"], sortby_index=0) self.assertEqual('+------+-------+\n' '| Name | Value |\n' '+------+-------+\n' '| k1 | 1 |\n' '| k2 | 3 |\n' '| k3 | 2 |\n' '+------+-------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_integer(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 2), _FakeResult("k2", 3)] utils.print_list(objs, ["Name", "Value"], sortby_index=1) self.assertEqual('+------+-------+\n' '| Name | Value |\n' '+------+-------+\n' '| k1 | 1 |\n' '| k3 | 2 |\n' '| k2 | 3 |\n' '+------+-------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_unicode_list(self): objs = [_FakeResult("k", u'\u2026')] utils.print_list(objs, ["Name", "Value"]) if six.PY3: s = u'\u2026' else: s = encodeutils.safe_encode(u'\u2026') self.assertEqual('+------+-------+\n' '| Name | Value |\n' '+------+-------+\n' '| k | %s |\n' '+------+-------+\n' % s, sys.stdout.getvalue()) # without sorting @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_none(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 3), _FakeResult("k2", 2)] utils.print_list(objs, ["Name", "Value"], sortby_index=None) self.assertEqual('+------+-------+\n' '| Name | Value |\n' '+------+-------+\n' '| k1 | 1 |\n' '| k3 | 3 |\n' '| k2 | 2 |\n' '+------+-------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_dictionary(self): dict = {'k': {'foo': 'bar'}} utils.print_dict(dict) self.assertEqual('+----------+----------------+\n' '| Property | Value |\n' '+----------+----------------+\n' '| k | {"foo": "bar"} |\n' '+----------+----------------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_list_dictionary(self): dict = {'k': [{'foo': 'bar'}]} utils.print_dict(dict) self.assertEqual('+----------+------------------+\n' '| Property | Value |\n' '+----------+------------------+\n' '| k | [{"foo": "bar"}] |\n' '+----------+------------------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_list(self): dict = {'k': ['foo', 'bar']} utils.print_dict(dict) self.assertEqual('+----------+----------------+\n' '| Property | Value |\n' '+----------+----------------+\n' '| k | ["foo", "bar"] |\n' '+----------+----------------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_large_dict_list(self): dict = {'k': ['foo1', 'bar1', 'foo2', 'bar2', 'foo3', 'bar3', 'foo4', 'bar4']} utils.print_dict(dict, wrap=40) self.assertEqual( '+----------+------------------------------------------+\n' '| Property | Value |\n' '+----------+------------------------------------------+\n' '| k | ["foo1", "bar1", "foo2", "bar2", "foo3", |\n' '| | "bar3", "foo4", "bar4"] |\n' '+----------+------------------------------------------+\n', sys.stdout.getvalue()) @mock.patch('sys.stdout', six.StringIO()) def test_print_unicode_dict(self): dict = {'k': u'\u2026'} utils.print_dict(dict) if six.PY3: s = u'\u2026' else: s = encodeutils.safe_encode(u'\u2026') self.assertEqual('+----------+-------+\n' '| Property | Value |\n' '+----------+-------+\n' '| k | %s |\n' '+----------+-------+\n' % s, sys.stdout.getvalue()) class FlattenTestCase(test_utils.TestCase): def test_flattening(self): squashed = utils.flatten_dict( {'a1': {'b1': 1234, 'b2': 'string', 'b3': set((1, 2, 3)), 'b4': {'c1': ['l', 'l', ['l']], 'c2': 'string'}}, 'a2': ['l'], 'a3': ('t',), 'a4': {}}) self.assertEqual({'a1_b1': 1234, 'a1_b2': 'string', 'a1_b3': set([1, 2, 3]), 'a1_b4_c1': ['l', 'l', ['l']], 'a1_b4_c2': 'string', 'a2': ['l'], 'a3': ('t',), 'a4': {}}, squashed) def test_pretty_choice_dict(self): d = {} r = utils.pretty_choice_dict(d) self.assertEqual("", r) d = {"k1": "v1", "k2": "v2", "k3": "v3"} r = utils.pretty_choice_dict(d) self.assertEqual("'k1=v1', 'k2=v2', 'k3=v3'", r) class ValidationsTestCase(test_utils.TestCase): def test_validate_flavor_metadata_keys_with_valid_keys(self): valid_keys = ['key1', 'month.price', 'I-Am:AK-ey.01-', 'spaces and _'] utils.validate_flavor_metadata_keys(valid_keys) def test_validate_flavor_metadata_keys_with_invalid_keys(self): invalid_keys = ['/1', '?1', '%1', '<', '>', '\1'] for key in invalid_keys: try: utils.validate_flavor_metadata_keys([key]) self.fail("Invalid key passed validation: %s" % key) except exceptions.CommandError as ce: self.assertIn(key, str(ce)) class ResourceManagerExtraKwargsHookTestCase(test_utils.TestCase): def test_get_resource_manager_extra_kwargs_hook_test(self): do_foo = mock.MagicMock() def hook1(args): return {'kwarg1': 'v_hook1'} def hook2(args): return {'kwarg1': 'v_hook2'} do_foo.resource_manager_kwargs_hooks = [hook1, hook2] args = {} exc = self.assertRaises(exceptions.NoUniqueMatch, utils.get_resource_manager_extra_kwargs, do_foo, args) except_error = ("Hook 'hook2' is attempting to redefine " "attributes") self.assertIn(except_error, six.text_type(exc)) class DoActionOnManyTestCase(test_utils.TestCase): def _test_do_action_on_many(self, side_effect, fail): action = mock.Mock(side_effect=side_effect) if fail: self.assertRaises(exceptions.CommandError, utils.do_action_on_many, action, [1, 2], 'success with %s', 'error') else: utils.do_action_on_many(action, [1, 2], 'success with %s', 'error') action.assert_has_calls([mock.call(1), mock.call(2)]) def test_do_action_on_many_success(self): self._test_do_action_on_many([None, None], fail=False) def test_do_action_on_many_first_fails(self): self._test_do_action_on_many([Exception(), None], fail=True) def test_do_action_on_many_last_fails(self): self._test_do_action_on_many([None, Exception()], fail=True) class RecordTimeTestCase(test_utils.TestCase): def test_record_time(self): times = [] with utils.record_time(times, True, 'a', 'b'): pass self.assertEqual(1, len(times)) self.assertEqual(3, len(times[0])) self.assertEqual('a b', times[0][0]) self.assertIsInstance(times[0][1], float) self.assertIsInstance(times[0][2], float) times = [] with utils.record_time(times, False, 'x'): pass self.assertEqual(0, len(times)) class PrepareQueryStringTestCase(test_utils.TestCase): def test_convert_dict_to_string(self): ustr = b'?\xd0\xbf=1&\xd1\x80=2' if six.PY3: # in py3 real unicode symbols will be urlencoded ustr = ustr.decode('utf8') cases = ( ({}, ''), ({'2': 2, '10': 1}, '?10=1&2=2'), ({'abc': 1, 'abc1': 2}, '?abc=1&abc1=2'), ({b'\xd0\xbf': 1, b'\xd1\x80': 2}, ustr), ({(1, 2): '1', (3, 4): '2'}, '?(1, 2)=1&(3, 4)=2') ) for case in cases: self.assertEqual( case[1], parse.unquote_plus(utils.prepare_query_string(case[0]))) python-novaclient-9.1.1/novaclient/tests/unit/fakes.py0000664000175000017500000001233013165151077024264 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ A fake server that "responds" to API methods with pre-canned responses. All of these responses come from the spec, so if for some reason the spec's wrong the tests might raise AssertionError. I've indicated in comments the places where actual behavior differs from the spec. """ from novaclient import base # fake request id FAKE_REQUEST_ID = 'req-3fdea7c2-e3e3-48b5-a656-6b12504c49a1' FAKE_REQUEST_ID_LIST = [FAKE_REQUEST_ID] def assert_has_keys(dict, required=None, optional=None): required = required or [] optional = optional or [] keys = dict.keys() for k in required: try: assert k in keys except AssertionError: extra_keys = set(keys).difference(set(required + optional)) raise AssertionError("found unexpected keys: %s" % list(extra_keys)) class FakeClient(object): def assert_called(self, method, url, body=None, pos=-1): """Assert than an HTTP method was called at given order/position. :param method: HTTP method name which is expected to be called :param url: Expected request url to be called with given method :param body: Expected request body to be called with given method and url. Default is None. :param pos: Order of the expected method call. If multiple methods calls are made in single API request, then, order of each method call can be checked by passing expected order to this arg. Default is -1 which means most recent call. Usage:: 1. self.run_command('flavor-list --extra-specs') self.assert_called('GET', '/flavors/aa1/os-extra_specs') 2. self.run_command(["boot", "--image", "1", "--flavor", "512 MB Server", "--max-count", "3", "server"]) self.assert_called('GET', '/images/1', pos=0) self.assert_called('GET', '/flavors/512 MB Server', pos=1) self.assert_called('GET', '/flavors?is_public=None', pos=2) self.assert_called('GET', '/flavors/2', pos=3) self.assert_called( 'POST', '/servers', { 'server': { 'flavorRef': '2', 'name': 'server', 'imageRef': '1', 'min_count': 1, 'max_count': 3, } }, pos=4) """ expected = (method, url) assert self.client.callstack, \ "Expected %s %s but no calls were made." % expected called = self.client.callstack[pos][0:2] assert expected == called, \ ('\nExpected: %(expected)s' '\nActual: %(called)s' '\nCall position: %(pos)s' '\nCalls:\n%(calls)s' % {'expected': expected, 'called': called, 'pos': pos, 'calls': '\n'.join(str(c) for c in self.client.callstack)}) if body is not None: if self.client.callstack[pos][2] != body: raise AssertionError('%r != %r' % (self.client.callstack[pos][2], body)) def assert_called_anytime(self, method, url, body=None): """Assert than an HTTP method was called anytime in the test. :param method: HTTP method name which is expected to be called :param url: Expected request url to be called with given method :param body: Expected request body to be called with given method and url. Default is None. Usage:: self.run_command('flavor-list --extra-specs') self.assert_called_anytime('GET', '/flavors/detail') """ expected = (method, url) assert self.client.callstack, \ "Expected %s %s but no calls were made." % expected found = False for entry in self.client.callstack: if expected == entry[0:2]: found = True break assert found, 'Expected %s; got %s' % (expected, self.client.callstack) if body is not None: try: assert entry[2] == body except AssertionError: print(entry[2]) print("!=") print(body) raise self.client.callstack = [] def clear_callstack(self): self.client.callstack = [] def authenticate(self): pass # Fake class that will be used as an extension class FakeManager(base.Manager): pass python-novaclient-9.1.1/novaclient/tests/unit/__init__.py0000664000175000017500000000000013165151077024721 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/unit/v2/0000775000175000017500000000000013165151230023140 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/unit/v2/test_certs.py0000664000175000017500000000333213165151077025703 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from novaclient.tests.unit.fixture_data import certs as data from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import certs class CertsTest(utils.FixturedTestCase): data_fixture_class = data.Fixture cert_type = certs.Certificate scenarios = [('original', {'client_fixture_class': client.V1}), ('session', {'client_fixture_class': client.SessionV1})] @mock.patch('warnings.warn') def test_create_cert(self, mock_warn): cert = self.cs.certs.create() self.assert_request_id(cert, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-certificates') self.assertIsInstance(cert, self.cert_type) self.assertEqual(1, mock_warn.call_count) @mock.patch('warnings.warn') def test_get_root_cert(self, mock_warn): cert = self.cs.certs.get() self.assert_request_id(cert, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-certificates/root') self.assertIsInstance(cert, self.cert_type) self.assertEqual(1, mock_warn.call_count) python-novaclient-9.1.1/novaclient/tests/unit/v2/fakes.py0000664000175000017500000024332613165151077024626 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import re import mock from oslo_utils import strutils import six from six.moves.urllib import parse import novaclient from novaclient import api_versions from novaclient import client as base_client from novaclient import exceptions from novaclient.tests.unit import fakes from novaclient.tests.unit import utils from novaclient.v2 import client # regex to compare callback to result of get_endpoint() # checks version number (vX or vX.X where X is a number) # and also checks if the id is on the end ENDPOINT_RE = re.compile( r"^get_http:__nova_api:8774_v\d(_\d)?_\w{32}$") # accepts formats like v2 or v2.1 ENDPOINT_TYPE_RE = re.compile(r"^v\d(\.\d)?$") # accepts formats like v2 or v2_1 CALLBACK_RE = re.compile(r"^get_http:__nova_api:8774_v\d(_\d)?$") # fake image uuids FAKE_IMAGE_UUID_1 = 'c99d7632-bd66-4be9-aed5-3dd14b223a76' FAKE_IMAGE_UUID_2 = 'f27f479a-ddda-419a-9bbc-d6b56b210161' FAKE_IMAGE_UUID_SNAPSHOT = '555cae93-fb41-4145-9c52-f5b923538a26' FAKE_IMAGE_UUID_SNAP_DEL = '55bb23af-97a4-4068-bdf8-f10c62880ddf' FAKE_IMAGE_UUID_BACKUP = '2f87e889-41a4-4778-8553-83f5eea68c5d' # fake request id FAKE_REQUEST_ID = fakes.FAKE_REQUEST_ID FAKE_REQUEST_ID_LIST = fakes.FAKE_REQUEST_ID_LIST FAKE_RESPONSE_HEADERS = {'x-openstack-request-id': FAKE_REQUEST_ID} FAKE_SERVICE_UUID_1 = '75e9eabc-ed3b-4f11-8bba-add1e7e7e2de' FAKE_SERVICE_UUID_2 = '1f140183-c914-4ddf-8757-6df73028aa86' class FakeClient(fakes.FakeClient, client.Client): def __init__(self, api_version, *args, **kwargs): client.Client.__init__(self, username='username', password='password', project_id='project_id', auth_url='auth_url', extensions=kwargs.get('extensions'), direct_use=False, api_version=api_version) self.client = FakeSessionClient(api_version=api_version, **kwargs) class FakeSessionClient(base_client.SessionClient): def __init__(self, *args, **kwargs): self.callstack = [] self.visited = [] self.auth = mock.Mock() self.session = mock.Mock() self.service_type = 'service_type' self.service_name = None self.endpoint_override = None self.interface = None self.region_name = None self.version = None self.api_version = kwargs.get('api_version') self.auth.get_auth_ref.return_value.project_id = 'tenant_id' # determines which endpoint to return in get_endpoint() # NOTE(augustina): this is a hacky workaround, ultimately # we need to fix our whole mocking architecture (fixtures?) if 'endpoint_type' in kwargs: self.endpoint_type = kwargs['endpoint_type'] else: self.endpoint_type = 'endpoint_type' self.logger = mock.MagicMock() def get_endpoint(self, **kwargs): # check if endpoint matches expected format (eg, v2.1) if (hasattr(self, 'endpoint_type') and ENDPOINT_TYPE_RE.search(self.endpoint_type)): return "http://nova-api:8774/%s/" % self.endpoint_type else: return ( "http://nova-api:8774/v2.1/190a755eef2e4aac9f06aa6be9786385") def request(self, url, method, **kwargs): return self._cs_request(url, method, **kwargs) def _cs_request(self, url, method, **kwargs): # Check that certain things are called correctly if method in ['GET', 'DELETE']: assert 'body' not in kwargs elif method == 'PUT': assert 'body' in kwargs if url is not None: # Call the method args = parse.parse_qsl(parse.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_') munged_url = munged_url.replace('.', '_') munged_url = munged_url.replace('-', '_') munged_url = munged_url.replace(' ', '_') munged_url = munged_url.replace('!', '_') munged_url = munged_url.replace('@', '_') callback = "%s_%s" % (method.lower(), munged_url) if url is None or callback == "get_http:__nova_api:8774": # To get API version information, it is necessary to GET # a nova endpoint directly without "v2/". callback = "get_versions" elif CALLBACK_RE.search(callback): callback = "get_current_version" elif ENDPOINT_RE.search(callback): # compare callback to result of get_endpoint() # NOTE(sdague): if we try to call a thing that doesn't # exist, just return a 404. This allows the stack to act # more like we'd expect when making REST calls. raise exceptions.NotFound('404') # Handle fake glance v2 requests v2_image = False if callback.startswith('get_v2_images'): v2_image = True callback = callback.replace('get_v2_', 'get_') simulate_pagination_next_links = [ 'get_os_simple_tenant_usage', 'get_os_simple_tenant_usage_tenant_id', ] if callback in simulate_pagination_next_links: while callback in self.visited: callback += '_next' if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) # Note the call self.visited.append(callback) self.callstack.append((method, url, kwargs.get('body'))) status, headers, body = getattr(self, callback)(**kwargs) # If we're dealing with a glance v2 image response, the response # isn't wrapped like the compute images API proxy is, so handle that. if body and v2_image and 'image' in body: body = body['image'] r = utils.TestResponse({ "status_code": status, "text": body, "headers": headers, }) return r, body def get_versions(self): return (200, FAKE_RESPONSE_HEADERS, { "versions": [ {"status": "SUPPORTED", "updated": "2011-01-21T11:33:21Z", "links": [{"href": "http://nova-api:8774/v2/", "rel": "self"}], "min_version": "", "version": "", "id": "v2.0"}, {"status": "CURRENT", "updated": "2013-07-23T11:33:21Z", "links": [{"href": "http://nova-api:8774/v2.1/", "rel": "self"}], "min_version": novaclient.API_MIN_VERSION.get_string(), "version": novaclient.API_MAX_VERSION.get_string(), "id": "v2.1"} ]}) def get_current_version(self): versions = { # v2 doesn't contain version or min_version fields "v2": { "version": { "status": "SUPPORTED", "updated": "2011-01-21T11:33:21Z", "links": [{ "href": "http://nova-api:8774/v2/", "rel": "self" }], "id": "v2.0" } }, "v2.1": { "version": { "status": "CURRENT", "updated": "2013-07-23T11:33:21Z", "links": [{ "href": "http://nova-api:8774/v2.1/", "rel": "self" }], "min_version": novaclient.API_MIN_VERSION.get_string(), "version": novaclient.API_MAX_VERSION.get_string(), "id": "v2.1" } } } # if an endpoint_type that matches a version wasn't initialized, # default to v2.1 endpoint = 'v2.1' if hasattr(self, 'endpoint_type'): if ENDPOINT_TYPE_RE.search(self.endpoint_type): if self.endpoint_type in versions: endpoint = self.endpoint_type else: raise AssertionError( "Unknown endpoint_type:%s" % self.endpoint_type) return (200, FAKE_RESPONSE_HEADERS, versions[endpoint]) # # agents # def get_os_agents(self, **kw): hypervisor = kw.get('hypervisor', 'kvm') return (200, {}, { 'agents': [{'hypervisor': hypervisor, 'os': 'win', 'architecture': 'x86', 'version': '7.0', 'url': 'xxx://xxxx/xxx/xxx', 'md5hash': 'add6bb58e139be103324d04d82d8f545', 'id': 1}, {'hypervisor': hypervisor, 'os': 'linux', 'architecture': 'x86', 'version': '16.0', 'url': 'xxx://xxxx/xxx/xxx1', 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'id': 2}]}) def post_os_agents(self, body): return (200, {}, {'agent': { 'url': '/xxx/xxx/xxx', 'hypervisor': body['agent']['hypervisor'], 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'version': '7.0', 'architecture': 'x86', 'os': 'win', 'id': 1}}) def delete_os_agents_1(self, **kw): return (202, {}, None) def put_os_agents_1(self, body, **kw): return (200, {}, { "agent": {"url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546", 'id': 1}}) # # List all extensions # def get_extensions(self, **kw): exts = [ { "alias": "NMN", "description": "Multiple network support", "links": [], "name": "Multinic", "namespace": ("http://docs.openstack.org/" "compute/ext/multinic/api/v1.1"), "updated": "2011-06-09T00:00:00+00:00" }, { "alias": "OS-DCF", "description": "Disk Management Extension", "links": [], "name": "DiskConfig", "namespace": ("http://docs.openstack.org/" "compute/ext/disk_config/api/v1.1"), "updated": "2011-09-27T00:00:00+00:00" }, { "alias": "OS-EXT-SRV-ATTR", "description": "Extended Server Attributes support.", "links": [], "name": "ExtendedServerAttributes", "namespace": ("http://docs.openstack.org/" "compute/ext/extended_status/api/v1.1"), "updated": "2011-11-03T00:00:00+00:00" }, { "alias": "OS-EXT-STS", "description": "Extended Status support", "links": [], "name": "ExtendedStatus", "namespace": ("http://docs.openstack.org/" "compute/ext/extended_status/api/v1.1"), "updated": "2011-11-03T00:00:00+00:00" }, ] return (200, FAKE_RESPONSE_HEADERS, { "extensions": exts, }) # # Limits # def get_limits(self, **kw): return (200, {}, {"limits": { "rate": [ { "uri": "*", "regex": ".*", "limit": [ { "value": 10, "verb": "POST", "remaining": 2, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" }, { "value": 10, "verb": "PUT", "remaining": 2, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" }, { "value": 100, "verb": "DELETE", "remaining": 100, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" } ] }, { "uri": "*/servers", "regex": "^/servers", "limit": [ { "verb": "POST", "value": 25, "remaining": 24, "unit": "DAY", "next-available": "2011-12-15T22:42:45Z" } ] } ], "absolute": { "maxTotalRAMSize": 51200, "maxServerMeta": 5, "maxImageMeta": 5, "maxPersonality": 5, "maxPersonalitySize": 10240 }, }}) # # Servers # def get_servers(self, **kw): return (200, {}, {"servers": [ {'id': '1234', 'name': 'sample-server'}, {'id': '5678', 'name': 'sample-server2'}, {'id': '9014', 'name': 'help'} ]}) def get_servers_detail(self, **kw): return (200, {}, {"servers": [ { "id": '1234', "name": "sample-server", "image": { "id": FAKE_IMAGE_UUID_2, "name": "sample image", }, "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", "status": "BUILD", "progress": 60, "addresses": { "public": [ { "version": 4, "addr": "1.2.3.4", }, { "version": 4, "addr": "5.6.7.8", }], "private": [{ "version": 4, "addr": "10.11.12.13", }], }, "metadata": { "Server Label": "Web Head 1", "Image Version": "2.1" }, "OS-EXT-SRV-ATTR:host": "computenode1", "security_groups": [{ 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }], "OS-EXT-MOD:some_thing": "mod_some_thing_value", }, { "id": '5678', "name": "sample-server2", "image": { "id": FAKE_IMAGE_UUID_1, "name": "sample image", }, "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", "addresses": { "public": [ { "version": 4, "addr": "4.5.6.7", }, { "version": 4, "addr": "5.6.9.8", }], "private": [{ "version": 4, "addr": "10.13.12.13", }], }, "metadata": { "Server Label": "DB 1" }, "OS-EXT-SRV-ATTR:host": "computenode2", "security_groups": [ { 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }, { 'id': 2, 'name': 'securitygroup2', 'description': 'ANOTHER_FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }], }, { "id": '9012', "name": "sample-server3", "image": "", "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", "addresses": { "public": [ { "version": 4, "addr": "4.5.6.7", }, { "version": 4, "addr": "5.6.9.8", }], "private": [{ "version": 4, "addr": "10.13.12.13", }], }, "metadata": { "Server Label": "DB 1" } }, { "id": '9013', "name": "sample-server4", "flavor": { "id": '80645cf4-6ad3-410a-bbc8-6f3e1e291f51', }, "image": { "id": '3e861307-73a6-4d1f-8d68-f68b03223032', }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", }, { "id": '9014', "name": "help", "flavor": { "id": '80645cf4-6ad3-410a-bbc8-6f3e1e291f51', }, "image": { "id": '3e861307-73a6-4d1f-8d68-f68b03223032', }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", }, ]}) def post_servers(self, body, **kw): assert set(body.keys()) <= set(['server', 'os:scheduler_hints']) fakes.assert_has_keys( body['server'], required=['name', 'imageRef', 'flavorRef'], optional=['metadata', 'personality']) if 'personality' in body['server']: for pfile in body['server']['personality']: fakes.assert_has_keys(pfile, required=['path', 'contents']) if body['server']['name'] == 'some-bad-server': return (202, {}, self.get_servers_1235()[2]) else: return (202, {}, self.get_servers_1234()[2]) def post_os_volumes_boot(self, body, **kw): assert set(body.keys()) <= set(['server', 'os:scheduler_hints']) fakes.assert_has_keys( body['server'], required=['name', 'flavorRef'], optional=['imageRef']) # Require one, and only one, of the keys for bdm if 'block_device_mapping' not in body['server']: if 'block_device_mapping_v2' not in body['server']: raise AssertionError( "missing required keys: 'block_device_mapping'" ) elif 'block_device_mapping_v2' in body['server']: raise AssertionError("found extra keys: 'block_device_mapping'") return (202, {}, self.get_servers_9012()[2]) def get_servers_1234(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][0]} return (200, {}, r) def get_servers_1235(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][0]} r['server']['id'] = '1235' r['server']['status'] = 'error' r['server']['fault'] = {'message': 'something went wrong!'} return (200, {}, r) def get_servers_5678(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][1]} return (200, {}, r) def get_servers_9012(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][2]} return (200, {}, r) def get_servers_9013(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][3]} return (200, {}, r) def get_servers_9014(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][4]} return (200, {}, r) def delete_os_server_groups_12345(self, **kw): return (202, {}, None) def delete_os_server_groups_56789(self, **kw): return (202, {}, None) def delete_servers_1234(self, **kw): return (202, {}, None) def delete_servers_5678(self, **kw): return (202, {}, None) def delete_servers_1234_metadata_key1(self, **kw): return (204, {}, None) def delete_servers_1234_metadata_key2(self, **kw): return (204, {}, None) def post_servers_1234_metadata(self, **kw): return (204, {}, {'metadata': {'test_key': 'test_value'}}) def get_servers_1234_diagnostics(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def post_servers_uuid1_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def post_servers_uuid2_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def post_servers_uuid3_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def post_servers_uuid4_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def delete_servers_uuid1_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def delete_servers_uuid2_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def delete_servers_uuid3_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def delete_servers_uuid4_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def get_servers_1234_os_security_groups(self, **kw): return (200, {}, { "security_groups": [{ 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7', 'rules': []}] }) # # Server password # # Testing with the following password and key # # Clear password: FooBar123 # # RSA Private Key: novaclient/tests/unit/idfake.pem # # Encrypted password # OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r # qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho # QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw # /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N # tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk # Hi/fmZZNQQqj1Ijq0caOIw== def get_servers_1234_os_server_password(self, **kw): return (200, {}, { 'password': 'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r' 'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho' 'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw' '/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N' 'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk' 'Hi/fmZZNQQqj1Ijq0caOIw=='}) def delete_servers_1234_os_server_password(self, **kw): return (202, {}, None) # # Server actions # none_actions = ['revertResize', 'migrate', 'os-stop', 'os-start', 'forceDelete', 'restore', 'pause', 'unpause', 'unlock', 'unrescue', 'resume', 'suspend', 'lock', 'shelve', 'shelveOffload', 'unshelve', 'resetNetwork'] type_actions = ['os-getVNCConsole', 'os-getSPICEConsole', 'os-getRDPConsole'] @classmethod def check_server_actions(cls, body): action = list(body)[0] if action == 'reboot': assert list(body[action]) == ['type'] assert body[action]['type'] in ['HARD', 'SOFT'] elif action == 'resize': assert 'flavorRef' in body[action] elif action in cls.none_actions: assert body[action] is None elif action == 'addFixedIp': assert list(body[action]) == ['networkId'] elif action in ['removeFixedIp', 'removeFloatingIp']: assert list(body[action]) == ['address'] elif action == 'addFloatingIp': assert (list(body[action]) == ['address'] or sorted(list(body[action])) == ['address', 'fixed_address']) elif action == 'changePassword': assert list(body[action]) == ['adminPass'] elif action in cls.type_actions: assert list(body[action]) == ['type'] elif action == 'os-resetState': assert list(body[action]) == ['state'] elif action == 'resetNetwork': assert body[action] is None elif action in ['addSecurityGroup', 'removeSecurityGroup']: assert list(body[action]) == ['name'] elif action == 'trigger_crash_dump': assert body[action] is None else: return False return True def post_servers_1234_action(self, body, **kw): _headers = dict() _body = None resp = 202 assert len(body.keys()) == 1 action = list(body)[0] if self.check_server_actions(body): # NOTE(snikitin): No need to do any operations here. This 'pass' # is needed to avoid AssertionError in the last 'else' statement # if we found 'action' in method check_server_actions and # raise AssertionError if we didn't find 'action' at all. pass elif action == 'os-migrateLive': expected = set(['host', 'block_migration']) if self.api_version >= api_versions.APIVersion("2.30"): if 'force' in body[action].keys(): # force can be optional expected.add('force') if self.api_version < api_versions.APIVersion("2.25"): expected.add('disk_over_commit') assert set(body[action].keys()) == expected elif action == 'rebuild': body = body[action] adminPass = body.get('adminPass', 'randompassword') assert 'imageRef' in body _body = self.get_servers_1234()[2] _body['server']['adminPass'] = adminPass elif action == 'confirmResize': assert body[action] is None # This one method returns a different response code return (204, {}, None) elif action == 'rescue': if body[action]: keys = set(body[action].keys()) assert not (keys - set(['adminPass', 'rescue_image_ref'])) else: assert body[action] is None _body = {'adminPass': 'RescuePassword'} elif action == 'createImage': assert set(body[action].keys()) == set(['name', 'metadata']) if self.api_version < api_versions.APIVersion('2.45'): _headers = dict(location="http://blah/images/%s" % FAKE_IMAGE_UUID_SNAPSHOT) else: _body = {'image_id': FAKE_IMAGE_UUID_SNAPSHOT} if body[action]['name'] == 'mysnapshot_deleted': _headers = dict(location="http://blah/images/%s" % FAKE_IMAGE_UUID_SNAP_DEL) elif action == 'createBackup': assert set(body[action].keys()) == set(['name', 'backup_type', 'rotation']) if self.api_version < api_versions.APIVersion('2.45'): _headers = dict(location="http://blah/images/%s" % FAKE_IMAGE_UUID_BACKUP) else: _body = {'image_id': FAKE_IMAGE_UUID_BACKUP} elif action == 'os-getConsoleOutput': assert list(body[action]) == ['length'] return (202, {}, {'output': 'foo'}) elif action == 'evacuate': keys = list(body[action]) if 'adminPass' in keys: keys.remove('adminPass') if 'host' in keys: keys.remove('host') if 'onSharedStorage' in keys: keys.remove('onSharedStorage') if 'force' in keys: keys.remove('force') assert set(keys) == set() else: raise AssertionError("Unexpected server action: %s" % action) _headers.update(FAKE_RESPONSE_HEADERS) return (resp, _headers, _body) def post_servers_5678_action(self, body, **kw): return self.post_servers_1234_action(body, **kw) # # Cloudpipe # def get_os_cloudpipe(self, **kw): return ( 200, {}, {'cloudpipes': [{'project_id': 1}]} ) def post_os_cloudpipe(self, **ks): return ( 202, {}, {'instance_id': '9d5824aa-20e6-4b9f-b967-76a699fc51fd'} ) def put_os_cloudpipe_configure_project(self, **kw): return (202, {}, None) # # Flavors # def get_flavors(self, **kw): status, header, flavors = self.get_flavors_detail(**kw) for flavor in flavors['flavors']: for k in list(flavor): if k not in ['id', 'name']: del flavor[k] return (200, FAKE_RESPONSE_HEADERS, flavors) def get_flavors_detail(self, **kw): flavors = {'flavors': [ {'id': 1, 'name': '256 MB Server', 'ram': 256, 'disk': 10, 'OS-FLV-EXT-DATA:ephemeral': 10, 'os-flavor-access:is_public': True, 'links': {}}, {'id': 2, 'name': '512 MB Server', 'ram': 512, 'disk': 20, 'OS-FLV-EXT-DATA:ephemeral': 20, 'os-flavor-access:is_public': False, 'links': {}}, {'id': 4, 'name': '1024 MB Server', 'ram': 1024, 'disk': 10, 'OS-FLV-EXT-DATA:ephemeral': 10, 'os-flavor-access:is_public': True, 'links': {}}, {'id': 'aa1', 'name': '128 MB Server', 'ram': 128, 'disk': 0, 'OS-FLV-EXT-DATA:ephemeral': 0, 'os-flavor-access:is_public': True, 'links': {}} ]} if 'is_public' not in kw: filter_is_public = True else: if kw['is_public'].lower() == 'none': filter_is_public = None else: filter_is_public = strutils.bool_from_string(kw['is_public'], True) if filter_is_public is not None: if filter_is_public: flavors['flavors'] = [ v for v in flavors['flavors'] if v['os-flavor-access:is_public'] ] else: flavors['flavors'] = [ v for v in flavors['flavors'] if not v['os-flavor-access:is_public'] ] return (200, FAKE_RESPONSE_HEADERS, flavors) def get_flavors_1(self, **kw): return ( 200, FAKE_RESPONSE_HEADERS, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][0]} ) def get_flavors_2(self, **kw): return ( 200, {}, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][1]} ) def get_flavors_3(self, **kw): # Diablo has no ephemeral return ( 200, FAKE_RESPONSE_HEADERS, {'flavor': { 'id': 3, 'name': '256 MB Server', 'ram': 256, 'disk': 10, }}, ) def get_flavors_512_MB_Server(self, **kw): raise exceptions.NotFound('404') def get_flavors_128_MB_Server(self, **kw): raise exceptions.NotFound('404') def get_flavors_80645cf4_6ad3_410a_bbc8_6f3e1e291f51(self, **kw): raise exceptions.NotFound('404') def get_flavors_aa1(self, **kw): # Alphanumeric flavor id are allowed. return ( 200, FAKE_RESPONSE_HEADERS, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][3]} ) def get_flavors_4(self, **kw): return ( 200, {}, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][2]} ) def delete_flavors_flavordelete(self, **kw): return (202, FAKE_RESPONSE_HEADERS, None) def delete_flavors_2(self, **kw): return (202, FAKE_RESPONSE_HEADERS, None) def post_flavors(self, body, **kw): return ( 202, FAKE_RESPONSE_HEADERS, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][0]} ) def get_flavors_1_os_extra_specs(self, **kw): return ( 200, {}, {'extra_specs': {"k1": "v1"}}) def get_flavors_2_os_extra_specs(self, **kw): return ( 200, {}, {'extra_specs': {"k2": "v2"}}) def get_flavors_aa1_os_extra_specs(self, **kw): return ( 200, {}, {'extra_specs': {"k3": "v3"}}) def get_flavors_4_os_extra_specs(self, **kw): return ( 200, {}, {'extra_specs': {"k4": "v4"}}) def post_flavors_1_os_extra_specs(self, body, **kw): assert list(body) == ['extra_specs'] fakes.assert_has_keys(body['extra_specs'], required=['k1']) return ( 200, FAKE_RESPONSE_HEADERS, {'extra_specs': {"k1": "v1"}}) def post_flavors_4_os_extra_specs(self, body, **kw): assert list(body) == ['extra_specs'] return ( 200, FAKE_RESPONSE_HEADERS, body) def delete_flavors_1_os_extra_specs_k1(self, **kw): return (204, {}, None) # # Flavor access # def get_flavors_2_os_flavor_access(self, **kw): return ( 200, FAKE_RESPONSE_HEADERS, {'flavor_access': [{'flavor_id': '2', 'tenant_id': 'proj1'}, {'flavor_id': '2', 'tenant_id': 'proj2'}]}) def post_flavors_2_action(self, body, **kw): return (202, FAKE_RESPONSE_HEADERS, self.get_flavors_2_os_flavor_access()[2]) # # Images # def get_images(self, **kw): return (200, {}, {'images': [ { "id": FAKE_IMAGE_UUID_SNAPSHOT, "name": "My Server Backup", "serverId": '1234', "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "SAVING", "progress": 80, "links": {}, }, { "id": FAKE_IMAGE_UUID_SNAP_DEL, "name": "My Server Backup Deleted", "serverId": '1234', "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "DELETED", "fault": {'message': 'Image has been deleted.'}, "links": {}, }, { 'id': FAKE_IMAGE_UUID_1, 'name': 'CentOS 5.2', "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "ACTIVE", "test_key": "test_value", "links": {}, }, { "id": FAKE_IMAGE_UUID_2, "name": "My Server Backup", "serverId": '1234', "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "SAVING", "progress": 80, "links": {}, }, { "id": FAKE_IMAGE_UUID_BACKUP, "name": "back1", "serverId": '1234', "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "SAVING", "progress": 80, "links": {}, }, ]}) def get_images_555cae93_fb41_4145_9c52_f5b923538a26(self, **kw): return (200, {}, {'image': self.get_images()[2]['images'][0]}) def get_images_55bb23af_97a4_4068_bdf8_f10c62880ddf(self, **kw): return (200, {}, {'image': self.get_images()[2]['images'][1]}) def get_images_c99d7632_bd66_4be9_aed5_3dd14b223a76(self, **kw): return (200, {}, {'image': self.get_images()[2]['images'][2]}) def get_images_f27f479a_ddda_419a_9bbc_d6b56b210161(self, **kw): return (200, {}, {'image': self.get_images()[2]['images'][3]}) def get_images_2f87e889_41a4_4778_8553_83f5eea68c5d(self, **kw): return (200, {}, {'image': self.get_images()[2]['images'][4]}) def get_images_3e861307_73a6_4d1f_8d68_f68b03223032(self): raise exceptions.NotFound('404') # # Keypairs # def get_os_keypairs_test(self, *kw): return (200, {}, {'keypair': self.get_os_keypairs()[2]['keypairs'][0]['keypair']}) def get_os_keypairs(self, user_id=None, limit=None, marker=None, *kw): return (200, {}, { "keypairs": [{"keypair": { "public_key": "FAKE_SSH_RSA", "private_key": "FAKE_PRIVATE_KEY", "user_id": "81e373b596d6466e99c4896826abaa46", "name": "test", "deleted": False, "created_at": "2014-04-19T02:16:44.000000", "updated_at": "2014-04-19T10:12:3.000000", "figerprint": "FAKE_KEYPAIR", "deleted_at": None, "id": 4}} ]}) def delete_os_keypairs_test(self, **kw): return (202, {}, None) def post_os_keypairs(self, body, **kw): assert list(body) == ['keypair'] fakes.assert_has_keys(body['keypair'], required=['name']) r = {'keypair': self.get_os_keypairs()[2]['keypairs'][0]['keypair']} return (202, {}, r) # # Virtual Interfaces # def get_servers_1234_os_virtual_interfaces(self, **kw): return (200, {}, {"virtual_interfaces": [ {'id': 'fakeid', 'mac_address': 'fakemac'} ]}) # # Quotas # def get_os_quota_sets_tenant_id(self, **kw): return (200, {}, { 'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, **kw): return (200, {}, { 'quota_set': { 'tenant_id': '97f4c221bff44578b0300df4ef119353', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_97f4c221bff44578b0300df4ef119353_detail(self, **kw): return (200, {}, { 'quota_set': { 'tenant_id': '97f4c221bff44578b0300df4ef119353', 'cores': { 'in_use': 0, 'limit': 20, 'reserved': 0 }, 'fixed_ips': { 'in_use': 0, 'limit': -1, 'reserved': 0 }, 'floating_ips': { 'in_use': 0, 'limit': 10, 'reserved': 0 }, 'injected_file_content_bytes': { 'in_use': 0, 'limit': 10240, 'reserved': 0 }, 'injected_file_path_bytes': { 'in_use': 0, 'limit': 255, 'reserved': 0 }, 'injected_files': { 'in_use': 0, 'limit': 5, 'reserved': 0 }, 'instances': { 'in_use': 0, 'limit': 10, 'reserved': 0 }, 'key_pairs': { 'in_use': 0, 'limit': 100, 'reserved': 0 }, 'metadata_items': { 'in_use': 0, 'limit': 128, 'reserved': 0 }, 'ram': { 'in_use': 0, 'limit': 51200, 'reserved': 0 }, 'security_group_rules': { 'in_use': 0, 'limit': 20, 'reserved': 0 }, 'security_groups': { 'in_use': 0, 'limit': 10, 'reserved': 0 }, 'server_group_members': { 'in_use': 0, 'limit': 10, 'reserved': 0 }, 'server_groups': { 'in_use': 0, 'limit': 10, 'reserved': 0 } }}) def get_os_quota_sets_97f4c221bff44578b0300df4ef119353_defaults(self): return (200, {}, { 'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_tenant_id_defaults(self): return (200, {}, { 'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def put_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, body, **kw): assert list(body) == ['quota_set'] fakes.assert_has_keys(body['quota_set']) return (200, {}, { 'quota_set': { 'tenant_id': '97f4c221bff44578b0300df4ef119353', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def delete_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, **kw): return (202, {}, {}) # # Quota Classes # def get_os_quota_class_sets_test(self, **kw): if self.api_version >= api_versions.APIVersion('2.50'): return (200, FAKE_RESPONSE_HEADERS, { 'quota_class_set': { 'id': 'test', 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'server_groups': 1, 'server_group_members': 1}}) return (200, FAKE_RESPONSE_HEADERS, { 'quota_class_set': { 'id': 'test', 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'fixed_ips': -1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def put_os_quota_class_sets_test(self, body, **kw): assert list(body) == ['quota_class_set'] if self.api_version >= api_versions.APIVersion('2.50'): return (200, {}, { 'quota_class_set': { 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'server_groups': 1, 'server_group_members': 1}}) return (200, {}, { 'quota_class_set': { 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'fixed_ips': -1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def put_os_quota_class_sets_97f4c221bff44578b0300df4ef119353(self, body, **kw): assert list(body) == ['quota_class_set'] return (200, {}, { 'quota_class_set': { 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) # # Tenant Usage # def get_os_simple_tenant_usage(self, **kw): return (200, FAKE_RESPONSE_HEADERS, {six.u('tenant_usages'): [{ six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('instance_id'): six.u('f079e394-1111-457b-b350-bb5ecc685cdd'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}]}) def get_os_simple_tenant_usage_next(self, **kw): return (200, FAKE_RESPONSE_HEADERS, {six.u('tenant_usages'): [{ six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('instance_id'): six.u('f079e394-2222-457b-b350-bb5ecc685cdd'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}]}) def get_os_simple_tenant_usage_next_next(self, **kw): return (200, FAKE_RESPONSE_HEADERS, {six.u('tenant_usages'): []}) def get_os_simple_tenant_usage_tenantfoo(self, **kw): return (200, FAKE_RESPONSE_HEADERS, {six.u('tenant_usage'): { six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('instance_id'): six.u('f079e394-1111-457b-b350-bb5ecc685cdd'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}}) def get_os_simple_tenant_usage_test(self, **kw): return (200, {}, {six.u('tenant_usage'): { six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('instance_id'): six.u('f079e394-1111-457b-b350-bb5ecc685cdd'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}}) def get_os_simple_tenant_usage_tenant_id(self, **kw): return (200, {}, {six.u('tenant_usage'): { six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('instance_id'): six.u('f079e394-1111-457b-b350-bb5ecc685cdd'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}}) def get_os_simple_tenant_usage_tenant_id_next(self, **kw): return (200, {}, {six.u('tenant_usage'): { six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('instance_id'): six.u('f079e394-2222-457b-b350-bb5ecc685cdd'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}}) def get_os_simple_tenant_usage_tenant_id_next_next(self, **kw): return (200, {}, {six.u('tenant_usage'): {}}) # # Aggregates # def get_os_aggregates(self, *kw): response = (200, {}, {"aggregates": [ {'id': '1', 'name': 'test', 'availability_zone': 'nova1'}, {'id': '2', 'name': 'test2', 'availability_zone': 'nova1'}, {'id': '3', 'name': 'test3', 'metadata': {'test': "dup", "none_key": "Nine"}}, ]}) # microversion >= 2.41 returns the uuid in the response if self.api_version >= api_versions.APIVersion('2.41'): aggregates = response[2]['aggregates'] aggregates[0]['uuid'] = '80785864-087b-45a5-a433-b20eac9b58aa' aggregates[1]['uuid'] = '30827713-5957-4b68-8fd3-ccaddb568c24' aggregates[2]['uuid'] = '9a651b22-ce3f-4a87-acd7-98446ef591c4' return response def _return_aggregate(self): r = {'aggregate': self.get_os_aggregates()[2]['aggregates'][0]} return (200, {}, r) def _return_aggregate_3(self): r = {'aggregate': self.get_os_aggregates()[2]['aggregates'][2]} return (200, {}, r) def get_os_aggregates_1(self, **kw): return self._return_aggregate() def get_os_aggregates_3(self, **kw): return self._return_aggregate_3() def post_os_aggregates(self, body, **kw): return self._return_aggregate() def put_os_aggregates_1(self, body, **kw): return self._return_aggregate() def post_os_aggregates_1_action(self, body, **kw): return self._return_aggregate() def post_os_aggregates_3_action(self, body, **kw): return self._return_aggregate_3() def delete_os_aggregates_1(self, **kw): return (202, {}, None) # # Services # def get_os_services(self, **kw): host = kw.get('host', 'host1') binary = kw.get('binary', 'nova-compute') if self.api_version >= api_versions.APIVersion('2.53'): service_id_1 = FAKE_SERVICE_UUID_1 service_id_2 = FAKE_SERVICE_UUID_2 else: service_id_1 = 1 service_id_2 = 2 return (200, FAKE_RESPONSE_HEADERS, {'services': [{'binary': binary, 'host': host, 'zone': 'nova', 'status': 'enabled', 'state': 'up', 'updated_at': datetime.datetime( 2012, 10, 29, 13, 42, 2), 'id': service_id_1}, {'binary': binary, 'host': host, 'zone': 'nova', 'status': 'disabled', 'state': 'down', 'updated_at': datetime.datetime( 2012, 9, 18, 8, 3, 38), 'id': service_id_2}, ]}) def put_os_services_enable(self, body, **kw): return (200, FAKE_RESPONSE_HEADERS, {'service': {'host': body['host'], 'binary': body['binary'], 'status': 'enabled'}}) def put_os_services_disable(self, body, **kw): return (200, FAKE_RESPONSE_HEADERS, {'service': {'host': body['host'], 'binary': body['binary'], 'status': 'disabled'}}) def put_os_services_disable_log_reason(self, body, **kw): return (200, FAKE_RESPONSE_HEADERS, {'service': { 'host': body['host'], 'binary': body['binary'], 'status': 'disabled', 'disabled_reason': body['disabled_reason']}}) def put_os_services_75e9eabc_ed3b_4f11_8bba_add1e7e7e2de( self, body, **kw): """This should only be called with microversion >= 2.53.""" return (200, FAKE_RESPONSE_HEADERS, {'service': { 'host': 'host1', 'binary': 'nova-compute', 'status': body.get('status', 'enabled'), 'disabled_reason': body.get('disabled_reason'), 'forced_down': body.get('forced_down', False)}}) def delete_os_services_1(self, **kw): return (204, FAKE_RESPONSE_HEADERS, None) def delete_os_services_75e9eabc_ed3b_4f11_8bba_add1e7e7e2de(self, **kwarg): return (204, FAKE_RESPONSE_HEADERS, None) def put_os_services_force_down(self, body, **kw): return (200, FAKE_RESPONSE_HEADERS, {'service': { 'host': body['host'], 'binary': body['binary'], 'forced_down': False}}) # # Hosts # def get_os_hosts(self, **kw): zone = kw.get('zone', 'nova1') return (200, {}, {'hosts': [{'host': 'host1', 'service': 'nova-compute', 'zone': zone}, {'host': 'host1', 'service': 'nova-cert', 'zone': zone}]}) def put_os_hosts_sample_host_1(self, body, **kw): return (200, {}, {'host': 'sample-host_1', 'status': 'enabled'}) def put_os_hosts_sample_host_2(self, body, **kw): return (200, {}, {'host': 'sample-host_2', 'maintenance_mode': 'on_maintenance'}) def put_os_hosts_sample_host_3(self, body, **kw): return (200, {}, {'host': 'sample-host_3', 'status': 'enabled', 'maintenance_mode': 'on_maintenance'}) def get_os_hosts_sample_host_reboot(self, **kw): return (200, {}, {'host': 'sample_host', 'power_action': 'reboot'}) def get_os_hosts_sample_host_startup(self, **kw): return (200, {}, {'host': 'sample_host', 'power_action': 'startup'}) def get_os_hosts_sample_host_shutdown(self, **kw): return (200, {}, {'host': 'sample_host', 'power_action': 'shutdown'}) def get_os_hypervisors(self, **kw): return (200, {}, { "hypervisors": [ {'id': 1234, 'hypervisor_hostname': 'hyper1'}, {'id': 5678, 'hypervisor_hostname': 'hyper2'}]}) def get_os_hypervisors_statistics(self, **kw): return (200, {}, { "hypervisor_statistics": { 'count': 2, 'vcpus': 8, 'memory_mb': 20 * 1024, 'local_gb': 500, 'vcpus_used': 4, 'memory_mb_used': 10 * 1024, 'local_gb_used': 250, 'free_ram_mb': 10 * 1024, 'free_disk_gb': 250, 'current_workload': 4, 'running_vms': 4, 'disk_available_least': 200} }) def get_os_hypervisors_hyper1(self, **kw): return (200, {}, { 'hypervisor': {'id': 1234, 'service': {'id': 1, 'host': 'compute1'}, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': "xen", 'hypervisor_version': 3, 'hypervisor_hostname': "hyper1", 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100}}) def get_os_hypervisors_region_child_1(self, **kw): return (200, {}, { 'hypervisor': {'id': 'region!child@1', 'service': {'id': 1, 'host': 'compute1'}, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': "xen", 'hypervisor_version': 3, 'hypervisor_hostname': "hyper1", 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100}}) def get_os_hypervisors_hyper_search(self, **kw): return (200, {}, { 'hypervisors': [ {'id': 1234, 'hypervisor_hostname': 'hyper1'}, {'id': 5678, 'hypervisor_hostname': 'hyper2'}]}) def get_os_hypervisors_hyper_servers(self, **kw): return (200, {}, { 'hypervisors': [ {'id': 1234, 'hypervisor_hostname': 'hyper1', 'servers': [ {'name': 'inst1', 'uuid': 'uuid1'}, {'name': 'inst2', 'uuid': 'uuid2'}]}, {'id': 5678, 'hypervisor_hostname': 'hyper2', 'servers': [ {'name': 'inst3', 'uuid': 'uuid3'}, {'name': 'inst4', 'uuid': 'uuid4'}]}] }) def get_os_hypervisors_hyper_no_servers_servers(self, **kw): return (200, {}, {'hypervisors': [{'id': 1234, 'hypervisor_hostname': 'hyper1'}]}) def get_os_hypervisors_1234(self, **kw): return (200, {}, { 'hypervisor': {'id': 1234, 'service': {'id': 1, 'host': 'compute1'}, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': "xen", 'hypervisor_version': 3, 'hypervisor_hostname': "hyper1", 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100}}) def get_os_hypervisors_1234_uptime(self, **kw): return (200, {}, { 'hypervisor': {'id': 1234, 'hypervisor_hostname': "hyper1", 'uptime': "fake uptime"}}) def get_os_hypervisors_region_child_1_uptime(self, **kw): return (200, {}, { 'hypervisor': {'id': 'region!child@1', 'hypervisor_hostname': "hyper1", 'uptime': "fake uptime"}}) def get_v2_0_networks(self, **kw): """Return neutron proxied networks. We establish a few different possible networks that we can get by name, which we can then call in tests. The only usage of this API should be for name -> id translation, however a full valid neutron block is provided for the private network to see the kinds of things that will be in that payload. """ name = kw.get('name', "blank") networks_by_name = { 'private': [ {"status": "ACTIVE", "router:external": False, "availability_zone_hints": [], "availability_zones": ["nova"], "description": "", "name": "private", "subnets": ["64706c26-336c-4048-ab3c-23e3283bca2c", "18512740-c760-4d5f-921f-668105c9ee44"], "shared": False, "tenant_id": "abd42f270bca43ea80fe4a166bc3536c", "created_at": "2016-08-15T17:34:49", "tags": [], "ipv6_address_scope": None, "updated_at": "2016-08-15T17:34:49", "admin_state_up": True, "ipv4_address_scope": None, "port_security_enabled": True, "mtu": 1450, "id": "e43a56c7-11d4-45c9-8681-ddc8171b5850", "revision": 2}], 'duplicate': [ {"status": "ACTIVE", "id": "e43a56c7-11d4-45c9-8681-ddc8171b5850"}, {"status": "ACTIVE", "id": "f43a56c7-11d4-45c9-8681-ddc8171b5850"}], 'blank': [] } return (200, {}, {"networks": networks_by_name[name]}) def get_os_availability_zone_detail(self, **kw): return (200, {}, { "availabilityZoneInfo": [ {"zoneName": "zone-1", "zoneState": {"available": True}, "hosts": { "fake_host-1": { "nova-compute": { "active": True, "available": True, "updated_at": datetime.datetime( 2012, 12, 26, 14, 45, 25, 0)}}}}, {"zoneName": "internal", "zoneState": {"available": True}, "hosts": { "fake_host-1": { "nova-sched": { "active": True, "available": True, "updated_at": datetime.datetime( 2012, 12, 26, 14, 45, 25, 0)}}, "fake_host-2": { "nova-network": { "active": True, "available": False, "updated_at": datetime.datetime( 2012, 12, 26, 14, 45, 24, 0)}}}}, {"zoneName": "zone-2", "zoneState": {"available": False}, "hosts": None}]}) def get_servers_1234_os_interface(self, **kw): return (200, {}, { "interfaceAttachments": [ {"port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], }, {"port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], }] }) def post_servers_1234_os_interface(self, **kw): return (200, {}, {'interfaceAttachment': {}}) def delete_servers_1234_os_interface_port_id(self, **kw): return (200, {}, None) def post_servers_1234_os_volume_attachments(self, **kw): return (200, FAKE_RESPONSE_HEADERS, { "volumeAttachment": {"device": "/dev/vdb", "volumeId": 2}}) def put_servers_1234_os_volume_attachments_Work(self, **kw): return (200, FAKE_RESPONSE_HEADERS, {"volumeAttachment": {"volumeId": 2}}) def get_servers_1234_os_volume_attachments(self, **kw): return (200, FAKE_RESPONSE_HEADERS, { "volumeAttachments": [ {"display_name": "Work", "display_description": "volume for work", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [{"id": "3333", "links": ''}], "metadata": {}}]}) def get_servers_1234_os_volume_attachments_Work(self, **kw): return (200, FAKE_RESPONSE_HEADERS, { "volumeAttachment": {"display_name": "Work", "display_description": "volume for work", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [{"id": "3333", "links": ''}], "metadata": {}}}) def delete_servers_1234_os_volume_attachments_Work(self, **kw): return (200, FAKE_RESPONSE_HEADERS, {}) def get_servers_1234_os_instance_actions(self, **kw): return (200, FAKE_RESPONSE_HEADERS, { "instanceActions": [{"instance_uuid": "1234", "user_id": "b968c25e04ab405f9fe4e6ca54cce9a5", "start_time": "2013-03-25T13:45:09.000000", "request_id": "req-abcde12345", "action": "create", "message": None, "project_id": "04019601fe3648c0abd4f4abfb9e6106"}]}) def get_servers_1234_os_instance_actions_req_abcde12345(self, **kw): return (200, FAKE_RESPONSE_HEADERS, { "instanceAction": {"instance_uuid": "1234", "user_id": "b968c25e04ab405f9fe4e6ca54cce9a5", "start_time": "2013-03-25T13:45:09.000000", "request_id": "req-abcde12345", "action": "create", "message": None, "project_id": "04019601fe3648c0abd4f4abfb9e6106"}}) def post_servers_uuid1_action(self, **kw): return 202, {}, {} def post_servers_uuid2_action(self, **kw): return 202, {}, {} def post_servers_uuid3_action(self, **kw): return 202, {}, {} def post_servers_uuid4_action(self, **kw): return 202, {}, {} def get_os_cells_child_cell(self, **kw): cell = {'cell': { 'username': 'cell1_user', 'name': 'cell1', 'rpc_host': '10.0.1.10', 'info': { 'username': 'cell1_user', 'rpc_host': '10.0.1.10', 'type': 'child', 'name': 'cell1', 'rpc_port': 5673}, 'type': 'child', 'rpc_port': 5673, 'loaded': True }} return (200, FAKE_RESPONSE_HEADERS, cell) def get_os_cells_capacities(self, **kw): cell_capacities_response = {"cell": {"capacities": {"ram_free": { "units_by_mb": {"8192": 0, "512": 13, "4096": 1, "2048": 3, "16384": 0}, "total_mb": 7680}, "disk_free": { "units_by_mb": {"81920": 11, "20480": 46, "40960": 23, "163840": 5, "0": 0}, "total_mb": 1052672}}}} return (200, FAKE_RESPONSE_HEADERS, cell_capacities_response) def get_os_cells_child_cell_capacities(self, **kw): return self.get_os_cells_capacities() def get_os_migrations(self, **kw): migration1 = { "created_at": "2012-10-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": '1234', "instance_uuid": "instance_id_123", "new_instance_type_id": 2, "old_instance_type_id": 1, "source_compute": "compute1", "source_node": "node1", "status": "Done", "updated_at": "2012-10-29T13:42:02.000000" } migration2 = { "created_at": "2012-10-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": '1234', "instance_uuid": "instance_id_456", "new_instance_type_id": 2, "old_instance_type_id": 1, "source_compute": "compute1", "source_node": "node1", "status": "Done", "updated_at": "2013-11-50T13:42:02.000000" } if self.api_version >= api_versions.APIVersion("2.23"): migration1.update({"migration_type": "live-migration"}) migration2.update({"migration_type": "live-migration"}) migration_list = [] instance_uuid = kw.get('instance_uuid', None) if instance_uuid == migration1['instance_uuid']: migration_list.append(migration1) elif instance_uuid == migration2['instance_uuid']: migration_list.append(migration2) elif instance_uuid is None: migration_list.extend([migration1, migration2]) migrations = {'migrations': migration_list} return (200, FAKE_RESPONSE_HEADERS, migrations) # # Server Groups # def get_os_server_groups(self, **kw): server_groups = [ {"members": [], "metadata": {}, "id": "2cbd51f4-fafe-4cdb-801b-cf913a6f288b", "policies": [], "name": "ig1"}, {"members": [], "metadata": {}, "id": "4473bb03-4370-4bfb-80d3-dc8cffc47d94", "policies": ["anti-affinity"], "name": "ig2"}, {"members": [], "metadata": {"key": "value"}, "id": "31ab9bdb-55e1-4ac3-b094-97eeb1b65cc4", "policies": [], "name": "ig3"}, {"members": ["2dccb4a1-02b9-482a-aa23-5799490d6f5d"], "metadata": {}, "id": "4890bb03-7070-45fb-8453-d34556c87d94", "policies": ["anti-affinity"], "name": "ig2"}] other_project_server_groups = [ {"members": [], "metadata": {}, "id": "11111111-1111-1111-1111-111111111111", "policies": [], "name": "ig4"}, {"members": [], "metadata": {}, "id": "22222222-2222-2222-2222-222222222222", "policies": ["anti-affinity"], "name": "ig5"}, {"members": [], "metadata": {"key": "value"}, "id": "31ab9bdb-55e1-4ac3-b094-97eeb1b65cc4", "policies": [], "name": "ig6"}, {"members": ["33333333-3333-3333-3333-333333333333"], "metadata": {}, "id": "44444444-4444-4444-4444-444444444444", "policies": ["anti-affinity"], "name": "ig5"}] if kw.get("all_projects", False): server_groups.extend(other_project_server_groups) limit = int(kw.get("limit", 1000)) offset = int(kw.get("offset", 0)) server_groups = server_groups[offset:limit + 1] return (200, {}, {"server_groups": server_groups}) def _return_server_group(self): r = {'server_group': self.get_os_server_groups()[2]['server_groups'][0]} return (200, {}, r) def post_os_server_groups(self, body, **kw): return self._return_server_group() def post_servers_1234_migrations_1_action(self, body): return (202, {}, None) @api_versions.wraps(start_version="2.23") def get_servers_1234_migrations_1(self, **kw): migration = {"migration": { "created_at": "2016-01-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": 1, "server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe", "source_compute": "compute1", "source_node": "node1", "status": "running", "memory_total_bytes": 123456, "memory_processed_bytes": 12345, "memory_remaining_bytes": 120000, "disk_total_bytes": 234567, "disk_processed_bytes": 23456, "disk_remaining_bytes": 230000, "updated_at": "2016-01-29T13:42:02.000000" }} return (200, FAKE_RESPONSE_HEADERS, migration) @api_versions.wraps(start_version="2.23") def get_servers_1234_migrations(self, **kw): migrations = {'migrations': [ { "created_at": "2016-01-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": 1, "server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe", "source_compute": "compute1", "source_node": "node1", "status": "running", "memory_total_bytes": 123456, "memory_processed_bytes": 12345, "memory_remaining_bytes": 120000, "disk_total_bytes": 234567, "disk_processed_bytes": 23456, "disk_remaining_bytes": 230000, "updated_at": "2016-01-29T13:42:02.000000" }]} return (200, FAKE_RESPONSE_HEADERS, migrations) def delete_servers_1234_migrations_1(self): return (202, {}, None) def put_servers_1234_tags_tag(self, **kw): return (201, {}, None) def put_servers_1234_tags_tag1(self, **kw): return (201, {}, None) def put_servers_1234_tags_tag2(self, **kw): return (201, {}, None) def put_servers_1234_tags_tag3(self, **kw): return (201, {}, None) def put_servers_1234_tags(self, **kw): return (201, {}, None) def get_servers_1234_tags(self, **kw): return (200, {}, {'tags': ['tag1', 'tag2']}) def delete_servers_1234_tags_tag(self, **kw): return (204, {}, None) def delete_servers_1234_tags_tag1(self, **kw): return (204, {}, None) def delete_servers_1234_tags_tag2(self, **kw): return (204, {}, None) def delete_servers_1234_tags_tag3(self, **kw): return (204, {}, None) def delete_servers_1234_tags(self, **kw): return (204, {}, None) def post_os_assisted_volume_snapshots(self, **kw): return (202, FAKE_RESPONSE_HEADERS, {'snapshot': {'id': 'blah', 'volumeId': '1'}}) def delete_os_assisted_volume_snapshots_x(self, **kw): return (202, FAKE_RESPONSE_HEADERS, {}) def post_os_server_external_events(self, **kw): return (200, FAKE_RESPONSE_HEADERS, { 'events': [ {'name': 'test-event', 'status': 'completed', 'tag': 'tag', 'server_uuid': 'fake-uuid1'}, {'name': 'test-event', 'status': 'completed', 'tag': 'tag', 'server_uuid': 'fake-uuid2'}]}) python-novaclient-9.1.1/novaclient/tests/unit/v2/__init__.py0000664000175000017500000000000013165151077025250 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/unit/v2/test_instance_actions.py0000664000175000017500000000312413165151077030106 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class InstanceActionExtensionTests(utils.TestCase): def setUp(self): super(InstanceActionExtensionTests, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.1")) def test_list_instance_actions(self): server_uuid = '1234' ial = self.cs.instance_action.list(server_uuid) self.assert_request_id(ial, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', '/servers/%s/os-instance-actions' % server_uuid) def test_get_instance_action(self): server_uuid = '1234' request_id = 'req-abcde12345' ia = self.cs.instance_action.get(server_uuid, request_id) self.assert_request_id(ia, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', '/servers/%s/os-instance-actions/%s' % (server_uuid, request_id)) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_flavor_access.py0000664000175000017500000000531613165151077027401 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import flavor_access class FlavorAccessTest(utils.TestCase): def setUp(self): super(FlavorAccessTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.0")) def test_list_access_by_flavor_private(self): kwargs = {'flavor': self.cs.flavors.get(2)} r = self.cs.flavor_access.list(**kwargs) self.assert_request_id(r, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/2/os-flavor-access') for a in r: self.assertIsInstance(a, flavor_access.FlavorAccess) def test_add_tenant_access(self): flavor = self.cs.flavors.get(2) tenant = 'proj2' r = self.cs.flavor_access.add_tenant_access(flavor, tenant) self.assert_request_id(r, fakes.FAKE_REQUEST_ID_LIST) body = { "addTenantAccess": { "tenant": "proj2" } } self.cs.assert_called('POST', '/flavors/2/action', body) for a in r: self.assertIsInstance(a, flavor_access.FlavorAccess) def test_remove_tenant_access(self): flavor = self.cs.flavors.get(2) tenant = 'proj2' r = self.cs.flavor_access.remove_tenant_access(flavor, tenant) self.assert_request_id(r, fakes.FAKE_REQUEST_ID_LIST) body = { "removeTenantAccess": { "tenant": "proj2" } } self.cs.assert_called('POST', '/flavors/2/action', body) for a in r: self.assertIsInstance(a, flavor_access.FlavorAccess) def test_repr_flavor_access(self): flavor = self.cs.flavors.get(2) tenant = 'proj3' r = self.cs.flavor_access.add_tenant_access(flavor, tenant) def get_expected(flavor_access): return ("" % (flavor_access.flavor_id, flavor_access.tenant_id)) for a in r: self.assertEqual(get_expected(a), repr(a)) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_images.py0000664000175000017500000000262213165151077026031 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import images as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import images class ImagesTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.V1 @mock.patch('novaclient.base.Manager.alternate_service_type') def test_list_images(self, mock_alternate_service_type): il = self.cs.glance.list() self.assert_request_id(il, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/v2/images') for i in il: self.assertIsInstance(i, images.Image) self.assertEqual(2, len(il)) mock_alternate_service_type.assert_called_once_with( 'image', allowed_types=('image',)) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_usage.py0000664000175000017500000001033313165151077025666 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import six from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import usage class UsageTest(utils.TestCase): def setUp(self): super(UsageTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.0")) self.usage_type = self._get_usage_type() def _get_usage_type(self): return usage.Usage def test_usage_list(self, detailed=False): now = datetime.datetime.now() usages = self.cs.usage.list(now, now, detailed) self.assert_request_id(usages, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', "/os-simple-tenant-usage?" + ("start=%s&" % now.isoformat()) + ("end=%s&" % now.isoformat()) + ("detailed=%s" % int(bool(detailed)))) for u in usages: self.assertIsInstance(u, usage.Usage) def test_usage_list_detailed(self): self.test_usage_list(True) def test_usage_get(self): now = datetime.datetime.now() u = self.cs.usage.get("tenantfoo", now, now) self.assert_request_id(u, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', "/os-simple-tenant-usage/tenantfoo?" + ("start=%s&" % now.isoformat()) + ("end=%s" % now.isoformat())) self.assertIsInstance(u, usage.Usage) def test_usage_class_get(self): start = six.u('2012-01-22T19:48:41.750722') stop = six.u('2012-01-22T19:48:41.750722') info = {'tenant_id': 'tenantfoo', 'start': start, 'stop': stop} u = usage.Usage(self.cs.usage, info) u.get() self.assert_request_id(u, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', "/os-simple-tenant-usage/tenantfoo?start=%s&end=%s" % (start, stop)) class UsageV40Test(UsageTest): def setUp(self): super(UsageV40Test, self).setUp() self.cs.api_version = api_versions.APIVersion('2.40') def test_usage_list_with_paging(self): now = datetime.datetime.now() usages = self.cs.usage.list(now, now, marker='some-uuid', limit=3) self.assert_request_id(usages, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', '/os-simple-tenant-usage?' + ('start=%s&' % now.isoformat()) + ('end=%s&' % now.isoformat()) + ('limit=3&marker=some-uuid&detailed=0')) for u in usages: self.assertIsInstance(u, usage.Usage) def test_usage_list_detailed_with_paging(self): now = datetime.datetime.now() usages = self.cs.usage.list( now, now, detailed=True, marker='some-uuid', limit=3) self.assert_request_id(usages, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', '/os-simple-tenant-usage?' + ('start=%s&' % now.isoformat()) + ('end=%s&' % now.isoformat()) + ('limit=3&marker=some-uuid&detailed=1')) for u in usages: self.assertIsInstance(u, usage.Usage) def test_usage_get_with_paging(self): now = datetime.datetime.now() u = self.cs.usage.get( 'tenantfoo', now, now, marker='some-uuid', limit=3) self.assert_request_id(u, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', '/os-simple-tenant-usage/tenantfoo?' + ('start=%s&' % now.isoformat()) + ('end=%s&' % now.isoformat()) + ('limit=3&marker=some-uuid')) self.assertIsInstance(u, usage.Usage) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_hypervisors.py0000664000175000017500000002050213165151077027156 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import hypervisors as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class HypervisorsTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.V1 def compare_to_expected(self, expected, hyper): for key, value in expected.items(): self.assertEqual(getattr(hyper, key), value) def test_hypervisor_index(self): expected = [ dict(id=self.data_fixture.hyper_id_1, hypervisor_hostname='hyper1'), dict(id=self.data_fixture.hyper_id_2, hypervisor_hostname='hyper2')] result = self.cs.hypervisors.list(False) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-hypervisors') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_detail(self): expected = [ dict(id=self.data_fixture.hyper_id_1, service=dict(id=self.data_fixture.service_id_1, host='compute1'), vcpus=4, memory_mb=10 * 1024, local_gb=250, vcpus_used=2, memory_mb_used=5 * 1024, local_gb_used=125, hypervisor_type="xen", hypervisor_version=3, hypervisor_hostname="hyper1", free_ram_mb=5 * 1024, free_disk_gb=125, current_workload=2, running_vms=2, cpu_info='cpu_info', disk_available_least=100), dict(id=self.data_fixture.hyper_id_2, service=dict(id=self.data_fixture.service_id_2, host="compute2"), vcpus=4, memory_mb=10 * 1024, local_gb=250, vcpus_used=2, memory_mb_used=5 * 1024, local_gb_used=125, hypervisor_type="xen", hypervisor_version=3, hypervisor_hostname="hyper2", free_ram_mb=5 * 1024, free_disk_gb=125, current_workload=2, running_vms=2, cpu_info='cpu_info', disk_available_least=100)] result = self.cs.hypervisors.list() self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-hypervisors/detail') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_search(self): expected = [ dict(id=self.data_fixture.hyper_id_1, hypervisor_hostname='hyper1'), dict(id=self.data_fixture.hyper_id_2, hypervisor_hostname='hyper2')] result = self.cs.hypervisors.search('hyper') self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) if self.cs.api_version >= api_versions.APIVersion('2.53'): self.assert_called( 'GET', '/os-hypervisors?hypervisor_hostname_pattern=hyper') else: self.assert_called('GET', '/os-hypervisors/hyper/search') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_servers(self): expected = [ dict(id=self.data_fixture.hyper_id_1, hypervisor_hostname='hyper1', servers=[ dict(name='inst1', uuid='uuid1'), dict(name='inst2', uuid='uuid2')]), dict(id=self.data_fixture.hyper_id_2, hypervisor_hostname='hyper2', servers=[ dict(name='inst3', uuid='uuid3'), dict(name='inst4', uuid='uuid4')]), ] result = self.cs.hypervisors.search('hyper', True) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) if self.cs.api_version >= api_versions.APIVersion('2.53'): self.assert_called( 'GET', '/os-hypervisors?hypervisor_hostname_pattern=hyper&' 'with_servers=True') else: self.assert_called('GET', '/os-hypervisors/hyper/servers') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_get(self): expected = dict( id=self.data_fixture.hyper_id_1, service=dict(id=self.data_fixture.service_id_1, host='compute1'), vcpus=4, memory_mb=10 * 1024, local_gb=250, vcpus_used=2, memory_mb_used=5 * 1024, local_gb_used=125, hypervisor_type="xen", hypervisor_version=3, hypervisor_hostname="hyper1", free_ram_mb=5 * 1024, free_disk_gb=125, current_workload=2, running_vms=2, cpu_info='cpu_info', disk_available_least=100) result = self.cs.hypervisors.get(self.data_fixture.hyper_id_1) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'GET', '/os-hypervisors/%s' % self.data_fixture.hyper_id_1) self.compare_to_expected(expected, result) def test_hypervisor_uptime(self): expected = dict( id=self.data_fixture.hyper_id_1, hypervisor_hostname="hyper1", uptime="fake uptime") result = self.cs.hypervisors.uptime(self.data_fixture.hyper_id_1) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'GET', '/os-hypervisors/%s/uptime' % self.data_fixture.hyper_id_1) self.compare_to_expected(expected, result) def test_hypervisor_statistics(self): expected = dict( count=2, vcpus=8, memory_mb=20 * 1024, local_gb=500, vcpus_used=4, memory_mb_used=10 * 1024, local_gb_used=250, free_ram_mb=10 * 1024, free_disk_gb=250, current_workload=4, running_vms=4, disk_available_least=200, ) result = self.cs.hypervisors.statistics() self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-hypervisors/statistics') self.compare_to_expected(expected, result) def test_hypervisor_statistics_data_model(self): result = self.cs.hypervisor_stats.statistics() self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-hypervisors/statistics') # Test for Bug #1370415, the line below used to raise AttributeError self.assertEqual("", result.__repr__()) class HypervisorsV233Test(HypervisorsTest): def setUp(self): super(HypervisorsV233Test, self).setUp() self.cs.api_version = api_versions.APIVersion("2.33") def test_use_limit_marker_params(self): params = {'limit': '10', 'marker': 'fake-marker'} self.cs.hypervisors.list(**params) for k, v in params.items(): self.assertEqual([v], self.requests_mock.last_request.qs[k]) class HypervisorsV2_53Test(HypervisorsV233Test): """Tests the os-hypervisors 2.53 API bindings.""" data_fixture_class = data.V2_53 def setUp(self): super(HypervisorsV2_53Test, self).setUp() self.cs.api_version = api_versions.APIVersion("2.53") python-novaclient-9.1.1/novaclient/tests/unit/v2/testfile.txt0000664000175000017500000000000513165151077025524 0ustar jenkinsjenkins00000000000000BLAH python-novaclient-9.1.1/novaclient/tests/unit/v2/test_keypairs.py0000664000175000017500000001212413165151077026411 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import keypairs as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import keypairs class KeypairsTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.V1 def setUp(self): super(KeypairsTest, self).setUp() self.keypair_type = self._get_keypair_type() self.keypair_prefix = self._get_keypair_prefix() def _get_keypair_type(self): return keypairs.Keypair def _get_keypair_prefix(self): return keypairs.KeypairManager.keypair_prefix def test_get_keypair(self): kp = self.cs.keypairs.get('test') self.assert_request_id(kp, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/%s/test' % self.keypair_prefix) self.assertIsInstance(kp, keypairs.Keypair) self.assertEqual('test', kp.name) def test_list_keypairs(self): kps = self.cs.keypairs.list() self.assert_request_id(kps, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/%s' % self.keypair_prefix) for kp in kps: self.assertIsInstance(kp, keypairs.Keypair) def test_delete_keypair(self): kp = self.cs.keypairs.list()[0] ret = kp.delete() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/%s/test' % self.keypair_prefix) ret = self.cs.keypairs.delete('test') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/%s/test' % self.keypair_prefix) ret = self.cs.keypairs.delete(kp) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/%s/test' % self.keypair_prefix) class KeypairsV2TestCase(KeypairsTest): def setUp(self): super(KeypairsV2TestCase, self).setUp() self.cs.api_version = api_versions.APIVersion("2.0") def test_create_keypair(self): name = "foo" kp = self.cs.keypairs.create(name) self.assert_request_id(kp, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/%s' % self.keypair_prefix, body={'keypair': {'name': name}}) self.assertIsInstance(kp, keypairs.Keypair) def test_import_keypair(self): name = "foo" pub_key = "fake-public-key" kp = self.cs.keypairs.create(name, pub_key) self.assert_request_id(kp, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/%s' % self.keypair_prefix, body={'keypair': {'name': name, 'public_key': pub_key}}) self.assertIsInstance(kp, keypairs.Keypair) class KeypairsV22TestCase(KeypairsTest): def setUp(self): super(KeypairsV22TestCase, self).setUp() self.cs.api_version = api_versions.APIVersion("2.2") def test_create_keypair(self): name = "foo" key_type = "some_type" kp = self.cs.keypairs.create(name, key_type=key_type) self.assert_request_id(kp, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/%s' % self.keypair_prefix, body={'keypair': {'name': name, 'type': key_type}}) self.assertIsInstance(kp, keypairs.Keypair) def test_import_keypair(self): name = "foo" pub_key = "fake-public-key" kp = self.cs.keypairs.create(name, pub_key) self.assert_request_id(kp, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/%s' % self.keypair_prefix, body={'keypair': {'name': name, 'public_key': pub_key, 'type': 'ssh'}}) self.assertIsInstance(kp, keypairs.Keypair) class KeypairsV35TestCase(KeypairsTest): def setUp(self): super(KeypairsV35TestCase, self).setUp() self.cs.api_version = api_versions.APIVersion("2.35") def test_list_keypairs(self): kps = self.cs.keypairs.list(user_id='test_user', marker='test_kp', limit=3) self.assert_request_id(kps, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/%s?limit=3&marker=test_kp&user_id=test_user' % self.keypair_prefix) for kp in kps: self.assertIsInstance(kp, keypairs.Keypair) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_servers.py0000664000175000017500000017526613165151100026257 0ustar jenkinsjenkins00000000000000# -*- 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 base64 import os import tempfile import mock import six from novaclient import api_versions from novaclient import base from novaclient import exceptions from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import floatingips from novaclient.tests.unit.fixture_data import servers as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import servers class _FloatingIPManager(base.Manager): resource_class = base.Resource @api_versions.deprecated_after('2.35') def list(self): """DEPRECATED: List floating IPs""" return self._list("/os-floating-ips", "floating_ips") @api_versions.deprecated_after('2.35') def get(self, floating_ip): """DEPRECATED: Retrieve a floating IP""" return self._get("/os-floating-ips/%s" % base.getid(floating_ip), "floating_ip") class ServersTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.V1 api_version = None def setUp(self): super(ServersTest, self).setUp() self.useFixture(floatingips.FloatingFixture(self.requests_mock)) if self.api_version: self.cs.api_version = api_versions.APIVersion(self.api_version) self.floating_ips = _FloatingIPManager(self.cs) def _get_server_create_default_nics(self): """Callback for default nics kwarg when creating a server. """ return None def test_list_servers(self): sl = self.cs.servers.list() self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/detail') for s in sl: self.assertIsInstance(s, servers.Server) def test_filter_servers_unicode(self): sl = self.cs.servers.list(search_opts={'name': u't€sting'}) self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/detail?name=t%E2%82%ACsting') for s in sl: self.assertIsInstance(s, servers.Server) def test_list_all_servers(self): # use marker just to identify this call in fixtures sl = self.cs.servers.list(limit=-1, marker=1234) self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(2, len(sl)) self.assertEqual(self.requests_mock.request_history[-2].method, 'GET') self.assertEqual(self.requests_mock.request_history[-2].path_url, '/servers/detail?marker=1234') self.assert_called('GET', '/servers/detail?marker=5678') for s in sl: self.assertIsInstance(s, servers.Server) def test_list_servers_undetailed(self): sl = self.cs.servers.list(detailed=False) self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers') for s in sl: self.assertIsInstance(s, servers.Server) def test_list_servers_with_marker_limit(self): sl = self.cs.servers.list(marker=1234, limit=2) self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/detail?limit=2&marker=1234') for s in sl: self.assertIsInstance(s, servers.Server) def test_list_servers_sort_single(self): sl = self.cs.servers.list(sort_keys=['display_name'], sort_dirs=['asc']) self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'GET', '/servers/detail?sort_dir=asc&sort_key=display_name') for s in sl: self.assertIsInstance(s, servers.Server) def test_list_servers_sort_multiple(self): sl = self.cs.servers.list(sort_keys=['display_name', 'id'], sort_dirs=['asc', 'desc']) self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'GET', ('/servers/detail?sort_dir=asc&sort_dir=desc&' 'sort_key=display_name&sort_key=id')) for s in sl: self.assertIsInstance(s, servers.Server) def test_get_server_details(self): s = self.cs.servers.get(1234) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/1234') self.assertIsInstance(s, servers.Server) self.assertEqual(1234, s.id) self.assertEqual('BUILD', s.status) def test_get_server_promote_details(self): s1 = self.cs.servers.list(detailed=False)[0] s2 = self.cs.servers.list(detailed=True)[0] self.assertNotEqual(s1._info, s2._info) s1.get() self.assertEqual(s1._info, s2._info) def test_create_server(self): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, nics=self._get_server_create_default_nics() ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_boot_from_volume_with_nics(self): old_boot = self.cs.servers._boot nics = [{'net-id': '11111111-1111-1111-1111-111111111111', 'v4-fixed-ip': '10.0.0.7'}] bdm = {"volume_size": "1", "volume_id": "11111111-1111-1111-1111-111111111111", "delete_on_termination": "0", "device_name": "vda"} def wrapped_boot(url, key, *boot_args, **boot_kwargs): self.assertEqual(boot_kwargs['block_device_mapping'], bdm) self.assertEqual(boot_kwargs['nics'], nics) return old_boot(url, key, *boot_args, **boot_kwargs) @mock.patch.object(self.cs.servers, '_boot', wrapped_boot) def test_create_server_from_volume(): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", block_device_mapping=bdm, nics=nics ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-volumes_boot') self.assertIsInstance(s, servers.Server) test_create_server_from_volume() def test_create_server_boot_from_volume_bdm_v2(self): old_boot = self.cs.servers._boot bdm = [{"volume_size": "1", "volume_id": "11111111-1111-1111-1111-111111111111", "delete_on_termination": "0", "device_name": "vda"}] def wrapped_boot(url, key, *boot_args, **boot_kwargs): self.assertEqual(boot_kwargs['block_device_mapping_v2'], bdm) return old_boot(url, key, *boot_args, **boot_kwargs) with mock.patch.object(self.cs.servers, '_boot', wrapped_boot): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", block_device_mapping_v2=bdm, nics=self._get_server_create_default_nics() ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-volumes_boot') self.assertIsInstance(s, servers.Server) def test_create_server_boot_with_nics_ipv6(self): old_boot = self.cs.servers._boot nics = [{'net-id': '11111111-1111-1111-1111-111111111111', 'v6-fixed-ip': '2001:db9:0:1::10'}] def wrapped_boot(url, key, *boot_args, **boot_kwargs): self.assertEqual(boot_kwargs['nics'], nics) return old_boot(url, key, *boot_args, **boot_kwargs) with mock.patch.object(self.cs.servers, '_boot', wrapped_boot): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", nics=nics ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_boot_with_address(self): old_boot = self.cs.servers._boot access_ip_v6 = '::1' access_ip_v4 = '10.10.10.10' def wrapped_boot(url, key, *boot_args, **boot_kwargs): self.assertEqual(boot_kwargs['access_ip_v6'], access_ip_v6) self.assertEqual(boot_kwargs['access_ip_v4'], access_ip_v4) return old_boot(url, key, *boot_args, **boot_kwargs) with mock.patch.object(self.cs.servers, '_boot', wrapped_boot): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", access_ip_v6=access_ip_v6, access_ip_v4=access_ip_v4, nics=self._get_server_create_default_nics() ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_file_object(self): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata=six.StringIO('hello moto'), files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, nics=self._get_server_create_default_nics(), ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_unicode(self): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata=six.u('こんにちは'), key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, nics=self._get_server_create_default_nics(), ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_utf8(self): s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata='こんにちは', key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, nics=self._get_server_create_default_nics(), ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_admin_pass(self): test_password = "test-pass" test_key = "fakekey" s = self.cs.servers.create( name="My server", image=1, flavor=1, admin_pass=test_password, key_name=test_key, nics=self._get_server_create_default_nics() ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) body = self.requests_mock.last_request.json() self.assertEqual(test_password, body['server']['adminPass']) def test_create_server_userdata_bin(self): with tempfile.TemporaryFile(mode='wb+') as bin_file: original_data = os.urandom(1024) bin_file.write(original_data) bin_file.flush() bin_file.seek(0) s = self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata=bin_file, key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, nics=self._get_server_create_default_nics(), ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) # verify userdata matches original body = self.requests_mock.last_request.json() transferred_data = body['server']['user_data'] transferred_data = base64.b64decode(transferred_data) self.assertEqual(original_data, transferred_data) def _create_disk_config(self, disk_config): s = self.cs.servers.create( name="My server", image=1, flavor=1, disk_config=disk_config, nics=self._get_server_create_default_nics(), ) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) # verify disk config param was used in the request: server = self.requests_mock.last_request.json()['server'] self.assertIn('OS-DCF:diskConfig', server) self.assertEqual(disk_config, server['OS-DCF:diskConfig']) def test_create_server_disk_config_auto(self): self._create_disk_config('AUTO') def test_create_server_disk_config_manual(self): self._create_disk_config('MANUAL') def test_update_server(self): s = self.cs.servers.get(1234) # Update via instance s.update(name='hi') self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/servers/1234') s.update(name='hi') self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/servers/1234') # Silly, but not an error s.update() # Update via manager self.cs.servers.update(s, name='hi') self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/servers/1234') def test_delete_server(self): s = self.cs.servers.get(1234) ret = s.delete() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234') ret = self.cs.servers.delete(1234) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234') ret = self.cs.servers.delete(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234') def test_delete_server_meta(self): ret = self.cs.servers.delete_meta(1234, ['test_key']) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234/metadata/test_key') def test_set_server_meta(self): m = self.cs.servers.set_meta(1234, {'test_key': 'test_value'}) self.assert_request_id(m, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/metadata', {'metadata': {'test_key': 'test_value'}}) def test_set_server_meta_item(self): m = self.cs.servers.set_meta_item(1234, 'test_key', 'test_value') self.assert_request_id(m, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/servers/1234/metadata/test_key', {'meta': {'test_key': 'test_value'}}) def test_find(self): server = self.cs.servers.find(name='sample-server') self.assert_request_id(server, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/1234') self.assertEqual('sample-server', server.name) self.assertRaises(exceptions.NoUniqueMatch, self.cs.servers.find, flavor={"id": 1, "name": "256 MB Server"}) sl = self.cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"}) self.assert_request_id(sl, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual([1234, 5678, 9012], [s.id for s in sl]) def test_reboot_server(self): s = self.cs.servers.get(1234) ret = s.reboot() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.reboot(s, reboot_type='HARD') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_rebuild_server(self): s = self.cs.servers.get(1234) ret = s.rebuild(image=1) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.rebuild(s, image=1) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = s.rebuild(image=1, password='5678') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.rebuild(s, image=1, password='5678') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def _rebuild_resize_disk_config(self, disk_config, operation="rebuild"): s = self.cs.servers.get(1234) if operation == "rebuild": ret = s.rebuild(image=1, disk_config=disk_config) elif operation == "resize": ret = s.resize(flavor=1, disk_config=disk_config) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') # verify disk config param was used in the request: d = self.requests_mock.last_request.json()[operation] self.assertIn('OS-DCF:diskConfig', d) self.assertEqual(disk_config, d['OS-DCF:diskConfig']) def test_rebuild_server_disk_config_auto(self): self._rebuild_resize_disk_config('AUTO') def test_rebuild_server_disk_config_manual(self): self._rebuild_resize_disk_config('MANUAL') def test_rebuild_server_preserve_ephemeral(self): s = self.cs.servers.get(1234) ret = s.rebuild(image=1, preserve_ephemeral=True) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') d = self.requests_mock.last_request.json()['rebuild'] self.assertIn('preserve_ephemeral', d) self.assertTrue(d['preserve_ephemeral']) def test_rebuild_server_name_meta_files(self): files = {'/etc/passwd': 'some data'} s = self.cs.servers.get(1234) ret = s.rebuild(image=1, name='new', meta={'foo': 'bar'}, files=files) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) d = self.requests_mock.last_request.json()['rebuild'] self.assertEqual('new', d['name']) self.assertEqual({'foo': 'bar'}, d['metadata']) self.assertEqual('/etc/passwd', d['personality'][0]['path']) def test_resize_server(self): s = self.cs.servers.get(1234) ret = s.resize(flavor=1) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') self.cs.servers.resize(s, flavor=1) self.assert_called('POST', '/servers/1234/action') def test_resize_server_disk_config_auto(self): self._rebuild_resize_disk_config('AUTO', 'resize') def test_resize_server_disk_config_manual(self): self._rebuild_resize_disk_config('MANUAL', 'resize') def test_confirm_resized_server(self): s = self.cs.servers.get(1234) ret = s.confirm_resize() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') self.cs.servers.confirm_resize(s) self.assert_called('POST', '/servers/1234/action') def test_revert_resized_server(self): s = self.cs.servers.get(1234) ret = s.revert_resize() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.revert_resize(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_migrate_server(self): s = self.cs.servers.get(1234) ret = s.migrate() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.migrate(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') @mock.patch('warnings.warn') def test_add_fixed_ip(self, mock_warn): s = self.cs.servers.get(1234) fip = s.add_fixed_ip(1) mock_warn.assert_called_once() self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') fip = self.cs.servers.add_fixed_ip(s, 1) self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') @mock.patch('warnings.warn') def test_remove_fixed_ip(self, mock_warn): s = self.cs.servers.get(1234) ret = s.remove_fixed_ip('10.0.0.1') mock_warn.assert_called_once() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.remove_fixed_ip(s, '10.0.0.1') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') @mock.patch('warnings.warn') def test_add_floating_ip(self, mock_warn): s = self.cs.servers.get(1234) fip = s.add_floating_ip('11.0.0.1') mock_warn.assert_called_once() self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') fip = self.cs.servers.add_floating_ip(s, '11.0.0.1') self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') f = self.floating_ips.list()[0] fip = self.cs.servers.add_floating_ip(s, f) self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') fip = s.add_floating_ip(f) self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_add_floating_ip_to_fixed(self): s = self.cs.servers.get(1234) fip = s.add_floating_ip('11.0.0.1', fixed_address='12.0.0.1') self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') fip = self.cs.servers.add_floating_ip(s, '11.0.0.1', fixed_address='12.0.0.1') self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') f = self.floating_ips.list()[0] fip = self.cs.servers.add_floating_ip(s, f) self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') fip = s.add_floating_ip(f) self.assert_request_id(fip, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') @mock.patch('warnings.warn') def test_remove_floating_ip(self, mock_warn): s = self.cs.servers.get(1234) ret = s.remove_floating_ip('11.0.0.1') mock_warn.assert_called_once() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.remove_floating_ip(s, '11.0.0.1') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') f = self.floating_ips.list()[0] ret = self.cs.servers.remove_floating_ip(s, f) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = s.remove_floating_ip(f) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_stop(self): s = self.cs.servers.get(1234) ret = s.stop() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.stop(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_force_delete(self): s = self.cs.servers.get(1234) ret = s.force_delete() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.force_delete(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_restore(self): s = self.cs.servers.get(1234) ret = s.restore() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.restore(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_start(self): s = self.cs.servers.get(1234) ret = s.start() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.start(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_rescue(self): s = self.cs.servers.get(1234) ret = s.rescue() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.rescue(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_rescue_password(self): s = self.cs.servers.get(1234) ret = s.rescue(password='asdf') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'rescue': {'adminPass': 'asdf'}}) ret = self.cs.servers.rescue(s, password='asdf') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'rescue': {'adminPass': 'asdf'}}) def test_rescue_image(self): s = self.cs.servers.get(1234) ret = s.rescue(image=1) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'rescue': {'rescue_image_ref': 1}}) ret = self.cs.servers.rescue(s, image=1) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'rescue': {'rescue_image_ref': 1}}) def test_unrescue(self): s = self.cs.servers.get(1234) ret = s.unrescue() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.unrescue(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_lock(self): s = self.cs.servers.get(1234) ret = s.lock() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.lock(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_unlock(self): s = self.cs.servers.get(1234) ret = s.unlock() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.unlock(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_backup(self): s = self.cs.servers.get(1234) sb = s.backup('back1', 'daily', 1) self.assert_request_id(sb, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') sb = self.cs.servers.backup(s, 'back1', 'daily', 2) self.assert_request_id(sb, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_get_console_output_without_length(self): success = 'foo' s = self.cs.servers.get(1234) co = s.get_console_output() self.assert_request_id(co, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(success, s.get_console_output()) self.assert_called('POST', '/servers/1234/action') co = self.cs.servers.get_console_output(s) self.assert_request_id(co, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(success, self.cs.servers.get_console_output(s)) self.assert_called('POST', '/servers/1234/action') def test_get_console_output_with_length(self): success = 'foo' s = self.cs.servers.get(1234) co = s.get_console_output(length=50) self.assert_request_id(co, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(success, s.get_console_output(length=50)) self.assert_called('POST', '/servers/1234/action') co = self.cs.servers.get_console_output(s, length=50) self.assert_request_id(co, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(success, self.cs.servers.get_console_output(s, length=50)) self.assert_called('POST', '/servers/1234/action') # Testing password methods with the following password and key # # Clear password: FooBar123 # # RSA Private Key: novaclient/tests/unit/idfake.pem # # Encrypted password # OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r # qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho # QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw # /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N # tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk # Hi/fmZZNQQqj1Ijq0caOIw== def test_get_password(self): s = self.cs.servers.get(1234) password = s.get_password('novaclient/tests/unit/idfake.pem') self.assert_request_id(password, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(b'FooBar123', password) self.assert_called('GET', '/servers/1234/os-server-password') def test_get_password_without_key(self): s = self.cs.servers.get(1234) password = s.get_password() self.assert_request_id(password, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual( 'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r' 'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho' 'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw' '/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N' 'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk' 'Hi/fmZZNQQqj1Ijq0caOIw==', password) self.assert_called('GET', '/servers/1234/os-server-password') def test_clear_password(self): s = self.cs.servers.get(1234) ret = s.clear_password() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234/os-server-password') def test_get_server_diagnostics(self): s = self.cs.servers.get(1234) diagnostics = s.diagnostics() self.assert_request_id(diagnostics, fakes.FAKE_REQUEST_ID_LIST) self.assertIsNotNone(diagnostics) self.assert_called('GET', '/servers/1234/diagnostics') diagnostics_from_manager = self.cs.servers.diagnostics(1234) self.assert_request_id(diagnostics_from_manager, fakes.FAKE_REQUEST_ID_LIST) self.assertIsNotNone(diagnostics_from_manager) self.assert_called('GET', '/servers/1234/diagnostics') self.assertEqual(diagnostics[1], diagnostics_from_manager[1]) def test_get_vnc_console(self): s = self.cs.servers.get(1234) vc = s.get_vnc_console('novnc') self.assert_request_id(vc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') vc = self.cs.servers.get_vnc_console(s, 'novnc') self.assert_request_id(vc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_get_spice_console(self): s = self.cs.servers.get(1234) sc = s.get_spice_console('spice-html5') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') sc = self.cs.servers.get_spice_console(s, 'spice-html5') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_get_serial_console(self): s = self.cs.servers.get(1234) sc = s.get_serial_console('serial') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') sc = self.cs.servers.get_serial_console(s, 'serial') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_get_rdp_console(self): s = self.cs.servers.get(1234) rc = s.get_rdp_console('rdp-html5') self.assert_request_id(rc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') rc = self.cs.servers.get_rdp_console(s, 'rdp-html5') self.assert_request_id(rc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_get_console_url(self): s = self.cs.servers.get(1234) rc = s.get_console_url('novnc') self.assert_request_id(rc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') rc = self.cs.servers.get_console_url(s, 'novnc') self.assert_request_id(rc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') # test the case with invalid console type self.assertRaises(exceptions.UnsupportedConsoleType, s.get_console_url, 'invalid') def test_create_image(self): s = self.cs.servers.get(1234) im = s.create_image('123') self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') im = s.create_image('123', {}) self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') im = self.cs.servers.create_image(s, '123') self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') im = self.cs.servers.create_image(s, '123', {}) self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_live_migrate_server(self): s = self.cs.servers.get(1234) ret = s.live_migrate(host='hostname', block_migration=False, disk_over_commit=False) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': False, 'disk_over_commit': False}}) ret = self.cs.servers.live_migrate(s, host='hostname', block_migration=False, disk_over_commit=False) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': False, 'disk_over_commit': False}}) def test_live_migrate_server_block_migration_none(self): s = self.cs.servers.get(1234) ret = s.live_migrate(host='hostname', block_migration=None, disk_over_commit=None) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': False, 'disk_over_commit': False}}) def test_reset_state(self): s = self.cs.servers.get(1234) ret = s.reset_state('newstate') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.reset_state(s, 'newstate') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_reset_network(self): s = self.cs.servers.get(1234) ret = s.reset_network() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.reset_network(s) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_add_security_group(self): s = self.cs.servers.get(1234) sg = s.add_security_group('newsg') self.assert_request_id(sg, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') sg = self.cs.servers.add_security_group(s, 'newsg') self.assert_request_id(sg, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_remove_security_group(self): s = self.cs.servers.get(1234) ret = s.remove_security_group('oldsg') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.remove_security_group(s, 'oldsg') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_list_security_group(self): s = self.cs.servers.get(1234) sgs = s.list_security_group() self.assert_request_id(sgs, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/1234/os-security-groups') def test_evacuate(self): s = self.cs.servers.get(1234) ret = s.evacuate('fake_target_host', 'True') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') ret = self.cs.servers.evacuate(s, 'fake_target_host', 'False', 'NewAdminPassword') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_interface_list(self): s = self.cs.servers.get(1234) il = s.interface_list() self.assert_request_id(il, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/1234/os-interface') def test_interface_list_result_string_representable(self): """Test for bugs.launchpad.net/python-novaclient/+bug/1280453.""" # According to https://github.com/openstack/nova/blob/master/ # nova/api/openstack/compute/contrib/attach_interfaces.py#L33, # the attach_interface extension get method will return a json # object partly like this: interface_list = [{ 'net_id': 'd7745cf5-63f9-4883-b0ae-983f061e4f23', 'port_id': 'f35079da-36d5-4513-8ec1-0298d703f70e', 'mac_addr': 'fa:16:3e:4c:37:c8', 'port_state': 'ACTIVE', 'fixed_ips': [ { 'subnet_id': 'f1ad93ad-2967-46ba-b403-e8cbbe65f7fa', 'ip_address': '10.2.0.96' }] }] # If server is not string representable, it will raise an exception, # because attribute named 'name' cannot be found. # Parameter 'loaded' must be True or it will try to get attribute # 'id' then fails (lazy load detail), this is exactly same as # novaclient.base.Manager._list() s = servers.Server(servers.ServerManager, interface_list[0], loaded=True) # Trigger the __repr__ magic method self.assertEqual('', '%r' % s) def test_interface_attach(self): s = self.cs.servers.get(1234) ret = s.interface_attach(None, None, None) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/os-interface') def test_interface_detach(self): s = self.cs.servers.get(1234) ret = s.interface_detach('port-id') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234/os-interface/port-id') def test_create_server_with_description(self): self.assertRaises(exceptions.UnsupportedAttribute, self.cs.servers.create, name="My server", description="descr", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey" ) def test_create_server_with_nics_auto(self): """Negative test for specifying nics='auto' before 2.37 """ self.assertRaises(ValueError, self.cs.servers.create, name='test', image='d9d8d53c-4b4a-4144-a5e5-b30d9f1fe46a', flavor='1', nics='auto') class ServersV26Test(ServersTest): api_version = "2.6" def test_get_vnc_console(self): s = self.cs.servers.get(1234) vc = s.get_vnc_console('novnc') self.assert_request_id(vc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') vc = self.cs.servers.get_vnc_console(s, 'novnc') self.assert_request_id(vc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') # test the case with invalid console type self.assertRaises(exceptions.UnsupportedConsoleType, s.get_vnc_console, 'invalid') def test_get_spice_console(self): s = self.cs.servers.get(1234) sc = s.get_spice_console('spice-html5') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') sc = self.cs.servers.get_spice_console(s, 'spice-html5') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') # test the case with invalid console type self.assertRaises(exceptions.UnsupportedConsoleType, s.get_spice_console, 'invalid') def test_get_serial_console(self): s = self.cs.servers.get(1234) sc = s.get_serial_console('serial') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') sc = self.cs.servers.get_serial_console(s, 'serial') self.assert_request_id(sc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') # test the case with invalid console type self.assertRaises(exceptions.UnsupportedConsoleType, s.get_serial_console, 'invalid') def test_get_rdp_console(self): s = self.cs.servers.get(1234) rc = s.get_rdp_console('rdp-html5') self.assert_request_id(rc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') rc = self.cs.servers.get_rdp_console(s, 'rdp-html5') self.assert_request_id(rc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') # test the case with invalid console type self.assertRaises(exceptions.UnsupportedConsoleType, s.get_rdp_console, 'invalid') def test_get_console_url(self): s = self.cs.servers.get(1234) vc = s.get_console_url('novnc') self.assert_request_id(vc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') vc = self.cs.servers.get_console_url(s, 'novnc') self.assert_request_id(vc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') # test the case with invalid console type self.assertRaises(exceptions.UnsupportedConsoleType, s.get_console_url, 'invalid') # console type webmks is supported since api version 2.8 self.assertRaises(exceptions.UnsupportedConsoleType, s.get_console_url, 'webmks') class ServersV28Test(ServersV26Test): api_version = "2.8" def test_get_mks_console(self): s = self.cs.servers.get(1234) mksc = s.get_mks_console() self.assert_request_id(mksc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') mksc = self.cs.servers.get_mks_console(s) self.assert_request_id(mksc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') def test_get_console_url(self): s = self.cs.servers.get(1234) mksc = s.get_console_url('novnc') self.assert_request_id(mksc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') mksc = self.cs.servers.get_console_url(s, 'novnc') self.assert_request_id(mksc, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/remote-consoles') # test the case with invalid console type self.assertRaises(exceptions.UnsupportedConsoleType, s.get_console_url, 'invalid') class ServersV214Test(ServersV28Test): api_version = "2.14" def test_evacuate(self): s = self.cs.servers.get(1234) s.evacuate('fake_target_host') self.assert_called('POST', '/servers/1234/action') self.cs.servers.evacuate(s, 'fake_target_host', password='NewAdminPassword') self.assert_called('POST', '/servers/1234/action') class ServersV217Test(ServersV214Test): api_version = "2.17" def test_trigger_crash_dump(self): s = self.cs.servers.get(1234) s.trigger_crash_dump() self.assert_called('POST', '/servers/1234/action') self.cs.servers.trigger_crash_dump(s) self.assert_called('POST', '/servers/1234/action') class ServersV219Test(ServersV217Test): api_version = "2.19" def test_create_server_with_description(self): self.cs.servers.create( name="My server", description="descr", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", nics=self._get_server_create_default_nics() ) self.assert_called('POST', '/servers') def test_update_server_with_description(self): s = self.cs.servers.get(1234) s.update(description='hi') s.update(name='hi', description='hi') self.assert_called('PUT', '/servers/1234') def test_rebuild_with_description(self): s = self.cs.servers.get(1234) ret = s.rebuild(image="1", description="descr") self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') class ServersV225Test(ServersV219Test): api_version = "2.25" def test_live_migrate_server(self): s = self.cs.servers.get(1234) ret = s.live_migrate(host='hostname', block_migration='auto') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto'}}) ret = self.cs.servers.live_migrate(s, host='hostname', block_migration='auto') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto'}}) def test_live_migrate_server_block_migration_true(self): s = self.cs.servers.get(1234) ret = s.live_migrate(host='hostname', block_migration=True) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': True}}) ret = self.cs.servers.live_migrate(s, host='hostname', block_migration=True) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': True}}) def test_live_migrate_server_block_migration_none(self): s = self.cs.servers.get(1234) ret = s.live_migrate(host='hostname', block_migration=None) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto'}}) ret = self.cs.servers.live_migrate(s, host='hostname', block_migration='auto') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto'}}) class ServersV226Test(ServersV225Test): api_version = "2.26" def test_tag_list(self): s = self.cs.servers.get(1234) ret = s.tag_list() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/1234/tags') def test_tag_delete(self): s = self.cs.servers.get(1234) ret = s.delete_tag('tag') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234/tags/tag') def test_tag_delete_all(self): s = self.cs.servers.get(1234) ret = s.delete_all_tags() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/servers/1234/tags') def test_tag_add(self): s = self.cs.servers.get(1234) ret = s.add_tag('tag') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/servers/1234/tags/tag') def test_tags_set(self): s = self.cs.servers.get(1234) ret = s.set_tags(['tag1', 'tag2']) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/servers/1234/tags') class ServersV229Test(ServersV226Test): api_version = "2.29" def test_evacuate(self): s = self.cs.servers.get(1234) s.evacuate('fake_target_host') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'fake_target_host'}}) self.cs.servers.evacuate(s, 'fake_target_host', force=True) self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'fake_target_host', 'force': True}}) class ServersV230Test(ServersV229Test): api_version = "2.30" def test_live_migrate_server(self): s = self.cs.servers.get(1234) ret = s.live_migrate(host='hostname', block_migration='auto') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto'}}) ret = self.cs.servers.live_migrate(s, host='hostname', block_migration='auto', force=True) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto', 'force': True}}) class ServersV232Test(ServersV226Test): api_version = "2.32" def test_create_server_boot_with_tagged_nics(self): nics = [{'net-id': '11111111-1111-1111-1111-111111111111', 'tag': 'one'}, {'net-id': '22222222-2222-2222-2222-222222222222', 'tag': 'two'}] self.cs.servers.create(name="Server with tagged nics", image=1, flavor=1, nics=nics) self.assert_called('POST', '/servers') def test_create_server_boot_with_tagged_nics_pre232(self): self.cs.api_version = api_versions.APIVersion("2.31") nics = [{'net-id': '11111111-1111-1111-1111-111111111111', 'tag': 'one'}, {'net-id': '22222222-2222-2222-2222-222222222222', 'tag': 'two'}] self.assertRaises(ValueError, self.cs.servers.create, name="Server with tagged nics", image=1, flavor=1, nics=nics) def test_create_server_boot_from_volume_tagged_bdm_v2(self): bdm = [{"volume_size": "1", "volume_id": "11111111-1111-1111-1111-111111111111", "delete_on_termination": "0", "device_name": "vda", "tag": "foo"}] s = self.cs.servers.create(name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", block_device_mapping_v2=bdm) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-volumes_boot') def test_create_server_boot_from_volume_tagged_bdm_v2_pre232(self): self.cs.api_version = api_versions.APIVersion("2.31") bdm = [{"volume_size": "1", "volume_id": "11111111-1111-1111-1111-111111111111", "delete_on_termination": "0", "device_name": "vda", "tag": "foo"}] self.assertRaises(ValueError, self.cs.servers.create, name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", block_device_mapping_v2=bdm) class ServersV2_37Test(ServersV226Test): api_version = "2.37" def _get_server_create_default_nics(self): return 'auto' def test_create_server_no_nics(self): """Tests that nics are required in microversion 2.37+ """ self.assertRaises(ValueError, self.cs.servers.create, name='test', image='d9d8d53c-4b4a-4144-a5e5-b30d9f1fe46a', flavor='1') def test_create_server_with_nics_auto(self): s = self.cs.servers.create( name='test', image='d9d8d53c-4b4a-4144-a5e5-b30d9f1fe46a', flavor='1', nics=self._get_server_create_default_nics()) self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_add_floating_ip(self): # self.floating_ips.list() is not available after 2.35 pass def test_add_floating_ip_to_fixed(self): # self.floating_ips.list() is not available after 2.35 pass def test_remove_floating_ip(self): # self.floating_ips.list() is not available after 2.35 pass class ServersCreateImageBackupV2_45Test(utils.FixturedTestCase): """Tests the 2.45 microversion for createImage and createBackup server actions. """ client_fixture_class = client.V1 data_fixture_class = data.V1 api_version = '2.45' def setUp(self): super(ServersCreateImageBackupV2_45Test, self).setUp() self.cs.api_version = api_versions.APIVersion(self.api_version) def test_create_image(self): """Tests the createImage API with the 2.45 microversion which does not return the Location header, it returns a json dict in the response body with an image_id key. """ s = self.cs.servers.get(1234) im = s.create_image('123') self.assertEqual('456', im) self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') im = s.create_image('123', {}) self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') im = self.cs.servers.create_image(s, '123') self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') im = self.cs.servers.create_image(s, '123', {}) self.assert_request_id(im, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') def test_backup(self): s = self.cs.servers.get(1234) # Test backup on the Server object. sb = s.backup('back1', 'daily', 1) self.assertIn('image_id', sb) self.assertEqual('456', sb['image_id']) self.assert_request_id(sb, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') # Test backup on the ServerManager. sb = self.cs.servers.backup(s, 'back1', 'daily', 2) self.assertEqual('456', sb['image_id']) self.assert_request_id(sb, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/servers/1234/action') class ServersV249Test(ServersV2_37Test): api_version = "2.49" def test_interface_attach_with_tag(self): s = self.cs.servers.get(1234) ret = s.interface_attach('7f42712e-63fe-484c-a6df-30ae4867ff66', None, None, 'test_tag') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'POST', '/servers/1234/os-interface', {'interfaceAttachment': {'port_id': '7f42712e-63fe-484c-a6df-30ae4867ff66', 'tag': 'test_tag'}}) def test_add_fixed_ip(self): # novaclient.v2.servers.Server.add_fixed_ip() # is not available after 2.44 pass def test_remove_fixed_ip(self): # novaclient.v2.servers.Server.remove_fixed_ip() # is not available after 2.44 pass class ServersV252Test(ServersV249Test): api_version = "2.52" def test_create_server_with_tags(self): self.cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", nics=self._get_server_create_default_nics(), tags=['tag1', 'tag2'] ) self.assert_called('POST', '/servers', {'server': { 'flavorRef': '1', 'imageRef': '1', 'key_name': 'fakekey', 'max_count': 1, 'metadata': {'foo': 'bar'}, 'min_count': 1, 'name': 'My server', 'networks': 'auto', 'tags': ['tag1', 'tag2'], 'user_data': 'aGVsbG8gbW90bw==' }} ) def test_create_server_with_tags_pre_252_fails(self): self.cs.api_version = api_versions.APIVersion('2.51') self.assertRaises(exceptions.UnsupportedAttribute, self.cs.servers.create, name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", nics=self._get_server_create_default_nics(), tags=['tag1', 'tag2']) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_flavors.py0000664000175000017500000002550513165151077026245 0ustar jenkinsjenkins00000000000000# Copyright (c) 2013, OpenStack # 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 novaclient import api_versions from novaclient import base from novaclient import exceptions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import flavors class FlavorsTest(utils.TestCase): def setUp(self): super(FlavorsTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.0")) self.flavor_type = self._get_flavor_type() def _get_flavor_type(self): return flavors.Flavor def test_list_flavors(self): fl = self.cs.flavors.list() self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_undetailed(self): fl = self.cs.flavors.list(detailed=False) self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_with_marker_limit(self): fl = self.cs.flavors.list(marker=1234, limit=4) self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail?limit=4&marker=1234') def test_list_flavors_with_min_disk(self): fl = self.cs.flavors.list(min_disk=20) self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail?minDisk=20') def test_list_flavors_with_min_ram(self): fl = self.cs.flavors.list(min_ram=512) self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail?minRam=512') def test_list_flavors_with_sort_key_dir(self): fl = self.cs.flavors.list(sort_key='id', sort_dir='asc') self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail?sort_dir=asc&sort_key=id') def test_list_flavors_is_public_none(self): fl = self.cs.flavors.list(is_public=None) self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail?is_public=None') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_is_public_false(self): fl = self.cs.flavors.list(is_public=False) self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail?is_public=False') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_is_public_true(self): fl = self.cs.flavors.list(is_public=True) self.assert_request_id(fl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_get_flavor_details(self): f = self.cs.flavors.get(1) self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/1') self.assertIsInstance(f, self.flavor_type) self.assertEqual(256, f.ram) self.assertEqual(10, f.disk) self.assertEqual(10, f.ephemeral) self.assertTrue(f.is_public) def test_get_flavor_details_alphanum_id(self): f = self.cs.flavors.get('aa1') self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/aa1') self.assertIsInstance(f, self.flavor_type) self.assertEqual(128, f.ram) self.assertEqual(0, f.disk) self.assertEqual(0, f.ephemeral) self.assertTrue(f.is_public) def test_get_flavor_details_diablo(self): f = self.cs.flavors.get(3) self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/3') self.assertIsInstance(f, self.flavor_type) self.assertEqual(256, f.ram) self.assertEqual(10, f.disk) self.assertEqual('N/A', f.ephemeral) self.assertEqual('N/A', f.is_public) def test_find(self): f = self.cs.flavors.find(ram=256) self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/flavors/detail') self.assertEqual('256 MB Server', f.name) f = self.cs.flavors.find(disk=0) self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual('128 MB Server', f.name) self.assertRaises(exceptions.NotFound, self.cs.flavors.find, disk=12345) def _create_body(self, name, ram, vcpus, disk, ephemeral, id, swap, rxtx_factor, is_public): return { "flavor": { "name": name, "ram": ram, "vcpus": vcpus, "disk": disk, "OS-FLV-EXT-DATA:ephemeral": ephemeral, "id": id, "swap": swap, "rxtx_factor": rxtx_factor, "os-flavor-access:is_public": is_public, } } def test_create(self): f = self.cs.flavors.create("flavorcreate", 512, 1, 10, 1234, ephemeral=10, is_public=False) self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) body = self._create_body("flavorcreate", 512, 1, 10, 10, 1234, 0, 1.0, False) self.cs.assert_called('POST', '/flavors', body) self.assertIsInstance(f, self.flavor_type) def test_create_with_id_as_string(self): flavor_id = 'foobar' f = self.cs.flavors.create("flavorcreate", 512, 1, 10, flavor_id, ephemeral=10, is_public=False) self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) body = self._create_body("flavorcreate", 512, 1, 10, 10, flavor_id, 0, 1.0, False) self.cs.assert_called('POST', '/flavors', body) self.assertIsInstance(f, self.flavor_type) def test_create_ephemeral_ispublic_defaults(self): f = self.cs.flavors.create("flavorcreate", 512, 1, 10, 1234) self.assert_request_id(f, fakes.FAKE_REQUEST_ID_LIST) body = self._create_body("flavorcreate", 512, 1, 10, 0, 1234, 0, 1.0, True) self.cs.assert_called('POST', '/flavors', body) self.assertIsInstance(f, self.flavor_type) def test_invalid_parameters_create(self): self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", "invalid", 1, 10, 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, "invalid", 10, 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, "invalid", 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap="invalid", ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap=0, ephemeral="invalid", rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap=0, ephemeral=0, rxtx_factor="invalid", is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public='invalid') def test_delete(self): ret = self.cs.flavors.delete("flavordelete") self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('DELETE', '/flavors/flavordelete') def test_delete_with_flavor_instance(self): f = self.cs.flavors.get(2) ret = self.cs.flavors.delete(f) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('DELETE', '/flavors/2') def test_delete_with_flavor_instance_method(self): f = self.cs.flavors.get(2) ret = f.delete() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('DELETE', '/flavors/2') def test_set_keys(self): f = self.cs.flavors.get(1) fk = f.set_keys({'k1': 'v1'}) self.assert_request_id(fk, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('POST', '/flavors/1/os-extra_specs', {"extra_specs": {'k1': 'v1'}}) def test_set_with_valid_keys(self): valid_keys = ['key4', 'month.price', 'I-Am:AK-ey.44-', 'key with spaces and _'] f = self.cs.flavors.get(4) for key in valid_keys: fk = f.set_keys({key: 'v4'}) self.assert_request_id(fk, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('POST', '/flavors/4/os-extra_specs', {"extra_specs": {key: 'v4'}}) def test_set_with_invalid_keys(self): invalid_keys = ['/1', '?1', '%1', '<', '>'] f = self.cs.flavors.get(1) for key in invalid_keys: self.assertRaises(exceptions.CommandError, f.set_keys, {key: 'v1'}) @mock.patch.object(flavors.FlavorManager, '_delete') def test_unset_keys(self, mock_delete): f = self.cs.flavors.get(1) keys = ['k1', 'k2'] mock_delete.return_value = base.TupleWithMeta( (), fakes.FAKE_REQUEST_ID_LIST) fu = f.unset_keys(keys) self.assert_request_id(fu, fakes.FAKE_REQUEST_ID_LIST) mock_delete.assert_has_calls([ mock.call("/flavors/1/os-extra_specs/k1"), mock.call("/flavors/1/os-extra_specs/k2") ]) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_services.py0000664000175000017500000002023213165151077026404 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import services class ServicesTest(utils.TestCase): api_version = "2.0" def setUp(self): super(ServicesTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion(self.api_version)) self.service_type = self._get_service_type() def _get_service_type(self): return services.Service def test_list_services(self): svs = self.cs.services.list() self.assert_request_id(svs, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-services') for s in svs: self.assertIsInstance(s, self._get_service_type()) self.assertEqual('nova-compute', s.binary) self.assertEqual('host1', s.host) self.assertEqual('' % s.id, str(s)) def test_list_services_with_hostname(self): svs = self.cs.services.list(host='host2') self.assert_request_id(svs, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-services?host=host2') for s in svs: self.assertIsInstance(s, self._get_service_type()) self.assertEqual('nova-compute', s.binary) self.assertEqual('host2', s.host) def test_list_services_with_binary(self): svs = self.cs.services.list(binary='nova-cert') self.assert_request_id(svs, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-services?binary=nova-cert') for s in svs: self.assertIsInstance(s, self._get_service_type()) self.assertEqual('nova-cert', s.binary) self.assertEqual('host1', s.host) def test_list_services_with_host_binary(self): svs = self.cs.services.list(host='host2', binary='nova-cert') self.assert_request_id(svs, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-services?host=host2&binary=nova-cert') for s in svs: self.assertIsInstance(s, self._get_service_type()) self.assertEqual('nova-cert', s.binary) self.assertEqual('host2', s.host) def _update_body(self, host, binary, disabled_reason=None): body = {"host": host, "binary": binary} if disabled_reason is not None: body["disabled_reason"] = disabled_reason return body def test_services_enable(self): service = self.cs.services.enable('host1', 'nova-cert') self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body("host1", "nova-cert") self.cs.assert_called('PUT', '/os-services/enable', values) self.assertIsInstance(service, self._get_service_type()) self.assertEqual('enabled', service.status) def test_services_delete(self): ret = self.cs.services.delete('1') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('DELETE', '/os-services/1') def test_services_disable(self): service = self.cs.services.disable('host1', 'nova-cert') self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body("host1", "nova-cert") self.cs.assert_called('PUT', '/os-services/disable', values) self.assertIsInstance(service, self._get_service_type()) self.assertEqual('disabled', service.status) def test_services_disable_log_reason(self): service = self.cs.services.disable_log_reason( 'compute1', 'nova-compute', 'disable bad host') self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body("compute1", "nova-compute", "disable bad host") self.cs.assert_called('PUT', '/os-services/disable-log-reason', values) self.assertIsInstance(service, self._get_service_type()) self.assertEqual('disabled', service.status) class ServicesV211TestCase(ServicesTest): api_version = "2.11" def _update_body(self, host, binary, disabled_reason=None, force_down=None): body = {"host": host, "binary": binary} if disabled_reason is not None: body["disabled_reason"] = disabled_reason if force_down is not None: body["forced_down"] = force_down return body def test_services_force_down(self): service = self.cs.services.force_down( 'compute1', 'nova-compute', False) self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body("compute1", "nova-compute", force_down=False) self.cs.assert_called('PUT', '/os-services/force-down', values) self.assertIsInstance(service, self._get_service_type()) self.assertFalse(service.forced_down) class ServicesV2_53TestCase(ServicesV211TestCase): api_version = "2.53" def _update_body(self, status=None, disabled_reason=None, force_down=None): body = {} if status is not None: body['status'] = status if disabled_reason is not None: body['disabled_reason'] = disabled_reason if force_down is not None: body['forced_down'] = force_down return body def test_services_enable(self): service = self.cs.services.enable(fakes.FAKE_SERVICE_UUID_1) self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body(status='enabled') self.cs.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values) self.assertIsInstance(service, self._get_service_type()) self.assertEqual('enabled', service.status) def test_services_delete(self): ret = self.cs.services.delete(fakes.FAKE_SERVICE_UUID_1) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('DELETE', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1) def test_services_disable(self): service = self.cs.services.disable(fakes.FAKE_SERVICE_UUID_1) self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body(status='disabled') self.cs.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values) self.assertIsInstance(service, self._get_service_type()) self.assertEqual('disabled', service.status) def test_services_disable_log_reason(self): service = self.cs.services.disable_log_reason( fakes.FAKE_SERVICE_UUID_1, 'disable bad host') self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body(status='disabled', disabled_reason='disable bad host') self.cs.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values) self.assertIsInstance(service, self._get_service_type()) self.assertEqual('disabled', service.status) self.assertEqual('disable bad host', service.disabled_reason) def test_services_force_down(self): service = self.cs.services.force_down( fakes.FAKE_SERVICE_UUID_1, False) self.assert_request_id(service, fakes.FAKE_REQUEST_ID_LIST) values = self._update_body(force_down=False) self.cs.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, values) self.assertIsInstance(service, self._get_service_type()) self.assertFalse(service.forced_down) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_limits.py0000664000175000017500000000660313165151077026070 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import limits as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import limits class LimitsTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.Fixture def test_get_limits(self): obj = self.cs.limits.get() self.assert_request_id(obj, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/limits') self.assertIsInstance(obj, limits.Limits) def test_get_limits_for_a_tenant(self): obj = self.cs.limits.get(tenant_id=1234) self.assert_request_id(obj, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/limits?tenant_id=1234') self.assertIsInstance(obj, limits.Limits) def test_absolute_limits_reserved(self): obj = self.cs.limits.get(reserved=True) self.assert_request_id(obj, fakes.FAKE_REQUEST_ID_LIST) expected = ( limits.AbsoluteLimit("maxTotalRAMSize", 51200), limits.AbsoluteLimit("maxServerMeta", 5), limits.AbsoluteLimit("maxImageMeta", 5), limits.AbsoluteLimit("maxPersonality", 5), limits.AbsoluteLimit("maxPersonalitySize", 10240), ) self.assert_called('GET', '/limits?reserved=1') abs_limits = list(obj.absolute) self.assertEqual(len(abs_limits), len(expected)) for limit in abs_limits: self.assertIn(limit, expected) def test_rate_absolute_limits(self): obj = self.cs.limits.get() self.assert_request_id(obj, fakes.FAKE_REQUEST_ID_LIST) expected = ( limits.RateLimit('POST', '*', '.*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), limits.RateLimit('PUT', '*', '.*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), limits.RateLimit('DELETE', '*', '.*', 100, 100, 'MINUTE', '2011-12-15T22:42:45Z'), limits.RateLimit('POST', '*/servers', '^/servers', 25, 24, 'DAY', '2011-12-15T22:42:45Z'), ) rate_limits = list(obj.rate) self.assertEqual(len(rate_limits), len(expected)) for limit in rate_limits: self.assertIn(limit, expected) expected = ( limits.AbsoluteLimit("maxTotalRAMSize", 51200), limits.AbsoluteLimit("maxServerMeta", 5), limits.AbsoluteLimit("maxImageMeta", 5), limits.AbsoluteLimit("maxPersonality", 5), limits.AbsoluteLimit("maxPersonalitySize", 10240), ) abs_limits = list(obj.absolute) self.assertEqual(len(abs_limits), len(expected)) for limit in abs_limits: self.assertIn(limit, expected) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_assisted_volume_snapshots.py0000664000175000017500000000275513165151077032103 0ustar jenkinsjenkins00000000000000# Copyright (C) 2013, Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Assisted volume snapshots - to be used by Cinder and not end users. """ from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class AssistedVolumeSnapshotsTestCase(utils.TestCase): def setUp(self): super(AssistedVolumeSnapshotsTestCase, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.1")) def test_create_snap(self): vs = self.cs.assisted_volume_snapshots.create('1', {}) self.assert_request_id(vs, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('POST', '/os-assisted-volume-snapshots') def test_delete_snap(self): vs = self.cs.assisted_volume_snapshots.delete('x', {}) self.assert_request_id(vs, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'DELETE', '/os-assisted-volume-snapshots/x?delete_info={}') python-novaclient-9.1.1/novaclient/tests/unit/v2/test_migrations.py0000664000175000017500000000567113165151077026747 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import migrations class MigrationsTest(utils.TestCase): def setUp(self): super(MigrationsTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.1")) def test_list_migrations(self): ml = self.cs.migrations.list() self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-migrations') for m in ml: self.assertIsInstance(m, migrations.Migration) self.assertRaises(AttributeError, getattr, m, "migration_type") def test_list_migrations_v223(self): cs = fakes.FakeClient(api_versions.APIVersion("2.23")) ml = cs.migrations.list() self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) cs.assert_called('GET', '/os-migrations') for m in ml: self.assertIsInstance(m, migrations.Migration) self.assertEqual(m.migration_type, 'live-migration') @mock.patch('novaclient.v2.migrations.warnings.warn') def test_list_migrations_with_cell_name(self, mock_warn): ml = self.cs.migrations.list(cell_name="abc") self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-migrations?cell_name=abc') for m in ml: self.assertIsInstance(m, migrations.Migration) self.assertTrue(mock_warn.called) def test_list_migrations_with_filters(self): ml = self.cs.migrations.list('host1', 'finished', 'child1') self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', '/os-migrations?cell_name=child1&host=host1&status=finished') for m in ml: self.assertIsInstance(m, migrations.Migration) def test_list_migrations_with_instance_uuid_filter(self): ml = self.cs.migrations.list('host1', 'finished', 'child1', 'instance_id_456') self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'GET', ('/os-migrations?cell_name=child1&host=host1&' 'instance_uuid=instance_id_456&status=finished')) self.assertEqual(1, len(ml)) self.assertEqual('instance_id_456', ml[0].instance_uuid) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_server_groups.py0000664000175000017500000001137013165151077027471 0ustar jenkinsjenkins00000000000000# Copyright (c) 2014 VMware, 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 novaclient import exceptions from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import server_groups as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import server_groups class ServerGroupsTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.Fixture def test_list_server_groups(self): result = self.cs.server_groups.list() self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-server-groups') self.assertEqual(4, len(result)) for server_group in result: self.assertIsInstance(server_group, server_groups.ServerGroup) def test_list_server_groups_with_all_projects(self): result = self.cs.server_groups.list(all_projects=True) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-server-groups?all_projects=True') self.assertEqual(8, len(result)) for server_group in result: self.assertIsInstance(server_group, server_groups.ServerGroup) def test_list_server_groups_with_limit_and_offset(self): all_groups = self.cs.server_groups.list() result = self.cs.server_groups.list(limit=2, offset=1) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-server-groups?limit=2&offset=1') self.assertEqual(2, len(result)) for server_group in result: self.assertIsInstance(server_group, server_groups.ServerGroup) self.assertEqual(all_groups[1:3], result) def test_create_server_group(self): kwargs = {'name': 'ig1', 'policies': ['anti-affinity']} server_group = self.cs.server_groups.create(**kwargs) self.assert_request_id(server_group, fakes.FAKE_REQUEST_ID_LIST) body = {'server_group': kwargs} self.assert_called('POST', '/os-server-groups', body) self.assertIsInstance(server_group, server_groups.ServerGroup) def test_get_server_group(self): id = '2cbd51f4-fafe-4cdb-801b-cf913a6f288b' server_group = self.cs.server_groups.get(id) self.assert_request_id(server_group, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-server-groups/%s' % id) self.assertIsInstance(server_group, server_groups.ServerGroup) def test_delete_server_group(self): id = '2cbd51f4-fafe-4cdb-801b-cf913a6f288b' ret = self.cs.server_groups.delete(id) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/os-server-groups/%s' % id) def test_delete_server_group_object(self): id = '2cbd51f4-fafe-4cdb-801b-cf913a6f288b' server_group = self.cs.server_groups.get(id) ret = server_group.delete() self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/os-server-groups/%s' % id) def test_find_server_groups_by_name(self): expected_name = 'ig1' kwargs = {self.cs.server_groups.resource_class.NAME_ATTR: expected_name} server_group = self.cs.server_groups.find(**kwargs) self.assert_request_id(server_group, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-server-groups') self.assertIsInstance(server_group, server_groups.ServerGroup) actual_name = getattr(server_group, self.cs.server_groups.resource_class.NAME_ATTR) self.assertEqual(expected_name, actual_name) def test_find_no_existing_server_groups_by_name(self): expected_name = 'no-exist' kwargs = {self.cs.server_groups.resource_class.NAME_ATTR: expected_name} self.assertRaises(exceptions.NotFound, self.cs.server_groups.find, **kwargs) self.assert_called('GET', '/os-server-groups') python-novaclient-9.1.1/novaclient/tests/unit/v2/test_cells.py0000664000175000017500000000317413165151077025671 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class CellsExtensionTests(utils.TestCase): def setUp(self): super(CellsExtensionTests, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.1")) def test_get_cells(self): cell_name = 'child_cell' cell = self.cs.cells.get(cell_name) self.assert_request_id(cell, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-cells/%s' % cell_name) def test_get_capacities_for_a_given_cell(self): cell_name = 'child_cell' ca = self.cs.cells.capacities(cell_name) self.assert_request_id(ca, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-cells/%s/capacities' % cell_name) def test_get_capacities_for_all_cells(self): ca = self.cs.cells.capacities() self.assert_request_id(ca, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-cells/capacities') python-novaclient-9.1.1/novaclient/tests/unit/v2/test_versions.py0000664000175000017500000001045513165151077026437 0ustar jenkinsjenkins00000000000000# 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 novaclient import api_versions from novaclient import exceptions as exc from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import versions class VersionsTest(utils.TestCase): def setUp(self): super(VersionsTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.0")) self.service_type = versions.Version def test_list_services(self): vl = self.cs.versions.list() self.assert_request_id(vl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', 'http://nova-api:8774') def test_get_current(self): self.cs.callback = [] v = self.cs.versions.get_current() self.assert_request_id(v, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', 'http://nova-api:8774/v2.1/') @mock.patch.object(versions.VersionManager, '_get', side_effect=exc.Unauthorized("401 RAX")) def test_get_current_with_rax_workaround(self, get): self.cs.callback = [] self.assertIsNone(self.cs.versions.get_current()) def test_get_endpoint_without_project_id(self): # create a fake client such that get_endpoint() # doesn't return uuid in url endpoint_type = 'v2.1' expected_endpoint = 'http://nova-api:8774/v2.1/' cs_2_1 = fakes.FakeClient(api_versions.APIVersion("2.0"), endpoint_type=endpoint_type) result = cs_2_1.versions.get_current() self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(result.manager.api.client.endpoint_type, endpoint_type, "Check endpoint_type was set") # check that the full request works as expected cs_2_1.assert_called('GET', expected_endpoint) def test_v2_get_endpoint_without_project_id(self): # create a fake client such that get_endpoint() # doesn't return uuid in url endpoint_type = 'v2' expected_endpoint = 'http://nova-api:8774/v2/' cs_2 = fakes.FakeClient(api_versions.APIVersion("2.0"), endpoint_type=endpoint_type) result = cs_2.versions.get_current() self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(result.manager.api.client.endpoint_type, endpoint_type, "Check v2 endpoint_type was set") # check that the full request works as expected cs_2.assert_called('GET', expected_endpoint) def test_list_versions(self): fapi = mock.Mock() version_mgr = versions.VersionManager(fapi) version_mgr._list = mock.Mock() data = [ ("https://example.com:777/v2", "https://example.com:777"), ("https://example.com/v2", "https://example.com"), ("http://example.com/compute/v2", "http://example.com/compute"), ("https://example.com/v2/prrrooojeect-uuid", "https://example.com"), ("https://example.com:777/v2.1", "https://example.com:777"), ("https://example.com/v2.1", "https://example.com"), ("http://example.com/compute/v2.1", "http://example.com/compute"), ("https://example.com/v2.1/prrrooojeect-uuid", "https://example.com"), ("http://example.com/compute", "http://example.com/compute"), ("http://compute.example.com", "http://compute.example.com"), ] for endpoint, expected in data: version_mgr._list.reset_mock() fapi.client.get_endpoint.return_value = endpoint version_mgr.list() version_mgr._list.assert_called_once_with(expected, "versions") python-novaclient-9.1.1/novaclient/tests/unit/v2/test_quotas.py0000664000175000017500000000643213165151077026103 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import quotas as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class QuotaSetsTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.V1 def test_tenant_quotas_get(self): tenant_id = 'test' q = self.cs.quotas.get(tenant_id) self.assert_request_id(q, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-quota-sets/%s' % tenant_id) def test_user_quotas_get(self): tenant_id = 'test' user_id = 'fake_user' q = self.cs.quotas.get(tenant_id, user_id=user_id) self.assert_request_id(q, fakes.FAKE_REQUEST_ID_LIST) url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) self.assert_called('GET', url) def test_tenant_quotas_get_detail(self): tenant_id = 'test' q = self.cs.quotas.get(tenant_id, detail=True) self.assert_request_id(q, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-quota-sets/%s/detail' % tenant_id) def test_user_quotas_get_detail(self): tenant_id = 'test' user_id = 'fake_user' q = self.cs.quotas.get(tenant_id, user_id=user_id, detail=True) self.assert_request_id(q, fakes.FAKE_REQUEST_ID_LIST) url = '/os-quota-sets/%s/detail?user_id=%s' % (tenant_id, user_id) self.assert_called('GET', url) def test_tenant_quotas_defaults(self): tenant_id = '97f4c221bff44578b0300df4ef119353' q = self.cs.quotas.defaults(tenant_id) self.assert_request_id(q, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id) def test_force_update_quota(self): q = self.cs.quotas.get('97f4c221bff44578b0300df4ef119353') qu = q.update(cores=2, force=True) self.assert_request_id(qu, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'force': True, 'cores': 2}}) def test_quotas_delete(self): tenant_id = 'test' ret = self.cs.quotas.delete(tenant_id) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/os-quota-sets/%s' % tenant_id) def test_user_quotas_delete(self): tenant_id = 'test' user_id = 'fake_user' ret = self.cs.quotas.delete(tenant_id, user_id=user_id) self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) self.assert_called('DELETE', url) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_server_migrations.py0000664000175000017500000000655113165151077030333 0ustar jenkinsjenkins00000000000000# Copyright 2016 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient import base from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import server_migrations as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import server_migrations class ServerMigrationsTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.Fixture def setUp(self): super(ServerMigrationsTest, self).setUp() self.cs.api_version = api_versions.APIVersion("2.22") def test_live_migration_force_complete(self): body = {'force_complete': None} self.cs.server_migrations.live_migrate_force_complete(1234, 1) self.assert_called('POST', '/servers/1234/migrations/1/action', body) class ServerMigrationsTestV223(ServerMigrationsTest): migration = { "created_at": "2016-01-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": 1, "server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe", "source_compute": "compute1", "source_node": "node1", "status": "running", "memory_total_bytes": 123456, "memory_processed_bytes": 12345, "memory_remaining_bytes": 120000, "disk_total_bytes": 234567, "disk_processed_bytes": 23456, "disk_remaining_bytes": 230000, "updated_at": "2016-01-29T13:42:02.000000" } def setUp(self): super(ServerMigrationsTestV223, self).setUp() self.cs.api_version = api_versions.APIVersion("2.23") def test_list_migrations(self): ml = self.cs.server_migrations.list(1234) self.assertIsInstance(ml, base.ListWithMeta) self.assert_request_id(ml, fakes.FAKE_REQUEST_ID_LIST) for k in self.migration: self.assertEqual(self.migration[k], getattr(ml[0], k)) self.assert_called('GET', '/servers/1234/migrations') def test_get_migration(self): migration = self.cs.server_migrations.get(1234, 1) self.assertIsInstance(migration, server_migrations.ServerMigration) for k in migration._info: self.assertEqual(self.migration[k], migration._info[k]) self.assert_request_id(migration, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/servers/1234/migrations/1') class ServerMigrationsTestV224(ServerMigrationsTest): def setUp(self): super(ServerMigrationsTestV224, self).setUp() self.cs.api_version = api_versions.APIVersion("2.24") def test_live_migration_abort(self): self.cs.server_migrations.live_migration_abort(1234, 1) self.assert_called('DELETE', '/servers/1234/migrations/1') python-novaclient-9.1.1/novaclient/tests/unit/v2/test_list_extensions.py0000664000175000017500000000227213165151077030017 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class ListExtensionsTests(utils.TestCase): def setUp(self): super(ListExtensionsTests, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.1")) def test_list_extensions(self): all_exts = self.cs.list_extensions.show_all() self.assert_request_id(all_exts, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/extensions') self.assertGreater(len(all_exts), 0) for r in all_exts: self.assertGreater(len(r.summary), 0) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_hosts.py0000664000175000017500000001320113165151077025717 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from novaclient import api_versions from novaclient import exceptions from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import hosts as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import hosts class HostsTest(utils.FixturedTestCase): client_fixture_class = client.V1 data_fixture_class = data.V1 def setUp(self): super(HostsTest, self).setUp() self.warning_mock = mock.patch('warnings.warn').start() self.addCleanup(self.warning_mock.stop) def test_describe_resource(self): hs = self.cs.hosts.get('host') self.warning_mock.assert_called_once() self.assert_request_id(hs, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-hosts/host') for h in hs: self.assertIsInstance(h, hosts.Host) def test_list_host(self): hs = self.cs.hosts.list() self.warning_mock.assert_called_once() self.assert_request_id(hs, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-hosts') for h in hs: self.assertIsInstance(h, hosts.Host) self.assertEqual(h.zone, 'nova1') def test_list_host_with_zone(self): hs = self.cs.hosts.list('nova') self.assert_request_id(hs, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-hosts?zone=nova') for h in hs: self.assertIsInstance(h, hosts.Host) self.assertEqual(h.zone, 'nova') def test_update_enable(self): host = self.cs.hosts.get('sample_host')[0] values = {"status": "enabled"} result = host.update(values) # one warning for the get, one warning for the update self.assertEqual(2, self.warning_mock.call_count) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/os-hosts/sample_host', values) self.assertIsInstance(result, hosts.Host) def test_update_maintenance(self): host = self.cs.hosts.get('sample_host')[0] values = {"maintenance_mode": "enable"} result = host.update(values) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/os-hosts/sample_host', values) self.assertIsInstance(result, hosts.Host) def test_update_both(self): host = self.cs.hosts.get('sample_host')[0] values = {"status": "enabled", "maintenance_mode": "enable"} result = host.update(values) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/os-hosts/sample_host', values) self.assertIsInstance(result, hosts.Host) def test_host_startup(self): host = self.cs.hosts.get('sample_host')[0] result = host.startup() # one warning for the get, one warning for the action self.assertEqual(2, self.warning_mock.call_count) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'GET', '/os-hosts/sample_host/startup') def test_host_reboot(self): host = self.cs.hosts.get('sample_host')[0] result = host.reboot() # one warning for the get, one warning for the action self.assertEqual(2, self.warning_mock.call_count) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'GET', '/os-hosts/sample_host/reboot') def test_host_shutdown(self): host = self.cs.hosts.get('sample_host')[0] result = host.shutdown() # one warning for the get, one warning for the action self.assertEqual(2, self.warning_mock.call_count) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called( 'GET', '/os-hosts/sample_host/shutdown') def test_hosts_repr(self): hs = self.cs.hosts.get('host') self.assertEqual('', repr(hs[0])) def test_hosts_list_repr(self): hs = self.cs.hosts.list() for h in hs: self.assertEqual('' % h.host_name, repr(h)) class DeprecatedHostsTestv2_43(utils.FixturedTestCase): """Tests the os-hosts API bindings at microversion 2.43 to ensure they fail with a 404 error. """ client_fixture_class = client.V1 def setUp(self): super(DeprecatedHostsTestv2_43, self).setUp() self.cs.api_version = api_versions.APIVersion('2.43') def test_get(self): self.assertRaises(exceptions.VersionNotFoundForAPIMethod, self.cs.hosts.get, 'host') def test_list(self): self.assertRaises(exceptions.VersionNotFoundForAPIMethod, self.cs.hosts.list) def test_update(self): self.assertRaises(exceptions.VersionNotFoundForAPIMethod, self.cs.hosts.update, 'host', {"status": "enabled"}) def test_host_action(self): self.assertRaises(exceptions.VersionNotFoundForAPIMethod, self.cs.hosts.host_action, 'host', 'reboot') python-novaclient-9.1.1/novaclient/tests/unit/v2/test_server_external_events.py0000664000175000017500000000311013165151077031351 0ustar jenkinsjenkins00000000000000# Copyright (C) 2014, 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. """ External event triggering for servers, not to be used by users. """ from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class ServerExternalEventsTestCase(utils.TestCase): def setUp(self): super(ServerExternalEventsTestCase, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.1")) def test_external_event(self): events = [{'server_uuid': 'fake-uuid1', 'name': 'test-event', 'status': 'completed', 'tag': 'tag'}, {'server_uuid': 'fake-uuid2', 'name': 'test-event', 'status': 'completed', 'tag': 'tag'}] result = self.cs.server_external_events.create(events) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assertEqual(events, result) self.cs.assert_called('POST', '/os-server-external-events') python-novaclient-9.1.1/novaclient/tests/unit/v2/test_cloudpipe.py0000664000175000017500000000436613165151077026557 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import six from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import cloudpipe as data from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import cloudpipe class CloudpipeTest(utils.FixturedTestCase): data_fixture_class = data.Fixture scenarios = [('original', {'client_fixture_class': client.V1}), ('session', {'client_fixture_class': client.SessionV1})] @mock.patch('warnings.warn') def test_list_cloudpipes(self, mock_warn): cp = self.cs.cloudpipe.list() self.assert_request_id(cp, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-cloudpipe') for c in cp: self.assertIsInstance(c, cloudpipe.Cloudpipe) mock_warn.assert_called_once_with(mock.ANY) @mock.patch('warnings.warn') def test_create(self, mock_warn): project = "test" cp = self.cs.cloudpipe.create(project) self.assert_request_id(cp, fakes.FAKE_REQUEST_ID_LIST) body = {'cloudpipe': {'project_id': project}} self.assert_called('POST', '/os-cloudpipe', body) self.assertIsInstance(cp, six.string_types) mock_warn.assert_called_once_with(mock.ANY) @mock.patch('warnings.warn') def test_update(self, mock_warn): cp = self.cs.cloudpipe.update("192.168.1.1", 2345) self.assert_request_id(cp, fakes.FAKE_REQUEST_ID_LIST) body = {'configure_project': {'vpn_ip': "192.168.1.1", 'vpn_port': 2345}} self.assert_called('PUT', '/os-cloudpipe/configure-project', body) mock_warn.assert_called_once_with(mock.ANY) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_quota_classes.py0000664000175000017500000001034513165151077027433 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes class QuotaClassSetsTest(utils.TestCase): def setUp(self): super(QuotaClassSetsTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.0")) def test_class_quotas_get(self): class_name = 'test' q = self.cs.quota_classes.get(class_name) self.assert_request_id(q, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/os-quota-class-sets/%s' % class_name) return q def test_update_quota(self): q = self.cs.quota_classes.get('test') self.assert_request_id(q, fakes.FAKE_REQUEST_ID_LIST) q.update(cores=2) self.cs.assert_called('PUT', '/os-quota-class-sets/test') return q def test_refresh_quota(self): q = self.cs.quota_classes.get('test') q2 = self.cs.quota_classes.get('test') self.assertEqual(q.cores, q2.cores) q2.cores = 0 self.assertNotEqual(q.cores, q2.cores) q2.get() self.assertEqual(q.cores, q2.cores) class QuotaClassSetsTest2_50(QuotaClassSetsTest): """Tests the quota classes API binding using the 2.50 microversion.""" def setUp(self): super(QuotaClassSetsTest2_50, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion("2.50")) def test_class_quotas_get(self): """Tests that network-related resources aren't in a 2.50 response and server group related resources are in the response. """ q = super(QuotaClassSetsTest2_50, self).test_class_quotas_get() for invalid_resource in ('floating_ips', 'fixed_ips', 'networks', 'security_groups', 'security_group_rules'): self.assertFalse(hasattr(q, invalid_resource), '%s should not be in %s' % (invalid_resource, q)) # Also make sure server_groups and server_group_members are in the # response. for valid_resource in ('server_groups', 'server_group_members'): self.assertTrue(hasattr(q, valid_resource), '%s should be in %s' % (invalid_resource, q)) def test_update_quota(self): """Tests that network-related resources aren't in a 2.50 response and server group related resources are in the response. """ q = super(QuotaClassSetsTest2_50, self).test_update_quota() for invalid_resource in ('floating_ips', 'fixed_ips', 'networks', 'security_groups', 'security_group_rules'): self.assertFalse(hasattr(q, invalid_resource), '%s should not be in %s' % (invalid_resource, q)) # Also make sure server_groups and server_group_members are in the # response. for valid_resource in ('server_groups', 'server_group_members'): self.assertTrue(hasattr(q, valid_resource), '%s should be in %s' % (invalid_resource, q)) def test_update_quota_invalid_resources(self): """Tests trying to update quota class values for invalid resources. This will fail with TypeError because the network-related resource kwargs aren't defined. """ q = self.cs.quota_classes.get('test') self.assertRaises(TypeError, q.update, floating_ips=1) self.assertRaises(TypeError, q.update, fixed_ips=1) self.assertRaises(TypeError, q.update, security_groups=1) self.assertRaises(TypeError, q.update, security_group_rules=1) self.assertRaises(TypeError, q.update, networks=1) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_aggregates.py0000664000175000017500000001614513165151077026702 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import aggregates as data from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import aggregates class AggregatesTest(utils.FixturedTestCase): data_fixture_class = data.Fixture scenarios = [('original', {'client_fixture_class': client.V1}), ('session', {'client_fixture_class': client.SessionV1})] def test_list_aggregates(self): result = self.cs.aggregates.list() self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-aggregates') for aggregate in result: self.assertIsInstance(aggregate, aggregates.Aggregate) def test_create_aggregate(self): body = {"aggregate": {"name": "test", "availability_zone": "nova1"}} aggregate = self.cs.aggregates.create("test", "nova1") self.assert_request_id(aggregate, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates', body) self.assertIsInstance(aggregate, aggregates.Aggregate) def test_get(self): aggregate = self.cs.aggregates.get("1") self.assert_request_id(aggregate, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate, aggregates.Aggregate) aggregate2 = self.cs.aggregates.get(aggregate) self.assert_request_id(aggregate2, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate2, aggregates.Aggregate) def test_get_details(self): aggregate = self.cs.aggregates.get_details("1") self.assert_request_id(aggregate, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate, aggregates.Aggregate) aggregate2 = self.cs.aggregates.get_details(aggregate) self.assert_request_id(aggregate2, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate2, aggregates.Aggregate) def test_update(self): aggregate = self.cs.aggregates.get("1") values = {"name": "foo"} body = {"aggregate": values} result1 = aggregate.update(values) self.assert_request_id(result1, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/os-aggregates/1', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.update(2, values) self.assert_request_id(result2, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/os-aggregates/2', body) self.assertIsInstance(result2, aggregates.Aggregate) def test_update_with_availability_zone(self): aggregate = self.cs.aggregates.get("1") values = {"name": "foo", "availability_zone": "new_zone"} body = {"aggregate": values} result3 = self.cs.aggregates.update(aggregate, values) self.assert_request_id(result3, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('PUT', '/os-aggregates/1', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_add_host(self): aggregate = self.cs.aggregates.get("1") host = "host1" body = {"add_host": {"host": "host1"}} result1 = aggregate.add_host(host) self.assert_request_id(result1, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.add_host("2", host) self.assert_request_id(result2, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/2/action', body) self.assertIsInstance(result2, aggregates.Aggregate) result3 = self.cs.aggregates.add_host(aggregate, host) self.assert_request_id(result3, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_remove_host(self): aggregate = self.cs.aggregates.get("1") host = "host1" body = {"remove_host": {"host": "host1"}} result1 = aggregate.remove_host(host) self.assert_request_id(result1, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.remove_host("2", host) self.assert_request_id(result2, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/2/action', body) self.assertIsInstance(result2, aggregates.Aggregate) result3 = self.cs.aggregates.remove_host(aggregate, host) self.assert_request_id(result3, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_set_metadata(self): aggregate = self.cs.aggregates.get("1") metadata = {"foo": "bar"} body = {"set_metadata": {"metadata": metadata}} result1 = aggregate.set_metadata(metadata) self.assert_request_id(result1, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.set_metadata(2, metadata) self.assert_request_id(result2, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/2/action', body) self.assertIsInstance(result2, aggregates.Aggregate) result3 = self.cs.aggregates.set_metadata(aggregate, metadata) self.assert_request_id(result3, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_delete_aggregate(self): aggregate = self.cs.aggregates.list()[0] result1 = aggregate.delete() self.assert_request_id(result1, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/os-aggregates/1') result2 = self.cs.aggregates.delete('1') self.assert_request_id(result2, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/os-aggregates/1') result3 = self.cs.aggregates.delete(aggregate) self.assert_request_id(result3, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/os-aggregates/1') python-novaclient-9.1.1/novaclient/tests/unit/v2/test_availability_zone.py0000664000175000017500000000777013165151077030302 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six from novaclient.tests.unit.fixture_data import availability_zones as data from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import availability_zones class AvailabilityZoneTest(utils.FixturedTestCase): # NOTE(cyeoh): import shell here so the V3 version of # this class can inherit off the v3 version of shell from novaclient.v2 import shell # noqa data_fixture_class = data.V1 scenarios = [('original', {'client_fixture_class': client.V1}), ('session', {'client_fixture_class': client.SessionV1})] def setUp(self): super(AvailabilityZoneTest, self).setUp() self.availability_zone_type = self._get_availability_zone_type() def _get_availability_zone_type(self): return availability_zones.AvailabilityZone def _assertZone(self, zone, name, status): self.assertEqual(zone.zoneName, name) self.assertEqual(zone.zoneState, status) def test_list_availability_zone(self): zones = self.cs.availability_zones.list(detailed=False) self.assert_request_id(zones, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-availability-zone') for zone in zones: self.assertIsInstance(zone, self.availability_zone_type) self.assertEqual(2, len(zones)) l0 = [six.u('zone-1'), six.u('available')] l1 = [six.u('zone-2'), six.u('not available')] z0 = self.shell._treeizeAvailabilityZone(zones[0]) z1 = self.shell._treeizeAvailabilityZone(zones[1]) self.assertEqual((1, 1), (len(z0), len(z1))) self._assertZone(z0[0], l0[0], l0[1]) self._assertZone(z1[0], l1[0], l1[1]) def test_detail_availability_zone(self): zones = self.cs.availability_zones.list(detailed=True) self.assert_request_id(zones, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('GET', '/os-availability-zone/detail') for zone in zones: self.assertIsInstance(zone, self.availability_zone_type) self.assertEqual(3, len(zones)) l0 = [six.u('zone-1'), six.u('available')] l1 = [six.u('|- fake_host-1'), six.u('')] l2 = [six.u('| |- nova-compute'), six.u('enabled :-) 2012-12-26 14:45:25')] l3 = [six.u('internal'), six.u('available')] l4 = [six.u('|- fake_host-1'), six.u('')] l5 = [six.u('| |- nova-sched'), six.u('enabled :-) 2012-12-26 14:45:25')] l6 = [six.u('|- fake_host-2'), six.u('')] l7 = [six.u('| |- nova-network'), six.u('enabled XXX 2012-12-26 14:45:24')] l8 = [six.u('zone-2'), six.u('not available')] z0 = self.shell._treeizeAvailabilityZone(zones[0]) z1 = self.shell._treeizeAvailabilityZone(zones[1]) z2 = self.shell._treeizeAvailabilityZone(zones[2]) self.assertEqual((3, 5, 1), (len(z0), len(z1), len(z2))) self._assertZone(z0[0], l0[0], l0[1]) self._assertZone(z0[1], l1[0], l1[1]) self._assertZone(z0[2], l2[0], l2[1]) self._assertZone(z1[0], l3[0], l3[1]) self._assertZone(z1[1], l4[0], l4[1]) self._assertZone(z1[2], l5[0], l5[1]) self._assertZone(z1[3], l6[0], l6[1]) self._assertZone(z1[4], l7[0], l7[1]) self._assertZone(z2[0], l8[0], l8[1]) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_shell.py0000664000175000017500000043162713165151077025706 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import base64 import datetime import os import fixtures import mock from oslo_utils import timeutils import six from six.moves import builtins import testtools import novaclient from novaclient import api_versions from novaclient import base import novaclient.client from novaclient import exceptions import novaclient.shell from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes import novaclient.v2.shell FAKE_UUID_1 = fakes.FAKE_IMAGE_UUID_1 FAKE_UUID_2 = fakes.FAKE_IMAGE_UUID_2 class ShellFixture(fixtures.Fixture): def setUp(self): super(ShellFixture, self).setUp() self.shell = novaclient.shell.OpenStackComputeShell() def tearDown(self): # For some method like test_image_meta_bad_action we are # testing a SystemExit to be thrown and object self.shell has # no time to get instantiated which is OK in this case, so # we make sure the method is there before launching it. if hasattr(self.shell, 'cs'): self.shell.cs.clear_callstack() super(ShellFixture, self).tearDown() class ShellTest(utils.TestCase): FAKE_ENV = { 'NOVA_USERNAME': 'username', 'NOVA_PASSWORD': 'password', 'NOVA_PROJECT_ID': 'project_id', 'OS_COMPUTE_API_VERSION': '2', 'NOVA_URL': 'http://no.where', 'OS_AUTH_URL': 'http://no.where/v2.0', } def setUp(self): """Run before each test.""" super(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) self.shell = self.useFixture(ShellFixture()).shell self.useFixture(fixtures.MonkeyPatch( 'novaclient.client.Client', fakes.FakeClient)) @mock.patch('sys.stdout', new_callable=six.StringIO) @mock.patch('sys.stderr', new_callable=six.StringIO) def run_command(self, cmd, mock_stderr, mock_stdout, api_version=None): version_options = [] if api_version: version_options.extend(["--os-compute-api-version", api_version, "--service-type", "computev21"]) if isinstance(cmd, list): self.shell.main(version_options + cmd) else: self.shell.main(version_options + cmd.split()) return mock_stdout.getvalue(), mock_stderr.getvalue() def assert_called(self, method, url, body=None, **kwargs): return self.shell.cs.assert_called(method, url, body, **kwargs) def assert_called_anytime(self, method, url, body=None): return self.shell.cs.assert_called_anytime(method, url, body) def test_agents_list_with_hypervisor(self): self.run_command('agent-list --hypervisor xen') self.assert_called('GET', '/os-agents?hypervisor=xen') def test_agents_create(self): self.run_command('agent-create win x86 7.0 ' '/xxx/xxx/xxx ' 'add6bb58e139be103324d04d82d8f546 ' 'kvm') self.assert_called( 'POST', '/os-agents', {'agent': { 'hypervisor': 'kvm', 'os': 'win', 'architecture': 'x86', 'version': '7.0', 'url': '/xxx/xxx/xxx', 'md5hash': 'add6bb58e139be103324d04d82d8f546'}}) def test_agents_delete(self): self.run_command('agent-delete 1') self.assert_called('DELETE', '/os-agents/1') def test_agents_modify(self): self.run_command('agent-modify 1 8.0 /yyy/yyyy/yyyy ' 'add6bb58e139be103324d04d82d8f546') self.assert_called('PUT', '/os-agents/1', {"para": { "url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546"}}) def test_boot(self): self.run_command('boot --flavor 1 --image %s ' 'some-server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_image_with(self): self.run_command("boot --flavor 1" " --image-with test_key=test_value some-server") self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_key(self): self.run_command('boot --flavor 1 --image %s --key-name 1 some-server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'key_name': '1', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_user_data(self): testfile = os.path.join(os.path.dirname(__file__), 'testfile.txt') with open(testfile) as testfile_fd: data = testfile_fd.read().encode('utf-8') expected_file_data = base64.b64encode(data).decode('utf-8') self.run_command( 'boot --flavor 1 --image %s --user-data %s some-server' % (FAKE_UUID_1, testfile)) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'user_data': expected_file_data }}, ) def test_boot_avzone(self): self.run_command( 'boot --flavor 1 --image %s --availability-zone avzone ' 'some-server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'availability_zone': 'avzone', 'min_count': 1, 'max_count': 1 }}, ) def test_boot_secgroup(self): self.run_command( 'boot --flavor 1 --image %s --security-groups secgroup1,' 'secgroup2 some-server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'security_groups': [{'name': 'secgroup1'}, {'name': 'secgroup2'}], 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_config_drive(self): self.run_command( 'boot --flavor 1 --image %s --config-drive 1 some-server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'config_drive': True }}, ) def test_boot_access_ip(self): self.run_command( 'boot --flavor 1 --image %s --access-ip-v4 10.10.10.10 ' '--access-ip-v6 ::1 some-server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'accessIPv4': '10.10.10.10', 'accessIPv6': '::1', 'max_count': 1, 'min_count': 1 }}, ) def test_boot_config_drive_custom(self): self.run_command( 'boot --flavor 1 --image %s --config-drive /dev/hda some-server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'config_drive': '/dev/hda' }}, ) def test_boot_invalid_user_data(self): invalid_file = os.path.join(os.path.dirname(__file__), 'no_such_file') cmd = ('boot some-server --flavor 1 --image %s' ' --user-data %s' % (FAKE_UUID_1, invalid_file)) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_image_no_bdms(self): cmd = 'boot --flavor 1 some-server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_flavor(self): cmd = 'boot --image %s some-server' % FAKE_UUID_1 self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_not_key_value_bdm(self): cmd = ('boot --flavor 1 --image %s --block-device %s,tag=foo ' 'test-server' % (FAKE_UUID_1, FAKE_UUID_2)) self.assertRaises(argparse.ArgumentTypeError, self.run_command, cmd) def test_boot_not_key_value_ephemeral(self): cmd = ('boot --flavor 1 --image %s --ephemeral %s,tag=foo ' 'test-server' % (FAKE_UUID_1, FAKE_UUID_2)) self.assertRaises(argparse.ArgumentTypeError, self.run_command, cmd) def test_boot_no_image_bdms(self): self.run_command( 'boot --flavor 1 --block-device-mapping vda=blah:::0 some-server' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping': [ { 'volume_id': 'blah', 'delete_on_termination': '0', 'device_name': 'vda' } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_image_bdms_v2(self): self.run_command( 'boot --flavor 1 --image %s --block-device id=fake-id,' 'source=volume,dest=volume,device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve some-server' % FAKE_UUID_1 ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': FAKE_UUID_1, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True, }, { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'device_name': 'vda', 'volume_size': '1', 'guest_format': 'ext4', 'device_type': 'disk', 'delete_on_termination': False, }, ], 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_image_bdms_v2_wrong_source_type(self): self.assertRaises( exceptions.CommandError, self.run_command, 'boot --flavor 1 --image %s --block-device id=fake-id,' 'source=fake,device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve some-server' % FAKE_UUID_1) def test_boot_image_bdms_v2_no_source_type_no_destination_type(self): self.run_command( 'boot --flavor 1 --image %s --block-device id=fake-id,' 'device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve some-server' % FAKE_UUID_1 ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': FAKE_UUID_1, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True, }, { 'uuid': 'fake-id', 'source_type': 'blank', 'destination_type': 'local', 'device_name': 'vda', 'volume_size': '1', 'guest_format': 'ext4', 'device_type': 'disk', 'delete_on_termination': False, }, ], 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_image_bdms_v2_no_destination_type(self): self.run_command( 'boot --flavor 1 --image %s --block-device id=fake-id,' 'source=volume,device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve some-server' % FAKE_UUID_1 ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': FAKE_UUID_1, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True, }, { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'device_name': 'vda', 'volume_size': '1', 'guest_format': 'ext4', 'device_type': 'disk', 'delete_on_termination': False, }, ], 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_image_bdms_v2_wrong_destination_type(self): self.assertRaises( exceptions.CommandError, self.run_command, 'boot --flavor 1 --image %s --block-device id=fake-id,' 'source=volume,dest=dest1,device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve some-server' % FAKE_UUID_1) def test_boot_image_bdms_v2_with_tag(self): self.run_command( 'boot --flavor 1 --image %s --block-device id=fake-id,' 'source=volume,dest=volume,device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve,tag=foo some-server' % FAKE_UUID_1, api_version='2.32' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': FAKE_UUID_1, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True, }, { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'device_name': 'vda', 'volume_size': '1', 'guest_format': 'ext4', 'device_type': 'disk', 'delete_on_termination': False, 'tag': 'foo', }, ], 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_no_image_bdms_v2(self): self.run_command( 'boot --flavor 1 --block-device id=fake-id,source=volume,' 'dest=volume,bus=virtio,device=vda,size=1,format=ext4,bootindex=0,' 'type=disk,shutdown=preserve some-server' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'disk_bus': 'virtio', 'device_name': 'vda', 'volume_size': '1', 'guest_format': 'ext4', 'boot_index': '0', 'device_type': 'disk', 'delete_on_termination': False, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) cmd = 'boot --flavor 1 --boot-volume fake-id some-server' self.run_command(cmd) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) cmd = 'boot --flavor 1 --snapshot fake-id some-server' self.run_command(cmd) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': 'fake-id', 'source_type': 'snapshot', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) self.run_command('boot --flavor 1 --swap 1 some-server') self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'guest_format': 'swap', 'volume_size': '1', 'delete_on_termination': True, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) self.run_command( 'boot --flavor 1 --ephemeral size=1,format=ext4 some-server' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'guest_format': 'ext4', 'volume_size': '1', 'delete_on_termination': True, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_bdms_v2_invalid_shutdown_value(self): self.assertRaises(exceptions.CommandError, self.run_command, ('boot --flavor 1 --image %s --block-device ' 'id=fake-id,source=volume,dest=volume,device=vda,' 'size=1,format=ext4,type=disk,shutdown=foobar ' 'some-server' % FAKE_UUID_1)) def test_boot_metadata(self): self.run_command('boot --image %s --flavor 1 --meta foo=bar=pants' ' --meta spam=eggs some-server ' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'metadata': {'foo': 'bar=pants', 'spam': 'eggs'}, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_with_incorrect_metadata(self): cmd = ('boot --image %s --flavor 1 --meta foo ' 'some-server ' % FAKE_UUID_1) result = self.assertRaises(argparse.ArgumentTypeError, self.run_command, cmd) expected = "'['foo']' is not in the format of 'key=value'" self.assertEqual(expected, result.args[0]) def test_boot_hints(self): self.run_command('boot --image %s --flavor 1 ' '--hint a=b0=c0 --hint a=b1=c1 some-server ' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }, 'os:scheduler_hints': {'a': ['b0=c0', 'b1=c1']}, }, ) def test_boot_nic_auto_not_alone_after(self): cmd = ('boot --image %s --flavor 1 ' '--nic auto,tag=foo some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nic_auto_not_alone_before(self): cmd = ('boot --image %s --flavor 1 ' '--nic tag=foo,auto some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nic_none_not_alone_before(self): cmd = ('boot --image %s --flavor 1 ' '--nic none,tag=foo some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nic_none_not_alone_after(self): cmd = ('boot --image %s --flavor 1 ' '--nic tag=foo,none some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1 some-server' % FAKE_UUID_1) self.run_command(cmd) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': [ {'uuid': 'a=c', 'fixed_ip': '10.0.0.1'}, ], }, }, ) def test_boot_with_multiple_nics(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=net_a,v4-fixed-ip=10.0.0.1 ' '--nic net-id=net_b some-server' % FAKE_UUID_1) self.run_command(cmd) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': [ {'uuid': 'net_a', 'fixed_ip': '10.0.0.1'}, {'uuid': 'net_b'} ], }, }, ) def test_boot_nics_with_tag(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1,tag=foo some-server' % FAKE_UUID_1) self.run_command(cmd, api_version='2.32') self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': [ {'uuid': 'a=c', 'fixed_ip': '10.0.0.1', 'tag': 'foo'}, ], }, }, ) def test_boot_invalid_nics_pre_v2_32(self): # This is a negative test to make sure we fail with the correct message cmd = ('boot --image %s --flavor 1 ' '--nic net-id=1,port-id=2 some-server' % FAKE_UUID_1) ex = self.assertRaises(exceptions.CommandError, self.run_command, cmd, api_version='2.1') self.assertNotIn('tag=tag', six.text_type(ex)) def test_boot_invalid_nics_v2_32(self): # This is a negative test to make sure we fail with the correct message cmd = ('boot --image %s --flavor 1 ' '--nic net-id=1,port-id=2 some-server' % FAKE_UUID_1) ex = self.assertRaises(exceptions.CommandError, self.run_command, cmd, api_version='2.32') self.assertIn('tag=tag', six.text_type(ex)) def test_boot_invalid_nics_v2_36_auto(self): """This is a negative test to make sure we fail with the correct message. --nic auto isn't allowed before 2.37. """ cmd = ('boot --image %s --flavor 1 --nic auto test' % FAKE_UUID_1) ex = self.assertRaises(exceptions.CommandError, self.run_command, cmd, api_version='2.36') self.assertNotIn('auto,none', six.text_type(ex)) def test_boot_invalid_nics_v2_37(self): """This is a negative test to make sure we fail with the correct message. """ cmd = ('boot --image %s --flavor 1 ' '--nic net-id=1 --nic auto some-server' % FAKE_UUID_1) ex = self.assertRaises(exceptions.CommandError, self.run_command, cmd, api_version='2.37') self.assertIn('auto,none', six.text_type(ex)) def test_boot_nics_auto_allocate_default(self): """Tests that if microversion>=2.37 is specified and no --nics are specified that a single --nic with net-id=auto is used. """ cmd = 'boot --image %s --flavor 1 some-server' % FAKE_UUID_1 self.run_command(cmd, api_version='2.37') self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': 'auto', }, }, ) def test_boot_nics_auto_allocate_none(self): """Tests specifying '--nic none' with microversion 2.37 """ cmd = 'boot --image %s --flavor 1 --nic none some-server' % FAKE_UUID_1 self.run_command(cmd, api_version='2.37') self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': 'none', }, }, ) def test_boot_nics_ipv6(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=a=c,v6-fixed-ip=2001:db9:0:1::10 some-server' % FAKE_UUID_1) self.run_command(cmd) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': [ {'uuid': 'a=c', 'fixed_ip': '2001:db9:0:1::10'}, ], }, }, ) def test_boot_nics_both_ipv4_and_ipv6(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1,' 'v6-fixed-ip=2001:db9:0:1::10 some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_no_value(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_random_key(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1,foo=bar some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_no_netid_or_portid(self): cmd = ('boot --image %s --flavor 1 ' '--nic v4-fixed-ip=10.0.0.1 some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_netid_and_portid(self): cmd = ('boot --image %s --flavor 1 ' '--nic port-id=some=port,net-id=some=net some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_invalid_ipv4(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=2001:db9:0:1::10 some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_invalid_ipv6(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=a=c,v6-fixed-ip=10.0.0.1 some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_net_id_twice(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-id=net-id1,net-id=net-id2 some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_net_name_neutron(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-name=private some-server' % FAKE_UUID_1) self.run_command(cmd) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': [ {'uuid': 'e43a56c7-11d4-45c9-8681-ddc8171b5850'}, ], }, }, ) def test_boot_nics_net_name_neutron_dup(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-name=duplicate some-server' % FAKE_UUID_1) # this should raise a multiple matches error msg = ("Multiple network matches found for 'duplicate', " "use an ID to be more specific.") with testtools.ExpectedException(exceptions.CommandError, msg): self.run_command(cmd) def test_boot_nics_net_name_neutron_blank(self): cmd = ('boot --image %s --flavor 1 ' '--nic net-name=blank some-server' % FAKE_UUID_1) # this should raise a multiple matches error msg = 'No Network matching blank\..*' with testtools.ExpectedException(exceptions.CommandError, msg): self.run_command(cmd) # TODO(sdague): the following tests should really avoid mocking # out other tests, and they should check the string in the # CommandError, because it's not really enough to distinguish # between various errors. @mock.patch('novaclient.v2.shell._find_network_id', return_value='net-id') def test_boot_nics_net_name_and_net_id(self, mock_find_network_id): cmd = ('boot --image %s --flavor 1 ' '--nic net-name=some-net,net-id=some-id some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) @mock.patch('novaclient.v2.shell._find_network_id', return_value='net-id') def test_boot_nics_net_name_and_port_id(self, mock_find_network_id): cmd = ('boot --image %s --flavor 1 ' '--nic net-name=some-net,port-id=some-id some-server' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_files(self): testfile = os.path.join(os.path.dirname(__file__), 'testfile.txt') with open(testfile) as testfile_fd: data = testfile_fd.read() expected = base64.b64encode(data.encode('utf-8')).decode('utf-8') cmd = ('boot some-server --flavor 1 --image %s' ' --file /tmp/foo=%s --file /tmp/bar=%s') self.run_command(cmd % (FAKE_UUID_1, testfile, testfile)) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'personality': [ {'path': '/tmp/bar', 'contents': expected}, {'path': '/tmp/foo', 'contents': expected}, ] }}, ) def test_boot_invalid_files(self): invalid_file = os.path.join(os.path.dirname(__file__), 'asdfasdfasdfasdf') cmd = ('boot some-server --flavor 1 --image %s' ' --file /foo=%s' % (FAKE_UUID_1, invalid_file)) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_max_min_count(self): self.run_command('boot --image %s --flavor 1 --min-count 1' ' --max-count 3 server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 3, } }) def test_boot_invalid_min_count(self): cmd = 'boot --image %s --flavor 1 --min-count 0 server' % FAKE_UUID_1 self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_min_max_count(self): self.run_command('boot --image %s --flavor 1 --max-count 3 server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 3, } }) self.run_command('boot --image %s --flavor 1 --min-count 3 server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'server', 'imageRef': FAKE_UUID_1, 'min_count': 3, 'max_count': 3, } }) self.run_command('boot --image %s --flavor 1 ' '--min-count 3 --max-count 3 server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'server', 'imageRef': FAKE_UUID_1, 'min_count': 3, 'max_count': 3, } }) self.run_command('boot --image %s --flavor 1 ' '--min-count 3 --max-count 5 server' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'server', 'imageRef': FAKE_UUID_1, 'min_count': 3, 'max_count': 5, } }) cmd = ('boot --image %s --flavor 1 --min-count 3 --max-count 1 serv' % FAKE_UUID_1) self.assertRaises(exceptions.CommandError, self.run_command, cmd) @mock.patch('novaclient.v2.shell._poll_for_status') def test_boot_with_poll(self, poll_method): self.run_command('boot --flavor 1 --image %s some-server --poll' % FAKE_UUID_1) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, }}, ) self.assertEqual(1, poll_method.call_count) poll_method.assert_has_calls( [mock.call(self.shell.cs.servers.get, '1234', 'building', ['active'])]) def test_boot_with_poll_to_check_VM_state_error(self): self.assertRaises(exceptions.ResourceInErrorState, self.run_command, 'boot --flavor 1 --image %s some-bad-server --poll' % FAKE_UUID_1) def test_boot_named_flavor(self): self.run_command(["boot", "--image", FAKE_UUID_1, "--flavor", "512 MB Server", "--max-count", "3", "server"]) self.assert_called('GET', '/v2/images/' + FAKE_UUID_1, pos=0) self.assert_called('GET', '/flavors/512 MB Server', pos=1) self.assert_called('GET', '/flavors?is_public=None', pos=2) self.assert_called('GET', '/flavors/2', pos=3) self.assert_called( 'POST', '/servers', { 'server': { 'flavorRef': '2', 'name': 'server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 3, } }, pos=4) def test_boot_invalid_ephemeral_data_format(self): cmd = ('boot --flavor 1 --image %s --ephemeral 1 some-server' % FAKE_UUID_1) self.assertRaises(argparse.ArgumentTypeError, self.run_command, cmd) def test_boot_with_tags(self): self.run_command('boot --flavor 1 --image %s --nic auto ' 'some-server --tags tag1,tag2' % FAKE_UUID_1, api_version='2.52') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': 'auto', 'tags': ['tag1', 'tag2'] }}, ) def test_boot_without_tags_v252(self): self.run_command('boot --flavor 1 --image %s --nic auto ' 'some-server' % FAKE_UUID_1, api_version='2.52') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': FAKE_UUID_1, 'min_count': 1, 'max_count': 1, 'networks': 'auto', }}, ) def test_boot_with_tags_pre_v2_52(self): cmd = ('boot --flavor 1 --image %s some-server ' '--tags tag1,tag2' % FAKE_UUID_1) self.assertRaises(SystemExit, self.run_command, cmd, api_version='2.51') def test_flavor_list(self): self.run_command('flavor-list') self.assert_called_anytime('GET', '/flavors/detail') def test_flavor_list_with_extra_specs(self): self.run_command('flavor-list --extra-specs') self.assert_called('GET', '/flavors/aa1/os-extra_specs') self.assert_called_anytime('GET', '/flavors/detail') def test_flavor_list_with_all(self): self.run_command('flavor-list --all') self.assert_called('GET', '/flavors/detail?is_public=None') def test_flavor_list_with_limit_and_marker(self): self.run_command('flavor-list --marker 1 --limit 2') self.assert_called('GET', '/flavors/detail?limit=2&marker=1') def test_flavor_list_with_min_disk(self): self.run_command('flavor-list --min-disk 20') self.assert_called('GET', '/flavors/detail?minDisk=20') def test_flavor_list_with_min_ram(self): self.run_command('flavor-list --min-ram 512') self.assert_called('GET', '/flavors/detail?minRam=512') def test_flavor_list_with_sort_key_dir(self): self.run_command('flavor-list --sort-key id --sort-dir asc') self.assert_called('GET', '/flavors/detail?sort_dir=asc&sort_key=id') def test_flavor_show(self): self.run_command('flavor-show 1') self.assert_called_anytime('GET', '/flavors/1') def test_flavor_show_with_alphanum_id(self): self.run_command('flavor-show aa1') self.assert_called_anytime('GET', '/flavors/aa1') def test_flavor_show_by_name(self): self.run_command(['flavor-show', '128 MB Server']) self.assert_called('GET', '/flavors/128 MB Server', pos=0) self.assert_called('GET', '/flavors?is_public=None', pos=1) self.assert_called('GET', '/flavors/aa1', pos=2) self.assert_called('GET', '/flavors/aa1/os-extra_specs', pos=3) def test_flavor_show_by_name_priv(self): self.run_command(['flavor-show', '512 MB Server']) self.assert_called('GET', '/flavors/512 MB Server', pos=0) self.assert_called('GET', '/flavors?is_public=None', pos=1) self.assert_called('GET', '/flavors/2', pos=2) self.assert_called('GET', '/flavors/2/os-extra_specs', pos=3) def test_flavor_key_set(self): self.run_command('flavor-key 1 set k1=v1') self.assert_called('POST', '/flavors/1/os-extra_specs', {'extra_specs': {'k1': 'v1'}}) def test_flavor_key_unset(self): self.run_command('flavor-key 1 unset k1') self.assert_called('DELETE', '/flavors/1/os-extra_specs/k1') def test_flavor_access_list_flavor(self): self.run_command('flavor-access-list --flavor 2') self.assert_called('GET', '/flavors/2/os-flavor-access') def test_flavor_access_list_no_filter(self): cmd = 'flavor-access-list' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_flavor_access_list_public(self): cmd = 'flavor-access-list --flavor 1' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_flavor_access_add_by_id(self): self.run_command('flavor-access-add 2 proj2') self.assert_called('POST', '/flavors/2/action', {'addTenantAccess': {'tenant': 'proj2'}}) def test_flavor_access_add_by_name(self): self.run_command(['flavor-access-add', '512 MB Server', 'proj2']) self.assert_called('POST', '/flavors/2/action', {'addTenantAccess': {'tenant': 'proj2'}}) def test_flavor_access_remove_by_id(self): self.run_command('flavor-access-remove 2 proj2') self.assert_called('POST', '/flavors/2/action', {'removeTenantAccess': {'tenant': 'proj2'}}) def test_flavor_access_remove_by_name(self): self.run_command(['flavor-access-remove', '512 MB Server', 'proj2']) self.assert_called('POST', '/flavors/2/action', {'removeTenantAccess': {'tenant': 'proj2'}}) def test_create_image(self): self.run_command('image-create sample-server mysnapshot') self.assert_called( 'POST', '/servers/1234/action', {'createImage': {'name': 'mysnapshot', 'metadata': {}}}, ) def test_create_image_2_45(self): """Tests the image-create command with microversion 2.45 which does not change the output of the command, just how the response from the server is processed. """ self.run_command('image-create sample-server mysnapshot', api_version='2.45') self.assert_called( 'POST', '/servers/1234/action', {'createImage': {'name': 'mysnapshot', 'metadata': {}}}, ) def test_create_image_with_incorrect_metadata(self): cmd = 'image-create sample-server mysnapshot --metadata foo' result = self.assertRaises(argparse.ArgumentTypeError, self.run_command, cmd) expected = "'['foo']' is not in the format of 'key=value'" self.assertEqual(expected, result.args[0]) def test_create_image_with_metadata(self): self.run_command( 'image-create sample-server mysnapshot --metadata mykey=123') self.assert_called( 'POST', '/servers/1234/action', {'createImage': {'name': 'mysnapshot', 'metadata': {'mykey': '123'}}}, ) def test_create_image_show(self): output, _err = self.run_command( 'image-create sample-server mysnapshot --show') self.assert_called_anytime( 'POST', '/servers/1234/action', {'createImage': {'name': 'mysnapshot', 'metadata': {}}}, ) self.assertIn('My Server Backup', output) self.assertIn('SAVING', output) @mock.patch('novaclient.v2.shell._poll_for_status') def test_create_image_with_poll(self, poll_method): self.run_command( 'image-create sample-server mysnapshot --poll') self.assert_called_anytime( 'POST', '/servers/1234/action', {'createImage': {'name': 'mysnapshot', 'metadata': {}}}, ) self.assertEqual(1, poll_method.call_count) poll_method.assert_has_calls( [mock.call(self.shell.cs.glance.find_image, fakes.FAKE_IMAGE_UUID_SNAPSHOT, 'snapshotting', ['active'])]) def test_create_image_with_poll_to_check_image_state_deleted(self): self.assertRaises( exceptions.InstanceInDeletedState, self.run_command, 'image-create sample-server mysnapshot_deleted --poll') def test_list(self): self.run_command('list') self.assert_called('GET', '/servers/detail') def test_list_minimal(self): self.run_command('list --minimal') self.assert_called('GET', '/servers') def test_list_deleted(self): self.run_command('list --deleted') self.assert_called('GET', '/servers/detail?deleted=True') def test_list_with_images(self): self.run_command('list --image %s' % FAKE_UUID_1) self.assert_called('GET', '/servers/detail?image=%s' % FAKE_UUID_1) def test_list_with_flavors(self): self.run_command('list --flavor 1') self.assert_called('GET', '/servers/detail?flavor=1') def test_list_by_tenant(self): self.run_command('list --tenant fake_tenant') self.assert_called( 'GET', '/servers/detail?all_tenants=1&tenant_id=fake_tenant') def test_list_by_user(self): self.run_command('list --user fake_user') self.assert_called( 'GET', '/servers/detail?all_tenants=1&user_id=fake_user') def test_list_with_single_sort_key_no_dir(self): self.run_command('list --sort 1') self.assert_called( 'GET', ('/servers/detail?sort_dir=desc&sort_key=1')) def test_list_with_single_sort_key_and_dir(self): self.run_command('list --sort 1:asc') self.assert_called( 'GET', ('/servers/detail?sort_dir=asc&sort_key=1')) def test_list_with_sort_keys_no_dir(self): self.run_command('list --sort 1,2') self.assert_called( 'GET', ('/servers/detail?sort_dir=desc&sort_dir=desc&' 'sort_key=1&sort_key=2')) def test_list_with_sort_keys_and_dirs(self): self.run_command('list --sort 1:asc,2:desc') self.assert_called( 'GET', ('/servers/detail?sort_dir=asc&sort_dir=desc&' 'sort_key=1&sort_key=2')) def test_list_with_sort_keys_and_some_dirs(self): self.run_command('list --sort 1,2:asc') self.assert_called( 'GET', ('/servers/detail?sort_dir=desc&sort_dir=asc&' 'sort_key=1&sort_key=2')) def test_list_with_invalid_sort_dir_one(self): cmd = 'list --sort 1:foo' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_list_with_invalid_sort_dir_two(self): cmd = 'list --sort 1:asc,2:foo' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_list_sortby_index_with_sort(self): # sortby_index is None if there is sort information for cmd in ['list --sort key', 'list --sort key:desc', 'list --sort key1,key2:asc']: with mock.patch('novaclient.utils.print_list') as mock_print_list: self.run_command(cmd) mock_print_list.assert_called_once_with( mock.ANY, mock.ANY, mock.ANY, sortby_index=None) def test_list_sortby_index_without_sort(self): # sortby_index is 1 without sort information for cmd in ['list', 'list --minimal', 'list --deleted']: with mock.patch('novaclient.utils.print_list') as mock_print_list: self.run_command(cmd) mock_print_list.assert_called_once_with( mock.ANY, mock.ANY, mock.ANY, sortby_index=1) def test_list_fields(self): output, _err = self.run_command( 'list --fields ' 'host,security_groups,OS-EXT-MOD:some_thing') self.assert_called('GET', '/servers/detail') self.assertIn('computenode1', output) self.assertIn('securitygroup1', output) self.assertIn('OS-EXT-MOD: Some Thing', output) self.assertIn('mod_some_thing_value', output) @mock.patch( 'novaclient.tests.unit.v2.fakes.FakeSessionClient.get_servers_detail') def test_list_fields_no_instances(self, mock_get_servers_detail): mock_get_servers_detail.return_value = (200, {}, {"servers": []}) stdout, _stderr = self.run_command('list --fields metadata,networks') # Because there are no instances, you just get the default columns # rather than the ones you actually asked for (Metadata, Networks). defaults = 'ID | Name | Status | Task State | Power State | Networks' self.assertIn(defaults, stdout) def test_list_invalid_fields(self): self.assertRaises(exceptions.CommandError, self.run_command, 'list --fields host,security_groups,' 'OS-EXT-MOD:some_thing,invalid') def test_list_with_marker(self): self.run_command('list --marker some-uuid') self.assert_called('GET', '/servers/detail?marker=some-uuid') def test_list_with_limit(self): self.run_command('list --limit 3') self.assert_called('GET', '/servers/detail?limit=3') def test_list_with_changes_since(self): self.run_command('list --changes-since 2016-02-29T06:23:22') self.assert_called( 'GET', '/servers/detail?changes-since=2016-02-29T06%3A23%3A22') def test_list_with_changes_since_invalid_value(self): self.assertRaises(exceptions.CommandError, self.run_command, 'list --changes-since 0123456789') def test_list_fields_redundant(self): output, _err = self.run_command('list --fields id,status,status') header = output.splitlines()[1] self.assertEqual(1, header.count('ID')) self.assertEqual(0, header.count('Id')) self.assertEqual(1, header.count('Status')) def test_meta_parsing(self): meta = ['key1=meta1', 'key2=meta2'] ref = {'key1': 'meta1', 'key2': 'meta2'} parsed_meta = novaclient.v2.shell._meta_parsing(meta) self.assertEqual(ref, parsed_meta) def test_reboot(self): self.run_command('reboot sample-server') self.assert_called('POST', '/servers/1234/action', {'reboot': {'type': 'SOFT'}}) self.run_command('reboot sample-server --hard') self.assert_called('POST', '/servers/1234/action', {'reboot': {'type': 'HARD'}}) def test_reboot_many(self): self.run_command('reboot sample-server sample-server2') self.assert_called('POST', '/servers/1234/action', {'reboot': {'type': 'SOFT'}}, pos=-2) self.assert_called('POST', '/servers/5678/action', {'reboot': {'type': 'SOFT'}}, pos=-1) def test_rebuild(self): output, _err = self.run_command('rebuild sample-server %s' % FAKE_UUID_1) self.assert_called('GET', '/servers?name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_1, pos=2) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': FAKE_UUID_1}}, pos=3) self.assert_called('GET', '/flavors/1', pos=4) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=5) self.assertIn('adminPass', output) def test_rebuild_password(self): output, _err = self.run_command('rebuild sample-server %s' ' --rebuild-password asdf' % FAKE_UUID_1) self.assert_called('GET', '/servers?name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_1, pos=2) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': FAKE_UUID_1, 'adminPass': 'asdf'}}, pos=3) self.assert_called('GET', '/flavors/1', pos=4) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=5) self.assertIn('adminPass', output) def test_rebuild_preserve_ephemeral(self): self.run_command('rebuild sample-server %s --preserve-ephemeral' % FAKE_UUID_1) self.assert_called('GET', '/servers?name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_1, pos=2) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': FAKE_UUID_1, 'preserve_ephemeral': True}}, pos=3) self.assert_called('GET', '/flavors/1', pos=4) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=5) def test_rebuild_name_meta(self): self.run_command('rebuild sample-server %s --name asdf --meta ' 'foo=bar' % FAKE_UUID_1) self.assert_called('GET', '/servers?name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_1, pos=2) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': FAKE_UUID_1, 'name': 'asdf', 'metadata': {'foo': 'bar'}}}, pos=3) self.assert_called('GET', '/flavors/1', pos=4) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=5) def test_rebuild_with_incorrect_metadata(self): cmd = 'rebuild sample-server %s --name asdf --meta foo' % FAKE_UUID_1 result = self.assertRaises(argparse.ArgumentTypeError, self.run_command, cmd) expected = "'['foo']' is not in the format of 'key=value'" self.assertEqual(expected, result.args[0]) def test_start(self): self.run_command('start sample-server') self.assert_called('POST', '/servers/1234/action', {'os-start': None}) def test_start_with_all_tenants(self): self.run_command('start sample-server --all-tenants') self.assert_called('GET', '/servers?all_tenants=1&name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('POST', '/servers/1234/action', {'os-start': None}) def test_stop(self): self.run_command('stop sample-server') self.assert_called('POST', '/servers/1234/action', {'os-stop': None}) def test_stop_with_all_tenants(self): self.run_command('stop sample-server --all-tenants') self.assert_called('GET', '/servers?all_tenants=1&name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('POST', '/servers/1234/action', {'os-stop': None}) def test_pause(self): self.run_command('pause sample-server') self.assert_called('POST', '/servers/1234/action', {'pause': None}) def test_unpause(self): self.run_command('unpause sample-server') self.assert_called('POST', '/servers/1234/action', {'unpause': None}) def test_lock(self): self.run_command('lock sample-server') self.assert_called('POST', '/servers/1234/action', {'lock': None}) def test_unlock(self): self.run_command('unlock sample-server') self.assert_called('POST', '/servers/1234/action', {'unlock': None}) def test_suspend(self): self.run_command('suspend sample-server') self.assert_called('POST', '/servers/1234/action', {'suspend': None}) def test_resume(self): self.run_command('resume sample-server') self.assert_called('POST', '/servers/1234/action', {'resume': None}) def test_rescue(self): self.run_command('rescue sample-server') self.assert_called('POST', '/servers/1234/action', {'rescue': None}) def test_rescue_password(self): self.run_command('rescue sample-server --password asdf') self.assert_called('POST', '/servers/1234/action', {'rescue': {'adminPass': 'asdf'}}) def test_rescue_image(self): self.run_command('rescue sample-server --image %s' % FAKE_UUID_1) self.assert_called('POST', '/servers/1234/action', {'rescue': {'rescue_image_ref': FAKE_UUID_1}}) def test_unrescue(self): self.run_command('unrescue sample-server') self.assert_called('POST', '/servers/1234/action', {'unrescue': None}) def test_shelve(self): self.run_command('shelve sample-server') self.assert_called('POST', '/servers/1234/action', {'shelve': None}) def test_shelve_offload(self): self.run_command('shelve-offload sample-server') self.assert_called('POST', '/servers/1234/action', {'shelveOffload': None}) def test_unshelve(self): self.run_command('unshelve sample-server') self.assert_called('POST', '/servers/1234/action', {'unshelve': None}) def test_migrate(self): self.run_command('migrate sample-server') self.assert_called('POST', '/servers/1234/action', {'migrate': None}) def test_resize(self): self.run_command('resize sample-server 1') self.assert_called('POST', '/servers/1234/action', {'resize': {'flavorRef': 1}}) def test_resize_confirm(self): self.run_command('resize-confirm sample-server') self.assert_called('POST', '/servers/1234/action', {'confirmResize': None}) def test_resize_revert(self): self.run_command('resize-revert sample-server') self.assert_called('POST', '/servers/1234/action', {'revertResize': None}) @mock.patch('getpass.getpass', mock.Mock(return_value='p')) def test_set_password(self): self.run_command('set-password sample-server') self.assert_called('POST', '/servers/1234/action', {'changePassword': {'adminPass': 'p'}}) def test_show(self): self.run_command('show 1234') self.assert_called('GET', '/servers?name=1234', pos=0) self.assert_called('GET', '/servers?name=1234', pos=1) self.assert_called('GET', '/servers/1234', pos=2) self.assert_called('GET', '/flavors/1', pos=3) self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4) def test_show_no_image(self): self.run_command('show 9012') self.assert_called('GET', '/servers/9012', pos=-2) self.assert_called('GET', '/flavors/1', pos=-1) def test_show_bad_id(self): self.assertRaises(exceptions.CommandError, self.run_command, 'show xxx') def test_show_unavailable_image_and_flavor(self): output, _ = self.run_command('show 9013') self.assert_called('GET', '/servers/9013', pos=-6) self.assert_called('GET', '/flavors/80645cf4-6ad3-410a-bbc8-6f3e1e291f51', pos=-5) self.assert_called('GET', '/v2/images/3e861307-73a6-4d1f-8d68-f68b03223032', pos=-1) self.assertIn('Image not found', output) self.assertIn('Flavor not found', output) def test_show_with_name_help(self): output, _ = self.run_command('show help') self.assert_called('GET', '/servers/9014', pos=-6) @mock.patch('novaclient.v2.shell.utils.print_dict') def test_print_server(self, mock_print_dict): self.run_command('show 5678') args, kwargs = mock_print_dict.call_args parsed_server = args[0] self.assertEqual('securitygroup1, securitygroup2', parsed_server['security_groups']) def test_delete(self): self.run_command('delete 1234') self.assert_called('DELETE', '/servers/1234') self.run_command('delete sample-server') self.assert_called('DELETE', '/servers/1234') def test_force_delete(self): self.run_command('force-delete 1234') self.assert_called('POST', '/servers/1234/action', {'forceDelete': None}) self.run_command('force-delete sample-server') self.assert_called('POST', '/servers/1234/action', {'forceDelete': None}) def test_restore(self): self.run_command('restore 1234') self.assert_called('POST', '/servers/1234/action', {'restore': None}) self.run_command('restore sample-server') self.assert_called('POST', '/servers/1234/action', {'restore': None}) def test_restore_withname(self): self.run_command('restore sample-server') self.assert_called('GET', '/servers?deleted=True&name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('POST', '/servers/1234/action', {'restore': None}, pos=2) def test_delete_two_with_two_existent(self): self.run_command('delete 1234 5678') self.assert_called('DELETE', '/servers/1234', pos=-5) self.assert_called('DELETE', '/servers/5678', pos=-1) self.run_command('delete sample-server sample-server2') self.assert_called('GET', '/servers?name=sample-server', pos=-6) self.assert_called('GET', '/servers/1234', pos=-5) self.assert_called('DELETE', '/servers/1234', pos=-4) self.assert_called('GET', '/servers?name=sample-server2', pos=-3) self.assert_called('GET', '/servers/5678', pos=-2) self.assert_called('DELETE', '/servers/5678', pos=-1) def test_delete_two_with_two_existent_all_tenants(self): self.run_command('delete sample-server sample-server2 --all-tenants') self.assert_called('GET', '/servers?all_tenants=1&name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('DELETE', '/servers/1234', pos=2) self.assert_called('GET', '/servers?all_tenants=1&name=sample-server2', pos=3) self.assert_called('GET', '/servers/5678', pos=4) self.assert_called('DELETE', '/servers/5678', pos=5) def test_delete_two_with_one_nonexistent(self): cmd = 'delete 1234 123456789' self.assertRaises(exceptions.CommandError, self.run_command, cmd) self.assert_called_anytime('DELETE', '/servers/1234') cmd = 'delete sample-server nonexistentserver' self.assertRaises(exceptions.CommandError, self.run_command, cmd) self.assert_called_anytime('DELETE', '/servers/1234') def test_delete_one_with_one_nonexistent(self): cmd = 'delete 123456789' self.assertRaises(exceptions.CommandError, self.run_command, cmd) cmd = 'delete nonexistent-server1' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_delete_two_with_two_nonexistent(self): cmd = 'delete 123456789 987654321' self.assertRaises(exceptions.CommandError, self.run_command, cmd) cmd = 'delete nonexistent-server1 nonexistent-server2' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_diagnostics(self): self.run_command('diagnostics 1234') self.assert_called('GET', '/servers/1234/diagnostics') self.run_command('diagnostics sample-server') self.assert_called('GET', '/servers/1234/diagnostics') def test_refresh_network(self): self.run_command('refresh-network 1234') self.assert_called('POST', '/os-server-external-events', {'events': [{'name': 'network-changed', 'server_uuid': '1234'}]}) def test_set_meta_set(self): self.run_command('meta 1234 set key1=val1 key2=val2') self.assert_called('POST', '/servers/1234/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}) def test_set_meta_delete_dict(self): self.run_command('meta 1234 delete key1=val1 key2=val2') self.assert_called('DELETE', '/servers/1234/metadata/key1') self.assert_called('DELETE', '/servers/1234/metadata/key2', pos=-2) def test_set_meta_delete_keys(self): self.run_command('meta 1234 delete key1 key2') self.assert_called('DELETE', '/servers/1234/metadata/key1') self.assert_called('DELETE', '/servers/1234/metadata/key2', pos=-2) def test_set_host_meta(self): self.run_command('host-meta hyper set key1=val1 key2=val2') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=1) self.assert_called('POST', '/servers/uuid2/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=2) self.assert_called('POST', '/servers/uuid3/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=3) self.assert_called('POST', '/servers/uuid4/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=4) def test_set_host_meta_with_no_servers(self): self.run_command('host-meta hyper_no_servers set key1=val1 key2=val2') self.assert_called('GET', '/os-hypervisors/hyper_no_servers/servers') def test_delete_host_meta(self): self.run_command('host-meta hyper delete key1') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('DELETE', '/servers/uuid1/metadata/key1', pos=1) self.assert_called('DELETE', '/servers/uuid2/metadata/key1', pos=2) def test_server_floating_ip_associate(self): _, err = self.run_command( 'floating-ip-associate sample-server 11.0.0.1') self.assertIn('WARNING: Command floating-ip-associate is deprecated', err) self.assert_called('POST', '/servers/1234/action', {'addFloatingIp': {'address': '11.0.0.1'}}) def test_server_floating_ip_disassociate(self): _, err = self.run_command( 'floating-ip-disassociate sample-server 11.0.0.1') self.assertIn( 'WARNING: Command floating-ip-disassociate is deprecated', err) self.assert_called('POST', '/servers/1234/action', {'removeFloatingIp': {'address': '11.0.0.1'}}) def test_usage_list(self): cmd = 'usage-list --start 2000-01-20 --end 2005-02-01' stdout, _stderr = self.run_command(cmd) self.assert_called('GET', '/os-simple-tenant-usage?' + 'start=2000-01-20T00:00:00&' + 'end=2005-02-01T00:00:00&' + 'detailed=1') # Servers, RAM MB-Hours, CPU Hours, Disk GB-Hours self.assertIn('1 | 25451.76 | 49.71 | 0.00', stdout) def test_usage_list_stitch_together_next_results(self): cmd = 'usage-list --start 2000-01-20 --end 2005-02-01' stdout, _stderr = self.run_command(cmd, api_version='2.40') self.assert_called('GET', '/os-simple-tenant-usage?' 'start=2000-01-20T00:00:00&' 'end=2005-02-01T00:00:00&' 'detailed=1', pos=0) markers = [ 'f079e394-1111-457b-b350-bb5ecc685cdd', 'f079e394-2222-457b-b350-bb5ecc685cdd', ] for pos, marker in enumerate(markers): self.assert_called('GET', '/os-simple-tenant-usage?' 'start=2000-01-20T00:00:00&' 'end=2005-02-01T00:00:00&' 'marker=%s&detailed=1' % (marker), pos=pos + 1) # Servers, RAM MB-Hours, CPU Hours, Disk GB-Hours self.assertIn('2 | 50903.53 | 99.42 | 0.00', stdout) def test_usage_list_no_args(self): timeutils.set_time_override(datetime.datetime(2005, 2, 1, 0, 0)) self.addCleanup(timeutils.clear_time_override) self.run_command('usage-list') self.assert_called('GET', '/os-simple-tenant-usage?' + 'start=2005-01-04T00:00:00&' + 'end=2005-02-02T00:00:00&' + 'detailed=1') def test_usage(self): cmd = 'usage --start 2000-01-20 --end 2005-02-01 --tenant test' stdout, _stderr = self.run_command(cmd) self.assert_called('GET', '/os-simple-tenant-usage/test?' + 'start=2000-01-20T00:00:00&' + 'end=2005-02-01T00:00:00') # Servers, RAM MB-Hours, CPU Hours, Disk GB-Hours self.assertIn('1 | 25451.76 | 49.71 | 0.00', stdout) def test_usage_stitch_together_next_results(self): cmd = 'usage --start 2000-01-20 --end 2005-02-01' stdout, _stderr = self.run_command(cmd, api_version='2.40') self.assert_called('GET', '/os-simple-tenant-usage/tenant_id?' 'start=2000-01-20T00:00:00&' 'end=2005-02-01T00:00:00', pos=0) markers = [ 'f079e394-1111-457b-b350-bb5ecc685cdd', 'f079e394-2222-457b-b350-bb5ecc685cdd', ] for pos, marker in enumerate(markers): self.assert_called('GET', '/os-simple-tenant-usage/tenant_id?' 'start=2000-01-20T00:00:00&' 'end=2005-02-01T00:00:00&' 'marker=%s' % (marker), pos=pos + 1) # Servers, RAM MB-Hours, CPU Hours, Disk GB-Hours self.assertIn('2 | 50903.53 | 99.42 | 0.00', stdout) def test_usage_no_tenant(self): self.run_command('usage --start 2000-01-20 --end 2005-02-01') self.assert_called('GET', '/os-simple-tenant-usage/tenant_id?' + 'start=2000-01-20T00:00:00&' + 'end=2005-02-01T00:00:00') def test_flavor_delete(self): self.run_command("flavor-delete 2") self.assert_called('DELETE', '/flavors/2') def test_flavor_create(self): self.run_command("flavor-create flavorcreate " "1234 512 10 1 --swap 1024 --ephemeral 10 " "--is-public true") self.assert_called('POST', '/flavors', pos=-2) self.assert_called('GET', '/flavors/1', pos=-1) def test_aggregate_list(self): out, err = self.run_command('aggregate-list') self.assert_called('GET', '/os-aggregates') self.assertNotIn('UUID', out) def test_aggregate_list_v2_41(self): out, err = self.run_command('aggregate-list', api_version='2.41') self.assert_called('GET', '/os-aggregates') self.assertIn('UUID', out) self.assertIn('80785864-087b-45a5-a433-b20eac9b58aa', out) self.assertIn('30827713-5957-4b68-8fd3-ccaddb568c24', out) self.assertIn('9a651b22-ce3f-4a87-acd7-98446ef591c4', out) def test_aggregate_create(self): out, err = self.run_command('aggregate-create test_name nova1') body = {"aggregate": {"name": "test_name", "availability_zone": "nova1"}} self.assert_called('POST', '/os-aggregates', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertNotIn('UUID', out) def test_aggregate_create_v2_41(self): out, err = self.run_command('aggregate-create test_name nova1', api_version='2.41') body = {"aggregate": {"name": "test_name", "availability_zone": "nova1"}} self.assert_called('POST', '/os-aggregates', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertIn('UUID', out) self.assertIn('80785864-087b-45a5-a433-b20eac9b58aa', out) def test_aggregate_delete_by_id(self): self.run_command('aggregate-delete 1') self.assert_called('DELETE', '/os-aggregates/1') def test_aggregate_delete_by_name(self): self.run_command('aggregate-delete test') self.assert_called('DELETE', '/os-aggregates/1') def test_aggregate_update_by_id(self): out, err = self.run_command('aggregate-update 1 --name new_name') body = {"aggregate": {"name": "new_name"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertNotIn('UUID', out) def test_aggregate_update_by_id_v2_41(self): out, err = self.run_command('aggregate-update 1 --name new_name', api_version='2.41') body = {"aggregate": {"name": "new_name"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertIn('UUID', out) self.assertIn('80785864-087b-45a5-a433-b20eac9b58aa', out) def test_aggregate_update_by_name(self): self.run_command('aggregate-update test --name new_name ') body = {"aggregate": {"name": "new_name"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_with_availability_zone_by_id(self): self.run_command('aggregate-update 1 --name foo ' '--availability-zone new_zone') body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_with_availability_zone_by_name(self): self.run_command('aggregate-update test --name foo ' '--availability-zone new_zone') body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_set_metadata_add_by_id(self): out, err = self.run_command('aggregate-set-metadata 3 foo=bar') body = {"set_metadata": {"metadata": {"foo": "bar"}}} self.assert_called('POST', '/os-aggregates/3/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/3', pos=-1) self.assertNotIn('UUID', out) def test_aggregate_set_metadata_add_by_id_v2_41(self): out, err = self.run_command('aggregate-set-metadata 3 foo=bar', api_version='2.41') body = {"set_metadata": {"metadata": {"foo": "bar"}}} self.assert_called('POST', '/os-aggregates/3/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/3', pos=-1) self.assertIn('UUID', out) self.assertIn('9a651b22-ce3f-4a87-acd7-98446ef591c4', out) def test_aggregate_set_metadata_add_duplicate_by_id(self): cmd = 'aggregate-set-metadata 3 test=dup' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_aggregate_set_metadata_delete_by_id(self): self.run_command('aggregate-set-metadata 3 none_key') body = {"set_metadata": {"metadata": {"none_key": None}}} self.assert_called('POST', '/os-aggregates/3/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/3', pos=-1) def test_aggregate_set_metadata_delete_missing_by_id(self): cmd = 'aggregate-set-metadata 3 delete_key2' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_aggregate_set_metadata_by_name(self): self.run_command('aggregate-set-metadata test foo=bar') body = {"set_metadata": {"metadata": {"foo": "bar"}}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_add_host_by_id(self): out, err = self.run_command('aggregate-add-host 1 host1') body = {"add_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertNotIn('UUID', out) def test_aggregate_add_host_by_id_v2_41(self): out, err = self.run_command('aggregate-add-host 1 host1', api_version='2.41') body = {"add_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertIn('UUID', out) self.assertIn('80785864-087b-45a5-a433-b20eac9b58aa', out) def test_aggregate_add_host_by_name(self): self.run_command('aggregate-add-host test host1') body = {"add_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_remove_host_by_id(self): out, err = self.run_command('aggregate-remove-host 1 host1') body = {"remove_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertNotIn('UUID', out) def test_aggregate_remove_host_by_id_v2_41(self): out, err = self.run_command('aggregate-remove-host 1 host1', api_version='2.41') body = {"remove_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) self.assertIn('UUID', out) self.assertIn('80785864-087b-45a5-a433-b20eac9b58aa', out) def test_aggregate_remove_host_by_name(self): self.run_command('aggregate-remove-host test host1') body = {"remove_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_show_by_id(self): out, err = self.run_command('aggregate-show 1') self.assert_called('GET', '/os-aggregates/1') self.assertNotIn('UUID', out) def test_aggregate_show_by_id_v2_41(self): out, err = self.run_command('aggregate-show 1', api_version='2.41') self.assert_called('GET', '/os-aggregates/1') self.assertIn('UUID', out) self.assertIn('80785864-087b-45a5-a433-b20eac9b58aa', out) def test_aggregate_show_by_name(self): self.run_command('aggregate-show test') self.assert_called('GET', '/os-aggregates') def test_live_migration(self): self.run_command('live-migration sample-server hostname') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': False, 'disk_over_commit': False}}) self.run_command('live-migration sample-server hostname' ' --block-migrate') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': True, 'disk_over_commit': False}}) self.run_command('live-migration sample-server hostname' ' --block-migrate --disk-over-commit') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': True, 'disk_over_commit': True}}) def test_live_migration_v225(self): self.run_command('live-migration sample-server hostname', api_version='2.25') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto'}}) self.run_command('live-migration sample-server hostname' ' --block-migrate', api_version='2.25') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': True}}) self.run_command('live-migration sample-server', api_version='2.25') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': None, 'block_migration': 'auto'}}) def test_live_migration_v2_30(self): self.run_command('live-migration sample-server hostname', api_version='2.30') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto'}}) self.run_command('live-migration --force sample-server hostname', api_version='2.30') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto', 'force': True}}) def test_live_migration_force_complete(self): self.run_command('live-migration-force-complete sample-server 1', api_version='2.22') self.assert_called('POST', '/servers/1234/migrations/1/action', {'force_complete': None}) def test_list_migrations(self): self.run_command('server-migration-list sample-server', api_version='2.23') self.assert_called('GET', '/servers/1234/migrations') def test_get_migration(self): self.run_command('server-migration-show sample-server 1', api_version='2.23') self.assert_called('GET', '/servers/1234/migrations/1') def test_live_migration_abort(self): self.run_command('live-migration-abort sample-server 1', api_version='2.24') self.assert_called('DELETE', '/servers/1234/migrations/1') def test_host_evacuate_live_with_no_target_host(self): self.run_command('host-evacuate-live hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': None, 'block_migration': False, 'disk_over_commit': False}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) self.assert_called('POST', '/servers/uuid2/action', body, pos=2) self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) def test_host_evacuate_live_2_25(self): self.run_command('host-evacuate-live hyper', api_version='2.25') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': None, 'block_migration': 'auto'}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) self.assert_called('POST', '/servers/uuid2/action', body, pos=2) self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) def test_host_evacuate_live_with_target_host(self): self.run_command('host-evacuate-live hyper ' '--target-host hostname') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': 'hostname', 'block_migration': False, 'disk_over_commit': False}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) self.assert_called('POST', '/servers/uuid2/action', body, pos=2) self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) def test_host_evacuate_live_2_30(self): self.run_command('host-evacuate-live --force hyper ' '--target-host hostname', api_version='2.30') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': 'hostname', 'block_migration': 'auto', 'force': True}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) self.assert_called('POST', '/servers/uuid2/action', body, pos=2) self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) def test_host_evacuate_live_with_block_migration(self): self.run_command('host-evacuate-live --block-migrate hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': None, 'block_migration': True, 'disk_over_commit': False}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) self.assert_called('POST', '/servers/uuid2/action', body, pos=2) self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) def test_host_evacuate_live_with_block_migration_2_25(self): self.run_command('host-evacuate-live --block-migrate hyper', api_version='2.25') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': None, 'block_migration': True}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) self.assert_called('POST', '/servers/uuid2/action', body, pos=2) self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) def test_host_evacuate_live_with_disk_over_commit(self): self.run_command('host-evacuate-live --disk-over-commit hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': None, 'block_migration': False, 'disk_over_commit': True}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) self.assert_called('POST', '/servers/uuid2/action', body, pos=2) self.assert_called('POST', '/servers/uuid3/action', body, pos=3) self.assert_called('POST', '/servers/uuid4/action', body, pos=4) def test_host_evacuate_live_with_disk_over_commit_2_25(self): self.assertRaises(SystemExit, self.run_command, 'host-evacuate-live --disk-over-commit hyper', api_version='2.25') def test_host_evacuate_list_with_max_servers(self): self.run_command('host-evacuate-live --max-servers 1 hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) body = {'os-migrateLive': {'host': None, 'block_migration': False, 'disk_over_commit': False}} self.assert_called('POST', '/servers/uuid1/action', body, pos=1) def test_reset_state(self): self.run_command('reset-state sample-server') self.assert_called('POST', '/servers/1234/action', {'os-resetState': {'state': 'error'}}) self.run_command('reset-state sample-server --active') self.assert_called('POST', '/servers/1234/action', {'os-resetState': {'state': 'active'}}) def test_reset_state_with_all_tenants(self): self.run_command('reset-state sample-server --all-tenants') self.assert_called('GET', '/servers?all_tenants=1&name=sample-server', pos=0) self.assert_called('GET', '/servers/1234', pos=1) self.assert_called('POST', '/servers/1234/action', {'os-resetState': {'state': 'error'}}) def test_reset_state_multiple(self): self.run_command('reset-state sample-server sample-server2') self.assert_called('POST', '/servers/1234/action', {'os-resetState': {'state': 'error'}}, pos=-4) self.assert_called('POST', '/servers/5678/action', {'os-resetState': {'state': 'error'}}, pos=-1) def test_reset_state_active_multiple(self): self.run_command('reset-state --active sample-server sample-server2') self.assert_called('POST', '/servers/1234/action', {'os-resetState': {'state': 'active'}}, pos=-4) self.assert_called('POST', '/servers/5678/action', {'os-resetState': {'state': 'active'}}, pos=-1) def test_reset_network(self): self.run_command('reset-network sample-server') self.assert_called('POST', '/servers/1234/action', {'resetNetwork': None}) def test_services_list(self): self.run_command('service-list') self.assert_called('GET', '/os-services') def test_services_list_v2_53(self): """Tests nova service-list at the 2.53 microversion.""" self.run_command('service-list', api_version='2.53') self.assert_called('GET', '/os-services') def test_services_list_with_host(self): self.run_command('service-list --host host1') self.assert_called('GET', '/os-services?host=host1') def test_services_list_with_binary(self): self.run_command('service-list --binary nova-cert') self.assert_called('GET', '/os-services?binary=nova-cert') def test_services_list_with_host_binary(self): self.run_command('service-list --host host1 --binary nova-cert') self.assert_called('GET', '/os-services?host=host1&binary=nova-cert') def test_services_enable(self): self.run_command('service-enable host1 nova-cert') body = {'host': 'host1', 'binary': 'nova-cert'} self.assert_called('PUT', '/os-services/enable', body) def test_services_enable_v2_53(self): """Tests nova service-enable at the 2.53 microversion.""" self.run_command('service-enable %s' % fakes.FAKE_SERVICE_UUID_1, api_version='2.53') body = {'status': 'enabled'} self.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body) def test_services_enable_default_binary(self): """Tests that the default binary is nova-compute if not specified.""" self.run_command('service-enable host1') body = {'host': 'host1', 'binary': 'nova-compute'} self.assert_called('PUT', '/os-services/enable', body) def test_services_disable(self): self.run_command('service-disable host1 nova-cert') body = {'host': 'host1', 'binary': 'nova-cert'} self.assert_called('PUT', '/os-services/disable', body) def test_services_disable_v2_53(self): """Tests nova service-disable at the 2.53 microversion.""" self.run_command('service-disable %s' % fakes.FAKE_SERVICE_UUID_1, api_version='2.53') body = {'status': 'disabled'} self.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body) def test_services_disable_default_binary(self): """Tests that the default binary is nova-compute if not specified.""" self.run_command('service-disable host1') body = {'host': 'host1', 'binary': 'nova-compute'} self.assert_called('PUT', '/os-services/disable', body) def test_services_disable_with_reason(self): self.run_command('service-disable host1 nova-cert --reason no_reason') body = {'host': 'host1', 'binary': 'nova-cert', 'disabled_reason': 'no_reason'} self.assert_called('PUT', '/os-services/disable-log-reason', body) def test_services_disable_with_reason_v2_53(self): """Tests nova service-disable --reason at microversion 2.53.""" self.run_command('service-disable %s --reason no_reason' % fakes.FAKE_SERVICE_UUID_1, api_version='2.53') body = {'status': 'disabled', 'disabled_reason': 'no_reason'} self.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body) def test_service_force_down_v2_53(self): """Tests nova service-force-down at the 2.53 microversion.""" self.run_command('service-force-down %s' % fakes.FAKE_SERVICE_UUID_1, api_version='2.53') body = {'forced_down': True} self.assert_called( 'PUT', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1, body) def test_services_delete(self): self.run_command('service-delete 1') self.assert_called('DELETE', '/os-services/1') def test_services_delete_v2_53(self): """Tests nova service-delete at the 2.53 microversion.""" self.run_command('service-delete %s' % fakes.FAKE_SERVICE_UUID_1) self.assert_called( 'DELETE', '/os-services/%s' % fakes.FAKE_SERVICE_UUID_1) def test_host_list(self): _, err = self.run_command('host-list') # make sure we said it's deprecated self.assertIn('WARNING: Command host-list is deprecated', err) # and replaced with hypervisor-list self.assertIn('hypervisor-list', err) self.assert_called('GET', '/os-hosts') def test_host_list_with_zone(self): self.run_command('host-list --zone nova') self.assert_called('GET', '/os-hosts?zone=nova') def test_host_update_status(self): _, err = self.run_command('host-update sample-host_1 --status enable') # make sure we said it's deprecated self.assertIn('WARNING: Command host-update is deprecated', err) # and replaced with service-enable self.assertIn('service-enable', err) body = {'status': 'enable'} self.assert_called('PUT', '/os-hosts/sample-host_1', body) def test_host_update_maintenance(self): _, err = ( self.run_command('host-update sample-host_2 --maintenance enable')) # make sure we said it's deprecated self.assertIn('WARNING: Command host-update is deprecated', err) # and there is no replacement self.assertIn('There is no replacement', err) body = {'maintenance_mode': 'enable'} self.assert_called('PUT', '/os-hosts/sample-host_2', body) def test_host_update_multiple_settings(self): _, err = self.run_command('host-update sample-host_3 ' '--status disable --maintenance enable') # make sure we said it's deprecated self.assertIn('WARNING: Command host-update is deprecated', err) # and replaced with service-disable self.assertIn('service-disable', err) body = {'status': 'disable', 'maintenance_mode': 'enable'} self.assert_called('PUT', '/os-hosts/sample-host_3', body) def test_host_startup(self): _, err = self.run_command('host-action sample-host --action startup') # make sure we said it's deprecated self.assertIn('WARNING: Command host-action is deprecated', err) # and there is no replacement self.assertIn('There is no replacement', err) self.assert_called( 'GET', '/os-hosts/sample-host/startup') def test_host_shutdown(self): self.run_command('host-action sample-host --action shutdown') self.assert_called( 'GET', '/os-hosts/sample-host/shutdown') def test_host_reboot(self): self.run_command('host-action sample-host --action reboot') self.assert_called( 'GET', '/os-hosts/sample-host/reboot') def test_host_evacuate_v2_14(self): self.run_command('host-evacuate hyper --target target_hyper', api_version='2.14') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'host': 'target_hyper'}}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'host': 'target_hyper'}}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'host': 'target_hyper'}}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': 'target_hyper'}}, pos=4) def test_host_evacuate(self): self.run_command('host-evacuate hyper --target target_hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=4) def test_host_evacuate_v2_29(self): self.run_command('host-evacuate hyper --target target_hyper --force', api_version='2.29') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'host': 'target_hyper', 'force': True} }, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'host': 'target_hyper', 'force': True} }, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'host': 'target_hyper', 'force': True} }, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': 'target_hyper', 'force': True} }, pos=4) def test_host_evacuate_with_shared_storage(self): self.run_command( 'host-evacuate --on-shared-storage hyper --target target_hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=4) def test_host_evacuate_with_no_target_host(self): self.run_command('host-evacuate --on-shared-storage hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'onSharedStorage': True}}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'onSharedStorage': True}}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'onSharedStorage': True}}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'onSharedStorage': True}}, pos=4) def test_host_servers_migrate(self): self.run_command('host-servers-migrate hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'migrate': None}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'migrate': None}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'migrate': None}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'migrate': None}, pos=4) def test_hypervisor_list(self): self.run_command('hypervisor-list') self.assert_called('GET', '/os-hypervisors') def test_hypervisor_list_matching(self): self.run_command('hypervisor-list --matching hyper') self.assert_called('GET', '/os-hypervisors/hyper/search') def test_hypervisor_list_limit_marker(self): self.run_command('hypervisor-list --limit 10 --marker hyper1', api_version='2.33') self.assert_called('GET', '/os-hypervisors?limit=10&marker=hyper1') def test_hypervisor_servers(self): self.run_command('hypervisor-servers hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers') def test_hypervisor_show_by_id(self): self.run_command('hypervisor-show 1234') self.assert_called('GET', '/os-hypervisors/1234') def test_hypervisor_list_show_by_cell_id(self): self.run_command('hypervisor-show region!child@1') self.assert_called('GET', '/os-hypervisors/region!child@1') def test_hypervisor_show_by_name(self): self.run_command('hypervisor-show hyper1') self.assert_called('GET', '/os-hypervisors/hyper1') def test_hypervisor_uptime_by_id(self): self.run_command('hypervisor-uptime 1234') self.assert_called('GET', '/os-hypervisors/1234/uptime') def test_hypervisor_uptime_by_cell_id(self): self.run_command('hypervisor-uptime region!child@1') self.assert_called('GET', '/os-hypervisors/region!child@1/uptime') def test_hypervisor_uptime_by_name(self): self.run_command('hypervisor-uptime hyper1') self.assert_called('GET', '/os-hypervisors/1234/uptime') def test_hypervisor_stats(self): self.run_command('hypervisor-stats') self.assert_called('GET', '/os-hypervisors/statistics') def test_quota_show(self): self.run_command( 'quota-show --tenant ' '97f4c221bff44578b0300df4ef119353') self.assert_called( 'GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353') def test_quota_show_detail(self): self.run_command( 'quota-show --tenant ' '97f4c221bff44578b0300df4ef119353 --detail') self.assert_called( 'GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353/detail') def test_user_quota_show(self): self.run_command( 'quota-show --tenant ' '97f4c221bff44578b0300df4ef119353 --user u1') self.assert_called( 'GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353?user_id=u1') def test_user_quota_show_detail(self): self.run_command( 'quota-show --tenant ' '97f4c221bff44578b0300df4ef119353 --user u1 --detail') self.assert_called( 'GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353/detail' '?user_id=u1') def test_quota_show_no_tenant(self): self.run_command('quota-show') self.assert_called('GET', '/os-quota-sets/tenant_id') def test_quota_defaults(self): self.run_command( 'quota-defaults --tenant ' '97f4c221bff44578b0300df4ef119353') self.assert_called( 'GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353/defaults') def test_quota_defaults_no_tenant(self): self.run_command('quota-defaults') self.assert_called('GET', '/os-quota-sets/tenant_id/defaults') def test_quota_update(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --instances=5') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'instances': 5}}) def test_user_quota_update(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --user=u1' ' --instances=5') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353?user_id=u1', {'quota_set': {'instances': 5}}) def test_quota_force_update(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --instances=5 --force') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'force': True, 'instances': 5}}) def test_quota_update_fixed_ip(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --fixed-ips=5') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'fixed_ips': 5}}) def test_quota_delete(self): self.run_command('quota-delete --tenant ' '97f4c221bff44578b0300df4ef119353') self.assert_called('DELETE', '/os-quota-sets/97f4c221bff44578b0300df4ef119353') def test_user_quota_delete(self): self.run_command('quota-delete --tenant ' '97f4c221bff44578b0300df4ef119353 ' '--user u1') self.assert_called( 'DELETE', '/os-quota-sets/97f4c221bff44578b0300df4ef119353?user_id=u1') def test_quota_class_show(self): self.run_command('quota-class-show test') self.assert_called('GET', '/os-quota-class-sets/test') def test_quota_class_update(self): # The list of args we can update. args = ( '--instances', '--cores', '--ram', '--floating-ips', '--fixed-ips', '--metadata-items', '--injected-files', '--injected-file-content-bytes', '--injected-file-path-bytes', '--key-pairs', '--security-groups', '--security-group-rules', '--server-groups', '--server-group-members' ) for arg in args: self.run_command('quota-class-update ' '97f4c221bff44578b0300df4ef119353 ' '%s=5' % arg) request_param = arg[2:].replace('-', '_') body = {'quota_class_set': {request_param: 5}} self.assert_called( 'PUT', '/os-quota-class-sets/97f4c221bff44578b0300df4ef119353', body) def test_cloudpipe_list(self): self.run_command('cloudpipe-list') self.assert_called('GET', '/os-cloudpipe') def test_cloudpipe_create(self): self.run_command('cloudpipe-create myproject') body = {'cloudpipe': {'project_id': "myproject"}} self.assert_called('POST', '/os-cloudpipe', body) def test_cloudpipe_configure(self): self.run_command('cloudpipe-configure 192.168.1.1 1234') body = {'configure_project': {'vpn_ip': "192.168.1.1", 'vpn_port': '1234'}} self.assert_called('PUT', '/os-cloudpipe/configure-project', body) def test_add_fixed_ip(self): _, err = self.run_command('add-fixed-ip sample-server 1') self.assertIn('WARNING: Command add-fixed-ip is deprecated', err) self.assert_called('POST', '/servers/1234/action', {'addFixedIp': {'networkId': '1'}}) def test_remove_fixed_ip(self): _, err = self.run_command('remove-fixed-ip sample-server 10.0.0.10') self.assertIn('WARNING: Command remove-fixed-ip is deprecated', err) self.assert_called('POST', '/servers/1234/action', {'removeFixedIp': {'address': '10.0.0.10'}}) def test_backup(self): out, err = self.run_command('backup sample-server back1 daily 1') # With microversion < 2.45 there is no output from this command. self.assertEqual(0, len(out)) self.assert_called('POST', '/servers/1234/action', {'createBackup': {'name': 'back1', 'backup_type': 'daily', 'rotation': '1'}}) self.run_command('backup 1234 back1 daily 1') self.assert_called('POST', '/servers/1234/action', {'createBackup': {'name': 'back1', 'backup_type': 'daily', 'rotation': '1'}}) def test_backup_2_45(self): """Tests the backup command with the 2.45 microversion which handles a different response and prints out the backup snapshot image details. """ out, err = self.run_command( 'backup sample-server back1 daily 1', api_version='2.45') # We should see the backup snapshot image name in the output. self.assertIn('back1', out) self.assertIn('SAVING', out) self.assert_called_anytime( 'POST', '/servers/1234/action', {'createBackup': {'name': 'back1', 'backup_type': 'daily', 'rotation': '1'}}) def test_limits(self): self.run_command('limits') self.assert_called('GET', '/limits') self.run_command('limits --reserved') self.assert_called('GET', '/limits?reserved=1') self.run_command('limits --tenant 1234') self.assert_called('GET', '/limits?tenant_id=1234') stdout, _err = self.run_command('limits --tenant 1234') self.assertIn('Verb', stdout) self.assertIn('Name', stdout) def test_evacuate(self): self.run_command('evacuate sample-server new_host') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'onSharedStorage': False}}) self.run_command('evacuate sample-server new_host ' '--password NewAdminPass') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'onSharedStorage': False, 'adminPass': 'NewAdminPass'}}) self.run_command('evacuate sample-server new_host ' '--on-shared-storage') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'onSharedStorage': True}}) def test_evacuate_v2_29(self): self.run_command('evacuate sample-server new_host', api_version="2.29") self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host'}}) self.run_command('evacuate sample-server new_host ' '--password NewAdminPass', api_version="2.29") self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'adminPass': 'NewAdminPass'}}) self.run_command('evacuate --force sample-server new_host', api_version="2.29") self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'force': True}}) def test_evacuate_with_no_target_host(self): self.run_command('evacuate sample-server') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'onSharedStorage': False}}) self.run_command('evacuate sample-server --password NewAdminPass') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'onSharedStorage': False, 'adminPass': 'NewAdminPass'}}) self.run_command('evacuate sample-server --on-shared-storage') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'onSharedStorage': True}}) def test_get_password(self): self.run_command('get-password sample-server /foo/id_rsa') self.assert_called('GET', '/servers/1234/os-server-password') def test_get_password_without_key(self): self.run_command('get-password sample-server') self.assert_called('GET', '/servers/1234/os-server-password') def test_clear_password(self): self.run_command('clear-password sample-server') self.assert_called('DELETE', '/servers/1234/os-server-password') def test_availability_zone_list(self): self.run_command('availability-zone-list') self.assert_called('GET', '/os-availability-zone/detail') def test_server_security_group_add(self): self.run_command('add-secgroup sample-server testgroup') self.assert_called('POST', '/servers/1234/action', {'addSecurityGroup': {'name': 'testgroup'}}) def test_server_security_group_remove(self): self.run_command('remove-secgroup sample-server testgroup') self.assert_called('POST', '/servers/1234/action', {'removeSecurityGroup': {'name': 'testgroup'}}) def test_server_security_group_list(self): self.run_command('list-secgroup 1234') self.assert_called('GET', '/servers/1234/os-security-groups') def test_interface_list(self): self.run_command('interface-list 1234') self.assert_called('GET', '/servers/1234/os-interface') def test_interface_attach(self): self.run_command('interface-attach --port-id port_id 1234') self.assert_called('POST', '/servers/1234/os-interface', {'interfaceAttachment': {'port_id': 'port_id'}}) def test_interface_attach_with_tag_pre_v2_49(self): self.assertRaises( SystemExit, self.run_command, 'interface-attach --port-id port_id --tag test_tag 1234', api_version='2.48') def test_interface_attach_with_tag(self): self.run_command( 'interface-attach --port-id port_id --tag test_tag 1234', api_version='2.49') self.assert_called('POST', '/servers/1234/os-interface', {'interfaceAttachment': {'port_id': 'port_id', 'tag': 'test_tag'}}) def test_interface_detach(self): self.run_command('interface-detach 1234 port_id') self.assert_called('DELETE', '/servers/1234/os-interface/port_id') def test_volume_attachments(self): self.run_command('volume-attachments 1234') self.assert_called('GET', '/servers/1234/os-volume_attachments') def test_volume_attach(self): self.run_command('volume-attach sample-server Work /dev/vdb') self.assert_called('POST', '/servers/1234/os-volume_attachments', {'volumeAttachment': {'device': '/dev/vdb', 'volumeId': 'Work'}}) def test_volume_attach_without_device(self): self.run_command('volume-attach sample-server Work') self.assert_called('POST', '/servers/1234/os-volume_attachments', {'volumeAttachment': {'volumeId': 'Work'}}) def test_volume_attach_with_tag_pre_v2_49(self): self.assertRaises( SystemExit, self.run_command, 'volume-attach --tag test_tag sample-server Work /dev/vdb', api_version='2.48') def test_volume_attach_with_tag(self): self.run_command( 'volume-attach --tag test_tag sample-server Work /dev/vdb', api_version='2.49') self.assert_called('POST', '/servers/1234/os-volume_attachments', {'volumeAttachment': {'device': '/dev/vdb', 'volumeId': 'Work', 'tag': 'test_tag'}}) def test_volume_update(self): self.run_command('volume-update sample-server Work Work') self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work', {'volumeAttachment': {'volumeId': 'Work'}}) def test_volume_detach(self): self.run_command('volume-detach sample-server Work') self.assert_called('DELETE', '/servers/1234/os-volume_attachments/Work') def test_instance_action_list(self): self.run_command('instance-action-list sample-server') self.assert_called('GET', '/servers/1234/os-instance-actions') def test_instance_action_get(self): self.run_command('instance-action sample-server req-abcde12345') self.assert_called( 'GET', '/servers/1234/os-instance-actions/req-abcde12345') def test_cell_show(self): self.run_command('cell-show child_cell') self.assert_called('GET', '/os-cells/child_cell') def test_cell_capacities_with_cell_name(self): self.run_command('cell-capacities --cell child_cell') self.assert_called('GET', '/os-cells/child_cell/capacities') def test_cell_capacities_without_cell_name(self): self.run_command('cell-capacities') self.assert_called('GET', '/os-cells/capacities') def test_migration_list(self): self.run_command('migration-list') self.assert_called('GET', '/os-migrations') def test_migration_list_v223(self): self.run_command('migration-list', api_version="2.23") self.assert_called('GET', '/os-migrations') def test_migration_list_with_filters(self): self.run_command('migration-list --host host1 --status finished') self.assert_called('GET', '/os-migrations?host=host1&status=finished') @mock.patch('novaclient.v2.shell._find_server') @mock.patch('os.system') def test_ssh(self, mock_system, mock_find_server): class FakeResources(object): addresses = { "skynet": [ {'version': 4, 'addr': "1.1.1.1", "OS-EXT-IPS:type": 'fixed'}, {'version': 4, 'addr': "2.2.2.2", "OS-EXT-IPS:type": 'floating'}, {'version': 6, 'addr': "2607:f0d0:1002::4", "OS-EXT-IPS:type": 'fixed'}, {'version': 6, 'addr': "7612:a1b2:2004::6"} ] } mock_find_server.return_value = FakeResources() self.run_command("ssh --login bob server") mock_system.assert_called_with("ssh -4 -p22 bob@2.2.2.2 ") self.run_command("ssh alice@server") mock_system.assert_called_with("ssh -4 -p22 alice@2.2.2.2 ") self.run_command("ssh --port 202 server") mock_system.assert_called_with("ssh -4 -p202 root@2.2.2.2 ") self.run_command("ssh --private server") mock_system.assert_called_with("ssh -4 -p22 root@1.1.1.1 ") self.run_command("ssh -i ~/my_rsa_key server --private") mock_system.assert_called_with("ssh -4 -p22 -i ~/my_rsa_key " "root@1.1.1.1 ") self.run_command("ssh --extra-opts -1 server") mock_system.assert_called_with("ssh -4 -p22 root@2.2.2.2 -1") self.run_command("ssh --ipv6 --login carol server") mock_system.assert_called_with("ssh -6 -p22 carol@7612:a1b2:2004::6 ") self.run_command("ssh --ipv6 dan@server") mock_system.assert_called_with("ssh -6 -p22 dan@7612:a1b2:2004::6 ") self.run_command("ssh --ipv6 --port 2022 server") mock_system.assert_called_with("ssh -6 -p2022 " "root@7612:a1b2:2004::6 ") self.run_command("ssh --ipv6 --private server") mock_system.assert_called_with("ssh -6 -p22 root@2607:f0d0:1002::4 ") self.run_command("ssh --ipv6 --identity /home/me/my_dsa_key " "--private server") mock_system.assert_called_with("ssh -6 -p22 -i /home/me/my_dsa_key " "root@2607:f0d0:1002::4 ") self.run_command("ssh --ipv6 --private --extra-opts -1 server") mock_system.assert_called_with("ssh -6 -p22 " "root@2607:f0d0:1002::4 -1") @mock.patch('novaclient.v2.shell._find_server') @mock.patch('os.system') def test_ssh_multinet(self, mock_system, mock_find_server): class FakeResources(object): addresses = { "skynet": [ {'version': 4, 'addr': "1.1.1.1", "OS-EXT-IPS:type": 'fixed'}, {'version': 4, 'addr': "2.2.2.2"}, {'version': 6, 'addr': "2607:f0d0:1002::4", "OS-EXT-IPS:type": 'fixed'} ], "other": [ {'version': 4, 'addr': "2.3.4.5"}, {'version': 6, 'addr': "7612:a1b2:2004::6"} ] } mock_find_server.return_value = FakeResources() self.run_command("ssh --network other server") mock_system.assert_called_with("ssh -4 -p22 root@2.3.4.5 ") self.run_command("ssh --ipv6 --network other server") mock_system.assert_called_with("ssh -6 -p22 root@7612:a1b2:2004::6 ") self.assertRaises(exceptions.ResourceNotFound, self.run_command, "ssh --ipv6 --network nonexistent server") def _check_keypair_add(self, expected_key_type=None, extra_args='', api_version=None): self.run_command("keypair-add %s test" % extra_args, api_version=api_version) expected_body = {"keypair": {"name": "test"}} if expected_key_type: expected_body["keypair"]["type"] = expected_key_type self.assert_called("POST", "/os-keypairs", expected_body) def test_keypair_add_v20(self): self._check_keypair_add(api_version="2.0") def test_keypair_add_v22(self): self._check_keypair_add('ssh', api_version="2.2") def test_keypair_add_ssh(self): self._check_keypair_add('ssh', '--key-type ssh', api_version="2.2") def test_keypair_add_ssh_x509(self): self._check_keypair_add('x509', '--key-type x509', api_version="2.2") def _check_keypair_import(self, expected_key_type=None, extra_args='', api_version=None): with mock.patch.object(builtins, 'open', mock.mock_open(read_data='FAKE_PUBLIC_KEY')): self.run_command('keypair-add --pub-key test.pub %s test' % extra_args, api_version=api_version) expected_body = {"keypair": {'public_key': 'FAKE_PUBLIC_KEY', 'name': 'test'}} if expected_key_type: expected_body["keypair"]["type"] = expected_key_type self.assert_called( 'POST', '/os-keypairs', expected_body) def test_keypair_import_v20(self): self._check_keypair_import(api_version="2.0") def test_keypair_import_v22(self): self._check_keypair_import('ssh', api_version="2.2") def test_keypair_import_ssh(self): self._check_keypair_import('ssh', '--key-type ssh', api_version="2.2") def test_keypair_import_x509(self): self._check_keypair_import('x509', '--key-type x509', api_version="2.2") def test_keypair_stdin(self): with mock.patch('sys.stdin', six.StringIO('FAKE_PUBLIC_KEY')): self.run_command('keypair-add --pub-key - test', api_version="2.2") self.assert_called( 'POST', '/os-keypairs', { 'keypair': {'public_key': 'FAKE_PUBLIC_KEY', 'name': 'test', 'type': 'ssh'}}) def test_keypair_list(self): self.run_command('keypair-list') self.assert_called('GET', '/os-keypairs') def test_keypair_list_with_user_id(self): self.run_command('keypair-list --user test_user', api_version='2.10') self.assert_called('GET', '/os-keypairs?user_id=test_user') def test_keypair_list_with_limit_and_marker(self): self.run_command('keypair-list --marker test_kp --limit 3', api_version='2.35') self.assert_called('GET', '/os-keypairs?limit=3&marker=test_kp') def test_keypair_list_with_user_id_limit_and_marker(self): self.run_command('keypair-list --user test_user --marker test_kp ' '--limit 3', api_version='2.35') self.assert_called( 'GET', '/os-keypairs?limit=3&marker=test_kp&user_id=test_user') def test_keypair_show(self): self.run_command('keypair-show test') self.assert_called('GET', '/os-keypairs/test') def test_keypair_delete(self): self.run_command('keypair-delete test') self.assert_called('DELETE', '/os-keypairs/test') def test_create_server_group(self): self.run_command('server-group-create wjsg affinity') self.assert_called('POST', '/os-server-groups', {'server_group': {'name': 'wjsg', 'policies': ['affinity']}}) def test_delete_multi_server_groups(self): self.run_command('server-group-delete 12345 56789') self.assert_called('DELETE', '/os-server-groups/56789') self.assert_called('DELETE', '/os-server-groups/12345', pos=-2) def test_list_server_group(self): self.run_command('server-group-list') self.assert_called('GET', '/os-server-groups') def test_list_server_group_with_all_projects(self): self.run_command('server-group-list --all-projects') self.assert_called('GET', '/os-server-groups?all_projects=True') def test_list_server_group_with_limit_and_offset(self): self.run_command('server-group-list --limit 20 --offset 5') self.assert_called('GET', '/os-server-groups?limit=20&offset=5') def test_list_server_os_virtual_interfaces(self): _, err = self.run_command('virtual-interface-list 1234') self.assertIn('WARNING: Command virtual-interface-list is deprecated', err) self.assert_called('GET', '/servers/1234/os-virtual-interfaces') def test_versions(self): exclusions = set([ 1, # Same as version 2.0 3, # doesn't require any changes in novaclient 4, # fixed-ip-get command is gone 5, # doesn't require any changes in novaclient 7, # doesn't require any changes in novaclient 9, # doesn't require any changes in novaclient 15, # doesn't require any changes in novaclient 16, # doesn't require any changes in novaclient 18, # NOTE(andreykurilin): this microversion requires changes in # HttpClient and our SessionClient, which is based on # keystoneauth1.session. Skipping this complicated change # allows to unblock implementation further microversions # before feature-freeze # (we can do it, since nova-api change didn't actually add # new microversion, just an additional checks. See # https://review.openstack.org/#/c/233076/ for more details) 20, # doesn't require any changes in novaclient 27, # NOTE(cdent): 27 adds support for updated microversion # headers, and is tested in test_api_versions, but is # not explicitly tested via wraps and _SUBSTITUTIONS. 28, # doesn't require any changes in novaclient 31, # doesn't require any changes in novaclient 32, # doesn't require separate version-wrapped methods in # novaclient 34, # doesn't require any changes in novaclient 37, # There are no versioned wrapped shell method changes for this 38, # doesn't require any changes in novaclient 39, # There are no versioned wrapped shell method changes for this 41, # There are no version-wrapped shell method changes for this. 42, # There are no version-wrapped shell method changes for this. 43, # There are no version-wrapped shell method changes for this. 44, # There are no version-wrapped shell method changes for this. 45, # There are no version-wrapped shell method changes for this. 46, # There are no version-wrapped shell method changes for this. 47, # NOTE(cfriesen): 47 adds support for flavor details embedded # within the server details 48, # There are no version-wrapped shell method changes for this. 51, # There are no version-wrapped shell method changes for this. 52, # There are no version-wrapped shell method changes for this. ]) versions_supported = set(range(0, novaclient.API_MAX_VERSION.ver_minor + 1)) versions_covered = set() for key, values in api_versions._SUBSTITUTIONS.items(): for value in values: if value.start_version.ver_major == 2: versions_covered.add(value.start_version.ver_minor) versions_not_covered = versions_supported - versions_covered unaccounted_for = versions_not_covered - exclusions failure_msg = ('Minor versions %s have been skipped. Please do not ' 'raise API_MAX_VERSION without adding support or ' 'excluding them.' % sorted(unaccounted_for)) self.assertEqual(set([]), unaccounted_for, failure_msg) def test_list_v2_10(self): self.run_command('list', api_version='2.10') self.assert_called('GET', '/servers/detail') def test_server_tag_add(self): self.run_command('server-tag-add sample-server tag', api_version='2.26') self.assert_called('PUT', '/servers/1234/tags/tag', None) def test_server_tag_add_many(self): self.run_command('server-tag-add sample-server tag1 tag2 tag3', api_version='2.26') self.assert_called('PUT', '/servers/1234/tags/tag1', None, pos=-3) self.assert_called('PUT', '/servers/1234/tags/tag2', None, pos=-2) self.assert_called('PUT', '/servers/1234/tags/tag3', None, pos=-1) def test_server_tag_set(self): self.run_command('server-tag-set sample-server tag1 tag2', api_version='2.26') self.assert_called('PUT', '/servers/1234/tags', {'tags': ['tag1', 'tag2']}) def test_server_tag_list(self): self.run_command('server-tag-list sample-server', api_version='2.26') self.assert_called('GET', '/servers/1234/tags') def test_server_tag_delete(self): self.run_command('server-tag-delete sample-server tag', api_version='2.26') self.assert_called('DELETE', '/servers/1234/tags/tag') def test_server_tag_delete_many(self): self.run_command('server-tag-delete sample-server tag1 tag2 tag3', api_version='2.26') self.assert_called('DELETE', '/servers/1234/tags/tag1', pos=-3) self.assert_called('DELETE', '/servers/1234/tags/tag2', pos=-2) self.assert_called('DELETE', '/servers/1234/tags/tag3', pos=-1) def test_server_tag_delete_all(self): self.run_command('server-tag-delete-all sample-server', api_version='2.26') self.assert_called('DELETE', '/servers/1234/tags') def test_list_v2_26_tags(self): self.run_command('list --tags tag1,tag2', api_version='2.26') self.assert_called('GET', '/servers/detail?tags=tag1%2Ctag2') def test_list_v2_26_tags_any(self): self.run_command('list --tags-any tag1,tag2', api_version='2.26') self.assert_called('GET', '/servers/detail?tags-any=tag1%2Ctag2') def test_list_v2_26_not_tags(self): self.run_command('list --not-tags tag1,tag2', api_version='2.26') self.assert_called('GET', '/servers/detail?not-tags=tag1%2Ctag2') def test_list_v2_26_not_tags_any(self): self.run_command('list --not-tags-any tag1,tag2', api_version='2.26') self.assert_called('GET', '/servers/detail?not-tags-any=tag1%2Ctag2') class PollForStatusTestCase(utils.TestCase): @mock.patch("novaclient.v2.shell.time") def test_simple_usage(self, mock_time): poll_period = 3 some_id = "uuuuuuuuuuuiiiiiiiii" updated_objects = ( base.Resource(None, info={"not_default_field": "INPROGRESS"}), base.Resource(None, info={"not_default_field": "OK"})) poll_fn = mock.MagicMock(side_effect=updated_objects) novaclient.v2.shell._poll_for_status( poll_fn=poll_fn, obj_id=some_id, status_field="not_default_field", final_ok_states=["ok"], poll_period=poll_period, # just want to test printing in separate tests action="some", silent=True, show_progress=False ) self.assertEqual([mock.call(poll_period)], mock_time.sleep.call_args_list) self.assertEqual([mock.call(some_id)] * 2, poll_fn.call_args_list) @mock.patch("novaclient.v2.shell.sys.stdout") @mock.patch("novaclient.v2.shell.time") def test_print_progress(self, mock_time, mock_stdout): updated_objects = ( base.Resource(None, info={"status": "INPROGRESS", "progress": 0}), base.Resource(None, info={"status": "INPROGRESS", "progress": 50}), base.Resource(None, info={"status": "OK", "progress": 100})) poll_fn = mock.MagicMock(side_effect=updated_objects) action = "some" novaclient.v2.shell._poll_for_status( poll_fn=poll_fn, obj_id="uuuuuuuuuuuiiiiiiiii", final_ok_states=["ok"], poll_period="3", action=action, show_progress=True, silent=False) stdout_arg_list = [ mock.call("\n"), mock.call("\rServer %s... 0%% complete" % action), mock.call("\rServer %s... 50%% complete" % action), mock.call("\rServer %s... 100%% complete" % action), mock.call("\nFinished"), mock.call("\n")] self.assertEqual( stdout_arg_list, mock_stdout.write.call_args_list ) @mock.patch("novaclient.v2.shell.time") def test_error_state(self, mock_time): fault_msg = "Oops" updated_objects = ( base.Resource(None, info={"status": "error", "fault": {"message": fault_msg}}), base.Resource(None, info={"status": "error"})) poll_fn = mock.MagicMock(side_effect=updated_objects) action = "some" self.assertRaises(exceptions.ResourceInErrorState, novaclient.v2.shell._poll_for_status, poll_fn=poll_fn, obj_id="uuuuuuuuuuuiiiiiiiii", final_ok_states=["ok"], poll_period="3", action=action, show_progress=True, silent=False) self.assertRaises(exceptions.ResourceInErrorState, novaclient.v2.shell._poll_for_status, poll_fn=poll_fn, obj_id="uuuuuuuuuuuiiiiiiiii", final_ok_states=["ok"], poll_period="3", action=action, show_progress=True, silent=False) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_volumes.py0000664000175000017500000001154113165151077026256 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import volumes class VolumesTest(utils.TestCase): api_version = "2.0" def setUp(self): super(VolumesTest, self).setUp() self.cs = fakes.FakeClient(api_versions.APIVersion(self.api_version)) def test_create_server_volume(self): v = self.cs.volumes.create_server_volume( server_id=1234, volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983', device='/dev/vdb' ) self.assert_request_id(v, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('POST', '/servers/1234/os-volume_attachments') self.assertIsInstance(v, volumes.Volume) def test_update_server_volume(self): vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983' v = self.cs.volumes.update_server_volume( server_id=1234, src_volid='Work', dest_volid=vol_id ) self.assert_request_id(v, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('PUT', '/servers/1234/os-volume_attachments/Work') self.assertIsInstance(v, volumes.Volume) def test_get_server_volume(self): v = self.cs.volumes.get_server_volume(1234, 'Work') self.assert_request_id(v, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/servers/1234/os-volume_attachments/Work') self.assertIsInstance(v, volumes.Volume) def test_get_server_volume_with_exception(self): self.assertRaises(TypeError, self.cs.volumes.get_server_volume, "1234") self.assertRaises(TypeError, self.cs.volumes.get_server_volume, "1234", volume_id="Work", attachment_id="123") @mock.patch('warnings.warn') def test_get_server_volume_with_warn(self, mock_warn): self.cs.volumes.get_server_volume(1234, volume_id=None, attachment_id="Work") mock_warn.assert_called_once() def test_list_server_volumes(self): vl = self.cs.volumes.get_server_volumes(1234) self.assert_request_id(vl, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('GET', '/servers/1234/os-volume_attachments') for v in vl: self.assertIsInstance(v, volumes.Volume) def test_delete_server_volume(self): ret = self.cs.volumes.delete_server_volume(1234, 'Work') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called('DELETE', '/servers/1234/os-volume_attachments/Work') class VolumesV249Test(VolumesTest): api_version = "2.49" def test_create_server_volume_with_tag(self): v = self.cs.volumes.create_server_volume( server_id=1234, volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983', device='/dev/vdb', tag='test_tag' ) self.assert_request_id(v, fakes.FAKE_REQUEST_ID_LIST) self.cs.assert_called( 'POST', '/servers/1234/os-volume_attachments', {'volumeAttachment': { 'volumeId': '15e59938-07d5-11e1-90e3-e3dffe0c5983', 'device': '/dev/vdb', 'tag': 'test_tag'}}) self.assertIsInstance(v, volumes.Volume) def test_delete_server_volume_with_exception(self): self.assertRaises(TypeError, self.cs.volumes.delete_server_volume, "1234") self.assertRaises(TypeError, self.cs.volumes.delete_server_volume, "1234", volume_id="Work", attachment_id="123") @mock.patch('warnings.warn') def test_delete_server_volume_with_warn(self, mock_warn): self.cs.volumes.delete_server_volume(1234, volume_id=None, attachment_id="Work") mock_warn.assert_called_once() python-novaclient-9.1.1/novaclient/tests/unit/v2/test_client.py0000664000175000017500000000330613165151077026042 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from keystoneauth1 import session from oslo_utils import uuidutils from novaclient import api_versions from novaclient.tests.unit import utils from novaclient.v2 import client class ClientTest(utils.TestCase): def test_adapter_properties(self): # sample of properties, there are many more user_agent = uuidutils.generate_uuid(dashed=False) endpoint_override = uuidutils.generate_uuid(dashed=False) s = session.Session() c = client.Client(session=s, api_version=api_versions.APIVersion("2.0"), user_agent=user_agent, endpoint_override=endpoint_override, direct_use=False) self.assertEqual(user_agent, c.client.user_agent) self.assertEqual(endpoint_override, c.client.endpoint_override) def test_passing_endpoint_type(self): endpoint_type = uuidutils.generate_uuid(dashed=False) s = session.Session() c = client.Client(session=s, endpoint_type=endpoint_type, direct_use=False) self.assertEqual(endpoint_type, c.client.interface) python-novaclient-9.1.1/novaclient/tests/unit/v2/test_agents.py0000664000175000017500000001047213165151077026047 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import agents as data from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import agents class AgentsTest(utils.FixturedTestCase): data_fixture_class = data.Fixture scenarios = [('original', {'client_fixture_class': client.V1}), ('session', {'client_fixture_class': client.SessionV1})] def stub_hypervisors(self, hypervisor='kvm'): get_os_agents = { 'agents': [ { 'hypervisor': hypervisor, 'os': 'win', 'architecture': 'x86', 'version': '7.0', 'url': 'xxx://xxxx/xxx/xxx', 'md5hash': 'add6bb58e139be103324d04d82d8f545', 'id': 1 }, { 'hypervisor': hypervisor, 'os': 'linux', 'architecture': 'x86', 'version': '16.0', 'url': 'xxx://xxxx/xxx/xxx1', 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'id': 2 }, ] } headers = {'Content-Type': 'application/json', 'x-openstack-request-id': fakes.FAKE_REQUEST_ID} self.requests_mock.get(self.data_fixture.url(), json=get_os_agents, headers=headers) def test_list_agents(self): self.stub_hypervisors() ags = self.cs.agents.list() self.assert_called('GET', '/os-agents') self.assert_request_id(ags, fakes.FAKE_REQUEST_ID_LIST) for a in ags: self.assertIsInstance(a, agents.Agent) self.assertEqual('kvm', a.hypervisor) def test_list_agents_with_hypervisor(self): self.stub_hypervisors('xen') ags = self.cs.agents.list('xen') self.assert_called('GET', '/os-agents?hypervisor=xen') self.assert_request_id(ags, fakes.FAKE_REQUEST_ID_LIST) for a in ags: self.assertIsInstance(a, agents.Agent) self.assertEqual('xen', a.hypervisor) def test_agents_create(self): ag = self.cs.agents.create('win', 'x86', '7.0', '/xxx/xxx/xxx', 'add6bb58e139be103324d04d82d8f546', 'xen') self.assert_request_id(ag, fakes.FAKE_REQUEST_ID_LIST) body = {'agent': {'url': '/xxx/xxx/xxx', 'hypervisor': 'xen', 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'version': '7.0', 'architecture': 'x86', 'os': 'win'}} self.assert_called('POST', '/os-agents', body) self.assertEqual(1, ag._info.copy()['id']) def test_agents_delete(self): ret = self.cs.agents.delete('1') self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST) self.assert_called('DELETE', '/os-agents/1') def _build_example_update_body(self): return {"para": { "url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546"}} def test_agents_modify(self): ag = self.cs.agents.update('1', '8.0', '/yyy/yyyy/yyyy', 'add6bb58e139be103324d04d82d8f546') self.assert_request_id(ag, fakes.FAKE_REQUEST_ID_LIST) body = self._build_example_update_body() self.assert_called('PUT', '/os-agents/1', body) self.assertEqual(1, ag.id) python-novaclient-9.1.1/novaclient/tests/unit/test_api_versions.py0000664000175000017500000004243213165151077026741 0ustar jenkinsjenkins00000000000000# Copyright 2016 Mirantis # 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 import novaclient from novaclient import api_versions from novaclient import exceptions from novaclient.tests.unit import utils from novaclient import utils as nutils from novaclient.v2 import versions class APIVersionTestCase(utils.TestCase): def test_valid_version_strings(self): def _test_string(version, exp_major, exp_minor): v = api_versions.APIVersion(version) self.assertEqual(v.ver_major, exp_major) self.assertEqual(v.ver_minor, exp_minor) _test_string("1.1", 1, 1) _test_string("2.10", 2, 10) _test_string("5.234", 5, 234) _test_string("12.5", 12, 5) _test_string("2.0", 2, 0) _test_string("2.200", 2, 200) def test_null_version(self): v = api_versions.APIVersion() self.assertTrue(v.is_null()) def test_invalid_version_strings(self): self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "2") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "200") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "2.1.4") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "200.23.66.3") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "5 .3") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "5. 3") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "5.03") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "02.1") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "2.001") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, " 2.1") self.assertRaises(exceptions.UnsupportedVersion, api_versions.APIVersion, "2.1 ") def test_version_comparisons(self): v1 = api_versions.APIVersion("2.0") v2 = api_versions.APIVersion("2.5") v3 = api_versions.APIVersion("5.23") v4 = api_versions.APIVersion("2.0") v_null = api_versions.APIVersion() self.assertTrue(v1.__lt__(v2)) self.assertTrue(v3.__gt__(v2)) self.assertTrue(v1.__ne__(v2)) self.assertTrue(v1.__eq__(v4)) self.assertTrue(v1.__ne__(v_null)) self.assertTrue(v_null.__eq__(v_null)) self.assertRaises(TypeError, v1.__le__, "2.1") def test_version_matches(self): v1 = api_versions.APIVersion("2.0") v2 = api_versions.APIVersion("2.5") v3 = api_versions.APIVersion("2.45") v4 = api_versions.APIVersion("3.3") v5 = api_versions.APIVersion("3.23") v6 = api_versions.APIVersion("2.0") v7 = api_versions.APIVersion("3.3") v8 = api_versions.APIVersion("4.0") v_null = api_versions.APIVersion() self.assertTrue(v2.matches(v1, v3)) self.assertTrue(v2.matches(v1, v_null)) self.assertTrue(v1.matches(v6, v2)) self.assertTrue(v4.matches(v2, v7)) self.assertTrue(v4.matches(v_null, v7)) self.assertTrue(v4.matches(v_null, v8)) self.assertFalse(v1.matches(v2, v3)) self.assertFalse(v5.matches(v2, v4)) self.assertFalse(v2.matches(v3, v1)) self.assertRaises(ValueError, v_null.matches, v1, v3) def test_get_string(self): v1_string = "3.23" v1 = api_versions.APIVersion(v1_string) self.assertEqual(v1_string, v1.get_string()) self.assertRaises(ValueError, api_versions.APIVersion().get_string) class UpdateHeadersTestCase(utils.TestCase): def test_api_version_is_null(self): headers = {} api_versions.update_headers(headers, api_versions.APIVersion()) self.assertEqual({}, headers) def test_api_version_is_major(self): headers = {} api_versions.update_headers(headers, api_versions.APIVersion("7.0")) self.assertEqual({}, headers) def test_api_version_is_not_null(self): api_version = api_versions.APIVersion("2.3") headers = {} api_versions.update_headers(headers, api_version) self.assertEqual( {"X-OpenStack-Nova-API-Version": api_version.get_string()}, headers) def test_api_version_is_gte_27(self): api_version = api_versions.APIVersion("2.27") headers = {} api_versions.update_headers(headers, api_version) self.assertIn('X-OpenStack-Nova-API-Version', headers) self.assertIn('OpenStack-API-Version', headers) self.assertEqual(api_version.get_string(), headers['X-OpenStack-Nova-API-Version']) self.assertEqual('%s %s' % (api_versions.SERVICE_TYPE, api_version.get_string()), headers['OpenStack-API-Version']) class CheckHeadersTestCase(utils.TestCase): def setUp(self): super(CheckHeadersTestCase, self).setUp() mock_log_patch = mock.patch("novaclient.api_versions.LOG") self.mock_log = mock_log_patch.start() self.addCleanup(mock_log_patch.stop) def test_legacy_microversion_is_specified(self): response = mock.MagicMock( headers={api_versions.LEGACY_HEADER_NAME: ""}) api_versions.check_headers(response, api_versions.APIVersion("2.2")) self.assertFalse(self.mock_log.warning.called) response = mock.MagicMock(headers={}) api_versions.check_headers(response, api_versions.APIVersion("2.2")) self.assertTrue(self.mock_log.warning.called) def test_generic_microversion_is_specified(self): response = mock.MagicMock( headers={api_versions.HEADER_NAME: ""}) api_versions.check_headers(response, api_versions.APIVersion("2.27")) self.assertFalse(self.mock_log.warning.called) response = mock.MagicMock(headers={}) api_versions.check_headers(response, api_versions.APIVersion("2.27")) self.assertTrue(self.mock_log.warning.called) def test_microversion_is_not_specified(self): response = mock.MagicMock( headers={api_versions.LEGACY_HEADER_NAME: ""}) api_versions.check_headers(response, api_versions.APIVersion("2.2")) self.assertFalse(self.mock_log.warning.called) response = mock.MagicMock(headers={}) api_versions.check_headers(response, api_versions.APIVersion("2.0")) self.assertFalse(self.mock_log.warning.called) class GetAPIVersionTestCase(utils.TestCase): def test_get_available_client_versions(self): output = api_versions.get_available_major_versions() self.assertNotEqual([], output) def test_wrong_format(self): self.assertRaises(exceptions.UnsupportedVersion, api_versions.get_api_version, "something_wrong") def test_wrong_major_version(self): self.assertRaises(exceptions.UnsupportedVersion, api_versions.get_api_version, "1") @mock.patch("novaclient.api_versions.APIVersion") def test_only_major_part_is_presented(self, mock_apiversion): version = 7 self.assertEqual(mock_apiversion.return_value, api_versions.get_api_version(version)) mock_apiversion.assert_called_once_with("%s.0" % str(version)) @mock.patch("novaclient.api_versions.APIVersion") def test_major_and_minor_parts_is_presented(self, mock_apiversion): version = "2.7" self.assertEqual(mock_apiversion.return_value, api_versions.get_api_version(version)) mock_apiversion.assert_called_once_with(version) class WrapsTestCase(utils.TestCase): def _get_obj_with_vers(self, vers): return mock.MagicMock(api_version=api_versions.APIVersion(vers)) def _side_effect_of_vers_method(self, *args, **kwargs): m = mock.MagicMock(start_version=args[1], end_version=args[2]) m.name = args[0] return m @mock.patch("novaclient.api_versions._get_function_name") @mock.patch("novaclient.api_versions.VersionedMethod") def test_end_version_is_none(self, mock_versioned_method, mock_name): mock_versioned_method.side_effect = self._side_effect_of_vers_method @api_versions.wraps("2.2") def foo(*args, **kwargs): pass foo(self._get_obj_with_vers("2.4")) mock_versioned_method.assert_called_once_with( mock_name.return_value, api_versions.APIVersion("2.2"), api_versions.APIVersion("2.latest"), mock.ANY) @mock.patch("novaclient.api_versions._get_function_name") @mock.patch("novaclient.api_versions.VersionedMethod") def test_start_and_end_version_are_presented(self, mock_versioned_method, mock_name): mock_versioned_method.side_effect = self._side_effect_of_vers_method @api_versions.wraps("2.2", "2.6") def foo(*args, **kwargs): pass foo(self._get_obj_with_vers("2.4")) mock_versioned_method.assert_called_once_with( mock_name.return_value, api_versions.APIVersion("2.2"), api_versions.APIVersion("2.6"), mock.ANY) @mock.patch("novaclient.api_versions._get_function_name") @mock.patch("novaclient.api_versions.VersionedMethod") def test_api_version_doesnt_match(self, mock_versioned_method, mock_name): mock_versioned_method.side_effect = self._side_effect_of_vers_method @api_versions.wraps("2.2", "2.6") def foo(*args, **kwargs): pass self.assertRaises(exceptions.VersionNotFoundForAPIMethod, foo, self._get_obj_with_vers("2.1")) mock_versioned_method.assert_called_once_with( mock_name.return_value, api_versions.APIVersion("2.2"), api_versions.APIVersion("2.6"), mock.ANY) def test_define_method_is_actually_called(self): checker = mock.MagicMock() @api_versions.wraps("2.2", "2.6") def some_func(*args, **kwargs): checker(*args, **kwargs) obj = self._get_obj_with_vers("2.4") some_args = ("arg_1", "arg_2") some_kwargs = {"key1": "value1", "key2": "value2"} some_func(obj, *some_args, **some_kwargs) checker.assert_called_once_with(*((obj,) + some_args), **some_kwargs) @mock.patch("novaclient.api_versions._get_function_name") def test_arguments_property_is_copied(self, mock_name): @nutils.arg("argument_1") @api_versions.wraps("2.666", "2.777") @nutils.arg("argument_2") def some_func(): pass versioned_method = api_versions.get_substitutions( mock_name.return_value, api_versions.APIVersion("2.700"))[0] self.assertEqual(some_func.arguments, versioned_method.func.arguments) self.assertIn((("argument_1",), {}), versioned_method.func.arguments) self.assertIn((("argument_2",), {}), versioned_method.func.arguments) def test_several_methods_with_same_name_in_one_module(self): class A(object): api_version = api_versions.APIVersion("777.777") @api_versions.wraps("777.777") def f(self): return 1 class B(object): api_version = api_versions.APIVersion("777.777") @api_versions.wraps("777.777") def f(self): return 2 self.assertEqual(1, A().f()) self.assertEqual(2, B().f()) def test_generate_function_name(self): expected_name = "novaclient.tests.unit.test_api_versions.fake_func" self.assertNotIn(expected_name, api_versions._SUBSTITUTIONS) @api_versions.wraps("7777777.7777777") def fake_func(): pass self.assertIn(expected_name, api_versions._SUBSTITUTIONS) self.assertEqual(expected_name, fake_func.__id__) class DiscoverVersionTestCase(utils.TestCase): def setUp(self): super(DiscoverVersionTestCase, self).setUp() self.orig_max = novaclient.API_MAX_VERSION self.orig_min = novaclient.API_MIN_VERSION self.addCleanup(self._clear_fake_version) def _clear_fake_version(self): novaclient.API_MAX_VERSION = self.orig_max novaclient.API_MIN_VERSION = self.orig_min def test_server_is_too_new(self): fake_client = mock.MagicMock() fake_client.versions.get_current.return_value = mock.MagicMock( version="2.7", min_version="2.4") novaclient.API_MAX_VERSION = api_versions.APIVersion("2.3") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertRaises(exceptions.UnsupportedVersion, api_versions.discover_version, fake_client, api_versions.APIVersion('2.latest')) def test_server_is_too_old(self): fake_client = mock.MagicMock() fake_client.versions.get_current.return_value = mock.MagicMock( version="2.7", min_version="2.4") novaclient.API_MAX_VERSION = api_versions.APIVersion("2.10") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.9") self.assertRaises(exceptions.UnsupportedVersion, api_versions.discover_version, fake_client, api_versions.APIVersion('2.latest')) def test_server_end_version_is_the_latest_one(self): fake_client = mock.MagicMock() fake_client.versions.get_current.return_value = mock.MagicMock( version="2.7", min_version="2.4") novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertEqual( "2.7", api_versions.discover_version( fake_client, api_versions.APIVersion('2.latest')).get_string()) def test_client_end_version_is_the_latest_one(self): fake_client = mock.MagicMock() fake_client.versions.get_current.return_value = mock.MagicMock( version="2.16", min_version="2.4") novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertEqual( "2.11", api_versions.discover_version( fake_client, api_versions.APIVersion('2.latest')).get_string()) def test_server_without_microversion(self): fake_client = mock.MagicMock() fake_client.versions.get_current.return_value = mock.MagicMock( version='', min_version='') novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertEqual( "2.0", api_versions.discover_version( fake_client, api_versions.APIVersion('2.latest')).get_string()) def test_server_without_microversion_and_no_version_field(self): fake_client = mock.MagicMock() fake_client.versions.get_current.return_value = versions.Version( None, {}) novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertEqual( "2.0", api_versions.discover_version( fake_client, api_versions.APIVersion('2.latest')).get_string()) def test_server_without_microversion_rax_workaround(self): fake_client = mock.MagicMock() fake_client.versions.get_current.return_value = None novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertEqual( "2.0", api_versions.discover_version( fake_client, api_versions.APIVersion('2.latest')).get_string()) class DecoratedAfterTestCase(utils.TestCase): def test_decorated_after(self): class Fake(object): api_version = api_versions.APIVersion('2.123') @api_versions.deprecated_after('2.123') def foo(self): pass with mock.patch('warnings.warn') as mock_warn: Fake().foo() msg = ('The novaclient.tests.unit.test_api_versions module ' 'is deprecated and will be removed.') mock_warn.assert_called_once_with(msg, mock.ANY) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/0000775000175000017500000000000013165151230025270 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/images.py0000664000175000017500000000511413165151077027121 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit import fakes from novaclient.tests.unit.fixture_data import base class V1(base.Fixture): base_url = 'v2/images' def setUp(self): super(V1, self).setUp() get_images = { 'images': [ {'id': 1, 'name': 'CentOS 5.2'}, {'id': 2, 'name': 'My Server Backup'} ] } headers = self.json_headers self.requests_mock.get(self.url(), json=get_images, headers=headers) image_1 = { 'id': 1, 'name': 'CentOS 5.2', "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "ACTIVE", "metadata": { "test_key": "test_value", }, "links": {}, } image_2 = { "id": 2, "name": "My Server Backup", "serverId": 1234, "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "SAVING", "progress": 80, "links": {}, } self.requests_mock.get(self.url('detail'), json={'images': [image_1, image_2]}, headers=headers) self.requests_mock.get(self.url(1), json={'image': image_1}, headers=headers) def post_images_1_metadata(request, context): body = request.json() assert list(body) == ['metadata'] fakes.assert_has_keys(body['metadata'], required=['test_key']) return {'metadata': image_1['metadata']} self.requests_mock.post(self.url(1, 'metadata'), json=post_images_1_metadata, headers=headers) for u in (1, '1/metadata/test_key'): self.requests_mock.delete(self.url(u), status_code=204, headers=headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/keypairs.py0000664000175000017500000000324613165151077027507 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit import fakes from novaclient.tests.unit.fixture_data import base class V1(base.Fixture): base_url = 'os-keypairs' def setUp(self): super(V1, self).setUp() keypair = {'fingerprint': 'FAKE_KEYPAIR', 'name': 'test'} headers = self.json_headers self.requests_mock.get(self.url(), json={'keypairs': [keypair]}, headers=headers) self.requests_mock.get(self.url('test'), json={'keypair': keypair}, headers=headers) self.requests_mock.delete(self.url('test'), status_code=202, headers=headers) def post_os_keypairs(request, context): body = request.json() assert list(body) == ['keypair'] fakes.assert_has_keys(body['keypair'], required=['name']) return {'keypair': keypair} self.requests_mock.post(self.url(), json=post_os_keypairs, headers=headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/server_migrations.py0000664000175000017500000000611213165151077031415 0ustar jenkinsjenkins00000000000000# Copyright 2016 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class Fixture(base.Fixture): base_url = 'servers' def setUp(self): super(Fixture, self).setUp() url = self.url('1234', 'migrations', '1', 'action') self.requests_mock.post(url, status_code=202, headers=self.json_headers) get_migrations = {'migrations': [ { "created_at": "2016-01-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": 1, "server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe", "source_compute": "compute1", "source_node": "node1", "status": "running", "memory_total_bytes": 123456, "memory_processed_bytes": 12345, "memory_remaining_bytes": 120000, "disk_total_bytes": 234567, "disk_processed_bytes": 23456, "disk_remaining_bytes": 230000, "updated_at": "2016-01-29T13:42:02.000000" }]} url = self.url('1234', 'migrations') self.requests_mock.get(url, json=get_migrations, headers=self.json_headers) get_migration = {'migration': { "created_at": "2016-01-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": 1, "server_uuid": "4cfba335-03d8-49b2-8c52-e69043d1e8fe", "source_compute": "compute1", "source_node": "node1", "status": "running", "memory_total_bytes": 123456, "memory_processed_bytes": 12345, "memory_remaining_bytes": 120000, "disk_total_bytes": 234567, "disk_processed_bytes": 23456, "disk_remaining_bytes": 230000, "updated_at": "2016-01-29T13:42:02.000000" }} url = self.url('1234', 'migrations', '1') self.requests_mock.get(url, json=get_migration, headers=self.json_headers) url = self.url('1234', 'migrations', '1') self.requests_mock.delete(url, status_code=202, headers=self.json_headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/__init__.py0000664000175000017500000000000013165151077027400 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/limits.py0000664000175000017500000000560213165151077027157 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class Fixture(base.Fixture): base_url = 'limits' def setUp(self): super(Fixture, self).setUp() get_limits = { "limits": { "rate": [ { "uri": "*", "regex": ".*", "limit": [ { "value": 10, "verb": "POST", "remaining": 2, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" }, { "value": 10, "verb": "PUT", "remaining": 2, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" }, { "value": 100, "verb": "DELETE", "remaining": 100, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" } ] }, { "uri": "*/servers", "regex": "^/servers", "limit": [ { "verb": "POST", "value": 25, "remaining": 24, "unit": "DAY", "next-available": "2011-12-15T22:42:45Z" } ] } ], "absolute": { "maxTotalRAMSize": 51200, "maxServerMeta": 5, "maxImageMeta": 5, "maxPersonality": 5, "maxPersonalitySize": 10240 }, }, } headers = self.json_headers self.requests_mock.get(self.url(), json=get_limits, headers=headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/agents.py0000664000175000017500000000334713165151077027143 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class Fixture(base.Fixture): base_url = 'os-agents' def setUp(self): super(Fixture, self).setUp() post_os_agents = { 'agent': { 'url': '/xxx/xxx/xxx', 'hypervisor': 'kvm', 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'version': '7.0', 'architecture': 'x86', 'os': 'win', 'id': 1 } } self.requests_mock.post(self.url(), json=post_os_agents, headers=self.json_headers) put_os_agents_1 = { "agent": { "url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546", 'id': 1 } } self.requests_mock.put(self.url(1), json=put_os_agents_1, headers=self.json_headers) self.requests_mock.delete(self.url(1), headers=self.json_headers, status_code=202) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/servers.py0000664000175000017500000004572413165151100027343 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit import fakes from novaclient.tests.unit.fixture_data import base from novaclient.tests.unit.v2 import fakes as v2_fakes class Base(base.Fixture): base_url = 'servers' def setUp(self): super(Base, self).setUp() get_servers = { "servers": [ {'id': 1234, 'name': 'sample-server'}, {'id': 5678, 'name': 'sample-server2'} ] } self.requests_mock.get(self.url(), json=get_servers, headers=self.json_headers) self.server_1234 = { "id": 1234, "name": "sample-server", "image": { "id": 2, "name": "sample image", }, "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", "status": "BUILD", "progress": 60, "addresses": { "public": [ { "version": 4, "addr": "1.2.3.4", }, { "version": 4, "addr": "5.6.7.8", }], "private": [{ "version": 4, "addr": "10.11.12.13", }], }, "metadata": { "Server Label": "Web Head 1", "Image Version": "2.1" }, "OS-EXT-SRV-ATTR:host": "computenode1", "security_groups": [{ 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }], "OS-EXT-MOD:some_thing": "mod_some_thing_value", } self.server_5678 = { "id": 5678, "name": "sample-server2", "image": { "id": 2, "name": "sample image", }, "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", "addresses": { "public": [ { "version": 4, "addr": "4.5.6.7", }, { "version": 4, "addr": "5.6.9.8", }], "private": [{ "version": 4, "addr": "10.13.12.13", }], }, "metadata": { "Server Label": "DB 1" }, "OS-EXT-SRV-ATTR:host": "computenode2", "security_groups": [ { 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }, { 'id': 2, 'name': 'securitygroup2', 'description': 'ANOTHER_FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }], } self.server_9012 = { "id": 9012, "name": "sample-server3", "image": "", "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", "addresses": { "public": [ { "version": 4, "addr": "4.5.6.7", }, { "version": 4, "addr": "5.6.9.8", }], "private": [{ "version": 4, "addr": "10.13.12.13", }], }, "metadata": { "Server Label": "DB 1" } } servers = [self.server_1234, self.server_5678, self.server_9012] get_servers_detail = {"servers": servers} self.requests_mock.get(self.url('detail'), json=get_servers_detail, headers=self.json_headers) self.requests_mock.get( self.url('detail', marker=self.server_1234["id"]), json={"servers": [self.server_1234, self.server_5678]}, headers=self.json_headers, complete_qs=True) self.requests_mock.get( self.url('detail', marker=self.server_5678["id"]), json={"servers": []}, headers=self.json_headers, complete_qs=True) self.server_1235 = self.server_1234.copy() self.server_1235['id'] = 1235 self.server_1235['status'] = 'error' self.server_1235['fault'] = {'message': 'something went wrong!'} for s in servers + [self.server_1235]: self.requests_mock.get(self.url(s['id']), json={'server': s}, headers=self.json_headers) for s in (1234, 5678): self.requests_mock.delete(self.url(s), status_code=202, headers=self.json_headers) for k in ('test_key', 'key1', 'key2'): self.requests_mock.delete(self.url(1234, 'metadata', k), status_code=204, headers=self.json_headers) metadata1 = {'metadata': {'test_key': 'test_value'}} self.requests_mock.post(self.url(1234, 'metadata'), json=metadata1, headers=self.json_headers) self.requests_mock.put(self.url(1234, 'metadata', 'test_key'), json=metadata1, headers=self.json_headers) self.diagnostic = {'data': 'Fake diagnostics'} metadata2 = {'metadata': {'key1': 'val1'}} for u in ('uuid1', 'uuid2', 'uuid3', 'uuid4'): self.requests_mock.post(self.url(u, 'metadata'), json=metadata2, status_code=204) self.requests_mock.delete(self.url(u, 'metadata', 'key1'), json=self.diagnostic, headers=self.json_headers) get_security_groups = { "security_groups": [{ 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7', 'rules': []}] } self.requests_mock.get(self.url('1234', 'os-security-groups'), json=get_security_groups, headers=self.json_headers) self.requests_mock.post(self.url(), json=self.post_servers, headers=self.json_headers) self.requests_mock.post(self.url('1234', 'remote-consoles'), json=self.post_servers_1234_remote_consoles, headers=self.json_headers) self.requests_mock.post(self.url('1234', 'action'), json=self.post_servers_1234_action, headers=self.json_headers) get_os_interface = { "interfaceAttachments": [ { "port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], }, { "port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], } ] } self.requests_mock.get(self.url('1234', 'os-interface'), json=get_os_interface, headers=self.json_headers) interface_data = {'interfaceAttachment': {}} self.requests_mock.post(self.url('1234', 'os-interface'), json=interface_data, headers=self.json_headers) def put_servers_1234(request, context): body = request.json() assert list(body) == ['server'] fakes.assert_has_keys(body['server'], optional=['name', 'adminPass']) return request.body self.requests_mock.put(self.url(1234), text=put_servers_1234, status_code=204, headers=self.json_headers) def post_os_volumes_boot(request, context): body = request.json() assert (set(body.keys()) <= set(['server', 'os:scheduler_hints'])) fakes.assert_has_keys(body['server'], required=['name', 'flavorRef'], optional=['imageRef']) data = body['server'] # Require one, and only one, of the keys for bdm if 'block_device_mapping' not in data: if 'block_device_mapping_v2' not in data: msg = "missing required keys: 'block_device_mapping'" raise AssertionError(msg) elif 'block_device_mapping_v2' in data: msg = "found extra keys: 'block_device_mapping'" raise AssertionError(msg) return {'server': self.server_9012} # NOTE(jamielennox): hack to make os_volumes mock go to the right place base_url = self.base_url self.base_url = None self.requests_mock.post(self.url('os-volumes_boot'), json=post_os_volumes_boot, status_code=202, headers=self.json_headers) self.base_url = base_url # # Server password # self.requests_mock.delete(self.url(1234, 'os-server-password'), status_code=202, headers=self.json_headers) # # Server tags # self.requests_mock.get(self.url(1234, 'tags'), json={'tags': ['tag1', 'tag2']}, headers=self.json_headers) self.requests_mock.get(self.url(1234, 'tags', 'tag'), status_code=204, headers=self.json_headers) self.requests_mock.delete(self.url(1234, 'tags', 'tag'), status_code=204, headers=self.json_headers) self.requests_mock.delete(self.url(1234, 'tags'), status_code=204, headers=self.json_headers) def put_server_tag(request, context): assert request.text is None context.status_code = 201 return None self.requests_mock.put(self.url(1234, 'tags', 'tag'), json=put_server_tag, headers=self.json_headers) def put_server_tags(request, context): body = request.json() assert list(body) == ['tags'] return body self.requests_mock.put(self.url(1234, 'tags'), json=put_server_tags, headers=self.json_headers) class V1(Base): def setUp(self): super(V1, self).setUp() # # Server Addresses # add = self.server_1234['addresses'] self.requests_mock.get(self.url(1234, 'ips'), json={'addresses': add}, headers=self.json_headers) self.requests_mock.get(self.url(1234, 'ips', 'public'), json={'public': add['public']}, headers=self.json_headers) self.requests_mock.get(self.url(1234, 'ips', 'private'), json={'private': add['private']}, headers=self.json_headers) self.requests_mock.delete(self.url(1234, 'ips', 'public', '1.2.3.4'), status_code=202) self.requests_mock.get(self.url('1234', 'diagnostics'), json=self.diagnostic, headers=self.json_headers) self.requests_mock.delete(self.url('1234', 'os-interface', 'port-id'), headers=self.json_headers) # Testing with the following password and key # # Clear password: FooBar123 # # RSA Private Key: novaclient/tests/unit/idfake.pem # # Encrypted password # OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r # qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho # QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw # /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N # tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk # Hi/fmZZNQQqj1Ijq0caOIw== get_server_password = { 'password': 'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r' 'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho' 'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw' '/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N' 'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk' 'Hi/fmZZNQQqj1Ijq0caOIw=='} self.requests_mock.get(self.url(1234, 'os-server-password'), json=get_server_password, headers=self.json_headers) def post_servers(self, request, context): body = request.json() context.status_code = 202 assert (set(body.keys()) <= set(['server', 'os:scheduler_hints'])) fakes.assert_has_keys(body['server'], required=['name', 'imageRef', 'flavorRef'], optional=['metadata', 'personality']) if 'personality' in body['server']: for pfile in body['server']['personality']: fakes.assert_has_keys(pfile, required=['path', 'contents']) if body['server']['name'] == 'some-bad-server': body = self.server_1235 else: body = self.server_1234 return {'server': body} def post_servers_1234_remote_consoles(self, request, context): _body = '' body = request.json() context.status_code = 202 assert len(body.keys()) == 1 assert 'remote_console' in body.keys() assert 'protocol' in body['remote_console'].keys() protocol = body['remote_console']['protocol'] _body = {'protocol': protocol, 'type': 'novnc', 'url': 'http://example.com:6080/vnc_auto.html?token=XYZ'} return {'remote_console': _body} def post_servers_1234_action(self, request, context): _body = '' body = request.json() context.status_code = 202 assert len(body.keys()) == 1 action = list(body)[0] api_version = api_versions.APIVersion( request.headers.get('X-OpenStack-Nova-API-Version', '2.1')) if v2_fakes.FakeSessionClient.check_server_actions(body): # NOTE(snikitin): No need to do any operations here. This 'pass' # is needed to avoid AssertionError in the last 'else' statement # if we found 'action' in method check_server_actions and # raise AssertionError if we didn't find 'action' at all. pass elif action == 'os-migrateLive': # Fixme(eliqiao): body of os-migrateLive changes from v2.25 # but we can not specify version in data_fixture now and this is # V1 data, so just let it pass pass elif action == 'rebuild': body = body[action] adminPass = body.get('adminPass', 'randompassword') assert 'imageRef' in body _body = self.server_1234.copy() _body['adminPass'] = adminPass elif action == 'confirmResize': assert body[action] is None # This one method returns a different response code context.status_code = 204 return None elif action == 'rescue': if body[action]: keys = set(body[action].keys()) assert not (keys - set(['adminPass', 'rescue_image_ref'])) else: assert body[action] is None _body = {'adminPass': 'RescuePassword'} elif action == 'createImage': assert set(body[action].keys()) == set(['name', 'metadata']) if api_version >= api_versions.APIVersion('2.45'): return {'image_id': '456'} context.headers['location'] = "http://blah/images/456" elif action == 'createBackup': assert set(body[action].keys()) == set(['name', 'backup_type', 'rotation']) if api_version >= api_versions.APIVersion('2.45'): return {'image_id': '456'} context.headers['location'] = "http://blah/images/456" elif action == 'os-getConsoleOutput': assert list(body[action]) == ['length'] context.status_code = 202 return {'output': 'foo'} elif action == 'os-getSerialConsole': assert list(body[action]) == ['type'] elif action == 'evacuate': keys = list(body[action]) if 'adminPass' in keys: keys.remove('adminPass') assert 'host' in keys else: raise AssertionError("Unexpected server action: %s" % action) return {'server': _body} python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/certs.py0000664000175000017500000000324113165151077026773 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class Fixture(base.Fixture): base_url = 'os-certificates' def get_os_certificates_root(self, **kw): return ( 200, {}, {'certificate': {'private_key': None, 'data': 'foo'}} ) def post_os_certificates(self, **kw): return ( 200, {}, {'certificate': {'private_key': 'foo', 'data': 'bar'}} ) def setUp(self): super(Fixture, self).setUp() get_os_certificate = { 'certificate': { 'private_key': None, 'data': 'foo' } } self.requests_mock.get(self.url('root'), json=get_os_certificate, headers=self.json_headers) post_os_certificates = { 'certificate': { 'private_key': 'foo', 'data': 'bar' } } self.requests_mock.post(self.url(), json=post_os_certificates, headers=self.json_headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/base.py0000664000175000017500000000253113165151077026566 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import fixtures from six.moves.urllib import parse from novaclient.tests.unit.v2 import fakes COMPUTE_URL = 'http://compute.host' class Fixture(fixtures.Fixture): base_url = None json_headers = {'Content-Type': 'application/json', 'x-openstack-request-id': fakes.FAKE_REQUEST_ID} def __init__(self, requests_mock, compute_url=COMPUTE_URL): super(Fixture, self).__init__() self.requests_mock = requests_mock self.compute_url = compute_url def url(self, *args, **kwargs): url_args = [self.compute_url] if self.base_url: url_args.append(self.base_url) url = '/'.join(str(a).strip('/') for a in tuple(url_args) + args) if kwargs: url += '?%s' % parse.urlencode(kwargs, doseq=True) return url python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/hypervisors.py0000664000175000017500000001633713165151077030262 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient.tests.unit.fixture_data import base class V1(base.Fixture): base_url = 'os-hypervisors' api_version = '2.1' hyper_id_1 = 1234 hyper_id_2 = 5678 service_id_1 = 1 service_id_2 = 2 def setUp(self): super(V1, self).setUp() uuid_as_id = (api_versions.APIVersion(self.api_version) >= api_versions.APIVersion('2.53')) get_os_hypervisors = { 'hypervisors': [ {'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'}, {'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2'}, ] } self.headers = self.json_headers self.requests_mock.get(self.url(), json=get_os_hypervisors, headers=self.headers) get_os_hypervisors_detail = { 'hypervisors': [ { 'id': self.hyper_id_1, 'service': { 'id': self.service_id_1, 'host': 'compute1', }, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': 'xen', 'hypervisor_version': 3, 'hypervisor_hostname': 'hyper1', 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100 }, { 'id': self.hyper_id_2, 'service': { 'id': self.service_id_2, 'host': 'compute2', }, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': 'xen', 'hypervisor_version': 3, 'hypervisor_hostname': 'hyper2', 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100 } ] } self.requests_mock.get(self.url('detail'), json=get_os_hypervisors_detail, headers=self.headers) get_os_hypervisors_stats = { 'hypervisor_statistics': { 'count': 2, 'vcpus': 8, 'memory_mb': 20 * 1024, 'local_gb': 500, 'vcpus_used': 4, 'memory_mb_used': 10 * 1024, 'local_gb_used': 250, 'free_ram_mb': 10 * 1024, 'free_disk_gb': 250, 'current_workload': 4, 'running_vms': 4, 'disk_available_least': 200, } } self.requests_mock.get(self.url('statistics'), json=get_os_hypervisors_stats, headers=self.headers) get_os_hypervisors_search = { 'hypervisors': [ {'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1'}, {'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2'} ] } if uuid_as_id: url = self.url(hypervisor_hostname_pattern='hyper') else: url = self.url('hyper', 'search') self.requests_mock.get(url, json=get_os_hypervisors_search, headers=self.headers) get_hyper_server = { 'hypervisors': [ { 'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1', 'servers': [ {'name': 'inst1', 'uuid': 'uuid1'}, {'name': 'inst2', 'uuid': 'uuid2'} ] }, { 'id': self.hyper_id_2, 'hypervisor_hostname': 'hyper2', 'servers': [ {'name': 'inst3', 'uuid': 'uuid3'}, {'name': 'inst4', 'uuid': 'uuid4'} ] } ] } if uuid_as_id: url = self.url(hypervisor_hostname_pattern='hyper', with_servers=True) else: url = self.url('hyper', 'servers') self.requests_mock.get(url, json=get_hyper_server, headers=self.headers) get_os_hypervisors_hyper1 = { 'hypervisor': { 'id': self.hyper_id_1, 'service': {'id': self.service_id_1, 'host': 'compute1'}, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': 'xen', 'hypervisor_version': 3, 'hypervisor_hostname': 'hyper1', 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100 } } self.requests_mock.get(self.url(self.hyper_id_1), json=get_os_hypervisors_hyper1, headers=self.headers) get_os_hypervisors_uptime = { 'hypervisor': { 'id': self.hyper_id_1, 'hypervisor_hostname': 'hyper1', 'uptime': 'fake uptime' } } self.requests_mock.get(self.url(self.hyper_id_1, 'uptime'), json=get_os_hypervisors_uptime, headers=self.headers) class V2_53(V1): """Fixture data for the os-hypervisors 2.53 API.""" api_version = '2.53' hyper_id_1 = 'd480b1b6-2255-43c2-b2c2-d60d42c2c074' hyper_id_2 = '43a8214d-f36a-4fc0-a25c-3cf35c17522d' service_id_1 = 'a87743ff-9c29-42ff-805d-2444659b5fc0' service_id_2 = '0486ab8b-1cfc-4ccb-9d94-9f22ec8bbd6b' python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/cloudpipe.py0000664000175000017500000000263213165151077027642 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class Fixture(base.Fixture): base_url = 'os-cloudpipe' def setUp(self): super(Fixture, self).setUp() get_os_cloudpipe = {'cloudpipes': [{'project_id': 1}]} self.requests_mock.get(self.url(), json=get_os_cloudpipe, headers=self.json_headers) instance_id = '9d5824aa-20e6-4b9f-b967-76a699fc51fd' post_os_cloudpipe = {'instance_id': instance_id} self.requests_mock.post(self.url(), json=post_os_cloudpipe, headers=self.json_headers, status_code=202) self.requests_mock.put(self.url('configure-project'), headers=self.json_headers, status_code=202) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/server_groups.py0000664000175000017500000000761213165151077030566 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class Fixture(base.Fixture): base_url = 'os-server-groups' def setUp(self): super(Fixture, self).setUp() server_groups = [ { "members": [], "metadata": {}, "id": "2cbd51f4-fafe-4cdb-801b-cf913a6f288b", "policies": [], "name": "ig1" }, { "members": [], "metadata": {}, "id": "4473bb03-4370-4bfb-80d3-dc8cffc47d94", "policies": ["anti-affinity"], "name": "ig2" }, { "members": [], "metadata": {"key": "value"}, "id": "31ab9bdb-55e1-4ac3-b094-97eeb1b65cc4", "policies": [], "name": "ig3" }, { "members": ["2dccb4a1-02b9-482a-aa23-5799490d6f5d"], "metadata": {}, "id": "4890bb03-7070-45fb-8453-d34556c87d94", "policies": ["anti-affinity"], "name": "ig2" } ] other_project_server_groups = [ { "members": [], "metadata": {}, "id": "11111111-1111-1111-1111-111111111111", "policies": [], "name": "ig4" }, { "members": [], "metadata": {}, "id": "22222222-2222-2222-2222-222222222222", "policies": ["anti-affinity"], "name": "ig5" }, { "members": [], "metadata": {"key": "value"}, "id": "33333333-3333-3333-3333-333333333333", "policies": [], "name": "ig6" }, { "members": ["2dccb4a1-02b9-482a-aa23-5799490d6f5d"], "metadata": {}, "id": "44444444-4444-4444-4444-444444444444", "policies": ["anti-affinity"], "name": "ig5" } ] headers = self.json_headers self.requests_mock.get(self.url(), json={'server_groups': server_groups}, headers=headers) self.requests_mock.get(self.url(all_projects=True), json={'server_groups': server_groups + other_project_server_groups}, headers=headers) self.requests_mock.get(self.url(limit=2, offset=1), json={'server_groups': server_groups[1:3]}, headers=headers) server = server_groups[0] def _register(method, *args): self.requests_mock.register_uri(method, self.url(*args), json={'server_group': server}, headers=headers) _register('POST') _register('POST', server['id']) _register('GET', server['id']) _register('PUT', server['id']) _register('POST', server['id'], '/action') self.requests_mock.delete(self.url(server['id']), status_code=202, headers=headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/floatingips.py0000664000175000017500000000355513165151077030202 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class FloatingFixture(base.Fixture): base_url = 'os-floating-ips' def setUp(self): super(FloatingFixture, self).setUp() floating_ips = [{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'}, {'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'}] get_os_floating_ips = {'floating_ips': floating_ips} self.requests_mock.get(self.url(), json=get_os_floating_ips, headers=self.json_headers) for ip in floating_ips: get_os_floating_ip = {'floating_ip': ip} self.requests_mock.get(self.url(ip['id']), json=get_os_floating_ip, headers=self.json_headers) self.requests_mock.delete(self.url(ip['id']), headers=self.json_headers, status_code=204) def post_os_floating_ips(request, context): ip = floating_ips[0].copy() ip['pool'] = request.json().get('pool') return {'floating_ip': ip} self.requests_mock.post(self.url(), json=post_os_floating_ips, headers=self.json_headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/aggregates.py0000664000175000017500000000366413165151077027775 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class Fixture(base.Fixture): base_url = 'os-aggregates' def setUp(self): super(Fixture, self).setUp() get_os_aggregates = {"aggregates": [ {'id': '1', 'name': 'test', 'availability_zone': 'nova1'}, {'id': '2', 'name': 'test2', 'availability_zone': 'nova1'}, ]} self.requests_mock.get(self.url(), json=get_os_aggregates, headers=self.json_headers) get_aggregates_1 = {'aggregate': get_os_aggregates['aggregates'][0]} self.requests_mock.post(self.url(), json=get_aggregates_1, headers=self.json_headers) for agg_id in (1, 2): for method in ('GET', 'PUT'): self.requests_mock.register_uri(method, self.url(agg_id), json=get_aggregates_1, headers=self.json_headers) self.requests_mock.post(self.url(agg_id, 'action'), json=get_aggregates_1, headers=self.json_headers) self.requests_mock.delete(self.url(1), status_code=202, headers=self.json_headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/hosts.py0000664000175000017500000001150213165151077027012 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class BaseFixture(base.Fixture): base_url = 'os-hosts' def setUp(self): super(BaseFixture, self).setUp() get_os_hosts_host = { 'host': [ {'resource': {'project': '(total)', 'host': 'dummy', 'cpu': 16, 'memory_mb': 32234, 'disk_gb': 128}}, {'resource': {'project': '(used_now)', 'host': 'dummy', 'cpu': 1, 'memory_mb': 2075, 'disk_gb': 45}}, {'resource': {'project': '(used_max)', 'host': 'dummy', 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}}, {'resource': {'project': 'admin', 'host': 'dummy', 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}} ] } headers = self.json_headers self.requests_mock.get(self.url('host'), json=get_os_hosts_host, headers=headers) def get_os_hosts(request, context): zone = 'nova1' service = None if request.query: try: zone = request.qs['zone'][0] except Exception: pass try: service = request.qs['service'][0] except Exception: pass return { 'hosts': [ { 'host_name': 'host1', 'service': service or 'nova-compute', 'zone': zone }, { 'host_name': 'host1', 'service': service or 'nova-cert', 'zone': zone } ] } self.requests_mock.get(self.url(), json=get_os_hosts, headers=headers) get_os_hosts_sample_host = { 'host': [ {'resource': {'host': 'sample_host'}} ], } self.requests_mock.get(self.url('sample_host'), json=get_os_hosts_sample_host, headers=headers) self.requests_mock.put(self.url('sample_host', 1), json=self.put_host_1(), headers=headers) self.requests_mock.put(self.url('sample_host', 2), json=self.put_host_2(), headers=headers) self.requests_mock.put(self.url('sample_host', 3), json=self.put_host_3(), headers=headers) self.requests_mock.get(self.url('sample_host', 'reboot'), json=self.get_host_reboot(), headers=headers) self.requests_mock.get(self.url('sample_host', 'startup'), json=self.get_host_startup(), headers=headers) self.requests_mock.get(self.url('sample_host', 'shutdown'), json=self.get_host_shutdown(), headers=headers) def put_os_hosts_sample_host(request, context): result = {'host': 'dummy'} result.update(request.json()) return result self.requests_mock.put(self.url('sample_host'), json=put_os_hosts_sample_host, headers=headers) class V1(BaseFixture): def put_host_1(self): return {'host': 'sample-host_1', 'status': 'enabled'} def put_host_2(self): return {'host': 'sample-host_2', 'maintenance_mode': 'on_maintenance'} def put_host_3(self): return {'host': 'sample-host_3', 'status': 'enabled', 'maintenance_mode': 'on_maintenance'} def get_host_reboot(self): return {'host': 'sample_host', 'power_action': 'reboot'} def get_host_startup(self): return {'host': 'sample_host', 'power_action': 'startup'} def get_host_shutdown(self): return {'host': 'sample_host', 'power_action': 'shutdown'} python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/client.py0000664000175000017500000000510413165151077027131 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import fixtures from keystoneauth1 import fixture from keystoneauth1 import loading from keystoneauth1 import session from novaclient import client IDENTITY_URL = 'http://identityserver:5000/v2.0' COMPUTE_URL = 'http://compute.host' class V1(fixtures.Fixture): def __init__(self, requests_mock, compute_url=COMPUTE_URL, identity_url=IDENTITY_URL, **client_kwargs): super(V1, self).__init__() self.identity_url = identity_url self.compute_url = compute_url self.client = None self.requests_mock = requests_mock self.token = fixture.V2Token() self.token.set_scope() self.discovery = fixture.V2Discovery(href=self.identity_url) s = self.token.add_service('compute') s.add_endpoint(self.compute_url) s = self.token.add_service('computev3') s.add_endpoint(self.compute_url) self._client_kwargs = client_kwargs def setUp(self): super(V1, self).setUp() auth_url = '%s/tokens' % self.identity_url headers = {'X-Content-Type': 'application/json'} self.requests_mock.post(auth_url, json=self.token, headers=headers) self.requests_mock.get(self.identity_url, json=self.discovery, headers=headers) self.client = self.new_client(**self._client_kwargs) def new_client(self, **client_kwargs): return client.Client("2", username='xx', password='xx', project_id='xx', auth_url=self.identity_url, **client_kwargs) class SessionV1(V1): def new_client(self): self.session = session.Session() loader = loading.get_plugin_loader('password') self.session.auth = loader.load_from_options( auth_url=self.identity_url, username='xx', password='xx') return client.Client("2", session=self.session) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/availability_zones.py0000664000175000017500000000623213165151077031546 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class V1(base.Fixture): base_url = 'os-availability-zone' zone_info_key = 'availabilityZoneInfo' zone_name_key = 'zoneName' zone_state_key = 'zoneState' def setUp(self): super(V1, self).setUp() get_os_availability_zone = { self.zone_info_key: [ { self.zone_name_key: "zone-1", self.zone_state_key: {"available": True}, "hosts": None }, { self.zone_name_key: "zone-2", self.zone_state_key: {"available": False}, "hosts": None } ] } self.requests_mock.get(self.url(), json=get_os_availability_zone, headers=self.json_headers) get_os_zone_detail = { self.zone_info_key: [ { self.zone_name_key: "zone-1", self.zone_state_key: {"available": True}, "hosts": { "fake_host-1": { "nova-compute": { "active": True, "available": True, "updated_at": '2012-12-26 14:45:25' } } } }, { self.zone_name_key: "internal", self.zone_state_key: {"available": True}, "hosts": { "fake_host-1": { "nova-sched": { "active": True, "available": True, "updated_at": '2012-12-26 14:45:25' } }, "fake_host-2": { "nova-network": { "active": True, "available": False, "updated_at": '2012-12-26 14:45:24' } } } }, { self.zone_name_key: "zone-2", self.zone_state_key: {"available": False}, "hosts": None } ] } self.requests_mock.get(self.url('detail'), json=get_os_zone_detail, headers=self.json_headers) python-novaclient-9.1.1/novaclient/tests/unit/fixture_data/quotas.py0000664000175000017500000000473513165151077027200 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.unit.fixture_data import base class V1(base.Fixture): base_url = 'os-quota-sets' def setUp(self): super(V1, self).setUp() uuid = '97f4c221-bff4-4578-b030-0df4ef119353' uuid2 = '97f4c221bff44578b0300df4ef119353' test_json = {'quota_set': self.test_quota('test')} self.headers = self.json_headers for u in ('test', 'tenant-id', 'tenant-id/defaults', '%s/defaults' % uuid2, 'test/detail'): self.requests_mock.get(self.url(u), json=test_json, headers=self.headers) self.requests_mock.put(self.url(uuid), json={'quota_set': self.test_quota(uuid)}, headers=self.headers) self.requests_mock.get(self.url(uuid), json={'quota_set': self.test_quota(uuid)}, headers=self.headers) self.requests_mock.put(self.url(uuid2), json={'quota_set': self.test_quota(uuid2)}, headers=self.headers) self.requests_mock.get(self.url(uuid2), json={'quota_set': self.test_quota(uuid2)}, headers=self.headers) for u in ('test', uuid2): self.requests_mock.delete(self.url(u), status_code=202, headers=self.headers) def test_quota(self, tenant_id='test'): return { 'tenant_id': tenant_id, 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1 } python-novaclient-9.1.1/novaclient/tests/unit/test_exceptions.py0000664000175000017500000000371213165151077026417 0ustar jenkinsjenkins00000000000000# Copyright 2016 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 novaclient import exceptions from novaclient.tests.unit import utils as test_utils class ExceptionsTestCase(test_utils.TestCase): def _test_from_response(self, body, expected_message): data = { 'status_code': 404, 'headers': { 'content-type': 'application/json', 'x-openstack-request-id': ( 'req-d9df03b0-4150-4b53-8157-7560ccf39f75'), } } response = test_utils.TestResponse(data) fake_url = 'http://localhost:8774/v2.1/fake/flavors/test' error = exceptions.from_response(response, body, fake_url, 'GET') self.assertIsInstance(error, exceptions.NotFound) self.assertEqual(expected_message, error.message) def test_from_response_webob_pre_1_6_0(self): # Tests error responses before webob 1.6.0 where the error details # are nested in the response body. message = "Flavor test could not be found." self._test_from_response( {"itemNotFound": {"message": message, "code": 404}}, message) def test_from_response_webob_post_1_6_0(self): # Tests error responses from webob 1.6.0 where the error details # are in the response body. message = "Flavor test could not be found." self._test_from_response({"message": message, "code": 404}, message) python-novaclient-9.1.1/novaclient/tests/unit/test_discover.py0000664000175000017500000000471513165151077026060 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import imp import inspect import mock import pkg_resources from novaclient import client from novaclient.tests.unit import utils class DiscoverTest(utils.TestCase): def test_discover_via_entry_points(self): def mock_iter_entry_points(group): if group == 'novaclient.extension': fake_ep = mock.Mock() fake_ep.name = 'foo' fake_ep.module = imp.new_module('foo') fake_ep.load.return_value = fake_ep.module return [fake_ep] @mock.patch.object(pkg_resources, 'iter_entry_points', mock_iter_entry_points) def test(): for name, module in client._discover_via_entry_points(): self.assertEqual('foo', name) self.assertTrue(inspect.ismodule(module)) test() def test_discover_extensions(self): def mock_discover_via_python_path(): yield 'foo', imp.new_module('foo') def mock_discover_via_entry_points(): yield 'baz', imp.new_module('baz') @mock.patch.object(client, '_discover_via_python_path', mock_discover_via_python_path) @mock.patch.object(client, '_discover_via_entry_points', mock_discover_via_entry_points) def test(): extensions = client.discover_extensions('1.1') self.assertEqual(2, len(extensions)) names = sorted(['foo', 'baz']) sorted_extensions = sorted(extensions, key=lambda ext: ext.name) for i in range(len(names)): ext = sorted_extensions[i] name = names[i] self.assertEqual(ext.name, name) self.assertTrue(inspect.ismodule(ext.module)) test() python-novaclient-9.1.1/novaclient/tests/unit/test_base.py0000664000175000017500000001375613165151077025161 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from requests import Response import six from novaclient import api_versions from novaclient import base from novaclient import exceptions from novaclient.tests.unit import utils from novaclient.tests.unit.v2 import fakes from novaclient.v2 import flavors def create_response_obj_with_header(): resp = Response() resp.headers['x-openstack-request-id'] = fakes.FAKE_REQUEST_ID return resp def create_response_obj_with_compute_header(): resp = Response() resp.headers['x-compute-request-id'] = fakes.FAKE_REQUEST_ID return resp class BaseTest(utils.TestCase): def test_resource_repr(self): r = base.Resource(None, dict(foo="bar", baz="spam")) self.assertEqual("", repr(r)) def test_getid(self): self.assertEqual(4, base.getid(4)) class TmpObject(object): id = 4 self.assertEqual(4, base.getid(TmpObject)) def test_resource_lazy_getattr(self): cs = fakes.FakeClient(api_versions.APIVersion("2.0")) f = flavors.Flavor(cs.flavors, {'id': 1}) self.assertEqual('256 MB Server', f.name) cs.assert_called('GET', '/flavors/1') # Missing stuff still fails after a second get self.assertRaises(AttributeError, getattr, f, 'blahblah') def test_eq(self): # Two resources of the same type with the same id: equal r1 = base.Resource(None, {'id': 1, 'name': 'hi'}) r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) self.assertEqual(r1, r2) # Two resources of different types: never equal r1 = base.Resource(None, {'id': 1}) r2 = flavors.Flavor(None, {'id': 1}) self.assertNotEqual(r1, r2) # Two resources with no ID: equal if their info is equal r1 = base.Resource(None, {'name': 'joe', 'age': 12}) r2 = base.Resource(None, {'name': 'joe', 'age': 12}) self.assertEqual(r1, r2) def test_ne(self): # Two resources of different types: never equal r1 = base.Resource(None, {'id': 1, 'name': 'test'}) r2 = object() self.assertNotEqual(r1, r2) def test_findall_invalid_attribute(self): cs = fakes.FakeClient(api_versions.APIVersion("2.0")) # Make sure findall with an invalid attribute doesn't cause errors. # The following should not raise an exception. cs.flavors.findall(vegetable='carrot') # However, find() should raise an error self.assertRaises(exceptions.NotFound, cs.flavors.find, vegetable='carrot') def test_resource_object_with_request_ids(self): resp_obj = create_response_obj_with_header() r = base.Resource(None, {"name": "1"}, resp=resp_obj) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, r.request_ids) def test_resource_object_with_compute_request_ids(self): resp_obj = create_response_obj_with_compute_header() r = base.Resource(None, {"name": "1"}, resp=resp_obj) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, r.request_ids) class ListWithMetaTest(utils.TestCase): def test_list_with_meta(self): resp = create_response_obj_with_header() obj = base.ListWithMeta([], resp) self.assertEqual([], obj) # Check request_ids attribute is added to obj self.assertTrue(hasattr(obj, 'request_ids')) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) class DictWithMetaTest(utils.TestCase): def test_dict_with_meta(self): resp = create_response_obj_with_header() obj = base.DictWithMeta({}, resp) self.assertEqual({}, obj) # Check request_ids attribute is added to obj self.assertTrue(hasattr(obj, 'request_ids')) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) class TupleWithMetaTest(utils.TestCase): def test_tuple_with_meta(self): resp = create_response_obj_with_header() expected_tuple = (1, 2) obj = base.TupleWithMeta(expected_tuple, resp) self.assertEqual(expected_tuple, obj) # Check request_ids attribute is added to obj self.assertTrue(hasattr(obj, 'request_ids')) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) class StrWithMetaTest(utils.TestCase): def test_str_with_meta(self): resp = create_response_obj_with_header() obj = base.StrWithMeta("test-str", resp) self.assertEqual("test-str", obj) # Check request_ids attribute is added to obj self.assertTrue(hasattr(obj, 'request_ids')) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) class BytesWithMetaTest(utils.TestCase): def test_bytes_with_meta(self): resp = create_response_obj_with_header() obj = base.BytesWithMeta(b'test-bytes', resp) self.assertEqual(b'test-bytes', obj) # Check request_ids attribute is added to obj self.assertTrue(hasattr(obj, 'request_ids')) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) if six.PY2: class UnicodeWithMetaTest(utils.TestCase): def test_unicode_with_meta(self): resp = create_response_obj_with_header() obj = base.UnicodeWithMeta(u'test-unicode', resp) self.assertEqual(u'test-unicode', obj) # Check request_ids attribute is added to obj self.assertTrue(hasattr(obj, 'request_ids')) self.assertEqual(fakes.FAKE_REQUEST_ID_LIST, obj.request_ids) python-novaclient-9.1.1/novaclient/tests/unit/idfake.pem0000664000175000017500000000321313165151077024547 0ustar jenkinsjenkins00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA9QstF/7prDY7a9La7GS9TpMX+MWWXQgK6pHRLakDFp1WX1Q3 Vly7rWitaZUGirUPMm181oJXBwkKlAxFD7hKjyHYaSswNszPYIAsVkc1+AO5epXz g9kUBNtfg44Pg72UecwLrZ8JpmNZpJlKQOx6vF+yi7JmHrrIf6il/grIGUPzoT2L yReimpyPoBrGtXhJYaCJ/XbKg1idRZiQdmwh1F/OmZWn9p0wunnsv08a0+qIywuw WhG9/Zy9fjnEByfusS6gI0GIxDRL4RWzOqphd3PZzunwIBgEKFhgiki9+2DgcRVO 9I5wnDvfwQREJRZWh1uJa5ZTcfPa1EzZryVeOQIDAQABAoIBABxO3Te/cBk/7p9n LXlPrfrszUEk+ljm+/PbQpIGy1+Kb5b1sKrebaP7ysS+vZG6lvXZZimVxx398mXm APhu7tYYL9r+bUR3ZqGcTQLumRJ8w6mgtxANPN3Oxfr5p1stxIBJjTPSgpfhNFLq joRvjUJDv+mZg2ibZVwyDHMLpdAdKp+3XMdyTLZcH9esqwii+natix7rHd1RuF85 L1dfpxjkItwhgHsfdYS++5X3fRByFOhQ+Nhabh/kPQbQMcteRn1bN6zeCWBSglNb Ka/ZrXb6ApRUc22Ji62mNO2ZPPekLJeCHk2h2E7ezYX+sGDNvvd/jHVDJJ20FjD1 Z9KXuK0CgYEA/2vniy9yWd925QQtWbmrxgy6yj89feMH/LTv4qP298rGZ2nqxsyd 9pdBdb4NMsi4HmV5PG1hp3VRNBHl53DNh5eqzT8WEXnIF+sbrIU3KzrCVAx1kZTl +OWKA6aVUsvvO3y85SOvInnsV+IsOGmU4/WBSjYoe39Bo7mq/YuZB9MCgYEA9ZlB KBm6PjFdHQGNgedXahWzRcwC+ALCYqequPYqJolNzhrK4Uc2sWPSGdnldcHZ4XCQ wbfCxUSwrMpA1oyuIQ0U4aowmOw5DjIueBWI8XBYEVRBlwvJwbXpBZ/DspGzTUDx MBrrEwEaMadQvxhRnAzhp0rQAepatcz6Fgb1JkMCgYBMwDLiew5kfSav6JJsDMPW DksurNQgeNEUmZYfx19V1EPMHWKj/CZXS9oqtEIpCXFyCNHmW4PlmvYcrGgmJJpN 7UAwzo0mES8UKNy2+Yy7W7u7H8dQSKrWILtZH3xtVcR8Xp4wSIm+1V40hkz9YpSP 71y7XQzLF1E1DnyYFZOVawKBgAFrmHfd5jjT2kD/sEzPBK9lXrsJmf7LLUqaw578 NXQxmRSXDRNOcR+Hf0CNBQmwTE1EdGHaaTLw2cC2Drfu6lbgl31SmaNYwl+1pJUn MrqKtseq4BI6jDkljypsKRqQQyQwOvTXQwLCH9+nowzn3Bj17hwkj51jOJESlWOp OKO3AoGBALm+jjqyqX7gSnqK3FAumB8mlhv3yI1Wr1ctwe18mKfKbz17HxXRu9pF K/6e7WMCA1p+jhoE8gj1h2WBcH0nV2qt8Ye8gJBbCi4dhI08o4AfrIV47oZx1RlO qYcA1U9lyaODY5SL8+6PHOy5J/aYtuA+wvfEnWiCIdKQrhWetcn3 -----END RSA PRIVATE KEY----- python-novaclient-9.1.1/novaclient/tests/unit/fake_actions_module.py0000664000175000017500000000244013165151077027167 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient import utils @api_versions.wraps("2.10", "2.20") def do_fake_action(): return 1 @api_versions.wraps("2.21", "2.30") def do_fake_action(): return 2 @api_versions.wraps("2.0") def do_another_fake_action(): return 0 @utils.arg( '--foo', start_version='2.1', end_version='2.2') @utils.arg( '--bar', start_version='2.3', end_version='2.4') def do_fake_action2(): return 3 @utils.arg( '--foo', help='first foo', start_version='2.10', end_version='2.20') @utils.arg( '--foo', help='second foo', start_version='2.21') def do_fake_action3(): return 3 python-novaclient-9.1.1/novaclient/tests/unit/utils.py0000664000175000017500000001100313165151077024327 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import fixtures import mock from oslo_serialization import jsonutils import requests from requests_mock.contrib import fixture as requests_mock_fixture import six import testscenarios import testtools AUTH_URL = "http://localhost:5002/auth_url" AUTH_URL_V1 = "http://localhost:5002/auth_url/v1.0" AUTH_URL_V2 = "http://localhost:5002/auth_url/v2.0" def _patch_mock_to_raise_for_invalid_assert_calls(): def raise_for_invalid_assert_calls(wrapped): def wrapper(_self, name): valid_asserts = [ 'assert_called_with', 'assert_called_once_with', 'assert_has_calls', 'assert_any_calls'] if name.startswith('assert') and name not in valid_asserts: raise AttributeError('%s is not a valid mock assert method' % name) return wrapped(_self, name) return wrapper mock.Mock.__getattr__ = raise_for_invalid_assert_calls( mock.Mock.__getattr__) # NOTE(gibi): needs to be called only once at import time # to patch the mock lib _patch_mock_to_raise_for_invalid_assert_calls() 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)) self.requests_mock = self.useFixture(requests_mock_fixture.Fixture()) def assert_request_id(self, request_id_mixin, request_id_list): self.assertEqual(request_id_list, request_id_mixin.request_ids) class FixturedTestCase(testscenarios.TestWithScenarios, TestCase): client_fixture_class = None data_fixture_class = None def setUp(self): super(FixturedTestCase, self).setUp() self.data_fixture = None self.client_fixture = None self.cs = None if self.client_fixture_class: fix = self.client_fixture_class(self.requests_mock) self.client_fixture = self.useFixture(fix) self.cs = self.client_fixture.client if self.data_fixture_class: fix = self.data_fixture_class(self.requests_mock) self.data_fixture = self.useFixture(fix) def assert_called(self, method, path, body=None): self.assertEqual(self.requests_mock.last_request.method, method) self.assertEqual(self.requests_mock.last_request.path_url, path) if body: req_data = self.requests_mock.last_request.body if isinstance(req_data, six.binary_type): req_data = req_data.decode('utf-8') if not isinstance(body, six.string_types): # json load if the input body to match against is not a string req_data = jsonutils.loads(req_data) self.assertEqual(req_data, body) class TestResponse(requests.Response): """Class used to wrap requests.Response. Provide some convenience to initialize with a dict. """ def __init__(self, data): super(TestResponse, self).__init__() self._text = None if isinstance(data, dict): self.status_code = data.get('status_code') self.headers = data.get('headers') # Fake the text attribute to streamline Response creation self._text = data.get('text') else: self.status_code = data def __eq__(self, other): return self.__dict__ == other.__dict__ @property def text(self): return self._text python-novaclient-9.1.1/novaclient/tests/unit/test_shell.py0000664000175000017500000011754413165151077025356 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import distutils.version as dist_version import re import sys import fixtures from keystoneauth1 import fixture import mock import prettytable import requests_mock import six from testtools import matchers from novaclient import api_versions import novaclient.client from novaclient import exceptions import novaclient.shell from novaclient.tests.unit import fake_actions_module from novaclient.tests.unit import utils FAKE_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', 'OS_AUTH_URL': 'http://no.where/v2.0', 'OS_COMPUTE_API_VERSION': '2'} FAKE_ENV2 = {'OS_USER_ID': 'user_id', 'OS_PASSWORD': 'password', 'OS_TENANT_ID': 'tenant_id', 'OS_AUTH_URL': 'http://no.where/v2.0', 'OS_COMPUTE_API_VERSION': '2'} FAKE_ENV3 = {'OS_USER_ID': 'user_id', 'OS_PASSWORD': 'password', 'OS_TENANT_ID': 'tenant_id', 'OS_AUTH_URL': 'http://no.where/v2.0', 'NOVA_ENDPOINT_TYPE': 'novaURL', 'OS_ENDPOINT_TYPE': 'osURL', 'OS_COMPUTE_API_VERSION': '2'} FAKE_ENV4 = {'OS_USER_ID': 'user_id', 'OS_PASSWORD': 'password', 'OS_TENANT_ID': 'tenant_id', 'OS_AUTH_URL': 'http://no.where/v2.0', 'NOVA_ENDPOINT_TYPE': 'internal', 'OS_ENDPOINT_TYPE': 'osURL', 'OS_COMPUTE_API_VERSION': '2'} FAKE_ENV5 = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', 'OS_AUTH_URL': 'http://no.where/v2.0'} def _create_ver_list(versions): return {'versions': {'values': versions}} class DeprecatedActionTest(utils.TestCase): @mock.patch.object(argparse.Action, '__init__', return_value=None) def test_init_emptyhelp_nouse(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertIsNone(result.use) self.assertEqual(result.real_action_args, ('option_strings', 'dest', 'Deprecated', {'a': 1, 'b': 2, 'c': 3})) self.assertIsNone(result.real_action) mock_init.assert_called_once_with( 'option_strings', 'dest', help='Deprecated', a=1, b=2, c=3) @mock.patch.object(novaclient.shell.argparse.Action, '__init__', return_value=None) def test_init_emptyhelp_withuse(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', use='use this instead', a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertEqual(result.use, 'use this instead') self.assertEqual(result.real_action_args, ('option_strings', 'dest', 'Deprecated; use this instead', {'a': 1, 'b': 2, 'c': 3})) self.assertIsNone(result.real_action) mock_init.assert_called_once_with( 'option_strings', 'dest', help='Deprecated; use this instead', a=1, b=2, c=3) @mock.patch.object(argparse.Action, '__init__', return_value=None) def test_init_withhelp_nouse(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', help='some help', a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertIsNone(result.use) self.assertEqual(result.real_action_args, ('option_strings', 'dest', 'some help (Deprecated)', {'a': 1, 'b': 2, 'c': 3})) self.assertIsNone(result.real_action) mock_init.assert_called_once_with( 'option_strings', 'dest', help='some help (Deprecated)', a=1, b=2, c=3) @mock.patch.object(novaclient.shell.argparse.Action, '__init__', return_value=None) def test_init_withhelp_withuse(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', help='some help', use='use this instead', a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertEqual(result.use, 'use this instead') self.assertEqual(result.real_action_args, ('option_strings', 'dest', 'some help (Deprecated; use this instead)', {'a': 1, 'b': 2, 'c': 3})) self.assertIsNone(result.real_action) mock_init.assert_called_once_with( 'option_strings', 'dest', help='some help (Deprecated; use this instead)', a=1, b=2, c=3) @mock.patch.object(argparse.Action, '__init__', return_value=None) def test_init_suppresshelp_nouse(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', help=argparse.SUPPRESS, a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertIsNone(result.use) self.assertEqual(result.real_action_args, ('option_strings', 'dest', argparse.SUPPRESS, {'a': 1, 'b': 2, 'c': 3})) self.assertIsNone(result.real_action) mock_init.assert_called_once_with( 'option_strings', 'dest', help=argparse.SUPPRESS, a=1, b=2, c=3) @mock.patch.object(novaclient.shell.argparse.Action, '__init__', return_value=None) def test_init_suppresshelp_withuse(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', help=argparse.SUPPRESS, use='use this instead', a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertEqual(result.use, 'use this instead') self.assertEqual(result.real_action_args, ('option_strings', 'dest', argparse.SUPPRESS, {'a': 1, 'b': 2, 'c': 3})) self.assertIsNone(result.real_action) mock_init.assert_called_once_with( 'option_strings', 'dest', help=argparse.SUPPRESS, a=1, b=2, c=3) @mock.patch.object(argparse.Action, '__init__', return_value=None) def test_init_action_nothing(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', real_action='nothing', a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertIsNone(result.use) self.assertIs(result.real_action_args, False) self.assertIsNone(result.real_action) mock_init.assert_called_once_with( 'option_strings', 'dest', help='Deprecated', a=1, b=2, c=3) @mock.patch.object(argparse.Action, '__init__', return_value=None) def test_init_action_string(self, mock_init): result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', real_action='store', a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertIsNone(result.use) self.assertEqual(result.real_action_args, ('option_strings', 'dest', 'Deprecated', {'a': 1, 'b': 2, 'c': 3})) self.assertEqual(result.real_action, 'store') mock_init.assert_called_once_with( 'option_strings', 'dest', help='Deprecated', a=1, b=2, c=3) @mock.patch.object(argparse.Action, '__init__', return_value=None) def test_init_action_other(self, mock_init): action = mock.Mock() result = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', real_action=action, a=1, b=2, c=3) self.assertEqual(result.emitted, set()) self.assertIsNone(result.use) self.assertIs(result.real_action_args, False) self.assertEqual(result.real_action, action.return_value) mock_init.assert_called_once_with( 'option_strings', 'dest', help='Deprecated', a=1, b=2, c=3) action.assert_called_once_with( 'option_strings', 'dest', help='Deprecated', a=1, b=2, c=3) @mock.patch.object(sys, 'stderr', six.StringIO()) def test_get_action_nolookup(self): action_class = mock.Mock() parser = mock.Mock(**{ '_registry_get.return_value': action_class, }) obj = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', real_action='nothing', const=1) obj.real_action = 'action' result = obj._get_action(parser) self.assertEqual(result, 'action') self.assertEqual(obj.real_action, 'action') self.assertFalse(parser._registry_get.called) self.assertFalse(action_class.called) self.assertEqual(sys.stderr.getvalue(), '') @mock.patch.object(sys, 'stderr', six.StringIO()) def test_get_action_lookup_noresult(self): parser = mock.Mock(**{ '_registry_get.return_value': None, }) obj = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', real_action='store', const=1) result = obj._get_action(parser) self.assertIsNone(result) self.assertIsNone(obj.real_action) parser._registry_get.assert_called_once_with( 'action', 'store') self.assertEqual(sys.stderr.getvalue(), 'WARNING: Programming error: Unknown real action ' '"store"\n') @mock.patch.object(sys, 'stderr', six.StringIO()) def test_get_action_lookup_withresult(self): action_class = mock.Mock() parser = mock.Mock(**{ '_registry_get.return_value': action_class, }) obj = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', real_action='store', const=1) result = obj._get_action(parser) self.assertEqual(result, action_class.return_value) self.assertEqual(obj.real_action, action_class.return_value) parser._registry_get.assert_called_once_with( 'action', 'store') action_class.assert_called_once_with( 'option_strings', 'dest', help='Deprecated', const=1) self.assertEqual(sys.stderr.getvalue(), '') @mock.patch.object(sys, 'stderr', six.StringIO()) @mock.patch.object(novaclient.shell.DeprecatedAction, '_get_action') def test_call_unemitted_nouse(self, mock_get_action): obj = novaclient.shell.DeprecatedAction( 'option_strings', 'dest') obj('parser', 'namespace', 'values', 'option_string') self.assertEqual(obj.emitted, set(['option_string'])) mock_get_action.assert_called_once_with('parser') mock_get_action.return_value.assert_called_once_with( 'parser', 'namespace', 'values', 'option_string') self.assertEqual(sys.stderr.getvalue(), 'WARNING: Option "option_string" is deprecated\n') @mock.patch.object(sys, 'stderr', six.StringIO()) @mock.patch.object(novaclient.shell.DeprecatedAction, '_get_action') def test_call_unemitted_withuse(self, mock_get_action): obj = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', use='use this instead') obj('parser', 'namespace', 'values', 'option_string') self.assertEqual(obj.emitted, set(['option_string'])) mock_get_action.assert_called_once_with('parser') mock_get_action.return_value.assert_called_once_with( 'parser', 'namespace', 'values', 'option_string') self.assertEqual(sys.stderr.getvalue(), 'WARNING: Option "option_string" is deprecated; ' 'use this instead\n') @mock.patch.object(sys, 'stderr', six.StringIO()) @mock.patch.object(novaclient.shell.DeprecatedAction, '_get_action') def test_call_emitted_nouse(self, mock_get_action): obj = novaclient.shell.DeprecatedAction( 'option_strings', 'dest') obj.emitted.add('option_string') obj('parser', 'namespace', 'values', 'option_string') self.assertEqual(obj.emitted, set(['option_string'])) mock_get_action.assert_called_once_with('parser') mock_get_action.return_value.assert_called_once_with( 'parser', 'namespace', 'values', 'option_string') self.assertEqual(sys.stderr.getvalue(), '') @mock.patch.object(sys, 'stderr', six.StringIO()) @mock.patch.object(novaclient.shell.DeprecatedAction, '_get_action') def test_call_emitted_withuse(self, mock_get_action): obj = novaclient.shell.DeprecatedAction( 'option_strings', 'dest', use='use this instead') obj.emitted.add('option_string') obj('parser', 'namespace', 'values', 'option_string') self.assertEqual(obj.emitted, set(['option_string'])) mock_get_action.assert_called_once_with('parser') mock_get_action.return_value.assert_called_once_with( 'parser', 'namespace', 'values', 'option_string') self.assertEqual(sys.stderr.getvalue(), '') class ParserTest(utils.TestCase): def setUp(self): super(ParserTest, self).setUp() self.parser = novaclient.shell.NovaClientArgumentParser() 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') def test_not_really_ambiguous_option(self): # current/deprecated forms of the same option self.parser.add_argument('--tic-tac', action="store_true") self.parser.add_argument('--tic_tac', action="store_true") args = self.parser.parse_args(['--tic']) self.assertTrue(args.tic_tac) class ShellTest(utils.TestCase): _msg_no_tenant_project = ("You must provide a project name or project" " ID via --os-project-name, --os-project-id," " env[OS_PROJECT_ID] or env[OS_PROJECT_NAME]." " You may use os-project and os-tenant" " interchangeably.") 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 setUp(self): super(ShellTest, self).setUp() self.mock_client = mock.MagicMock() self.mock_client.return_value.api_version = novaclient.API_MIN_VERSION self.useFixture(fixtures.MonkeyPatch('novaclient.client.Client', self.mock_client)) self.nc_util = mock.patch('novaclient.utils.isunauthenticated').start() self.nc_util.return_value = False self.mock_server_version_range = mock.patch( 'novaclient.api_versions._get_server_version_range').start() self.mock_server_version_range.return_value = ( novaclient.API_MIN_VERSION, novaclient.API_MIN_VERSION) self.orig_max_ver = novaclient.API_MAX_VERSION self.orig_min_ver = novaclient.API_MIN_VERSION self.addCleanup(self._clear_fake_version) self.addCleanup(mock.patch.stopall) def _clear_fake_version(self): novaclient.API_MAX_VERSION = self.orig_max_ver novaclient.API_MIN_VERSION = self.orig_min_ver def shell(self, argstr, exitcodes=(0,)): orig = sys.stdout orig_stderr = sys.stderr try: sys.stdout = six.StringIO() sys.stderr = six.StringIO() _shell = novaclient.shell.OpenStackComputeShell() _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) def register_keystone_discovery_fixture(self, mreq): v2_url = "http://no.where/v2.0" v2_version = fixture.V2Discovery(v2_url) mreq.register_uri( 'GET', v2_url, json=_create_ver_list([v2_version]), status_code=200) def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') def test_invalid_timeout(self): for f in [0, -1, -10]: cmd_text = '--timeout %s' % (f) stdout, stderr = self.shell(cmd_text, exitcodes=[0, 2]) required = [ 'argument --timeout: %s must be greater than 0' % (f), ] for r in required: self.assertIn(r, stderr) def _test_help(self, command, required=None): if required is None: required = [ '.*?^usage: ', '.*?^\s+set-password\s+Change the admin password', '.*?^See "nova help COMMAND" for help on a specific command', ] stdout, stderr = self.shell(command) for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_help(self): self._test_help('help') def test_help_option(self): self._test_help('--help') self._test_help('-h') def test_help_no_options(self): self._test_help('') def test_help_on_subcommand(self): required = [ '.*?^usage: nova set-password', '.*?^Change the admin password', '.*?^Positional arguments:', ] self._test_help('help set-password', required=required) def test_bash_completion(self): stdout, stderr = self.shell('bash-completion') # just check we have some output required = [ '.*--matching', '.*--wrap', '.*help', '.*server-group-delete', '.*--image-with'] 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 or user ID' ' via --os-username, --os-user-id,' ' env[OS_USERNAME] or env[OS_USER_ID]') self.make_env(exclude='OS_USERNAME') try: self.shell('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 or user ID' ' via --os-username, --os-user-id,' ' env[OS_USERNAME] or env[OS_USER_ID]') self.make_env(exclude='OS_USER_ID', fake_env=FAKE_ENV2) try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') def test_no_tenant_name(self): required = self._msg_no_tenant_project self.make_env(exclude='OS_TENANT_NAME') try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') def test_no_tenant_id(self): required = self._msg_no_tenant_project self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2) try: self.shell('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 env[OS_AUTH_URL].',) self.make_env(exclude='OS_AUTH_URL') try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args) else: self.fail('CommandError not raised') @requests_mock.Mocker() def test_nova_endpoint_type(self, m_requests): self.make_env(fake_env=FAKE_ENV3) self.register_keystone_discovery_fixture(m_requests) self.shell('list') client_kwargs = self.mock_client.call_args_list[0][1] self.assertEqual(client_kwargs['endpoint_type'], 'novaURL') @requests_mock.Mocker() def test_endpoint_type_like_other_clients(self, m_requests): self.make_env(fake_env=FAKE_ENV4) self.register_keystone_discovery_fixture(m_requests) self.shell('list') client_kwargs = self.mock_client.call_args_list[0][1] self.assertEqual(client_kwargs['endpoint_type'], 'internalURL') @requests_mock.Mocker() def test_os_endpoint_type(self, m_requests): self.make_env(exclude='NOVA_ENDPOINT_TYPE', fake_env=FAKE_ENV3) self.register_keystone_discovery_fixture(m_requests) self.shell('list') client_kwargs = self.mock_client.call_args_list[0][1] self.assertEqual(client_kwargs['endpoint_type'], 'osURL') def test_default_endpoint_type(self): self.make_env() self.shell('list') client_kwargs = self.mock_client.call_args_list[0][1] self.assertEqual(client_kwargs['endpoint_type'], 'publicURL') @mock.patch('sys.stdin', side_effect=mock.MagicMock) @mock.patch('getpass.getpass', return_value='password') @requests_mock.Mocker() def test_password(self, mock_getpass, mock_stdin, m_requests): mock_stdin.encoding = "utf-8" # default output of empty tables differs depending between prettytable # versions if (hasattr(prettytable, '__version__') and dist_version.StrictVersion(prettytable.__version__) < dist_version.StrictVersion('0.7.2')): ex = '\n' else: ex = '\n'.join([ '+----+------+--------+------------+-------------+----------+', '| ID | Name | Status | Task State | Power State | Networks |', '+----+------+--------+------------+-------------+----------+', '+----+------+--------+------------+-------------+----------+', '' ]) self.make_env(exclude='OS_PASSWORD') self.register_keystone_discovery_fixture(m_requests) stdout, stderr = self.shell('list') self.assertEqual((stdout + stderr), ex) @mock.patch('sys.stdin', side_effect=mock.MagicMock) @mock.patch('getpass.getpass', side_effect=EOFError) def test_no_password(self, mock_getpass, mock_stdin): required = ('Expecting a password provided' ' via either --os-password, env[OS_PASSWORD],' ' or prompted response',) self.make_env(exclude='OS_PASSWORD') try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args) else: self.fail('CommandError not raised') def _test_service_type(self, version, service_type, mock_client): if version is None: cmd = 'list' else: cmd = ('--service-type %s --os-compute-api-version %s list' % (service_type, version)) self.make_env() self.shell(cmd) _client_args, client_kwargs = mock_client.call_args_list[0] self.assertEqual(service_type, client_kwargs['service_type']) def test_default_service_type(self): self._test_service_type(None, 'compute', self.mock_client) def test_v2_service_type(self): self._test_service_type('2', 'compute', self.mock_client) def test_v_unknown_service_type(self): self.assertRaises(exceptions.UnsupportedVersion, self._test_service_type, 'unknown', 'compute', self.mock_client) @mock.patch('sys.argv', ['nova']) @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: novaclient.shell.main() except SystemExit: self.fail('Unexpected SystemExit') # We expect the normal usage as a result self.assertIn('Command-line interface to the OpenStack Nova API', sys.stdout.getvalue()) @mock.patch.object(novaclient.shell.OpenStackComputeShell, 'main') def test_main_keyboard_interrupt(self, mock_compute_shell): # Ensure that exit code is 130 for KeyboardInterrupt mock_compute_shell.side_effect = KeyboardInterrupt() try: novaclient.shell.main() except SystemExit as ex: self.assertEqual(ex.code, 130) @mock.patch.object(novaclient.shell.OpenStackComputeShell, 'times') @requests_mock.Mocker() def test_timing(self, m_times, m_requests): m_times.append.side_effect = RuntimeError('Boom!') self.make_env() self.register_keystone_discovery_fixture(m_requests) self.shell('list') exc = self.assertRaises(RuntimeError, self.shell, '--timings list') self.assertEqual('Boom!', str(exc)) @requests_mock.Mocker() def test_osprofiler(self, m_requests): self.make_env() def client(*args, **kwargs): self.assertEqual('swordfish', kwargs['profile']) with mock.patch('novaclient.client.Client', client): # we are only interested in the fact Client is initialized properly self.shell('list --profile swordfish', (0, 2)) @requests_mock.Mocker() def test_osprofiler_not_installed(self, m_requests): self.make_env() # NOTE(rpodolyaka): osprofiler is in test-requirements, so we have to # simulate its absence here with mock.patch('novaclient.shell.osprofiler_profiler', None): _, stderr = self.shell('list --profile swordfish', (0, 2)) self.assertIn('unrecognized arguments: --profile swordfish', stderr) @mock.patch('novaclient.shell.SecretsHelper.tenant_id', return_value=True) @mock.patch('novaclient.shell.SecretsHelper.auth_token', return_value=True) @mock.patch('novaclient.shell.SecretsHelper.management_url', return_value=True) @requests_mock.Mocker() def test_keyring_saver_helper(self, sh_management_url_function, sh_auth_token_function, sh_tenant_id_function, m_requests): self.make_env(fake_env=FAKE_ENV) self.register_keystone_discovery_fixture(m_requests) self.shell('list') mock_client_instance = self.mock_client.return_value keyring_saver = mock_client_instance.client.keyring_saver self.assertIsInstance(keyring_saver, novaclient.shell.SecretsHelper) def test_microversion_with_default_behaviour(self): self.make_env(fake_env=FAKE_ENV5) self.mock_server_version_range.return_value = ( api_versions.APIVersion("2.1"), api_versions.APIVersion("2.3")) self.shell('list') client_args = self.mock_client.call_args_list[1][0] self.assertEqual(api_versions.APIVersion("2.3"), client_args[0]) def test_microversion_with_default_behaviour_with_legacy_server(self): self.make_env(fake_env=FAKE_ENV5) self.mock_server_version_range.return_value = ( api_versions.APIVersion(), api_versions.APIVersion()) self.shell('list') client_args = self.mock_client.call_args_list[1][0] self.assertEqual(api_versions.APIVersion("2.0"), client_args[0]) def test_microversion_with_latest(self): self.make_env() novaclient.API_MAX_VERSION = api_versions.APIVersion('2.3') self.mock_server_version_range.return_value = ( api_versions.APIVersion("2.1"), api_versions.APIVersion("2.3")) self.shell('--os-compute-api-version 2.latest list') client_args = self.mock_client.call_args_list[1][0] self.assertEqual(api_versions.APIVersion("2.3"), client_args[0]) def test_microversion_with_specified_version(self): self.make_env() self.mock_server_version_range.return_value = ( api_versions.APIVersion("2.10"), api_versions.APIVersion("2.100")) novaclient.API_MAX_VERSION = api_versions.APIVersion("2.100") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.90") self.shell('--os-compute-api-version 2.99 list') client_args = self.mock_client.call_args_list[1][0] self.assertEqual(api_versions.APIVersion("2.99"), client_args[0]) def test_microversion_with_specified_version_out_of_range(self): novaclient.API_MAX_VERSION = api_versions.APIVersion("2.100") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.90") self.assertRaises(exceptions.CommandError, self.shell, '--os-compute-api-version 2.199 list') def test_microversion_with_v2_and_v2_1_server(self): self.make_env() self.mock_server_version_range.return_value = ( api_versions.APIVersion('2.1'), api_versions.APIVersion('2.3')) novaclient.API_MAX_VERSION = api_versions.APIVersion("2.100") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.shell('--os-compute-api-version 2 list') client_args = self.mock_client.call_args_list[1][0] self.assertEqual(api_versions.APIVersion("2.0"), client_args[0]) def test_microversion_with_v2_and_v2_server(self): self.make_env() self.mock_server_version_range.return_value = ( api_versions.APIVersion(), api_versions.APIVersion()) novaclient.API_MAX_VERSION = api_versions.APIVersion("2.100") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.shell('--os-compute-api-version 2 list') client_args = self.mock_client.call_args_list[1][0] self.assertEqual(api_versions.APIVersion("2.0"), client_args[0]) def test_microversion_with_v2_without_server_compatible(self): self.make_env() self.mock_server_version_range.return_value = ( api_versions.APIVersion('2.2'), api_versions.APIVersion('2.3')) novaclient.API_MAX_VERSION = api_versions.APIVersion("2.100") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertRaises( exceptions.UnsupportedVersion, self.shell, '--os-compute-api-version 2 list') def test_microversion_with_specific_version_without_microversions(self): self.make_env() self.mock_server_version_range.return_value = ( api_versions.APIVersion(), api_versions.APIVersion()) novaclient.API_MAX_VERSION = api_versions.APIVersion("2.100") novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1") self.assertRaises( exceptions.UnsupportedVersion, self.shell, '--os-compute-api-version 2.3 list') @mock.patch.object(novaclient.shell.OpenStackComputeShell, 'main') def test_main_error_handling(self, mock_compute_shell): class MyException(Exception): pass with mock.patch('sys.stderr', six.StringIO()): mock_compute_shell.side_effect = MyException('message') self.assertRaises(SystemExit, novaclient.shell.main) err = sys.stderr.getvalue() self.assertEqual(err, 'ERROR (MyException): message\n') class TestLoadVersionedActions(utils.TestCase): def test_load_versioned_actions(self): parser = novaclient.shell.NovaClientArgumentParser() subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.15"), False) self.assertIn('fake-action', shell.subcommands.keys()) self.assertEqual( 1, shell.subcommands['fake-action'].get_default('func')()) shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.25"), False) self.assertIn('fake-action', shell.subcommands.keys()) self.assertEqual( 2, shell.subcommands['fake-action'].get_default('func')()) self.assertIn('fake-action2', shell.subcommands.keys()) self.assertEqual( 3, shell.subcommands['fake-action2'].get_default('func')()) def test_load_versioned_actions_not_in_version_range(self): parser = novaclient.shell.NovaClientArgumentParser() subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.10000"), False) self.assertNotIn('fake-action', shell.subcommands.keys()) self.assertIn('fake-action2', shell.subcommands.keys()) def test_load_versioned_actions_with_help(self): parser = novaclient.shell.NovaClientArgumentParser() subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.15"), True) self.assertIn('fake-action', shell.subcommands.keys()) expected_desc = (" (Supported by API versions '%(start)s' - " "'%(end)s')") % {'start': '2.10', 'end': '2.30'} self.assertEqual(expected_desc, shell.subcommands['fake-action'].description) def test_load_versioned_actions_with_help_on_latest(self): parser = novaclient.shell.NovaClientArgumentParser() subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.latest"), True) self.assertIn('another-fake-action', shell.subcommands.keys()) expected_desc = (" (Supported by API versions '%(start)s' - " "'%(end)s')%(hint)s") % { 'start': '2.0', 'end': '2.latest', 'hint': novaclient.shell.HINT_HELP_MSG} self.assertEqual(expected_desc, shell.subcommands['another-fake-action'].description) @mock.patch.object(novaclient.shell.NovaClientArgumentParser, 'add_argument') def test_load_versioned_actions_with_args(self, mock_add_arg): parser = novaclient.shell.NovaClientArgumentParser(add_help=False) subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.1"), False) self.assertIn('fake-action2', shell.subcommands.keys()) mock_add_arg.assert_has_calls([ mock.call('-h', '--help', action='help', help='==SUPPRESS=='), mock.call('--foo')]) @mock.patch.object(novaclient.shell.NovaClientArgumentParser, 'add_argument') def test_load_versioned_actions_with_args2(self, mock_add_arg): parser = novaclient.shell.NovaClientArgumentParser(add_help=False) subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.4"), False) self.assertIn('fake-action2', shell.subcommands.keys()) mock_add_arg.assert_has_calls([ mock.call('-h', '--help', action='help', help='==SUPPRESS=='), mock.call('--bar')]) @mock.patch.object(novaclient.shell.NovaClientArgumentParser, 'add_argument') def test_load_versioned_actions_with_args_not_in_version_range( self, mock_add_arg): parser = novaclient.shell.NovaClientArgumentParser(add_help=False) subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.10000"), False) self.assertIn('fake-action2', shell.subcommands.keys()) mock_add_arg.assert_has_calls([ mock.call('-h', '--help', action='help', help='==SUPPRESS==')]) @mock.patch.object(novaclient.shell.NovaClientArgumentParser, 'add_argument') def test_load_versioned_actions_with_args_and_help(self, mock_add_arg): parser = novaclient.shell.NovaClientArgumentParser(add_help=False) subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.4"), True) mock_add_arg.assert_has_calls([ mock.call('-h', '--help', action='help', help='==SUPPRESS=='), mock.call('--bar', help=" (Supported by API versions '2.3' - '2.4')")]) @mock.patch.object(novaclient.shell.NovaClientArgumentParser, 'add_argument') def test_load_actions_with_versioned_args(self, mock_add_arg): parser = novaclient.shell.NovaClientArgumentParser(add_help=False) subparsers = parser.add_subparsers(metavar='') shell = novaclient.shell.OpenStackComputeShell() shell.subcommands = {} shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.20"), False) self.assertIn(mock.call('--foo', help="first foo"), mock_add_arg.call_args_list) self.assertNotIn(mock.call('--foo', help="second foo"), mock_add_arg.call_args_list) mock_add_arg.reset_mock() shell._find_actions(subparsers, fake_actions_module, api_versions.APIVersion("2.21"), False) self.assertNotIn(mock.call('--foo', help="first foo"), mock_add_arg.call_args_list) self.assertIn(mock.call('--foo', help="second foo"), mock_add_arg.call_args_list) class ShellTestKeystoneV3(ShellTest): def make_env(self, exclude=None, fake_env=FAKE_ENV): if 'OS_AUTH_URL' in fake_env: fake_env.update({'OS_AUTH_URL': 'http://no.where/v3'}) 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) python-novaclient-9.1.1/novaclient/tests/unit/test_client.py0000664000175000017500000001523013165151077025512 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import uuid from keystoneauth1 import session import mock import novaclient.api_versions import novaclient.client import novaclient.extension from novaclient.tests.unit import utils import novaclient.v2.client class ClientTest(utils.TestCase): def test_get_client_class_v2(self): output = novaclient.client.get_client_class('2') self.assertEqual(output, novaclient.v2.client.Client) def test_get_client_class_v2_int(self): output = novaclient.client.get_client_class(2) self.assertEqual(output, novaclient.v2.client.Client) def test_get_client_class_unknown(self): self.assertRaises(novaclient.exceptions.UnsupportedVersion, novaclient.client.get_client_class, '0') def test_get_client_class_latest(self): self.assertRaises(novaclient.exceptions.UnsupportedVersion, novaclient.client.get_client_class, 'latest') self.assertRaises(novaclient.exceptions.UnsupportedVersion, novaclient.client.get_client_class, '2.latest') class SessionClientTest(utils.TestCase): def test_timings(self): self.requests_mock.get('http://no.where') client = novaclient.client.SessionClient(session=session.Session()) client.request("http://no.where", 'GET') self.assertEqual(0, len(client.times)) client = novaclient.client.SessionClient(session=session.Session(), timings=True) client.request("http://no.where", 'GET') self.assertEqual(1, len(client.times)) self.assertEqual('GET http://no.where', client.times[0][0]) def test_client_get_reset_timings_v2(self): cs = novaclient.client.SessionClient(session=session.Session()) self.assertEqual(0, len(cs.get_timings())) cs.times.append("somevalue") self.assertEqual(1, len(cs.get_timings())) self.assertEqual("somevalue", cs.get_timings()[0]) cs.reset_timings() self.assertEqual(0, len(cs.get_timings())) def test_global_id(self): global_id = "req-%s" % uuid.uuid4() self.requests_mock.get('http://no.where') client = novaclient.client.SessionClient(session=session.Session(), global_request_id=global_id) client.request("http://no.where", 'GET') headers = self.requests_mock.last_request.headers self.assertEqual(headers['X-OpenStack-Request-ID'], global_id) class ClientsUtilsTest(utils.TestCase): @mock.patch("novaclient.client._discover_via_entry_points") @mock.patch("novaclient.client._discover_via_python_path") @mock.patch("novaclient.extension.Extension") def test_discover_extensions_all(self, mock_extension, mock_discover_via_python_path, mock_discover_via_entry_points): def make_gen(start, end): def f(*args, **kwargs): for i in range(start, end): yield "name-%s" % i, i return f mock_discover_via_python_path.side_effect = make_gen(0, 3) mock_discover_via_entry_points.side_effect = make_gen(3, 4) version = novaclient.api_versions.APIVersion("2.0") result = novaclient.client.discover_extensions(version) self.assertEqual([mock.call("name-%s" % i, i) for i in range(0, 4)], mock_extension.call_args_list) mock_discover_via_python_path.assert_called_once_with() mock_discover_via_entry_points.assert_called_once_with() self.assertEqual([mock_extension()] * 4, result) @mock.patch('novaclient.client.warnings') @mock.patch("novaclient.client._discover_via_entry_points") @mock.patch("novaclient.client._discover_via_python_path") @mock.patch("novaclient.extension.Extension") def test_discover_extensions_only_contrib( self, mock_extension, mock_discover_via_python_path, mock_discover_via_entry_points, mock_warnings): version = novaclient.api_versions.APIVersion("2.0") self.assertEqual([], novaclient.client.discover_extensions( version, only_contrib=True)) self.assertFalse(mock_discover_via_python_path.called) self.assertFalse(mock_discover_via_entry_points.called) self.assertFalse(mock_extension.called) self.assertTrue(mock_warnings.warn.called) @mock.patch("novaclient.client.warnings") def test__check_arguments(self, mock_warnings): release = "Coolest" # no reference novaclient.client._check_arguments({}, release=release, deprecated_name="foo") self.assertFalse(mock_warnings.warn.called) novaclient.client._check_arguments({}, release=release, deprecated_name="foo", right_name="bar") self.assertFalse(mock_warnings.warn.called) # with alternative original_kwargs = {"foo": "text"} actual_kwargs = copy.copy(original_kwargs) self.assertEqual(original_kwargs, actual_kwargs) novaclient.client._check_arguments(actual_kwargs, release=release, deprecated_name="foo", right_name="bar") self.assertNotEqual(original_kwargs, actual_kwargs) self.assertEqual({"bar": original_kwargs["foo"]}, actual_kwargs) self.assertTrue(mock_warnings.warn.called) mock_warnings.warn.reset_mock() # without alternative original_kwargs = {"foo": "text"} actual_kwargs = copy.copy(original_kwargs) self.assertEqual(original_kwargs, actual_kwargs) novaclient.client._check_arguments(actual_kwargs, release=release, deprecated_name="foo") self.assertNotEqual(original_kwargs, actual_kwargs) self.assertEqual({}, actual_kwargs) self.assertTrue(mock_warnings.warn.called) python-novaclient-9.1.1/novaclient/tests/functional/0000775000175000017500000000000013165151230023774 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/test_auth.py0000664000175000017500000000672013165151077026364 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from six.moves.urllib import parse import tempest.lib.cli.base from novaclient import client from novaclient.tests.functional import base class TestAuthentication(base.ClientTestBase): def _get_url(self, identity_api_version): url = parse.urlparse(self.cli_clients.uri) return parse.urlunparse((url.scheme, url.netloc, '/identity/v%s' % identity_api_version, url.params, url.query, url.fragment)) def nova_auth_with_password(self, action, identity_api_version): flags = ('--os-username %s --os-tenant-name %s --os-password %s ' '--os-auth-url %s --os-endpoint-type publicURL' % ( self.cli_clients.username, self.cli_clients.tenant_name, self.cli_clients.password, self._get_url(identity_api_version))) if self.cli_clients.insecure: flags += ' --insecure ' return tempest.lib.cli.base.execute( "nova", action, flags, cli_dir=self.cli_clients.cli_dir) def nova_auth_with_token(self, identity_api_version): auth_ref = self.client.client.session.auth.get_access( self.client.client.session) token = auth_ref.auth_token auth_url = self._get_url(identity_api_version) kw = {} if identity_api_version == "3": kw["project_domain_id"] = self.project_domain_id nova = client.Client("2", auth_token=token, auth_url=auth_url, project_name=self.project_name, **kw) nova.servers.list() # NOTE(andreykurilin): token auth is completely broken in CLI # flags = ('--os-username %s --os-tenant-name %s --os-auth-token %s ' # '--os-auth-url %s --os-endpoint-type publicURL' % ( # self.cli_clients.username, # self.cli_clients.tenant_name, # token, auth_url)) # if self.cli_clients.insecure: # flags += ' --insecure ' # # return tempest.lib.cli.base.execute( # "nova", action, flags, cli_dir=self.cli_clients.cli_dir) def test_auth_via_keystone_v2(self): session = self.keystone.session version = (2, 0) if not base.is_keystone_version_available(session, version): self.skipTest("Identity API version 2.0 is not available.") self.nova_auth_with_password("list", identity_api_version="2.0") self.nova_auth_with_token(identity_api_version="2.0") def test_auth_via_keystone_v3(self): session = self.keystone.session version = (3, 0) if not base.is_keystone_version_available(session, version): self.skipTest("Identity API version 3.0 is not available.") self.nova_auth_with_password("list", identity_api_version="3") self.nova_auth_with_token(identity_api_version="3") python-novaclient-9.1.1/novaclient/tests/functional/__init__.py0000664000175000017500000000000013165151077026104 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/v2/0000775000175000017500000000000013165151230024323 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/v2/test_trigger_crash_dump.py0000664000175000017500000001357313165151077031626 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import time from tempest.lib import decorators from novaclient.tests.functional import base from novaclient.v2 import shell @decorators.skip_because(bug="1675526") class TestTriggerCrashDumpNovaClientV217(base.TenantTestBase): """Functional tests for trigger crash dump""" COMPUTE_API_VERSION = "2.17" # It's a resource-consuming task to implement full-flow (up to getting # and reading a dump file) functional test for trigger-crash-dump. # We need to upload Ubuntu image for booting an instance based on it, # and to install kdump with its further configuring on this instance. # Here, the "light" version of functional test is proposed. # It's based on knowledge that trigger-crash-dump uses a NMI injection, # and when the 'trigger-crash-dump' operation is executed, # instance's kernel receives the NMI signal, and an appropriate # message will appear in the instance's log. # The server status must be ACTIVE, PAUSED, RESCUED, RESIZED or ERROR. # If not, the conflictingRequest(409) code is returned def _assert_nmi(self, server_id, timeout=60, poll_interval=1): start_time = time.time() while time.time() - start_time < timeout: if 'trigger_crash_dump' in self.nova('instance-action-list %s ' % server_id): break time.sleep(poll_interval) else: self.fail("Trigger crash dump hasn't been executed for server %s" % server_id) def test_trigger_crash_dump_in_active_state(self): server = self._create_server() self.wait_for_server_os_boot(server.id) self.nova('trigger-crash-dump %s ' % server.id) self._assert_nmi(server.id) def test_trigger_crash_dump_in_error_state(self): server = self._create_server() self.wait_for_server_os_boot(server.id) self.nova('reset-state %s ' % server.id) shell._poll_for_status( self.client.servers.get, server.id, 'active', ['error']) self.nova('trigger-crash-dump %s ' % server.id) self._assert_nmi(server.id) def test_trigger_crash_dump_in_paused_state(self): server = self._create_server() self.wait_for_server_os_boot(server.id) self.nova('pause %s ' % server.id) shell._poll_for_status( self.client.servers.get, server.id, 'active', ['paused']) self.nova('trigger-crash-dump %s ' % server.id) self._assert_nmi(server.id) def test_trigger_crash_dump_in_rescued_state(self): server = self._create_server() self.wait_for_server_os_boot(server.id) self.nova('rescue %s ' % server.id) shell._poll_for_status( self.client.servers.get, server.id, 'active', ['rescue']) self.wait_for_server_os_boot(server.id) self.nova('trigger-crash-dump %s ' % server.id) self._assert_nmi(server.id) def test_trigger_crash_dump_in_resized_state(self): server = self._create_server() self.wait_for_server_os_boot(server.id) self.nova('resize %s %s' % (server.id, 'm1.small')) shell._poll_for_status( self.client.servers.get, server.id, 'active', ['verify_resize']) self.nova('trigger-crash-dump %s ' % server.id) self._assert_nmi(server.id) def test_trigger_crash_dump_in_shutoff_state(self): server = self._create_server() self.wait_for_server_os_boot(server.id) self.nova('stop %s ' % server.id) shell._poll_for_status( self.client.servers.get, server.id, 'active', ['shutoff']) output = self.nova('trigger-crash-dump %s ' % server.id, fail_ok=True, merge_stderr=True) self.assertIn("ERROR (Conflict): " "Cannot 'trigger_crash_dump' instance %s " "while it is in vm_state stopped (HTTP 409) " % server.id, output) # If the specified server is locked, the conflictingRequest(409) code # is returned to a user without administrator privileges. def test_trigger_crash_dump_in_locked_state_admin(self): server = self._create_server() self.wait_for_server_os_boot(server.id) self.nova('lock %s ' % server.id) self.nova('trigger-crash-dump %s ' % server.id) self._assert_nmi(server.id) def test_trigger_crash_dump_in_locked_state_nonadmin(self): name = self.name_generate(prefix='server') server = self.another_nova('boot --flavor %s --image %s --poll %s' % (self.flavor.name, self.image.name, name)) self.addCleanup(self.another_nova, 'delete', params=name) server_id = self._get_value_from_the_table( server, 'id') self.wait_for_server_os_boot(server_id) self.another_nova('lock %s ' % server_id) self.addCleanup(self.another_nova, 'unlock', params=name) output = self.another_nova('trigger-crash-dump %s ' % server_id, fail_ok=True, merge_stderr=True) # NOTE(mriedem): Depending on the version of the server you can get # different error messages back from this, so just assert that it's a # 409 either way. self.assertIn("ERROR (Conflict)", output) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_image_meta.py0000664000175000017500000000237113165151077030040 0ustar jenkinsjenkins00000000000000# Copyright 2016 Mirantis, Inc. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base class TestImageMetaV239(base.ClientTestBase): """Functional tests for image-meta proxy API.""" # 'image-metadata' proxy API was deprecated in 2.39 but the CLI should # fallback to 2.35 and emit a warning. COMPUTE_API_VERSION = "2.39" def test_limits(self): """Tests that 2.39 won't return 'maxImageMeta' resource limit and the CLI output won't show it. """ output = self.nova('limits') # assert that MaxImageMeta isn't in the table output self.assertRaises(ValueError, self._get_value_from_the_table, output, 'maxImageMeta') python-novaclient-9.1.1/novaclient/tests/functional/v2/test_instance_action.py0000664000175000017500000000463513165151077031116 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils import six from tempest.lib import exceptions from novaclient.tests.functional import base class TestInstanceActionCLI(base.ClientTestBase): COMPUTE_API_VERSION = "2.21" def _test_cmd_with_not_existing_instance(self, cmd, args): try: self.nova("%s %s" % (cmd, args)) except exceptions.CommandFailed as e: self.assertIn("ERROR (NotFound):", six.text_type(e)) else: self.fail("%s is not failed on non existing instance." % cmd) def test_show_action_with_not_existing_instance(self): name_or_uuid = uuidutils.generate_uuid() request_id = uuidutils.generate_uuid() self._test_cmd_with_not_existing_instance( "instance-action", "%s %s" % (name_or_uuid, request_id)) def test_list_actions_with_not_existing_instance(self): name_or_uuid = uuidutils.generate_uuid() self._test_cmd_with_not_existing_instance("instance-action-list", name_or_uuid) def test_show_and_list_actions_on_deleted_instance(self): server = self._create_server(add_cleanup=False) server.delete() self.wait_for_resource_delete(server, self.client.servers) output = self.nova("instance-action-list %s" % server.id) # NOTE(andreykurilin): output is not a single row table, so we can # obtain just "create" action. It should be enough for testing # "nova instance-action " command request_id = self._get_column_value_from_single_row_table( output, "Request_ID") output = self.nova("instance-action %s %s" % (server.id, request_id)) # ensure that obtained action is "create". self.assertEqual("create", self._get_value_from_the_table(output, "action")) python-novaclient-9.1.1/novaclient/tests/functional/v2/__init__.py0000664000175000017500000000000013165151077026433 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/v2/test_flavor_access.py0000664000175000017500000000274213165151077030564 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional.v2.legacy import test_flavor_access class TestFlvAccessNovaClientV27(test_flavor_access.TestFlvAccessNovaClient): """Check that an attempt to grant an access to a public flavor for the given tenant fails with Conflict error in accordance with 2.7 microversion REST API History """ COMPUTE_API_VERSION = "2.7" def test_add_access_public_flavor(self): flv_name = self.name_generate('v' + self.COMPUTE_API_VERSION) self.nova('flavor-create %s auto 512 1 1' % flv_name) self.addCleanup(self.nova, 'flavor-delete %s' % flv_name) output = self.nova('flavor-access-add %s %s' % (flv_name, self.project_id), fail_ok=True, merge_stderr=True) self.assertIn("ERROR (Conflict): " "Can not add access to a public flavor. (HTTP 409) ", output) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_usage.py0000664000175000017500000000265213165151077027056 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional.v2.legacy import test_usage class TestUsageCLI_V240(test_usage.TestUsageCLI): COMPUTE_API_VERSION = '2.40' class TestUsageClient_V240(test_usage.TestUsageClient): COMPUTE_API_VERSION = '2.40' def test_get(self): start, end = self._create_servers_in_time_window() tenant_id = self._get_project_id(self.cli_clients.tenant_name) usage = self.client.usage.get( tenant_id, start=start, end=end, limit=1) self.assertEqual(tenant_id, usage.tenant_id) self.assertEqual(1, len(usage.server_usages)) def test_list(self): start, end = self._create_servers_in_time_window() usages = self.client.usage.list( start=start, end=end, detailed=True, limit=1) self.assertEqual(1, len(usages)) self.assertEqual(1, len(usages[0].server_usages)) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_hypervisors.py0000664000175000017500000000170213165151077030342 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional.v2.legacy import test_hypervisors class TestHypervisorsV28(test_hypervisors.TestHypervisors): COMPUTE_API_VERSION = "2.28" def test_list(self): self._test_list(dict) class TestHypervisorsV2_53(TestHypervisorsV28): COMPUTE_API_VERSION = "2.53" def test_list(self): self._test_list(cpu_info_type=dict, uuid_as_id=True) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_resize.py0000664000175000017500000001567613165151077027265 0ustar jenkinsjenkins00000000000000# Copyright 2017 Huawei Technologies Co.,LTD. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 novaclient.tests.functional import base class TestServersResize(base.ClientTestBase): """Servers resize functional tests.""" COMPUTE_API_VERSION = '2.1' def _create_server(self, name, flavor): """Boots a server with the given name and flavor and waits for it to be ACTIVE. """ params = ( "%(name)s --flavor %(flavor)s --image %(image)s --poll " % { "name": self.name_generate(name), "flavor": flavor, "image": self.image.id}) # check to see if we have to pass in a network id if self.multiple_networks: params += ' --nic net-id=%s' % self.network.id server_info = self.nova("boot", params=params) server_id = self._get_value_from_the_table(server_info, "id") self.addCleanup(self._cleanup_server, server_id) return server_id def _pick_alternate_flavor(self): """Given the flavor picked in the base class setup, this finds the opposite flavor to use for a resize test. For example, if m1.nano is the flavor, then use m1.micro, but those are only available if Tempest is configured. If m1.tiny, then use m1.small. """ flavor_name = self.flavor.name if flavor_name == 'm1.nano': # This is an upsize test. return 'm1.micro' if flavor_name == 'm1.micro': # This is a downsize test. return 'm1.nano' if flavor_name == 'm1.tiny': # This is an upsize test. return 'm1.small' if flavor_name == 'm1.small': # This is a downsize test. return 'm1.tiny' self.fail('Unable to find alternate for flavor: %s' % flavor_name) def _compare_quota_usage(self, old_usage, new_usage, expect_diff=True): """Compares the quota usage in the provided AbsoluteLimits.""" # For a resize, instance usage shouldn't change. self.assertEqual(old_usage['totalInstancesUsed'], new_usage['totalInstancesUsed'], 'totalInstancesUsed does not match') # For the resize we're doing, those flavors have the same vcpus so we # don't expect any quota change. self.assertEqual(old_usage['totalCoresUsed'], new_usage['totalCoresUsed'], 'totalCoresUsed does not match') # RAM is the only thing that will change for these flavors in a resize. if expect_diff: self.assertNotEqual(old_usage['totalRAMUsed'], new_usage['totalRAMUsed'], 'totalRAMUsed should have changed') else: self.assertEqual(old_usage['totalRAMUsed'], new_usage['totalRAMUsed'], 'totalRAMUsed does not match') def test_resize_up_confirm(self): """Tests creating a server and resizes up and confirms the resize. Compares quota before, during and after the resize. """ server_id = self._create_server('resize-up-confirm', self.flavor.name) # get the starting quota now that we've created a server starting_usage = self._get_absolute_limits() # now resize up alternate_flavor = self._pick_alternate_flavor() self.nova('resize', params='%s %s --poll' % (server_id, alternate_flavor)) resize_usage = self._get_absolute_limits() # compare the starting usage against the resize usage self._compare_quota_usage(starting_usage, resize_usage) # now confirm the resize self.nova('resize-confirm', params='%s' % server_id) # we have to wait for the server to be ACTIVE before we can check quota self._wait_for_state_change(server_id, 'active') # get the final quota usage which should be the same as the resize # usage before confirm confirm_usage = self._get_absolute_limits() self._compare_quota_usage( resize_usage, confirm_usage, expect_diff=False) def _create_resize_down_flavors(self): """Creates two flavors with different size ram but same size vcpus and disk. :returns: tuple of (larger_flavor_name, smaller_flavor_name) """ self.nova('flavor-create', params='resize-larger-flavor auto 128 0 1') self.addCleanup( self.nova, 'flavor-delete', params='resize-larger-flavor') self.nova('flavor-create', params='resize-smaller-flavor auto 64 0 1') self.addCleanup( self.nova, 'flavor-delete', params='resize-smaller-flavor') return 'resize-larger-flavor', 'resize-smaller-flavor' def test_resize_down_revert(self): """Tests creating a server and resizes down and reverts the resize. Compares quota before, during and after the resize. """ # devstack's m1.tiny and m1.small have different size disks so we # can't use those as you can't resize down the disk. So we have to # create our own flavors. larger_flavor, smaller_flavor = self._create_resize_down_flavors() # Now create the server with the larger flavor. server_id = self._create_server('resize-down-revert', larger_flavor) # get the starting quota now that we've created a server starting_usage = self._get_absolute_limits() # now resize down self.nova('resize', params='%s %s --poll' % (server_id, smaller_flavor)) resize_usage = self._get_absolute_limits() # compare the starting usage against the resize usage; with counting # quotas in the server there are no reservations, so the # usage changes after the resize happens before it's confirmed. self._compare_quota_usage(starting_usage, resize_usage) # now revert the resize self.nova('resize-revert', params='%s' % server_id) # we have to wait for the server to be ACTIVE before we can check quota self._wait_for_state_change(server_id, 'active') # get the final quota usage which will be different from the resize # usage since we've reverted back *up* to the original flavor; the API # code checks quota again if we revert up in size revert_usage = self._get_absolute_limits() self._compare_quota_usage(resize_usage, revert_usage) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_keypairs.py0000664000175000017500000001207613165151077027602 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base from novaclient.tests.functional.v2 import fake_crypto from novaclient.tests.functional.v2.legacy import test_keypairs class TestKeypairsNovaClientV22(test_keypairs.TestKeypairsNovaClient): """Keypairs functional tests for v2.2 nova-api microversion.""" COMPUTE_API_VERSION = "2.2" def test_create_keypair(self): keypair = super(TestKeypairsNovaClientV22, self).test_create_keypair() self.assertIn('ssh', keypair) def test_create_keypair_x509(self): key_name = self._create_keypair(key_type='x509') keypair = self._show_keypair(key_name) self.assertIn(key_name, keypair) self.assertIn('x509', keypair) def test_import_keypair(self): pub_key, fingerprint = fake_crypto.get_ssh_pub_key_and_fingerprint() pub_key_file = self._create_public_key_file(pub_key) keypair = self._test_import_keypair(fingerprint, pub_key=pub_key_file) self.assertIn('ssh', keypair) def test_import_keypair_x509(self): certif, fingerprint = fake_crypto.get_x509_cert_and_fingerprint() pub_key_file = self._create_public_key_file(certif) keypair = self._test_import_keypair(fingerprint, key_type='x509', pub_key=pub_key_file) self.assertIn('x509', keypair) class TestKeypairsNovaClientV210(base.TenantTestBase): """Keypairs functional tests for v2.10 nova-api microversion.""" COMPUTE_API_VERSION = "2.10" def test_create_and_list_keypair(self): name = self.name_generate("v2_10") self.nova("keypair-add %s --user %s" % (name, self.user_id)) self.addCleanup(self.another_nova, "keypair-delete %s" % name) output = self.nova("keypair-list") self.assertRaises(ValueError, self._get_value_from_the_table, output, name) output_1 = self.another_nova("keypair-list") output_2 = self.nova("keypair-list --user %s" % self.user_id) self.assertEqual(output_1, output_2) # it should be table with one key-pair self.assertEqual(name, self._get_column_value_from_single_row_table( output_1, "Name")) output_1 = self.another_nova("keypair-show %s " % name) output_2 = self.nova("keypair-show --user %s %s" % (self.user_id, name)) self.assertEqual(output_1, output_2) self.assertEqual(self.user_id, self._get_value_from_the_table(output_1, "user_id")) def test_create_and_delete(self): name = self.name_generate("v2_10") def cleanup(): # We should check keypair existence and remove it from correct user # if keypair is presented o = self.another_nova("keypair-list") if name in o: self.another_nova("keypair-delete %s" % name) self.nova("keypair-add %s --user %s" % (name, self.user_id)) self.addCleanup(cleanup) output = self.another_nova("keypair-list") self.assertEqual(name, self._get_column_value_from_single_row_table( output, "Name")) self.nova("keypair-delete %s --user %s " % (name, self.user_id)) output = self.another_nova("keypair-list") self.assertRaises( ValueError, self._get_column_value_from_single_row_table, output, "Name") class TestKeypairsNovaClientV235(base.TenantTestBase): """Keypairs functional tests for v2.35 nova-api microversion.""" COMPUTE_API_VERSION = "2.35" def test_create_and_list_keypair_with_marker_and_limit(self): names = [] for i in range(3): names.append(self.name_generate("v2_35")) self.nova("keypair-add %s --user %s" % (names[i], self.user_id)) self.addCleanup(self.another_nova, "keypair-delete %s" % names[i]) # sort keypairs before pagination names = sorted(names) # list only one keypair after the first output_1 = self.another_nova("keypair-list --limit 1 --marker %s" % names[0]) output_2 = self.nova("keypair-list --limit 1 --marker %s --user %s" % (names[0], self.user_id)) self.assertEqual(output_1, output_2) # it should be table with only one second key-pair self.assertEqual( names[1], self._get_column_value_from_single_row_table(output_1, "Name")) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_servers.py0000664000175000017500000003310313165151077027436 0ustar jenkinsjenkins00000000000000# -*- 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 random import string from tempest.lib import decorators from novaclient.tests.functional import base from novaclient.tests.functional.v2.legacy import test_servers from novaclient.v2 import shell class TestServersBootNovaClient(test_servers.TestServersBootNovaClient): """Servers boot functional tests.""" COMPUTE_API_VERSION = "2.latest" class TestServersListNovaClient(test_servers.TestServersListNovaClient): """Servers list functional tests.""" COMPUTE_API_VERSION = "2.latest" class TestServerLockV29(base.ClientTestBase): COMPUTE_API_VERSION = "2.9" def _show_server_and_check_lock_attr(self, server, value): output = self.nova("show %s" % server.id) self.assertEqual(str(value), self._get_value_from_the_table(output, "locked")) def test_attribute_presented(self): # prepare server = self._create_server() # testing self._show_server_and_check_lock_attr(server, False) self.nova("lock %s" % server.id) self._show_server_and_check_lock_attr(server, True) self.nova("unlock %s" % server.id) self._show_server_and_check_lock_attr(server, False) class TestServersDescription(base.ClientTestBase): COMPUTE_API_VERSION = "2.19" def _boot_server_with_description(self): descr = "Some words about this test VM." server = self._create_server(description=descr) self.assertEqual(descr, server.description) return server, descr def test_create(self): # Add a description to the tests that create a server server, descr = self._boot_server_with_description() output = self.nova("show %s" % server.id) self.assertEqual(descr, self._get_value_from_the_table(output, "description")) def test_list_servers_with_description(self): # Check that the description is returned as part of server details # for a server list server, descr = self._boot_server_with_description() output = self.nova("list --fields description") self.assertEqual(server.id, self._get_column_value_from_single_row_table( output, "ID")) self.assertEqual(descr, self._get_column_value_from_single_row_table( output, "Description")) @decorators.skip_because(bug="1694371") def test_rebuild(self): # Add a description to the tests that rebuild a server server, descr = self._boot_server_with_description() descr = "New description for rebuilt VM." self.nova("rebuild --description '%s' %s %s" % (descr, server.id, self.image.name)) shell._poll_for_status( self.client.servers.get, server.id, 'rebuild', ['active']) output = self.nova("show %s" % server.id) self.assertEqual(descr, self._get_value_from_the_table(output, "description")) def test_remove_description(self): # Remove description from server booted with it server, descr = self._boot_server_with_description() self.nova("update %s --description ''" % server.id) output = self.nova("show %s" % server.id) self.assertEqual("-", self._get_value_from_the_table(output, "description")) def test_add_remove_description_on_existing_server(self): # Set and remove the description on an existing server server = self._create_server() descr = "Add a description for previously-booted VM." self.nova("update %s --description '%s'" % (server.id, descr)) output = self.nova("show %s" % server.id) self.assertEqual(descr, self._get_value_from_the_table(output, "description")) self.nova("update %s --description ''" % server.id) output = self.nova("show %s" % server.id) self.assertEqual("-", self._get_value_from_the_table(output, "description")) def test_update_with_description_longer_than_255_symbols(self): # Negative case for description longer than 255 characters server = self._create_server() descr = ''.join(random.choice(string.ascii_letters) for i in range(256)) output = self.nova("update %s --description '%s'" % (server.id, descr), fail_ok=True, merge_stderr=True) self.assertIn("ERROR (BadRequest): Invalid input for field/attribute" " description. Value: %s. u\'%s\' is too long (HTTP 400)" % (descr, descr), output) class TestServersTagsV226(base.ClientTestBase): COMPUTE_API_VERSION = "2.26" def _boot_server_with_tags(self, tags=["t1", "t2"]): uuid = self._create_server().id self.client.servers.set_tags(uuid, tags) return uuid def test_show(self): uuid = self._boot_server_with_tags() output = self.nova("show %s" % uuid) self.assertEqual('["t1", "t2"]', self._get_value_from_the_table( output, "tags")) def test_unicode_tag_correctly_displayed(self): """Regression test for bug #1669683. List and dict fields with unicode cannot be correctly displayed. Ensure that once we fix this it doesn't regress. """ # create an instance with chinese tag uuid = self._boot_server_with_tags(tags=["中文标签"]) output = self.nova("show %s" % uuid) self.assertEqual('["中文标签"]', self._get_value_from_the_table( output, "tags")) def test_list(self): uuid = self._boot_server_with_tags() output = self.nova("server-tag-list %s" % uuid) tags = self._get_list_of_values_from_single_column_table( output, "Tag") self.assertEqual(["t1", "t2"], tags) def test_add(self): uuid = self._boot_server_with_tags() self.nova("server-tag-add %s t3" % uuid) self.assertEqual(["t1", "t2", "t3"], self.client.servers.tag_list(uuid)) def test_add_many(self): uuid = self._boot_server_with_tags() self.nova("server-tag-add %s t3 t4" % uuid) self.assertEqual(["t1", "t2", "t3", "t4"], self.client.servers.tag_list(uuid)) def test_set(self): uuid = self._boot_server_with_tags() self.nova("server-tag-set %s t3 t4" % uuid) self.assertEqual(["t3", "t4"], self.client.servers.tag_list(uuid)) def test_delete(self): uuid = self._boot_server_with_tags() self.nova("server-tag-delete %s t2" % uuid) self.assertEqual(["t1"], self.client.servers.tag_list(uuid)) def test_delete_many(self): uuid = self._boot_server_with_tags() self.nova("server-tag-delete %s t1 t2" % uuid) self.assertEqual([], self.client.servers.tag_list(uuid)) def test_delete_all(self): uuid = self._boot_server_with_tags() self.nova("server-tag-delete-all %s" % uuid) self.assertEqual([], self.client.servers.tag_list(uuid)) class TestServersAutoAllocateNetworkCLI(base.ClientTestBase): COMPUTE_API_VERSION = '2.37' def _find_network_in_table(self, table): # Example: # +-----------------+-----------------------------------+ # | Property | Value | # +-----------------+-----------------------------------+ # | private network | 192.168.154.128 | # +-----------------+-----------------------------------+ for line in table.split('\n'): if '|' in line: l_property, l_value = line.split('|')[1:3] if ' network' in l_property.strip(): return ' '.join(l_property.strip().split()[:-1]) def test_boot_server_with_auto_network(self): """Tests that the CLI defaults to 'auto' when --nic isn't specified. """ # check to see if multiple networks are available because if so we # have to skip this test as auto will fail with a 409 conflict as it's # an ambiguous request and nova won't know which network to pick if self.multiple_networks: # we could potentially get around this by extending TenantTestBase self.skipTest('multiple networks available') server_info = self.nova('boot', params=( '%(name)s --flavor %(flavor)s --poll ' '--image %(image)s ' % {'name': self.name_generate('server'), 'flavor': self.flavor.id, 'image': self.image.id})) server_id = self._get_value_from_the_table(server_info, 'id') self.addCleanup(self.wait_for_resource_delete, server_id, self.client.servers) self.addCleanup(self.client.servers.delete, server_id) # get the server details to verify there is a network, we don't care # what the network name is, we just want to see an entry show up server_info = self.nova('show', params=server_id) network = self._find_network_in_table(server_info) self.assertIsNotNone( network, 'Auto-allocated network not found: %s' % server_info) def test_boot_server_with_no_network(self): """Tests that '--nic none' is honored. """ server_info = self.nova('boot', params=( '%(name)s --flavor %(flavor)s --poll ' '--image %(image)s --nic none' % {'name': self.name_generate('server'), 'flavor': self.flavor.id, 'image': self.image.id})) server_id = self._get_value_from_the_table(server_info, 'id') self.addCleanup(self.wait_for_resource_delete, server_id, self.client.servers) self.addCleanup(self.client.servers.delete, server_id) # get the server details to verify there is not a network server_info = self.nova('show', params=server_id) network = self._find_network_in_table(server_info) self.assertIsNone( network, 'Unexpected network allocation: %s' % server_info) class TestServersDetailsFlavorInfo(base.ClientTestBase): COMPUTE_API_VERSION = '2.47' def _validate_flavor_details(self, flavor_details, server_details): # This is a mapping between the keys used in the flavor GET response # and the keys used for the flavor information embedded in the server # details. flavor_key_mapping = { "OS-FLV-EXT-DATA:ephemeral": "flavor:ephemeral", "disk": "flavor:disk", "extra_specs": "flavor:extra_specs", "name": "flavor:original_name", "ram": "flavor:ram", "swap": "flavor:swap", "vcpus": "flavor:vcpus", } for key in flavor_key_mapping: flavor_val = self._get_value_from_the_table( flavor_details, key) server_flavor_val = self._get_value_from_the_table( server_details, flavor_key_mapping[key]) if key is "swap" and flavor_val is "": # "flavor-show" displays zero swap as empty string. flavor_val = '0' self.assertEqual(flavor_val, server_flavor_val) def _setup_extra_specs(self, flavor_id): extra_spec_key = "dummykey" self.nova('flavor-key', params=('%(flavor)s set %(key)s=dummyval' % {'flavor': flavor_id, 'key': extra_spec_key})) unset_params = ('%(flavor)s unset %(key)s' % {'flavor': flavor_id, 'key': extra_spec_key}) self.addCleanup(self.nova, 'flavor-key', params=unset_params) def test_show(self): self._setup_extra_specs(self.flavor.id) uuid = self._create_server().id server_output = self.nova("show %s" % uuid) flavor_output = self.nova("flavor-show %s" % self.flavor.id) self._validate_flavor_details(flavor_output, server_output) def test_show_minimal(self): uuid = self._create_server().id server_output = self.nova("show --minimal %s" % uuid) server_output_flavor = self._get_value_from_the_table( server_output, 'flavor') self.assertEqual(self.flavor.name, server_output_flavor) def test_list(self): self._setup_extra_specs(self.flavor.id) self._create_server() server_output = self.nova("list --fields flavor:disk") # namespaced fields get reformatted slightly as column names server_flavor_val = self._get_column_value_from_single_row_table( server_output, 'flavor: Disk') flavor_output = self.nova("flavor-show %s" % self.flavor.id) flavor_val = self._get_value_from_the_table(flavor_output, 'disk') self.assertEqual(flavor_val, server_flavor_val) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_networks.py0000664000175000017500000000317613165151077027630 0ustar jenkinsjenkins00000000000000# Copyright 2016 Red Hat, Inc. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base class TestNetworkCommandsV2_36(base.ClientTestBase): """Deprecated network command functional tests.""" # Proxy APIs were deprecated in 2.36 but the CLI should fallback to 2.35 # and emit a warning. COMPUTE_API_VERSION = "2.36" def test_limits(self): """Tests that 2.36 won't return network-related resource limits and the CLI output won't show them. """ output = self.nova('limits') # assert that SecurityGroups isn't in the table output self.assertRaises(ValueError, self._get_value_from_the_table, output, 'SecurityGroups') def test_quota_show(self): """Tests that 2.36 won't return network-related resource quotas and the CLI output won't show them. """ output = self.nova('quota-show') # assert that security_groups isn't in the table output self.assertRaises(ValueError, self._get_value_from_the_table, output, 'security_groups') python-novaclient-9.1.1/novaclient/tests/functional/v2/test_consoles.py0000664000175000017500000000261213165151077027573 0ustar jenkinsjenkins00000000000000# Copyright 2015 IBM Corp. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional.v2.legacy import test_consoles class TestConsolesNovaClientV26(test_consoles.TestConsolesNovaClient): """Consoles functional tests for >=v2.6 api microversions.""" COMPUTE_API_VERSION = "2.6" def test_vnc_console_get(self): self._test_vnc_console_get() def test_spice_console_get(self): self._test_spice_console_get() def test_rdp_console_get(self): self._test_rdp_console_get() def test_serial_console_get(self): self._test_serial_console_get() class TestConsolesNovaClientV28(test_consoles.TestConsolesNovaClient): """Consoles functional tests for >=v2.8 api microversions.""" COMPUTE_API_VERSION = "2.8" def test_webmks_console_get(self): self._test_console_get('get-mks-console %s ', 'webmks') python-novaclient-9.1.1/novaclient/tests/functional/v2/test_server_groups.py0000664000175000017500000000436213165151077030657 0ustar jenkinsjenkins00000000000000# Copyright 2015 Huawei Technology 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 novaclient.tests.functional.v2.legacy import test_server_groups class TestServerGroupClientV213(test_server_groups.TestServerGroupClient): """Server groups v2.13 functional tests.""" COMPUTE_API_VERSION = "2.latest" def test_create_server_group(self): sg_id = self._create_sg("affinity") self.addCleanup(self.nova, 'server-group-delete %s' % sg_id) sg = self.nova('server-group-get %s' % sg_id) result = self._get_column_value_from_single_row_table(sg, "Id") self._get_column_value_from_single_row_table( sg, "User Id") self._get_column_value_from_single_row_table( sg, "Project Id") self.assertEqual(sg_id, result) def test_list_server_groups(self): sg_id = self._create_sg("affinity") self.addCleanup(self.nova, 'server-group-delete %s' % sg_id) sg = self.nova("server-group-list") result = self._get_column_value_from_single_row_table(sg, "Id") self._get_column_value_from_single_row_table( sg, "User Id") self._get_column_value_from_single_row_table( sg, "Project Id") self.assertEqual(sg_id, result) def test_get_server_group(self): sg_id = self._create_sg("affinity") self.addCleanup(self.nova, 'server-group-delete %s' % sg_id) sg = self.nova('server-group-get %s' % sg_id) result = self._get_column_value_from_single_row_table(sg, "Id") self._get_column_value_from_single_row_table( sg, "User Id") self._get_column_value_from_single_row_table( sg, "Project Id") self.assertEqual(sg_id, result) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/0000775000175000017500000000000013165151230025567 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/__init__.py0000664000175000017500000000000013165151077027677 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_flavor_access.py0000664000175000017500000000607313165151077032031 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base class TestFlvAccessNovaClient(base.TenantTestBase): """Functional tests for flavors with public and non-public access""" COMPUTE_API_VERSION = "2.1" def test_public_flavor_list(self): # Check that flavors with public access are available for both admin # and non-admin tenants flavor_list1 = self.nova('flavor-list') flavor_list2 = self.another_nova('flavor-list') self.assertEqual(flavor_list1, flavor_list2) def test_non_public_flavor_list(self): # Check that non-public flavor appears in flavor list # only for admin tenant and only with --all attribute # and doesn't appear for non-admin tenant flv_name = self.name_generate(prefix='flv') self.nova('flavor-create --is-public false %s auto 512 1 1' % flv_name) self.addCleanup(self.nova, 'flavor-delete %s' % flv_name) flavor_list1 = self.nova('flavor-list') self.assertNotIn(flv_name, flavor_list1) flavor_list2 = self.nova('flavor-list --all') flavor_list3 = self.another_nova('flavor-list --all') self.assertIn(flv_name, flavor_list2) self.assertNotIn(flv_name, flavor_list3) def test_add_access_non_public_flavor(self): # Check that it's allowed to grant an access to non-public flavor for # the given tenant flv_name = self.name_generate(prefix='flv') self.nova('flavor-create --is-public false %s auto 512 1 1' % flv_name) self.addCleanup(self.nova, 'flavor-delete %s' % flv_name) self.nova('flavor-access-add', params="%s %s" % (flv_name, self.project_id)) self.assertIn(self.project_id, self.nova('flavor-access-list --flavor %s' % flv_name)) def test_add_access_public_flavor(self): # For microversion < 2.7 the 'flavor-access-add' operation is executed # successfully for public flavor, but the next operation, # 'flavor-access-list --flavor %(name_of_public_flavor)' returns # a CommandError flv_name = self.name_generate(prefix='flv') self.nova('flavor-create %s auto 512 1 1' % flv_name) self.addCleanup(self.nova, 'flavor-delete %s' % flv_name) self.nova('flavor-access-add %s %s' % (flv_name, self.project_id)) output = self.nova('flavor-access-list --flavor %s' % flv_name, fail_ok=True, merge_stderr=True) self.assertIn("CommandError", output) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_usage.py0000664000175000017500000000545413165151077030325 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from novaclient.tests.functional import base class TestUsageCLI(base.ClientTestBase): COMPUTE_API_VERSION = '2.1' def _get_num_servers_from_usage_output(self): output = self.nova('usage') servers = self._get_column_value_from_single_row_table( output, 'Servers') return int(servers) def _get_num_servers_by_tenant_from_usage_output(self): tenant_id = self._get_project_id(self.cli_clients.tenant_name) output = self.nova('usage --tenant=%s' % tenant_id) servers = self._get_column_value_from_single_row_table( output, 'Servers') return int(servers) def test_usage(self): before = self._get_num_servers_from_usage_output() self._create_server('some-server') after = self._get_num_servers_from_usage_output() self.assertGreater(after, before) def test_usage_tenant(self): before = self._get_num_servers_by_tenant_from_usage_output() self._create_server('some-server') after = self._get_num_servers_by_tenant_from_usage_output() self.assertGreater(after, before) class TestUsageClient(base.ClientTestBase): COMPUTE_API_VERSION = '2.1' def _create_servers_in_time_window(self): start = datetime.datetime.now() self._create_server('some-server') self._create_server('another-server') end = datetime.datetime.now() return start, end def test_get(self): start, end = self._create_servers_in_time_window() tenant_id = self._get_project_id(self.cli_clients.tenant_name) usage = self.client.usage.get(tenant_id, start=start, end=end) self.assertEqual(tenant_id, usage.tenant_id) self.assertGreaterEqual(len(usage.server_usages), 2) def test_list(self): start, end = self._create_servers_in_time_window() tenant_id = self._get_project_id(self.cli_clients.tenant_name) usages = self.client.usage.list(start=start, end=end, detailed=True) tenant_ids = [usage.tenant_id for usage in usages] self.assertIn(tenant_id, tenant_ids) for usage in usages: if usage.tenant_id == tenant_id: self.assertGreaterEqual(len(usage.server_usages), 2) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_readonly_nova.py0000664000175000017500000000776213165151077032065 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib import decorators from tempest.lib import exceptions from novaclient.tests.functional import base class SimpleReadOnlyNovaClientTest(base.ClientTestBase): """Read only functional python-novaclient tests. This only exercises client commands that are read only. """ COMPUTE_API_VERSION = "2.1" def test_admin_fake_action(self): self.assertRaises(exceptions.CommandFailed, self.nova, 'this-does-nova-exist') # NOTE(jogo): Commands in order listed in 'nova help' def test_admin_aggregate_list(self): self.nova('aggregate-list') def test_admin_availability_zone_list(self): self.assertIn("internal", self.nova('availability-zone-list')) def test_admin_flavor_acces_list(self): self.assertRaises(exceptions.CommandFailed, self.nova, 'flavor-access-list') # Failed to get access list for public flavor type self.assertRaises(exceptions.CommandFailed, self.nova, 'flavor-access-list', params='--flavor m1.tiny') def test_admin_flavor_list(self): self.assertIn("Memory_MB", self.nova('flavor-list')) def test_admin_host_list(self): self.nova('host-list') def test_admin_hypervisor_list(self): self.nova('hypervisor-list') @decorators.skip_because(bug="1157349") def test_admin_interface_list(self): self.nova('interface-list') def test_admin_keypair_list(self): self.nova('keypair-list') def test_admin_list(self): self.nova('list') self.nova('list', params='--all-tenants 1') self.nova('list', params='--all-tenants 0') self.assertRaises(exceptions.CommandFailed, self.nova, 'list', params='--all-tenants bad') def test_admin_server_group_list(self): self.nova('server-group-list') def test_admin_servce_list(self): self.nova('service-list') def test_admin_usage(self): self.nova('usage') def test_admin_usage_list(self): self.nova('usage-list') def test_admin_help(self): self.nova('help') def test_admin_list_extensions(self): self.nova('list-extensions') def test_agent_list(self): self.nova('agent-list') self.nova('agent-list', flags='--debug') def test_migration_list(self): self.nova('migration-list') self.nova('migration-list', flags='--debug') def test_version_list(self): self.nova('version-list', flags='--debug') def test_quota_defaults(self): self.nova('quota-defaults') self.nova('quota-defaults', flags='--debug') def test_bash_completion(self): self.nova('bash-completion') # Optional arguments: def test_admin_version(self): self.nova('', flags='--version') def test_admin_debug_list(self): self.nova('list', flags='--debug') def test_admin_timeout(self): self.nova('list', flags='--timeout %d' % 60) def test_admin_timing(self): self.nova('list', flags='--timing') def test_admin_invalid_bypass_url(self): self.assertRaises(exceptions.CommandFailed, self.nova, 'list', flags='--endpoint-override badurl') python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_hypervisors.py0000664000175000017500000000347513165151077031617 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six from novaclient.tests.functional import base from novaclient import utils class TestHypervisors(base.ClientTestBase): COMPUTE_API_VERSION = "2.1" def _test_list(self, cpu_info_type, uuid_as_id=False): hypervisors = self.client.hypervisors.list() if not len(hypervisors): self.fail("No hypervisors detected.") for hypervisor in hypervisors: self.assertIsInstance(hypervisor.cpu_info, cpu_info_type) if uuid_as_id: # microversion >= 2.53 returns a uuid for the id self.assertFalse(utils.is_integer_like(hypervisor.id), 'Expected hypervisor.id to be a UUID.') self.assertFalse( utils.is_integer_like(hypervisor.service['id']), 'Expected hypervisor.service.id to be a UUID.') else: self.assertTrue(utils.is_integer_like(hypervisor.id), 'Expected hypervisor.id to be an integer.') self.assertTrue( utils.is_integer_like(hypervisor.service['id']), 'Expected hypervisor.service.id to be an integer.') def test_list(self): self._test_list(six.text_type) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_keypairs.py0000664000175000017500000000633413165151077031046 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import tempfile from oslo_utils import uuidutils from tempest.lib import exceptions from novaclient.tests.functional import base from novaclient.tests.functional.v2 import fake_crypto class TestKeypairsNovaClient(base.ClientTestBase): """Keypairs functional tests.""" COMPUTE_API_VERSION = "2.1" def _serialize_kwargs(self, kwargs): kwargs_pairs = ['--%(key)s %(val)s' % {'key': key.replace('_', '-'), 'val': val} for key, val in kwargs.items()] return " ".join(kwargs_pairs) def _create_keypair(self, **kwargs): key_name = self._raw_create_keypair(**kwargs) self.addCleanup(self.nova, 'keypair-delete %s' % key_name) return key_name def _raw_create_keypair(self, **kwargs): key_name = 'keypair-' + uuidutils.generate_uuid() kwargs_str = self._serialize_kwargs(kwargs) self.nova('keypair-add %s %s' % (kwargs_str, key_name)) return key_name def _show_keypair(self, key_name): return self.nova('keypair-show %s' % key_name) def _list_keypairs(self): return self.nova('keypair-list') def _delete_keypair(self, key_name): self.nova('keypair-delete %s' % key_name) def _create_public_key_file(self, public_key): pubfile = tempfile.mkstemp()[1] with open(pubfile, 'w') as f: f.write(public_key) return pubfile def test_create_keypair(self): key_name = self._create_keypair() keypair = self._show_keypair(key_name) self.assertIn(key_name, keypair) return keypair def _test_import_keypair(self, fingerprint, **create_kwargs): key_name = self._create_keypair(**create_kwargs) keypair = self._show_keypair(key_name) self.assertIn(key_name, keypair) self.assertIn(fingerprint, keypair) return keypair def test_import_keypair(self): pub_key, fingerprint = fake_crypto.get_ssh_pub_key_and_fingerprint() pub_key_file = self._create_public_key_file(pub_key) self._test_import_keypair(fingerprint, pub_key=pub_key_file) def test_list_keypair(self): key_name = self._create_keypair() keypairs = self._list_keypairs() self.assertIn(key_name, keypairs) def test_delete_keypair(self): key_name = self._raw_create_keypair() keypair = self._show_keypair(key_name) self.assertIsNotNone(keypair) self._delete_keypair(key_name) # keypair-show should fail if no keypair with given name is found. self.assertRaises(exceptions.CommandFailed, self._show_keypair, key_name) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_servers.py0000664000175000017500000001641713165151077030713 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from oslo_utils import timeutils from oslo_utils import uuidutils from novaclient.tests.functional import base class TestServersBootNovaClient(base.ClientTestBase): """Servers boot functional tests.""" COMPUTE_API_VERSION = "2.1" def _boot_server_with_legacy_bdm(self, bdm_params=()): volume_size = 1 volume_name = uuidutils.generate_uuid() volume = self.cinder.volumes.create(size=volume_size, name=volume_name, imageRef=self.image.id) self.wait_for_volume_status(volume, "available") if (len(bdm_params) >= 3 and bdm_params[2] == '1'): delete_volume = False else: delete_volume = True bdm_params = ':'.join(bdm_params) if bdm_params: bdm_params = ''.join((':', bdm_params)) params = ( "%(name)s --flavor %(flavor)s --poll " "--block-device-mapping vda=%(volume_id)s%(bdm_params)s" % { "name": uuidutils.generate_uuid(), "flavor": self.flavor.id, "volume_id": volume.id, "bdm_params": bdm_params}) # check to see if we have to pass in a network id if self.multiple_networks: params += ' --nic net-id=%s' % self.network.id server_info = self.nova("boot", params=params) server_id = self._get_value_from_the_table(server_info, "id") self.client.servers.delete(server_id) self.wait_for_resource_delete(server_id, self.client.servers) if delete_volume: self.cinder.volumes.delete(volume.id) self.wait_for_resource_delete(volume.id, self.cinder.volumes) def test_boot_server_with_legacy_bdm(self): # bdm v1 format # ::: # params = (type, size, delete-on-terminate) params = ('', '', '1') self._boot_server_with_legacy_bdm(bdm_params=params) def test_boot_server_with_legacy_bdm_volume_id_only(self): self._boot_server_with_legacy_bdm() def test_boot_server_with_net_name(self): server_info = self.nova("boot", params=( "%(name)s --flavor %(flavor)s --image %(image)s --poll " "--nic net-name=%(net-name)s" % {"name": uuidutils.generate_uuid(), "image": self.image.id, "flavor": self.flavor.id, "net-name": self.network.name})) server_id = self._get_value_from_the_table(server_info, "id") self.client.servers.delete(server_id) self.wait_for_resource_delete(server_id, self.client.servers) def test_boot_server_using_image_with(self): """Scenario test which does the following: 1. Create a server. 2. Create a snapshot image of the server with a special meta key. 3. Create a second server using the --image-with option using the meta key stored in the snapshot image created in step 2. """ # create the first server and wait for it to be active server_info = self.nova('boot', params=( '--flavor %(flavor)s --image %(image)s --poll ' 'image-with-server-1' % {'image': self.image.id, 'flavor': self.flavor.id})) server_id = self._get_value_from_the_table(server_info, 'id') self.addCleanup(self._cleanup_server, server_id) # create a snapshot of the server with an image metadata key snapshot_info = self.nova('image-create', params=( '--metadata image_with_meta=%(meta_value)s ' '--show --poll %(server_id)s image-with-snapshot' % { 'meta_value': server_id, 'server_id': server_id})) # get the snapshot image id out of the output table for the second # server create request snapshot_id = self._get_value_from_the_table(snapshot_info, 'id') self.addCleanup(self.glance.images.delete, snapshot_id) # verify the metadata was set on the snapshot image meta_value = self._get_value_from_the_table( snapshot_info, 'image_with_meta') self.assertEqual(server_id, meta_value) # create the second server using --image-with server_info = self.nova('boot', params=( '--flavor %(flavor)s --image-with image_with_meta=%(meta_value)s ' '--poll image-with-server-2' % {'meta_value': server_id, 'flavor': self.flavor.id})) server_id = self._get_value_from_the_table(server_info, 'id') self.addCleanup(self._cleanup_server, server_id) class TestServersListNovaClient(base.ClientTestBase): """Servers list functional tests.""" COMPUTE_API_VERSION = "2.1" def _create_servers(self, name, number): return [self._create_server(name) for i in range(number)] def test_list_with_limit(self): name = uuidutils.generate_uuid() self._create_servers(name, 2) output = self.nova("list", params="--limit 1 --name %s" % name) # Cut header and footer of the table servers = output.split("\n")[3:-2] self.assertEqual(1, len(servers), output) def test_list_with_changes_since(self): now = datetime.datetime.isoformat(timeutils.utcnow()) name = uuidutils.generate_uuid() self._create_servers(name, 1) output = self.nova("list", params="--changes-since %s" % now) self.assertIn(name, output, output) now = datetime.datetime.isoformat(timeutils.utcnow()) output = self.nova("list", params="--changes-since %s" % now) self.assertNotIn(name, output, output) def test_list_all_servers(self): name = uuidutils.generate_uuid() precreated_servers = self._create_servers(name, 3) # there are no possibility to exceed the limit on API side, so just # check that "-1" limit processes by novaclient side output = self.nova("list", params="--limit -1 --name %s" % name) # Cut header and footer of the table for server in precreated_servers: self.assertIn(server.id, output) def test_list_minimal(self): name = uuidutils.generate_uuid() uuid = self._create_server(name).id server_output = self.nova("list --minimal") # The only fields output are "ID" and "Name" output_uuid = self._get_column_value_from_single_row_table( server_output, 'ID') output_name = self._get_column_value_from_single_row_table( server_output, 'Name') self.assertEqual(output_uuid, uuid) self.assertEqual(output_name, name) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_consoles.py0000664000175000017500000000421713165151077031042 0ustar jenkinsjenkins00000000000000# Copyright 2015 IBM Corp. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib import exceptions from novaclient.tests.functional import base class TestConsolesNovaClient(base.ClientTestBase): """Consoles functional tests.""" COMPUTE_API_VERSION = "2.1" def _test_console_get(self, command, expected_response_type): server = self._create_server() completed_command = command % server.id try: output = self.nova(completed_command) # if we didn't fail, check that the expected response type is in # the output console_type = self._get_column_value_from_single_row_table( output, 'Type') self.assertEqual(expected_response_type, console_type, output) except exceptions.CommandFailed as cf: self.assertIn('HTTP 400', str(cf.stderr)) def _test_vnc_console_get(self): self._test_console_get('get-vnc-console %s novnc', 'novnc') def _test_spice_console_get(self): self._test_console_get('get-spice-console %s spice-html5', 'spice-html5') def _test_rdp_console_get(self): self._test_console_get('get-rdp-console %s rdp-html5', 'rdp-html5') def _test_serial_console_get(self): self._test_console_get('get-serial-console %s', 'serial') def test_vnc_console_get(self): self._test_vnc_console_get() def test_spice_console_get(self): self._test_spice_console_get() def test_rdp_console_get(self): self._test_rdp_console_get() def test_serial_console_get(self): self._test_serial_console_get() python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_server_groups.py0000664000175000017500000000375113165151077032124 0ustar jenkinsjenkins00000000000000# Copyright 2015 Huawei Technology corp. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from novaclient.tests.functional import base class TestServerGroupClient(base.ClientTestBase): """Server groups v2.1 functional tests.""" COMPUTE_API_VERSION = "2.1" def _create_sg(self, policy): sg_name = 'server_group-' + uuidutils.generate_uuid() output = self.nova('server-group-create %s %s' % (sg_name, policy)) sg_id = self._get_column_value_from_single_row_table(output, "Id") return sg_id def test_create_server_group(self): sg_id = self._create_sg("affinity") self.addCleanup(self.nova, 'server-group-delete %s' % sg_id) sg = self.nova('server-group-get %s' % sg_id) result = self._get_column_value_from_single_row_table(sg, "Id") self.assertEqual(sg_id, result) def test_list_server_group(self): sg_id = self._create_sg("affinity") self.addCleanup(self.nova, 'server-group-delete %s' % sg_id) sg = self.nova('server-group-list') result = self._get_column_value_from_single_row_table(sg, "Id") self.assertEqual(sg_id, result) def test_delete_server_group(self): sg_id = self._create_sg("affinity") sg = self.nova('server-group-get %s' % sg_id) result = self._get_column_value_from_single_row_table(sg, "Id") self.assertIsNotNone(result) self.nova('server-group-delete %s' % sg_id) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_quotas.py0000664000175000017500000000374413165151077030535 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base class TestQuotasNovaClient(base.ClientTestBase): """Nova quotas functional tests.""" COMPUTE_API_VERSION = "2.1" _quota_resources = ['instances', 'cores', 'ram', 'floating_ips', 'fixed_ips', 'metadata_items', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', 'key_pairs', 'security_groups', 'security_group_rules', 'server_groups', 'server_group_members'] def test_quotas_update(self): # `nova quota-update` requires tenant-id. tenant_id = self._get_project_id(self.cli_clients.tenant_name) self.addCleanup(self.client.quotas.delete, tenant_id) original_quotas = self.client.quotas.get(tenant_id) difference = 10 params = [tenant_id] for quota_name in self._quota_resources: params.append("--%(name)s %(value)s" % { "name": quota_name.replace("_", "-"), "value": getattr(original_quotas, quota_name) + difference}) self.nova("quota-update", params=" ".join(params)) updated_quotas = self.client.quotas.get(tenant_id) for quota_name in self._quota_resources: self.assertEqual(getattr(original_quotas, quota_name), getattr(updated_quotas, quota_name) - difference) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_instances.py0000664000175000017500000000540513165151077031204 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base class TestInstanceCLI(base.ClientTestBase): COMPUTE_API_VERSION = "2.1" def test_attach_volume(self): """Test we can attach a volume via the cli. This test was added after bug 1423695. That bug exposed inconsistencies in how to talk to API services from the CLI vs. API level. The volumes api calls that were designed to populate the completion cache were incorrectly routed to the Nova endpoint. Novaclient volumes support actually talks to Cinder endpoint directly. This would case volume-attach to return a bad error code, however it does this *after* the attach command is correctly dispatched. So the volume-attach still works, but the user is presented a 404 error. This test ensures we can do a through path test of: boot, create volume, attach volume, detach volume, delete volume, destroy. """ name = self.name_generate('Instance') # Boot via the cli, as we're primarily testing the cli in this test self.nova('boot', params="--flavor %s --image %s %s --nic net-id=%s --poll" % (self.flavor.name, self.image.name, name, self.network.id)) # Be nice about cleaning up, however, use the API for this to avoid # parsing text. servers = self.client.servers.list(search_opts={"name": name}) # the name is a random uuid, there better only be one self.assertEqual(1, len(servers), servers) server = servers[0] self.addCleanup(server.delete) # create a volume for attachment volume = self.cinder.volumes.create(1) self.addCleanup(volume.delete) # allow volume to become available self.wait_for_volume_status(volume, 'available') # attach the volume self.nova('volume-attach', params="%s %s" % (name, volume.id)) # volume needs to transition to 'in-use' to be attached self.wait_for_volume_status(volume, 'in-use') # clean up on success self.nova('volume-detach', params="%s %s" % (name, volume.id)) self.wait_for_volume_status(volume, 'available') python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_extended_attributes.py0000664000175000017500000000457613165151077033273 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json from novaclient.tests.functional import base class TestExtAttrNovaClient(base.ClientTestBase): """Functional tests for os-extended-server-attributes""" COMPUTE_API_VERSION = "2.1" def _create_server_and_attach_volume(self): server = self._create_server() volume = self.cinder.volumes.create(1) self.addCleanup(volume.delete) self.wait_for_volume_status(volume, 'available') self.nova('volume-attach', params="%s %s" % (server.name, volume.id)) self.addCleanup(self._release_volume, server, volume) self.wait_for_volume_status(volume, 'in-use') return server, volume def _release_volume(self, server, volume): self.nova('volume-detach', params="%s %s" % (server.id, volume.id)) self.wait_for_volume_status(volume, 'available') def test_extended_server_attributes(self): server, volume = self._create_server_and_attach_volume() table = self.nova('show %s' % server.id) # Check that attributes listed below exist in 'nova show' table and # they are exactly Property attributes (not an instance's name, e.g.) # The _get_value_from_the_table() will raise an exception # if attr is not a key (first column) of the table dict for attr in ['OS-EXT-SRV-ATTR:host', 'OS-EXT-SRV-ATTR:hypervisor_hostname', 'OS-EXT-SRV-ATTR:instance_name']: self._get_value_from_the_table(table, attr) # Check that attribute given below also exists in 'nova show' table # as a key (first column) of table dict volume_attr = self._get_value_from_the_table( table, 'os-extended-volumes:volumes_attached') # Check that 'id' exists as a key of volume_attr dict self.assertIn('id', json.loads(volume_attr)[0]) python-novaclient-9.1.1/novaclient/tests/functional/v2/legacy/test_os_services.py0000664000175000017500000000772113165151077031544 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base class TestOsServicesNovaClient(base.ClientTestBase): """Functional tests for os-services attributes""" COMPUTE_API_VERSION = "2.1" def test_os_services_list(self): table = self.nova('service-list') for serv in self.client.services.list(): self.assertIn(serv.binary, table) def test_os_service_disable_enable(self): # Disable and enable Nova services in accordance with list of nova # services returned by client # NOTE(sdague): service disable has the chance in racing # with other tests. Now functional tests for novaclient are launched # in serial way (https://review.openstack.org/#/c/217768/), but # it's a potential issue for making these tests parallel in the future for serv in self.client.services.list(): # In Pike the os-services API was made multi-cell aware and it # looks up services by host, which uses the host mapping record # in the API DB which is only populated for nova-compute services, # effectively making it impossible to perform actions like enable # or disable non-nova-compute services since the API won't be able # to find them. So filter out anything that's not nova-compute. if serv.binary != 'nova-compute': continue host = self._get_column_value_from_single_row_table( self.nova('service-list --binary %s' % serv.binary), 'Host') service = self.nova('service-disable %s %s' % (host, serv.binary)) self.addCleanup(self.nova, 'service-enable', params="%s %s" % (host, serv.binary)) status = self._get_column_value_from_single_row_table( service, 'Status') self.assertEqual('disabled', status) service = self.nova('service-enable %s %s' % (host, serv.binary)) status = self._get_column_value_from_single_row_table( service, 'Status') self.assertEqual('enabled', status) def test_os_service_disable_log_reason(self): for serv in self.client.services.list(): # In Pike the os-services API was made multi-cell aware and it # looks up services by host, which uses the host mapping record # in the API DB which is only populated for nova-compute services, # effectively making it impossible to perform actions like enable # or disable non-nova-compute services since the API won't be able # to find them. So filter out anything that's not nova-compute. if serv.binary != 'nova-compute': continue host = self._get_column_value_from_single_row_table( self.nova('service-list --binary %s' % serv.binary), 'Host') service = self.nova('service-disable --reason test_disable %s %s' % (host, serv.binary)) self.addCleanup(self.nova, 'service-enable', params="%s %s" % (host, serv.binary)) status = self._get_column_value_from_single_row_table( service, 'Status') log_reason = self._get_column_value_from_single_row_table( service, 'Disabled Reason') self.assertEqual('disabled', status) self.assertEqual('test_disable', log_reason) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_quotas.py0000664000175000017500000000507013165151077027263 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional.v2.legacy import test_quotas class TestQuotasNovaClient2_35(test_quotas.TestQuotasNovaClient): """Nova quotas functional tests.""" COMPUTE_API_VERSION = "2.35" _quota_resources = ['instances', 'cores', 'ram', 'floating_ips', 'fixed_ips', 'metadata_items', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', 'key_pairs', 'security_groups', 'security_group_rules', 'server_groups', 'server_group_members'] def test_quotas_update(self): # `nova quota-update` requires tenant-id. tenant_id = self._get_project_id(self.cli_clients.tenant_name) self.addCleanup(self.client.quotas.delete, tenant_id) original_quotas = self.client.quotas.get(tenant_id) difference = 10 params = [tenant_id] for quota_name in self._quota_resources: params.append("--%(name)s %(value)s" % { "name": quota_name.replace("_", "-"), "value": getattr(original_quotas, quota_name) + difference}) self.nova("quota-update", params=" ".join(params)) updated_quotas = self.client.quotas.get(tenant_id) for quota_name in self._quota_resources: self.assertEqual(getattr(original_quotas, quota_name), getattr(updated_quotas, quota_name) - difference) class TestQuotasNovaClient2_36(TestQuotasNovaClient2_35): """Nova quotas functional tests.""" COMPUTE_API_VERSION = "2.latest" # The 2.36 microversion stops proxying network quota resources like # floating/fixed IPs and security groups/rules. _quota_resources = ['instances', 'cores', 'ram', 'metadata_items', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', 'key_pairs', 'server_groups', 'server_group_members'] python-novaclient-9.1.1/novaclient/tests/functional/v2/test_instances.py0000664000175000017500000000133513165151077027736 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional.v2.legacy import test_instances class TestInstanceCLI(test_instances.TestInstanceCLI): COMPUTE_API_VERSION = "2.latest" python-novaclient-9.1.1/novaclient/tests/functional/v2/test_quota_classes.py0000664000175000017500000001427513165151077030624 0ustar jenkinsjenkins00000000000000# Copyright 2017 Huawei Technologies Co.,LTD. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from tempest.lib import exceptions from novaclient.tests.functional import base class TestQuotaClassesNovaClient(base.ClientTestBase): """Nova quota classes functional tests for the v2.1 microversion.""" COMPUTE_API_VERSION = '2.1' # The list of quota class resources we expect in the output table. _included_resources = ['instances', 'cores', 'ram', 'floating_ips', 'fixed_ips', 'metadata_items', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', 'key_pairs', 'security_groups', 'security_group_rules'] # The list of quota class resources we do not expect in the output table. _excluded_resources = ['server_groups', 'server_group_members'] # Any resources that are not shown but can be updated. For example, before # microversion 2.50 you can update server_groups and server_groups_members # quota class values but they are not shown in the GET response. _extra_update_resources = _excluded_resources # The list of resources which are blocked from being updated. _blocked_update_resources = [] def _get_quota_class_name(self): """Returns a fake quota class name specific to this test class.""" return 'fake-class-%s' % self.COMPUTE_API_VERSION.replace('.', '-') def _verify_qouta_class_show_output(self, output, expected_values): # Assert that the expected key/value pairs are in the output table for quota_name in self._included_resources: # First make sure the resource is actually in expected quota. self.assertIn(quota_name, expected_values) expected_value = expected_values[quota_name] actual_value = self._get_value_from_the_table(output, quota_name) self.assertEqual(expected_value, actual_value) # Now make sure anything that we don't expect in the output table is # actually not showing up. for quota_name in self._excluded_resources: # ValueError is raised when the key isn't found in the table. self.assertRaises(ValueError, self._get_value_from_the_table, output, quota_name) def test_quota_class_show(self): """Tests showing quota class values for a fake non-existing quota class. The API will return the defaults if the quota class does not actually exist. We use a fake class to avoid any interaction with the real default quota class values. """ default_quota_class_set = self.client.quota_classes.get('default') default_values = { quota_name: str(getattr(default_quota_class_set, quota_name)) for quota_name in self._included_resources } output = self.nova('quota-class-show %s' % self._get_quota_class_name()) self._verify_qouta_class_show_output(output, default_values) def test_quota_class_update(self): """Tests updating a fake quota class. The way this works in the API is that if the quota class is not found, it is created. So in this test we can use a fake quota class with fake values and they will all get set. We don't use the default quota class because it is global and we don't want to interfere with other tests. """ class_name = self._get_quota_class_name() params = [class_name] expected_values = {} for quota_name in ( self._included_resources + self._extra_update_resources): params.append("--%s 99" % quota_name.replace("_", "-")) expected_values[quota_name] = '99' # Note that the quota-class-update CLI doesn't actually output any # information from the response. self.nova("quota-class-update", params=" ".join(params)) # Assert the results using the quota-class-show output. output = self.nova('quota-class-show %s' % class_name) self._verify_qouta_class_show_output(output, expected_values) # Assert that attempting to update resources that are blocked will # result in a failure. for quota_name in self._blocked_update_resources: self.assertRaises( exceptions.CommandFailed, self.nova, "quota-class-update %s --%s 99" % (class_name, quota_name.replace("_", "-"))) class TestQuotasNovaClient2_50(TestQuotaClassesNovaClient): """Nova quota classes functional tests for the v2.50 microversion.""" COMPUTE_API_VERSION = '2.50' # The 2.50 microversion added the server_groups and server_group_members # to the response, and filtered out floating_ips, fixed_ips, # security_groups and security_group_members, similar to the 2.36 # microversion in the os-qouta-sets API. _included_resources = ['instances', 'cores', 'ram', 'metadata_items', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', 'key_pairs', 'server_groups', 'server_group_members'] # The list of quota class resources we do not expect in the output table. _excluded_resources = ['floating_ips', 'fixed_ips', 'security_groups', 'security_group_rules'] # In 2.50, server_groups and server_group_members can be both updated # in a PUT request and shown in a GET response. _extra_update_resources = [] # In 2.50, you can't update the network-related resources. _blocked_update_resources = _excluded_resources python-novaclient-9.1.1/novaclient/tests/functional/v2/test_extended_attributes.py0000664000175000017500000000411613165151077032015 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json from novaclient.tests.functional.v2.legacy import test_extended_attributes class TestExtAttrNovaClientV23(test_extended_attributes.TestExtAttrNovaClient): """Functional tests for os-extended-server-attributes, microversion 2.3""" COMPUTE_API_VERSION = "2.3" def test_extended_server_attributes(self): server, volume = self._create_server_and_attach_volume() table = self.nova('show %s' % server.id) # Check that attributes listed below exist in 'nova show' table and # they are exactly Property attributes (not an instance's name, e.g.) # The _get_value_from_the_table() will raise an exception # if attr is not a key of the table dict (first column) for attr in ['OS-EXT-SRV-ATTR:reservation_id', 'OS-EXT-SRV-ATTR:launch_index', 'OS-EXT-SRV-ATTR:ramdisk_id', 'OS-EXT-SRV-ATTR:kernel_id', 'OS-EXT-SRV-ATTR:hostname', 'OS-EXT-SRV-ATTR:root_device_name', 'OS-EXT-SRV-ATTR:user_data']: self._get_value_from_the_table(table, attr) # Check that attribute given below also exists in 'nova show' table # as a key (first column) of table dict volume_attr = self._get_value_from_the_table( table, 'os-extended-volumes:volumes_attached') # Check that 'delete_on_termination' exists as a key # of volume_attr dict self.assertIn('delete_on_termination', json.loads(volume_attr)[0]) python-novaclient-9.1.1/novaclient/tests/functional/v2/test_aggregates.py0000664000175000017500000000360313165151077030060 0ustar jenkinsjenkins00000000000000# -*- 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 oslo_utils import uuidutils from novaclient.tests.functional import base class TestAggregatesNovaClient(base.ClientTestBase): COMPUTE_API_VERSION = '2.1' def setUp(self): super(TestAggregatesNovaClient, self).setUp() self.agg1 = 'agg-%s' % uuidutils.generate_uuid() self.agg2 = 'agg-%s' % uuidutils.generate_uuid() self.addCleanup(self._clean_aggregates) def _clean_aggregates(self): for a in (self.agg1, self.agg2): try: self.nova('aggregate-delete', params=a) except Exception: pass def test_aggregate_update_name(self): self.nova('aggregate-create', params=self.agg1) self.nova('aggregate-update', params='--name=%s %s' % (self.agg2, self.agg1)) output = self.nova('aggregate-show', params=self.agg2) self.assertIn(self.agg2, output) self.nova('aggregate-delete', params=self.agg2) def test_aggregate_update_az(self): self.nova('aggregate-create', params=self.agg2) self.nova('aggregate-update', params='--availability-zone=myaz %s' % self.agg2) output = self.nova('aggregate-show', params=self.agg2) self.assertIn('myaz', output) self.nova('aggregate-delete', params=self.agg2) python-novaclient-9.1.1/novaclient/tests/functional/v2/fake_crypto.py0000664000175000017500000000513713165151077027222 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions # 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. def get_x509_cert_and_fingerprint(): fingerprint = "a1:6f:6d:ea:a6:36:d0:3a:c6:eb:b6:ee:07:94:3e:2a:90:98:2b:c9" certif = ( "-----BEGIN CERTIFICATE-----\n" "MIIDIjCCAgqgAwIBAgIJAIE8EtWfZhhFMA0GCSqGSIb3DQEBCwUAMCQxIjAgBgNV\n" "BAMTGWNsb3VkYmFzZS1pbml0LXVzZXItMTM1NTkwHhcNMTUwMTI5MTgyMzE4WhcN\n" "MjUwMTI2MTgyMzE4WjAkMSIwIAYDVQQDExljbG91ZGJhc2UtaW5pdC11c2VyLTEz\n" "NTU5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv4lv95ofkXLIbALU\n" "UEb1f949TYNMUvMGNnLyLgGOY+D61TNG7RZn85cRg9GVJ7KDjSLN3e3LwH5rgv5q\n" "pU+nM/idSMhG0CQ1lZeExTsMEJVT3bG7LoU5uJ2fJSf5+hA0oih2M7/Kap5ggHgF\n" "h+h8MWvDC9Ih8x1aadkk/OEmJsTrziYm0C/V/FXPHEuXfZn8uDNKZ/tbyfI6hwEj\n" "nLz5Zjgg29n6tIPYMrnLNDHScCwtNZOcnixmWzsxCt1bxsAEA/y9gXUT7xWUf52t\n" "2+DGQbLYxo0PHjnPf3YnFXNavfTt+4c7ZdHhOQ6ZA8FGQ2LJHDHM1r2/8lK4ld2V\n" "qgNTcQIDAQABo1cwVTATBgNVHSUEDDAKBggrBgEFBQcDAjA+BgNVHREENzA1oDMG\n" "CisGAQQBgjcUAgOgJQwjY2xvdWRiYXNlLWluaXQtdXNlci0xMzU1OUBsb2NhbGhv\n" "c3QwDQYJKoZIhvcNAQELBQADggEBAHHX/ZUOMR0ZggQnfXuXLIHWlffVxxLOV/bE\n" "7JC/dtedHqi9iw6sRT5R6G1pJo0xKWr2yJVDH6nC7pfxCFkby0WgVuTjiu6iNRg2\n" "4zNJd8TGrTU+Mst+PPJFgsxrAY6vjwiaUtvZ/k8PsphHXu4ON+oLurtVDVgog7Vm\n" "fQCShx434OeJj1u8pb7o2WyYS5nDVrHBhlCAqVf2JPKu9zY+i9gOG2kimJwH7fJD\n" "xXpMIwAQ+flwlHR7OrE0L8TNcWwKPRAY4EPcXrT+cWo1k6aTqZDSK54ygW2iWtni\n" "ZBcstxwcB4GIwnp1DrPW9L2gw5eLe1Sl6wdz443TW8K/KPV9rWQ=\n" "-----END CERTIFICATE-----\n") return certif, fingerprint def get_ssh_pub_key_and_fingerprint(): fingerprint = "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c" public_key = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGg" "B4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0l" "RE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv" "9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYc" "pSxsIbECHw== Generated-by-Nova") return public_key, fingerprint python-novaclient-9.1.1/novaclient/tests/functional/v2/test_device_tagging.py0000664000175000017500000001574113165151077030714 0ustar jenkinsjenkins00000000000000# Copyright (C) 2016, Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils import six from tempest.lib import exceptions from novaclient.tests.functional import base class TestBlockDeviceTaggingCLIError(base.ClientTestBase): """Negative test that asserts that creating a server with a tagged block device with a specific microversion will fail. """ COMPUTE_API_VERSION = "2.31" def test_boot_server_with_tagged_block_devices_with_error(self): try: output = self.nova('boot', params=( '%(name)s --flavor %(flavor)s --poll ' '--nic net-id=%(net-uuid)s ' '--block-device ' 'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,' 'shutdown=remove,tag=bar' % {'name': uuidutils.generate_uuid(), 'flavor': self.flavor.id, 'net-uuid': self.network.id, 'image': self.image.id})) except exceptions.CommandFailed as e: self.assertIn("ERROR (CommandError): " "'tag' in block device mapping is not supported " "in API version %s." % self.COMPUTE_API_VERSION, six.text_type(e)) else: server_id = self._get_value_from_the_table(output, 'id') self.client.servers.delete(server_id) self.wait_for_resource_delete(server_id, self.client.servers) self.fail("Booting a server with block device tag is not failed.") class TestNICDeviceTaggingCLIError(base.ClientTestBase): """Negative test that asserts that creating a server with a tagged nic with a specific microversion will fail. """ COMPUTE_API_VERSION = "2.31" def test_boot_server_with_tagged_nic_devices_with_error(self): try: output = self.nova('boot', params=( '%(name)s --flavor %(flavor)s --poll ' '--nic net-id=%(net-uuid)s,tag=foo ' '--block-device ' 'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,' 'shutdown=remove' % {'name': uuidutils.generate_uuid(), 'flavor': self.flavor.id, 'net-uuid': self.network.id, 'image': self.image.id})) except exceptions.CommandFailed as e: self.assertIn("Invalid nic argument", six.text_type(e)) else: server_id = self._get_value_from_the_table(output, 'id') self.client.servers.delete(server_id) self.wait_for_resource_delete(server_id, self.client.servers) self.fail("Booting a server with network interface tag " "is not failed.") class TestBlockDeviceTaggingCLI(base.ClientTestBase): """Tests that creating a server with a tagged block device will work with the 2.32 microversion, where the feature was originally added. """ COMPUTE_API_VERSION = "2.32" def test_boot_server_with_tagged_block_devices(self): server_info = self.nova('boot', params=( '%(name)s --flavor %(flavor)s --poll ' '--nic net-id=%(net-uuid)s ' '--block-device ' 'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,' 'shutdown=remove,tag=bar' % {'name': uuidutils.generate_uuid(), 'flavor': self.flavor.id, 'net-uuid': self.network.id, 'image': self.image.id})) server_id = self._get_value_from_the_table(server_info, 'id') self.client.servers.delete(server_id) self.wait_for_resource_delete(server_id, self.client.servers) class TestNICDeviceTaggingCLI(base.ClientTestBase): """Tests that creating a server with a tagged nic will work with the 2.32 microversion, where the feature was originally added. """ COMPUTE_API_VERSION = "2.32" def test_boot_server_with_tagged_nic_devices(self): server_info = self.nova('boot', params=( '%(name)s --flavor %(flavor)s --poll ' '--nic net-id=%(net-uuid)s,tag=foo ' '--block-device ' 'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,' 'shutdown=remove' % {'name': uuidutils.generate_uuid(), 'flavor': self.flavor.id, 'net-uuid': self.network.id, 'image': self.image.id})) server_id = self._get_value_from_the_table(server_info, 'id') self.client.servers.delete(server_id) self.wait_for_resource_delete(server_id, self.client.servers) class TestDeviceTaggingCLIV233(TestBlockDeviceTaggingCLIError, TestNICDeviceTaggingCLI): """Tests that in microversion 2.33, creating a server with a tagged block device will fail, but creating a server with a tagged nic will succeed. """ COMPUTE_API_VERSION = "2.33" class TestDeviceTaggingCLIV236(TestBlockDeviceTaggingCLIError, TestNICDeviceTaggingCLI): """Tests that in microversion 2.36, creating a server with a tagged block device will fail, but creating a server with a tagged nic will succeed. This is testing the boundary before 2.37 where nic tagging was broken. """ COMPUTE_API_VERSION = "2.36" class TestDeviceTaggingCLIV237(TestBlockDeviceTaggingCLIError, TestNICDeviceTaggingCLIError): """Tests that in microversion 2.37, creating a server with either a tagged block device or tagged nic would fail. """ COMPUTE_API_VERSION = "2.37" class TestDeviceTaggingCLIV241(TestBlockDeviceTaggingCLIError, TestNICDeviceTaggingCLIError): """Tests that in microversion 2.41, creating a server with either a tagged block device or tagged nic would fail. This is testing the boundary before 2.42 where block device tags and nic tags were fixed for server create requests. """ COMPUTE_API_VERSION = "2.41" class TestDeviceTaggingCLIV242(TestBlockDeviceTaggingCLI, TestNICDeviceTaggingCLI): """Tests that in microversion 2.42 you could once again create a server with a tagged block device or a tagged nic. """ COMPUTE_API_VERSION = "2.42" python-novaclient-9.1.1/novaclient/tests/functional/v2/test_os_services.py0000664000175000017500000001734513165151077030303 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.tests.functional import base from novaclient.tests.functional.v2.legacy import test_os_services from novaclient import utils class TestOsServicesNovaClientV211(test_os_services.TestOsServicesNovaClient): """Functional tests for os-services attributes, microversion 2.11""" COMPUTE_API_VERSION = "2.11" def test_os_services_force_down_force_up(self): for serv in self.client.services.list(): # In Pike the os-services API was made multi-cell aware and it # looks up services by host, which uses the host mapping record # in the API DB which is only populated for nova-compute services, # effectively making it impossible to perform actions like enable # or disable non-nova-compute services since the API won't be able # to find them. So filter out anything that's not nova-compute. if serv.binary != 'nova-compute': continue service_list = self.nova('service-list --binary %s' % serv.binary) # Check the 'service-list' table has the 'Forced down' column status = self._get_column_value_from_single_row_table( service_list, 'Forced down') self.assertEqual('False', status) host = self._get_column_value_from_single_row_table(service_list, 'Host') service = self.nova('service-force-down %s %s' % (host, serv.binary)) self.addCleanup(self.nova, 'service-force-down --unset', params="%s %s" % (host, serv.binary)) status = self._get_column_value_from_single_row_table( service, 'Forced down') self.assertEqual('True', status) service = self.nova('service-force-down --unset %s %s' % (host, serv.binary)) status = self._get_column_value_from_single_row_table( service, 'Forced down') self.assertEqual('False', status) class TestOsServicesNovaClientV2_53(base.ClientTestBase): """Tests the nova service-* commands using the 2.53 microversion. The main difference with the 2.53 microversion in these commands is the host/binary combination is replaced with the service.id as the unique identifier for a service. """ COMPUTE_API_VERSION = "2.53" def test_os_services_list(self): table = self.nova('service-list') for serv in self.client.services.list(): self.assertIn(serv.binary, table) # the id should not be an integer and should be in the table self.assertFalse(utils.is_integer_like(serv.id)) self.assertIn(serv.id, table) def test_os_service_disable_enable(self): # Disable and enable Nova services in accordance with list of nova # services returned by client # NOTE(sdague): service disable has the chance in racing # with other tests. Now functional tests for novaclient are launched # in serial way (https://review.openstack.org/#/c/217768/), but # it's a potential issue for making these tests parallel in the future for serv in self.client.services.list(): # In Pike the os-services API was made multi-cell aware and it # looks up services by host, which uses the host mapping record # in the API DB which is only populated for nova-compute services, # effectively making it impossible to perform actions like enable # or disable non-nova-compute services since the API won't be able # to find them. So filter out anything that's not nova-compute. if serv.binary != 'nova-compute': continue service = self.nova('service-disable %s' % serv.id) self.addCleanup(self.nova, 'service-enable', params="%s" % serv.id) service_id = self._get_column_value_from_single_row_table( service, 'ID') self.assertEqual(serv.id, service_id) status = self._get_column_value_from_single_row_table( service, 'Status') self.assertEqual('disabled', status) service = self.nova('service-enable %s' % serv.id) service_id = self._get_column_value_from_single_row_table( service, 'ID') self.assertEqual(serv.id, service_id) status = self._get_column_value_from_single_row_table( service, 'Status') self.assertEqual('enabled', status) def test_os_service_disable_log_reason(self): for serv in self.client.services.list(): # In Pike the os-services API was made multi-cell aware and it # looks up services by host, which uses the host mapping record # in the API DB which is only populated for nova-compute services, # effectively making it impossible to perform actions like enable # or disable non-nova-compute services since the API won't be able # to find them. So filter out anything that's not nova-compute. if serv.binary != 'nova-compute': continue service = self.nova('service-disable --reason test_disable %s' % serv.id) self.addCleanup(self.nova, 'service-enable', params="%s" % serv.id) service_id = self._get_column_value_from_single_row_table( service, 'ID') self.assertEqual(serv.id, service_id) status = self._get_column_value_from_single_row_table( service, 'Status') log_reason = self._get_column_value_from_single_row_table( service, 'Disabled Reason') self.assertEqual('disabled', status) self.assertEqual('test_disable', log_reason) def test_os_services_force_down_force_up(self): for serv in self.client.services.list(): # In Pike the os-services API was made multi-cell aware and it # looks up services by host, which uses the host mapping record # in the API DB which is only populated for nova-compute services, # effectively making it impossible to perform actions like enable # or disable non-nova-compute services since the API won't be able # to find them. So filter out anything that's not nova-compute. if serv.binary != 'nova-compute': continue service = self.nova('service-force-down %s' % serv.id) self.addCleanup(self.nova, 'service-force-down --unset', params="%s" % serv.id) service_id = self._get_column_value_from_single_row_table( service, 'ID') self.assertEqual(serv.id, service_id) forced_down = self._get_column_value_from_single_row_table( service, 'Forced down') self.assertEqual('True', forced_down) service = self.nova('service-force-down --unset %s' % serv.id) forced_down = self._get_column_value_from_single_row_table( service, 'Forced down') self.assertEqual('False', forced_down) python-novaclient-9.1.1/novaclient/tests/functional/hooks/0000775000175000017500000000000013165151230025117 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/hooks/post_test_hook.sh0000775000175000017500000000323413165151077030535 0ustar jenkinsjenkins00000000000000#!/bin/bash -xe # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 script is executed inside post_test_hook function in devstack gate. function generate_testr_results { if [ -f .testrepository/0 ]; then sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit sudo /usr/os-testr-env/bin/subunit2html $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html sudo gzip -9 $BASE/logs/testrepository.subunit sudo gzip -9 $BASE/logs/testr_results.html sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz fi } export NOVACLIENT_DIR="$BASE/new/python-novaclient" sudo chown -R jenkins:stack $NOVACLIENT_DIR # Go to the novaclient dir cd $NOVACLIENT_DIR # Run tests echo "Running novaclient functional test suite" set +e # Preserve env for OS_ credentials sudo -E -H -u jenkins tox -e ${TOX_ENV:-functional} EXIT_CODE=$? set -e # Collect and parse result generate_testr_results exit $EXIT_CODE python-novaclient-9.1.1/novaclient/tests/functional/clouds.yaml.sample0000664000175000017500000000032013165151077027435 0ustar jenkinsjenkins00000000000000clouds: devstack: auth: username: admin password: change_me project_name: admin auth_url: http://localhost:5000/v3 user_domain_id: default project_domain_id: default python-novaclient-9.1.1/novaclient/tests/functional/base.py0000664000175000017500000005404613165151077025302 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import time from cinderclient.v2 import client as cinderclient import fixtures from glanceclient import client as glanceclient from keystoneauth1.exceptions import discovery as discovery_exc from keystoneauth1 import identity from keystoneauth1 import session as ksession from keystoneclient import client as keystoneclient from keystoneclient import discover as keystone_discover from neutronclient.v2_0 import client as neutronclient import os_client_config from oslo_utils import uuidutils import tempest.lib.cli.base import testtools import novaclient import novaclient.api_versions import novaclient.client from novaclient.v2 import networks import novaclient.v2.shell BOOT_IS_COMPLETE = ("login as 'cirros' user. default password: " "'cubswin:)'. use 'sudo' for root.") def is_keystone_version_available(session, version): """Given a (major, minor) pair, check if the API version is enabled.""" d = keystone_discover.Discover(session) try: d.create_client(version) except (discovery_exc.DiscoveryFailure, discovery_exc.VersionNotAvailable): return False else: return True # The following are simple filter functions that filter our available # image / flavor list so that they can be used in standard testing. def pick_flavor(flavors): """Given a flavor list pick a reasonable one.""" for flavor_priority in ('m1.nano', 'm1.micro', 'm1.tiny', 'm1.small'): for flavor in flavors: if flavor.name == flavor_priority: return flavor raise NoFlavorException() def pick_image(images): firstImage = None for image in images: firstImage = firstImage or image if image.name.startswith('cirros') and ( image.name.endswith('-uec') or image.name.endswith('-disk.img')): return image # We didn't find the specific cirros image we'd like to use, so just use # the first available. if firstImage: return firstImage raise NoImageException() def pick_network(networks): network_name = os.environ.get('OS_NOVACLIENT_NETWORK') if network_name: for network in networks: if network.name == network_name: return network raise NoNetworkException() return networks[0] class NoImageException(Exception): """We couldn't find an acceptable image.""" pass class NoFlavorException(Exception): """We couldn't find an acceptable flavor.""" pass class NoNetworkException(Exception): """We couldn't find an acceptable network.""" pass class NoCloudConfigException(Exception): """We couldn't find a cloud configuration.""" pass CACHE = {} class ClientTestBase(testtools.TestCase): """Base test class for read only python-novaclient commands. This is a first pass at a simple read only python-novaclient test. This only exercises client commands that are read only. This should test commands: * as a regular user * as a admin user * with and without optional parameters * initially just check return codes, and later test command outputs """ COMPUTE_API_VERSION = None log_format = ('%(asctime)s %(process)d %(levelname)-8s ' '[%(name)s] %(message)s') def setUp(self): super(ClientTestBase, self).setUp() test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: test_timeout = int(test_timeout) except ValueError: test_timeout = 0 if test_timeout > 0: self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or os.environ.get('OS_STDOUT_CAPTURE') == '1'): stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or os.environ.get('OS_STDERR_CAPTURE') == '1'): stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) if (os.environ.get('OS_LOG_CAPTURE') != 'False' and os.environ.get('OS_LOG_CAPTURE') != '0'): self.useFixture(fixtures.LoggerFixture(nuke_handlers=False, format=self.log_format, level=None)) # Collecting of credentials: # # Grab the cloud config from a user's clouds.yaml file. # First look for a functional_admin cloud, as this is a cloud # that the user may have defined for functional testing that has # admin credentials. # If that is not found, get the devstack config and override the # username and project_name to be admin so that admin credentials # will be used. # # Finally, fall back to looking for environment variables to support # existing users running these the old way. We should deprecate that # as tox 2.0 blanks out environment. # # TODO(sdague): while we collect this information in # tempest-lib, we do it in a way that's not available for top # level tests. Long term this probably needs to be in the base # class. openstack_config = os_client_config.config.OpenStackConfig() try: cloud_config = openstack_config.get_one_cloud('functional_admin') except os_client_config.exceptions.OpenStackConfigException: try: cloud_config = openstack_config.get_one_cloud( 'devstack', auth=dict( username='admin', project_name='admin')) except os_client_config.exceptions.OpenStackConfigException: try: cloud_config = openstack_config.get_one_cloud('envvars') except os_client_config.exceptions.OpenStackConfigException: cloud_config = None if cloud_config is None: raise NoCloudConfigException( "Could not find a cloud named functional_admin or a cloud" " named devstack. Please check your clouds.yaml file and" " try again.") auth_info = cloud_config.config['auth'] user = auth_info['username'] passwd = auth_info['password'] self.project_name = auth_info['project_name'] auth_url = auth_info['auth_url'] user_domain_id = auth_info['user_domain_id'] self.project_domain_id = auth_info['project_domain_id'] if 'insecure' in cloud_config.config: self.insecure = cloud_config.config['insecure'] else: self.insecure = False auth = identity.Password(username=user, password=passwd, project_name=self.project_name, auth_url=auth_url, project_domain_id=self.project_domain_id, user_domain_id=user_domain_id) session = ksession.Session(auth=auth, verify=(not self.insecure)) self.client = self._get_novaclient(session) self.glance = glanceclient.Client('2', session=session) # pick some reasonable flavor / image combo if "flavor" not in CACHE: CACHE["flavor"] = pick_flavor(self.client.flavors.list()) if "image" not in CACHE: CACHE["image"] = pick_image(self.glance.images.list()) self.flavor = CACHE["flavor"] self.image = CACHE["image"] if "network" not in CACHE: # Get the networks from neutron. neutron = neutronclient.Client(session=session) neutron_networks = neutron.list_networks()['networks'] # Convert the neutron dicts to Network objects. nets = [] for network in neutron_networks: nets.append(networks.Network( networks.NeutronManager, network)) # Keep track of whether or not there are multiple networks # available to the given tenant because if so, a specific # network ID has to be passed in on server create requests # otherwise the server POST will fail with a 409. CACHE['multiple_networks'] = len(nets) > 1 CACHE["network"] = pick_network(nets) self.network = CACHE["network"] self.multiple_networks = CACHE['multiple_networks'] # create a CLI client in case we'd like to do CLI # testing. tempest.lib does this really weird thing where it # builds a giant factory of all the CLIs that it knows # about. Eventually that should really be unwound into # something more sensible. cli_dir = os.environ.get( 'OS_NOVACLIENT_EXEC_DIR', os.path.join(os.path.abspath('.'), '.tox/functional/bin')) self.cli_clients = tempest.lib.cli.base.CLIClient( username=user, password=passwd, tenant_name=self.project_name, uri=auth_url, cli_dir=cli_dir, insecure=self.insecure) self.keystone = keystoneclient.Client(session=session, username=user, password=passwd) self.cinder = cinderclient.Client(auth=auth, session=session) def _get_novaclient(self, session): nc = novaclient.client.Client("2", session=session) if self.COMPUTE_API_VERSION: if "min_api_version" not in CACHE: # Obtain supported versions by API side v = nc.versions.get_current() if not hasattr(v, 'version') or not v.version: # API doesn't support microversions CACHE["min_api_version"] = ( novaclient.api_versions.APIVersion("2.0")) CACHE["max_api_version"] = ( novaclient.api_versions.APIVersion("2.0")) else: CACHE["min_api_version"] = ( novaclient.api_versions.APIVersion(v.min_version)) CACHE["max_api_version"] = ( novaclient.api_versions.APIVersion(v.version)) if self.COMPUTE_API_VERSION == "2.latest": requested_version = min(novaclient.API_MAX_VERSION, CACHE["max_api_version"]) else: requested_version = novaclient.api_versions.APIVersion( self.COMPUTE_API_VERSION) if not requested_version.matches(CACHE["min_api_version"], CACHE["max_api_version"]): msg = ("%s is not supported by Nova-API. Supported version" % self.COMPUTE_API_VERSION) if CACHE["min_api_version"] == CACHE["max_api_version"]: msg += ": %s" % CACHE["min_api_version"].get_string() else: msg += "s: %s - %s" % ( CACHE["min_api_version"].get_string(), CACHE["max_api_version"].get_string()) self.skipTest(msg) nc.api_version = requested_version return nc def nova(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): if self.COMPUTE_API_VERSION: flags += " --os-compute-api-version %s " % self.COMPUTE_API_VERSION return self.cli_clients.nova(action, flags, params, fail_ok, endpoint_type, merge_stderr) def wait_for_volume_status(self, volume, status, timeout=60, poll_interval=1): """Wait until volume reaches given status. :param volume: volume resource :param status: expected status of volume :param timeout: timeout in seconds :param poll_interval: poll interval in seconds """ start_time = time.time() while time.time() - start_time < timeout: volume = self.cinder.volumes.get(volume.id) if volume.status == status: break time.sleep(poll_interval) else: self.fail("Volume %s did not reach status %s after %d s" % (volume.id, status, timeout)) def wait_for_server_os_boot(self, server_id, timeout=300, poll_interval=1): """Wait until instance's operating system is completely booted. :param server_id: uuid4 id of given instance :param timeout: timeout in seconds :param poll_interval: poll interval in seconds """ start_time = time.time() console = None while time.time() - start_time < timeout: console = self.nova('console-log %s ' % server_id) if BOOT_IS_COMPLETE in console: break time.sleep(poll_interval) else: self.fail("Server %s did not boot after %d s.\nConsole:\n%s" % (server_id, timeout, console)) def wait_for_resource_delete(self, resource, manager, timeout=60, poll_interval=1): """Wait until getting the resource raises NotFound exception. :param resource: Resource object. :param manager: Manager object with get method. :param timeout: timeout in seconds :param poll_interval: poll interval in seconds """ start_time = time.time() while time.time() - start_time < timeout: try: manager.get(resource) except Exception as e: if getattr(e, "http_status", None) == 404: break else: raise time.sleep(poll_interval) else: self.fail("The resource '%s' still exists." % resource.id) def name_generate(self, prefix='Entity'): """Generate randomized name for some entity. :param prefix: string prefix """ name = "%s-%s" % (prefix, uuidutils.generate_uuid()) return name def _get_value_from_the_table(self, table, key): """Parses table to get desired value. EXAMPLE of the table: # +-------------+----------------------------------+ # | Property | Value | # +-------------+----------------------------------+ # | description | | # | enabled | True | # | id | 582df899eabc47018c96713c2f7196ba | # | name | admin | # +-------------+----------------------------------+ """ lines = table.split("\n") for line in lines: if "|" in line: l_property, l_value = line.split("|")[1:3] if l_property.strip() == key: return l_value.strip() raise ValueError("Property '%s' is missing from the table:\n%s" % (key, table)) def _get_column_value_from_single_row_table(self, table, column): """Get the value for the column in the single-row table Example table: +----------+-------------+----------+----------+ | address | cidr | hostname | host | +----------+-------------+----------+----------+ | 10.0.0.3 | 10.0.0.0/24 | test | myhost | +----------+-------------+----------+----------+ :param table: newline-separated table with |-separated cells :param column: name of the column to look for :raises: ValueError if the column value is not found """ lines = table.split("\n") # Determine the column header index first. column_index = -1 for line in lines: if "|" in line: if column_index == -1: headers = line.split("|")[1:-1] for index, header in enumerate(headers): if header.strip() == column: column_index = index break else: # We expect a single-row table so we should be able to get # the value now using the column index. return line.split("|")[1:-1][column_index].strip() raise ValueError("Unable to find value for column '%s'." % column) def _get_list_of_values_from_single_column_table(self, table, column): """Get the list of values for the column in the single-column table Example table: +------+ | Tags | +------+ | tag1 | | tag2 | +------+ :param table: newline-separated table with |-separated cells :param column: name of the column to look for :raises: ValueError if the single column has some other name """ lines = table.split("\n") column_name = None values = [] for line in lines: if "|" in line: if not column_name: column_name = line.split("|")[1].strip() if column_name != column: raise ValueError( "The table has no column %(expected)s " "but has column %(actual)s." % { 'expected': column, 'actual': column_name}) else: values.append(line.split("|")[1].strip()) return values def _create_server(self, name=None, with_network=True, add_cleanup=True, **kwargs): name = name or self.name_generate(prefix='server') if with_network: nics = [{"net-id": self.network.id}] else: nics = None server = self.client.servers.create(name, self.image, self.flavor, nics=nics, **kwargs) if add_cleanup: self.addCleanup(server.delete) novaclient.v2.shell._poll_for_status( self.client.servers.get, server.id, 'building', ['active']) return server def _wait_for_state_change(self, server_id, status): novaclient.v2.shell._poll_for_status( self.client.servers.get, server_id, None, [status], show_progress=False, poll_period=1, silent=True) def _get_project_id(self, name): """Obtain project id by project name.""" if self.keystone.version == "v3": project = self.keystone.projects.find(name=name) else: project = self.keystone.tenants.find(name=name) return project.id def _cleanup_server(self, server_id): """Deletes a server and waits for it to be gone.""" self.client.servers.delete(server_id) self.wait_for_resource_delete(server_id, self.client.servers) def _get_absolute_limits(self): """Returns the absolute limits (quota usage) including reserved quota usage for the given tenant running the test. :return: A dict where the key is the limit (or usage) and value. """ # The absolute limits are returned in a generator so convert to a dict. return {limit.name: limit.value for limit in self.client.limits.get(reserved=True).absolute} class TenantTestBase(ClientTestBase): """Base test class for additional tenant and user creation which could be required in various test scenarios """ def setUp(self): super(TenantTestBase, self).setUp() user_name = self.name_generate('v' + self.COMPUTE_API_VERSION) project_name = self.name_generate('v' + self.COMPUTE_API_VERSION) password = 'password' if self.keystone.version == "v3": project = self.keystone.projects.create(project_name, self.project_domain_id) self.project_id = project.id self.addCleanup(self.keystone.projects.delete, self.project_id) self.user_id = self.keystone.users.create( name=user_name, password=password, default_project=self.project_id).id for role in self.keystone.roles.list(): if "member" in role.name.lower(): self.keystone.roles.grant(role.id, user=self.user_id, project=self.project_id) break else: project = self.keystone.tenants.create(project_name) self.project_id = project.id self.addCleanup(self.keystone.tenants.delete, self.project_id) self.user_id = self.keystone.users.create( user_name, password, tenant_id=self.project_id).id self.addCleanup(self.keystone.users.delete, self.user_id) self.cli_clients_2 = tempest.lib.cli.base.CLIClient( username=user_name, password=password, tenant_name=project_name, uri=self.cli_clients.uri, cli_dir=self.cli_clients.cli_dir, insecure=self.insecure) def another_nova(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False): flags += " --os-compute-api-version %s " % self.COMPUTE_API_VERSION return self.cli_clients_2.nova(action, flags, params, fail_ok, endpoint_type, merge_stderr) python-novaclient-9.1.1/novaclient/tests/functional/README.rst0000664000175000017500000000325613165151077025502 0ustar jenkinsjenkins00000000000000==================================== python-novaclient functional testing ==================================== Idea ---- Over time we have noticed two issues with novaclient unit tests. * Does not exercise the CLI * We can get the expected server behavior wrong, and test the wrong thing. We are using functional tests, run against a running cloud (primarily devstack), to address these two cases. Additionally these functional tests can be considered example uses of python-novaclient. These tests started out in tempest as read only nova CLI tests, to make sure the CLI didn't simply stacktrace when being used (which happened on multiple occasions). Testing Theory -------------- We are treating python-novaclient as legacy code, so we do not want to spend a lot of effort adding in missing features. In the future the CLI will move to python-openstackclient, and the python API will be based on the OpenStack SDK project. But until that happens we still need better functional testing, to prevent regressions etc. Since python-novaclient has two uses, CLI and python API, we should have two sets of functional tests. CLI and python API. The python API tests should never use the CLI. But the CLI tests can use the python API where adding native support to the CLI for the required functionality would involve a non trivial amount of work. Functional Test Guidelines -------------------------- * Consume credentials via standard client environmental variables:: OS_USERNAME OS_PASSWORD OS_TENANT_NAME OS_AUTH_URL * Usage of insecure SSL can be configured via the standard client environment variable:: OS_INSECURE * Try not to require an additional configuration file python-novaclient-9.1.1/novaclient/tests/functional/api/0000775000175000017500000000000013165151230024545 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/api/__init__.py0000664000175000017500000000000013165151077026655 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/tests/functional/api/test_servers.py0000664000175000017500000000241513165151077027662 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import time from novaclient.tests.functional import base class TestServersAPI(base.ClientTestBase): def test_server_ips(self): server_name = "test_server" initial_server = self.client.servers.create( server_name, self.image, self.flavor, nics=[{"net-id": self.network.id}]) self.addCleanup(initial_server.delete) for x in range(60): server = self.client.servers.get(initial_server) if server.status == "ACTIVE": break else: time.sleep(1) else: self.fail("Server %s did not go ACTIVE after 60s" % server) ips = self.client.servers.ips(server) self.assertIn(self.network.name, ips) python-novaclient-9.1.1/novaclient/__init__.py0000664000175000017500000000223013165151077022607 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version from novaclient import api_versions __version__ = pbr.version.VersionInfo('python-novaclient').version_string() API_MIN_VERSION = api_versions.APIVersion("2.1") # The max version should be the latest version that is supported in the client, # not necessarily the latest that the server can provide. This is only bumped # when client supported the max version, and bumped sequentially, otherwise # the client may break due to server side new version may include some # backward incompatible change. API_MAX_VERSION = api_versions.APIVersion("2.53") python-novaclient-9.1.1/novaclient/v2/0000775000175000017500000000000013165151230021017 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/v2/volumes.py0000664000175000017500000001367413165151077023107 0ustar jenkinsjenkins00000000000000# Copyright 2011 Denali Systems, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Volume interface """ import warnings from novaclient import api_versions from novaclient import base class Volume(base.Resource): """ A volume is an extra block level storage to the OpenStack instances. """ NAME_ATTR = 'display_name' def __repr__(self): return "" % self.id class VolumeManager(base.Manager): """ Manage :class:`Volume` resources. This is really about volume attachments. """ resource_class = Volume @api_versions.wraps("2.0", "2.48") def create_server_volume(self, server_id, volume_id, device=None): """ Attach a volume identified by the volume ID to the given server ID :param server_id: The ID of the server :param volume_id: The ID of the volume to attach. :param device: The device name (optional) :rtype: :class:`Volume` """ body = {'volumeAttachment': {'volumeId': volume_id}} if device is not None: body['volumeAttachment']['device'] = device return self._create("/servers/%s/os-volume_attachments" % server_id, body, "volumeAttachment") @api_versions.wraps("2.49") def create_server_volume(self, server_id, volume_id, device=None, tag=None): """ Attach a volume identified by the volume ID to the given server ID :param server_id: The ID of the server :param volume_id: The ID of the volume to attach. :param device: The device name (optional) :param tag: The tag (optional) :rtype: :class:`Volume` """ body = {'volumeAttachment': {'volumeId': volume_id}} if device is not None: body['volumeAttachment']['device'] = device if tag is not None: body['volumeAttachment']['tag'] = tag return self._create("/servers/%s/os-volume_attachments" % server_id, body, "volumeAttachment") def update_server_volume(self, server_id, src_volid, dest_volid): """ Swaps the existing volume attachment to point to a new volume. Takes a server, a source (attached) volume and a destination volume and performs a hypervisor assisted data migration from src to dest volume, detaches the original (source) volume and attaches the new destination volume. Note that not all backing hypervisor drivers support this operation and it may be disabled via policy. :param server_id: The ID of the server :param source_volume: The ID of the src volume :param dest_volume: The ID of the destination volume :rtype: :class:`Volume` """ body = {'volumeAttachment': {'volumeId': dest_volid}} return self._update("/servers/%s/os-volume_attachments/%s" % (server_id, src_volid,), body, "volumeAttachment") def get_server_volume(self, server_id, volume_id=None, attachment_id=None): """ Get the volume identified by the volume ID, that is attached to the given server ID :param server_id: The ID of the server :param volume_id: The ID of the volume to attach :rtype: :class:`Volume` """ if attachment_id is not None and volume_id is not None: raise TypeError("You cannot specify both volume_id " "and attachment_id arguments.") elif attachment_id is not None: warnings.warn("attachment_id argument " "of volumes.get_server_volume " "method is deprecated in favor " "of volume_id.") volume_id = attachment_id if volume_id is None: raise TypeError("volume_id is required argument.") return self._get("/servers/%s/os-volume_attachments/%s" % (server_id, volume_id,), "volumeAttachment") def get_server_volumes(self, server_id): """ Get a list of all the attached volumes for the given server ID :param server_id: The ID of the server :rtype: list of :class:`Volume` """ return self._list("/servers/%s/os-volume_attachments" % server_id, "volumeAttachments") def delete_server_volume(self, server_id, volume_id=None, attachment_id=None): """ Detach a volume identified by the volume ID from the given server :param server_id: The ID of the server :param volume_id: The ID of the volume to attach :returns: An instance of novaclient.base.TupleWithMeta """ if attachment_id is not None and volume_id is not None: raise TypeError("You cannot specify both volume_id " "and attachment_id arguments.") elif attachment_id is not None: warnings.warn("attachment_id argument " "of volumes.delete_server_volume " "method is deprecated in favor " "of volume_id.") volume_id = attachment_id if volume_id is None: raise TypeError("volume_id is required argument.") return self._delete("/servers/%s/os-volume_attachments/%s" % (server_id, volume_id,)) python-novaclient-9.1.1/novaclient/v2/images.py0000664000175000017500000000545713165151077022662 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from novaclient import base from novaclient import exceptions from novaclient.i18n import _ class Image(base.Resource): HUMAN_ID = True def __repr__(self): return "" % self.name class GlanceManager(base.Manager): """Use glance directly from service catalog. This is used to do name to id lookups for images and listing images for the --image-with option to the 'boot' command. Do not use it for anything else besides that. You have been warned. """ resource_class = Image def find_image(self, name_or_id): """Find an image by name or id (user provided input).""" with self.alternate_service_type( 'image', allowed_types=('image',)): # glance catalog entries are the unversioned endpoint, so # we need to jam a version bit in here. if uuidutils.is_uuid_like(name_or_id): # if it's a uuid, then just go collect the info and be # done with it. return self._get('/v2/images/%s' % name_or_id, None) else: matches = self._list('/v2/images?name=%s' % name_or_id, "images") num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % ( self.resource_class.__name__, name_or_id) raise exceptions.NotFound(404, msg) elif num_matches > 1: msg = (_("Multiple %(class)s matches found for " "'%(name)s', use an ID to be more specific.") % {'class': self.resource_class.__name__.lower(), 'name': name_or_id}) raise exceptions.NoUniqueMatch(msg) else: matches[0].append_request_ids(matches.request_ids) return matches[0] def list(self): """ Get a detailed list of all images. :rtype: list of :class:`Image` """ with self.alternate_service_type('image', allowed_types=('image',)): return self._list('/v2/images', 'images') python-novaclient-9.1.1/novaclient/v2/migrations.py0000664000175000017500000000376213165151077023566 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ migration interface """ from six.moves.urllib import parse from novaclient import base from novaclient.i18n import _ import warnings class Migration(base.Resource): def __repr__(self): return "" % self.id class MigrationManager(base.ManagerWithFind): resource_class = Migration def list(self, host=None, status=None, cell_name=None, instance_uuid=None): """ Get a list of migrations. :param host: (optional) filter migrations by host name. :param status: (optional) filter migrations by status. :param cell_name: (optional) filter migrations for a cell. """ opts = {} if host: opts['host'] = host if status: opts['status'] = status if cell_name: warnings.warn(_("Argument 'cell_name' is " "deprecated since Pike, and will " "be removed in a future release.")) opts['cell_name'] = cell_name if instance_uuid: opts['instance_uuid'] = instance_uuid # Transform the dict to a sequence of two-element tuples in fixed # order, then the encoded string will be consistent in Python 2&3. new_opts = sorted(opts.items(), key=lambda x: x[0]) query_string = "?%s" % parse.urlencode(new_opts) if new_opts else "" return self._list("/os-migrations%s" % query_string, "migrations") python-novaclient-9.1.1/novaclient/v2/keypairs.py0000664000175000017500000001477513165151077023247 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Keypair interface """ from novaclient import api_versions from novaclient import base from novaclient import utils class Keypair(base.Resource): """ A keypair is a ssh key that can be injected into a server on launch. """ def __repr__(self): return "" % self.id def _add_details(self, info): dico = 'keypair' in info and \ info['keypair'] or info for (k, v) in dico.items(): # NOTE(rpodolyaka): keypair name allows us to uniquely identify # a specific keypair, while its id attribute # is nothing more than an implementation # detail. We can safely omit the id attribute # here to ensure setattr() won't raise # AttributeError trying to set read-only # property id if k != 'id': setattr(self, k, v) @property def id(self): return self.name def delete(self): """ Delete this keypair. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.delete(self) class KeypairManager(base.ManagerWithFind): resource_class = Keypair keypair_prefix = "os-keypairs" is_alphanum_id_allowed = True @api_versions.wraps("2.0", "2.9") def get(self, keypair): """ Get a keypair. :param keypair: The ID of the keypair to get. :rtype: :class:`Keypair` """ return self._get("/%s/%s" % (self.keypair_prefix, base.getid(keypair)), "keypair") @api_versions.wraps("2.10") def get(self, keypair, user_id=None): """ Get a keypair. :param keypair: The ID of the keypair to get. :param user_id: Id of key-pair owner (Admin only). :rtype: :class:`Keypair` """ query_string = "?user_id=%s" % user_id if user_id else "" url = "/%s/%s%s" % (self.keypair_prefix, base.getid(keypair), query_string) return self._get(url, "keypair") @api_versions.wraps("2.0", "2.1") def create(self, name, public_key=None): """ Create a keypair :param name: name for the keypair to create :param public_key: existing public key to import """ body = {'keypair': {'name': name}} if public_key: body['keypair']['public_key'] = public_key return self._create('/%s' % self.keypair_prefix, body, 'keypair') @api_versions.wraps("2.2", "2.9") def create(self, name, public_key=None, key_type="ssh"): """ Create a keypair :param name: name for the keypair to create :param public_key: existing public key to import :param key_type: keypair type to create """ body = {'keypair': {'name': name, 'type': key_type}} if public_key: body['keypair']['public_key'] = public_key return self._create('/%s' % self.keypair_prefix, body, 'keypair') @api_versions.wraps("2.10") def create(self, name, public_key=None, key_type="ssh", user_id=None): """ Create a keypair :param name: name for the keypair to create :param public_key: existing public key to import :param key_type: keypair type to create :param user_id: user to add. """ body = {'keypair': {'name': name, 'type': key_type}} if public_key: body['keypair']['public_key'] = public_key if user_id: body['keypair']['user_id'] = user_id return self._create('/%s' % self.keypair_prefix, body, 'keypair') @api_versions.wraps("2.0", "2.9") def delete(self, key): """ Delete a keypair :param key: The :class:`Keypair` (or its ID) to delete. :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete('/%s/%s' % (self.keypair_prefix, base.getid(key))) @api_versions.wraps("2.10") def delete(self, key, user_id=None): """ Delete a keypair :param key: The :class:`Keypair` (or its ID) to delete. :param user_id: Id of key-pair owner (Admin only). :returns: An instance of novaclient.base.TupleWithMeta """ query_string = "?user_id=%s" % user_id if user_id else "" url = '/%s/%s%s' % (self.keypair_prefix, base.getid(key), query_string) return self._delete(url) @api_versions.wraps("2.0", "2.9") def list(self): """ Get a list of keypairs. """ return self._list('/%s' % self.keypair_prefix, 'keypairs') @api_versions.wraps("2.10", "2.34") def list(self, user_id=None): """ Get a list of keypairs. :param user_id: Id of key-pairs owner (Admin only). """ query_string = "?user_id=%s" % user_id if user_id else "" url = '/%s%s' % (self.keypair_prefix, query_string) return self._list(url, 'keypairs') @api_versions.wraps("2.35") def list(self, user_id=None, marker=None, limit=None): """ Get a list of keypairs. :param user_id: Id of key-pairs owner (Admin only). :param marker: Begin returning keypairs that appear later in the keypair list than that represented by this keypair name (optional). :param limit: maximum number of keypairs to return (optional). """ params = {} if user_id: params['user_id'] = user_id if limit: params['limit'] = int(limit) if marker: params['marker'] = str(marker) query_string = utils.prepare_query_string(params) url = '/%s%s' % (self.keypair_prefix, query_string) return self._list(url, 'keypairs') python-novaclient-9.1.1/novaclient/v2/virtual_interfaces.py0000664000175000017500000000273013165151077025275 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ DEPRECATED Virtual Interfaces """ import warnings from novaclient import api_versions from novaclient import base from novaclient.i18n import _ class VirtualInterface(base.Resource): def __repr__(self): return "" class VirtualInterfaceManager(base.ManagerWithFind): """DEPRECATED""" resource_class = VirtualInterface @api_versions.wraps('2.0', '2.43') def list(self, instance_id): """DEPRECATED""" warnings.warn(_('The os-virtual-interfaces API is deprecated. This ' 'API binding will be removed in the first major ' 'release after the Nova server 16.0.0 Pike release.'), DeprecationWarning) return self._list('/servers/%s/os-virtual-interfaces' % instance_id, 'virtual_interfaces') python-novaclient-9.1.1/novaclient/v2/shell.py0000664000175000017500000052300213165151100022476 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import print_function import argparse import collections import datetime import getpass import logging import os import pprint import sys import time from oslo_utils import netutils from oslo_utils import strutils from oslo_utils import timeutils import six import novaclient from novaclient import api_versions from novaclient import base from novaclient import client from novaclient import exceptions from novaclient.i18n import _ from novaclient import shell from novaclient import utils from novaclient.v2 import availability_zones from novaclient.v2 import quotas from novaclient.v2 import servers logger = logging.getLogger(__name__) CERT_DEPRECATION_WARNING = ( _('The nova-cert service is deprecated. This command will be removed ' 'in the first major release after the Nova server 16.0.0 Pike release.') ) CLOUDPIPE_DEPRECATION_WARNING = ( _('The os-cloudpipe Nova API has been removed. This command will be ' 'removed in the first major release after the Nova server 16.0.0 Pike ' 'release.') ) # NOTE(mriedem): Remove this along with the deprecated commands in the first # major python-novaclient release AFTER the nova server 16.0.0 Pike release. def emit_hosts_deprecation_warning(command_name, replacement=None): if replacement is None: print(_('WARNING: Command %s is deprecated and will be removed ' 'in the first major release after the Nova server 16.0.0 ' 'Pike release. There is no replacement or alternative for ' 'this command. Specify --os-compute-api-version less than ' '2.43 to continue using this command until it is removed.') % command_name, file=sys.stderr) else: print(_('WARNING: Command %(command)s is deprecated and will be ' 'removed in the first major release after the Nova server ' '16.0.0 Pike release. Use %(replacement)s instead. Specify ' '--os-compute-api-version less than 2.43 to continue using ' 'this command until it is removed.') % {'command': command_name, 'replacement': replacement}, file=sys.stderr) # NOTE(mriedem): Remove this along with the deprecated commands in the first # major python-novaclient release AFTER the nova server 16.0.0 Pike release. def emit_fixed_floating_deprecation_warning(command_name): print(_('WARNING: Command %s is deprecated and will be removed ' 'in the first major release after the Nova server 16.0.0 ' 'Pike release. Use python-neutronclient or python-openstackclient' 'instead. Specify --os-compute-api-version less than 2.44 ' 'to continue using this command until it is removed.') % command_name, file=sys.stderr) CLIENT_BDM2_KEYS = { 'id': 'uuid', 'source': 'source_type', 'dest': 'destination_type', 'bus': 'disk_bus', 'device': 'device_name', 'size': 'volume_size', 'format': 'guest_format', 'bootindex': 'boot_index', 'type': 'device_type', 'shutdown': 'delete_on_termination', 'tag': 'tag', } def _key_value_pairing(text): try: (k, v) = text.split('=', 1) return (k, v) except ValueError: msg = _("'%s' is not in the format of 'key=value'") % text raise argparse.ArgumentTypeError(msg) def _meta_parsing(metadata): try: return dict(v.split('=', 1) for v in metadata) except ValueError: msg = _("'%s' is not in the format of 'key=value'") % metadata raise argparse.ArgumentTypeError(msg) def _match_image(cs, wanted_properties): image_list = cs.glance.list() images_matched = [] match = set(wanted_properties) for img in image_list: img_dict = {} # exclude any unhashable entries for key, value in img.to_dict().items(): try: set([key, value]) except TypeError: pass else: img_dict[key] = value if match == match.intersection(set(img_dict.items())): images_matched.append(img) return images_matched def _supports_block_device_tags(cs): if (cs.api_version == api_versions.APIVersion('2.32') or cs.api_version >= api_versions.APIVersion('2.42')): return True else: return False def _parse_device_spec(device_spec): spec_dict = {} for arg in device_spec.split(','): if '=' in arg: spec_dict.update([arg.split('=')]) else: raise argparse.ArgumentTypeError( _("Expected a comma-separated list of key=value pairs. '%s' " "is not a key=value pair.") % arg) return spec_dict def _parse_block_device_mapping_v2(cs, args, image): bdm = [] if args.boot_volume: bdm_dict = {'uuid': args.boot_volume, 'source_type': 'volume', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False} bdm.append(bdm_dict) if args.snapshot: bdm_dict = {'uuid': args.snapshot, 'source_type': 'snapshot', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False} bdm.append(bdm_dict) for device_spec in args.block_device: spec_dict = _parse_device_spec(device_spec) bdm_dict = {} if ('tag' in spec_dict and not _supports_block_device_tags(cs)): raise exceptions.CommandError( _("'tag' in block device mapping is not supported " "in API version %(version)s.") % {'version': cs.api_version.get_string()}) for key, value in spec_dict.items(): bdm_dict[CLIENT_BDM2_KEYS[key]] = value source_type = bdm_dict.get('source_type') if not source_type: bdm_dict['source_type'] = 'blank' elif source_type not in ( 'volume', 'image', 'snapshot', 'blank'): raise exceptions.CommandError( _("The value of source_type key of --block-device " "should be one of 'volume', 'image', 'snapshot' " "or 'blank' but it was '%(action)s'") % {'action': source_type}) destination_type = bdm_dict.get('destination_type') if not destination_type: source_type = bdm_dict['source_type'] if source_type in ('image', 'blank'): bdm_dict['destination_type'] = 'local' if source_type in ('snapshot', 'volume'): bdm_dict['destination_type'] = 'volume' elif destination_type not in ('local', 'volume'): raise exceptions.CommandError( _("The value of destination_type key of --block-device " "should be either 'local' or 'volume' but it " "was '%(action)s'") % {'action': destination_type}) # Convert the delete_on_termination to a boolean or set it to true by # default for local block devices when not specified. if 'delete_on_termination' in bdm_dict: action = bdm_dict['delete_on_termination'] if action not in ['remove', 'preserve']: raise exceptions.CommandError( _("The value of shutdown key of --block-device shall be " "either 'remove' or 'preserve' but it was '%(action)s'") % {'action': action}) bdm_dict['delete_on_termination'] = (action == 'remove') elif bdm_dict.get('destination_type') == 'local': bdm_dict['delete_on_termination'] = True bdm.append(bdm_dict) for ephemeral_spec in args.ephemeral: bdm_dict = {'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'delete_on_termination': True} try: eph_dict = _parse_device_spec(ephemeral_spec) except ValueError: err_msg = (_("Invalid ephemeral argument '%s'.") % args.ephemeral) raise argparse.ArgumentTypeError(err_msg) if 'size' in eph_dict: bdm_dict['volume_size'] = eph_dict['size'] if 'format' in eph_dict: bdm_dict['guest_format'] = eph_dict['format'] bdm.append(bdm_dict) if args.swap: bdm_dict = {'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'delete_on_termination': True, 'guest_format': 'swap', 'volume_size': args.swap} bdm.append(bdm_dict) return bdm def _supports_nic_tags(cs): if ((cs.api_version >= api_versions.APIVersion('2.32') and cs.api_version <= api_versions.APIVersion('2.36')) or cs.api_version >= api_versions.APIVersion('2.42')): return True else: return False def _parse_nics(cs, args): supports_auto_alloc = cs.api_version >= api_versions.APIVersion('2.37') supports_nic_tags = _supports_nic_tags(cs) nic_keys = {'net-id', 'v4-fixed-ip', 'v6-fixed-ip', 'port-id', 'net-name'} if supports_auto_alloc and supports_nic_tags: # API version >= 2.42 nic_keys.add('tag') err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of " "the form --nic , " "with only one of net-id, net-name or port-id " "specified. Specifying a --nic of auto or none cannot " "be used with any other --nic value.")) elif supports_auto_alloc and not supports_nic_tags: # 2.41 >= API version >= 2.37 err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of " "the form --nic , " "with only one of net-id, net-name or port-id " "specified. Specifying a --nic of auto or none cannot " "be used with any other --nic value.")) elif not supports_auto_alloc and supports_nic_tags: # 2.36 >= API version >= 2.32 nic_keys.add('tag') err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of " "the form --nic , " "with only one of net-id, net-name or port-id " "specified.")) else: # API version <= 2.31 err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of " "the form --nic , " "with only one of net-id, net-name or port-id " "specified.")) auto_or_none = False nics = [] for nic_str in args.nics: nic_info = {} nic_info_set = False for kv_str in nic_str.split(","): if auto_or_none: # Since we start with auto_or_none being False, it being true # means we've parsed an auto or none argument, then continued # after the comma to another key=value pair. Since auto or none # can only be given by themselves, raise. raise exceptions.CommandError(_("'auto' or 'none' cannot be " "used with any other nic " "arguments")) try: # handle the special auto/none cases if kv_str in ('auto', 'none'): if not supports_auto_alloc: raise exceptions.CommandError(err_msg % nic_str) if nic_info_set: # Since we start with nic_info_set being False, it # being true means we've parsed a key=value pair, then # landed on a auto or none argument after the comma. # Since auto or none can only be given by themselves, # raise. raise exceptions.CommandError( _("'auto' or 'none' cannot be used with any " "other nic arguments")) nics.append(kv_str) auto_or_none = True continue k, v = kv_str.split("=", 1) except ValueError: raise exceptions.CommandError(err_msg % nic_str) if k in nic_keys: # if user has given a net-name resolve it to network ID if k == 'net-name': k = 'net-id' v = _find_network_id(cs, v) # if some argument was given multiple times if k in nic_info: raise exceptions.CommandError(err_msg % nic_str) nic_info[k] = v nic_info_set = True else: raise exceptions.CommandError(err_msg % nic_str) if auto_or_none: continue if 'v4-fixed-ip' in nic_info and not netutils.is_valid_ipv4( nic_info['v4-fixed-ip']): raise exceptions.CommandError(_("Invalid ipv4 address.")) if 'v6-fixed-ip' in nic_info and not netutils.is_valid_ipv6( nic_info['v6-fixed-ip']): raise exceptions.CommandError(_("Invalid ipv6 address.")) if bool(nic_info.get('net-id')) == bool(nic_info.get('port-id')): raise exceptions.CommandError(err_msg % nic_str) nics.append(nic_info) if nics: if auto_or_none: if len(nics) > 1: raise exceptions.CommandError(err_msg % nic_str) # change the single list entry to a string nics = nics[0] else: # Default to 'auto' if API version >= 2.37 and nothing was specified if supports_auto_alloc: nics = 'auto' return nics def _boot(cs, args): """Boot a new server.""" if not args.flavor: raise exceptions.CommandError(_("you need to specify a Flavor ID.")) if args.image: image = _find_image(cs, args.image) else: image = None if not image and args.image_with: images = _match_image(cs, args.image_with) if images: # TODO(harlowja): log a warning that we # are selecting the first of many? image = images[0] min_count = 1 max_count = 1 if args.min_count is not None: if args.min_count < 1: raise exceptions.CommandError(_("min_count should be >= 1")) min_count = args.min_count max_count = min_count if args.max_count is not None: if args.max_count < 1: raise exceptions.CommandError(_("max_count should be >= 1")) max_count = args.max_count if (args.min_count is not None and args.max_count is not None and args.min_count > args.max_count): raise exceptions.CommandError(_("min_count should be <= max_count")) flavor = _find_flavor(cs, args.flavor) meta = _meta_parsing(args.meta) files = {} for f in args.files: try: dst, src = f.split('=', 1) files[dst] = open(src) except IOError as e: raise exceptions.CommandError(_("Can't open '%(src)s': %(exc)s") % {'src': src, 'exc': e}) except ValueError: raise exceptions.CommandError(_("Invalid file argument '%s'. " "File arguments must be of the " "form '--file " "'") % f) # use the os-keypair extension key_name = None if args.key_name is not None: key_name = args.key_name if args.user_data: try: userdata = open(args.user_data) except IOError as e: raise exceptions.CommandError(_("Can't open '%(user_data)s': " "%(exc)s") % {'user_data': args.user_data, 'exc': e}) else: userdata = None if args.availability_zone: availability_zone = args.availability_zone else: availability_zone = None if args.security_groups: security_groups = args.security_groups.split(',') else: security_groups = None block_device_mapping = {} for bdm in args.block_device_mapping: device_name, mapping = bdm.split('=', 1) block_device_mapping[device_name] = mapping block_device_mapping_v2 = _parse_block_device_mapping_v2(cs, args, image) n_boot_args = len(list(filter( bool, (image, args.boot_volume, args.snapshot)))) have_bdm = block_device_mapping_v2 or block_device_mapping # Fail if more than one boot devices are present # or if there is no device to boot from. if n_boot_args > 1 or n_boot_args == 0 and not have_bdm: raise exceptions.CommandError( _("you need to specify at least one source ID (Image, Snapshot, " "or Volume), a block device mapping or provide a set of " "properties to match against an image")) if block_device_mapping and block_device_mapping_v2: raise exceptions.CommandError( _("you can't mix old block devices (--block-device-mapping) " "with the new ones (--block-device, --boot-volume, --snapshot, " "--ephemeral, --swap)")) nics = _parse_nics(cs, args) hints = {} if args.scheduler_hints: for hint in args.scheduler_hints: key, _sep, value = hint.partition('=') # NOTE(vish): multiple copies of the same hint will # result in a list of values if key in hints: if isinstance(hints[key], six.string_types): hints[key] = [hints[key]] hints[key] += [value] else: hints[key] = value boot_args = [args.name, image, flavor] if str(args.config_drive).lower() in ("true", "1"): config_drive = True elif str(args.config_drive).lower() in ("false", "0", "", "none"): config_drive = None else: config_drive = args.config_drive boot_kwargs = dict( meta=meta, files=files, key_name=key_name, min_count=min_count, max_count=max_count, userdata=userdata, availability_zone=availability_zone, security_groups=security_groups, block_device_mapping=block_device_mapping, block_device_mapping_v2=block_device_mapping_v2, nics=nics, scheduler_hints=hints, config_drive=config_drive, admin_pass=args.admin_pass, access_ip_v4=args.access_ip_v4, access_ip_v6=args.access_ip_v6) if 'description' in args: boot_kwargs["description"] = args.description if 'tags' in args and args.tags: boot_kwargs["tags"] = args.tags.split(',') return boot_args, boot_kwargs @utils.arg( '--flavor', default=None, metavar='', help=_("Name or ID of flavor (see 'nova flavor-list').")) @utils.arg( '--image', default=None, metavar='', help=_("Name or ID of image (see 'glance image-list'). ")) @utils.arg( '--image-with', default=[], type=_key_value_pairing, action='append', metavar='', help=_("Image metadata property (see 'glance image-show'). ")) @utils.arg( '--boot-volume', default=None, metavar="", help=_("Volume ID to boot from.")) @utils.arg( '--snapshot', default=None, metavar="", help=_("Snapshot ID to boot from (will create a volume).")) @utils.arg( '--min-count', default=None, type=int, metavar='', help=_("Boot at least servers (limited by quota).")) @utils.arg( '--max-count', default=None, type=int, metavar='', help=_("Boot up to servers (limited by quota).")) @utils.arg( '--meta', metavar="", action='append', default=[], help=_("Record arbitrary key/value metadata to /meta_data.json " "on the metadata server. Can be specified multiple times.")) @utils.arg( '--file', metavar="", action='append', dest='files', default=[], help=_("Store arbitrary files from locally to " "on the new server. Limited by the injected_files quota value.")) @utils.arg( '--key-name', default=os.environ.get('NOVACLIENT_DEFAULT_KEY_NAME'), metavar='', help=_("Key name of keypair that should be created earlier with \ the command keypair-add.")) @utils.arg('name', metavar='', help=_('Name for the new server.')) @utils.arg( '--user-data', default=None, metavar='', help=_("user data file to pass to be exposed by the metadata server.")) @utils.arg( '--availability-zone', default=None, metavar='', help=_("The availability zone for server placement.")) @utils.arg( '--security-groups', default=None, metavar='', help=_("Comma separated list of security group names.")) @utils.arg( '--block-device-mapping', metavar="", action='append', default=[], help=_("Block device mapping in the format " "=:::.")) @utils.arg( '--block-device', metavar="key1=value1[,key2=value2...]", action='append', default=[], start_version='2.0', end_version='2.31', help=_("Block device mapping with the keys: " "id=UUID (image_id, snapshot_id or volume_id only if using source " "image, snapshot or volume) " "source=source type (image, snapshot, volume or blank), " "dest=destination type of the block device (volume or local), " "bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, " "hypervisor driver chooses a suitable default, " "honoured only if device type is supplied) " "type=device type (e.g. disk, cdrom, ...; defaults to 'disk') " "device=name of the device (e.g. vda, xda, ...; " "if omitted, hypervisor driver chooses suitable device " "depending on selected bus; note the libvirt driver always " "uses default device names), " "size=size of the block device in MB(for swap) and in " "GB(for other formats) " "(if omitted, hypervisor driver calculates size), " "format=device will be formatted (e.g. swap, ntfs, ...; optional), " "bootindex=integer used for ordering the boot disks " "(for image backed instances it is equal to 0, " "for others need to be specified) and " "shutdown=shutdown behaviour (either preserve or remove, " "for local destination set to remove).")) @utils.arg( '--block-device', metavar="key1=value1[,key2=value2...]", action='append', default=[], start_version='2.32', end_version='2.32', help=_("Block device mapping with the keys: " "id=UUID (image_id, snapshot_id or volume_id only if using source " "image, snapshot or volume) " "source=source type (image, snapshot, volume or blank), " "dest=destination type of the block device (volume or local), " "bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, " "hypervisor driver chooses a suitable default, " "honoured only if device type is supplied) " "type=device type (e.g. disk, cdrom, ...; defaults to 'disk') " "device=name of the device (e.g. vda, xda, ...; " "tag=device metadata tag (optional) " "if omitted, hypervisor driver chooses suitable device " "depending on selected bus; note the libvirt driver always " "uses default device names), " "size=size of the block device in MB(for swap) and in " "GB(for other formats) " "(if omitted, hypervisor driver calculates size), " "format=device will be formatted (e.g. swap, ntfs, ...; optional), " "bootindex=integer used for ordering the boot disks " "(for image backed instances it is equal to 0, " "for others need to be specified) and " "shutdown=shutdown behaviour (either preserve or remove, " "for local destination set to remove).")) @utils.arg( '--block-device', metavar="key1=value1[,key2=value2...]", action='append', default=[], start_version='2.33', end_version='2.41', help=_("Block device mapping with the keys: " "id=UUID (image_id, snapshot_id or volume_id only if using source " "image, snapshot or volume) " "source=source type (image, snapshot, volume or blank), " "dest=destination type of the block device (volume or local), " "bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, " "hypervisor driver chooses a suitable default, " "honoured only if device type is supplied) " "type=device type (e.g. disk, cdrom, ...; defaults to 'disk') " "device=name of the device (e.g. vda, xda, ...; " "if omitted, hypervisor driver chooses suitable device " "depending on selected bus; note the libvirt driver always " "uses default device names), " "size=size of the block device in MB(for swap) and in " "GB(for other formats) " "(if omitted, hypervisor driver calculates size), " "format=device will be formatted (e.g. swap, ntfs, ...; optional), " "bootindex=integer used for ordering the boot disks " "(for image backed instances it is equal to 0, " "for others need to be specified) and " "shutdown=shutdown behaviour (either preserve or remove, " "for local destination set to remove).")) @utils.arg( '--block-device', metavar="key1=value1[,key2=value2...]", action='append', default=[], start_version='2.42', help=_("Block device mapping with the keys: " "id=UUID (image_id, snapshot_id or volume_id only if using source " "image, snapshot or volume) " "source=source type (image, snapshot, volume or blank), " "dest=destination type of the block device (volume or local), " "bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, " "hypervisor driver chooses a suitable default, " "honoured only if device type is supplied) " "type=device type (e.g. disk, cdrom, ...; defaults to 'disk') " "device=name of the device (e.g. vda, xda, ...; " "if omitted, hypervisor driver chooses suitable device " "depending on selected bus; note the libvirt driver always " "uses default device names), " "size=size of the block device in MB(for swap) and in " "GB(for other formats) " "(if omitted, hypervisor driver calculates size), " "format=device will be formatted (e.g. swap, ntfs, ...; optional), " "bootindex=integer used for ordering the boot disks " "(for image backed instances it is equal to 0, " "for others need to be specified), " "shutdown=shutdown behaviour (either preserve or remove, " "for local destination set to remove) and " "tag=device metadata tag (optional).")) @utils.arg( '--swap', metavar="", default=None, help=_("Create and attach a local swap block device of MB.")) @utils.arg( '--ephemeral', metavar="size=[,format=]", action='append', default=[], help=_("Create and attach a local ephemeral block device of GB " "and format it to .")) @utils.arg( '--hint', action='append', dest='scheduler_hints', default=[], metavar='', help=_("Send arbitrary key/value pairs to the scheduler for custom " "use.")) @utils.arg( '--nic', metavar="", action='append', dest='nics', default=[], start_version='2.0', end_version='2.31', help=_("Create a NIC on the server. " "Specify option multiple times to create multiple NICs. " "net-id: attach NIC to network with this UUID " "net-name: attach NIC to network with this name " "(either port-id or net-id or net-name must be provided), " "v4-fixed-ip: IPv4 fixed address for NIC (optional), " "v6-fixed-ip: IPv6 fixed address for NIC (optional), " "port-id: attach NIC to port with this UUID " "(either port-id or net-id must be provided).")) @utils.arg( '--nic', metavar="", action='append', dest='nics', default=[], start_version='2.32', end_version='2.36', help=_("Create a NIC on the server. " "Specify option multiple times to create multiple nics. " "net-id: attach NIC to network with this UUID " "net-name: attach NIC to network with this name " "(either port-id or net-id or net-name must be provided), " "v4-fixed-ip: IPv4 fixed address for NIC (optional), " "v6-fixed-ip: IPv6 fixed address for NIC (optional), " "port-id: attach NIC to port with this UUID " "tag: interface metadata tag (optional) " "(either port-id or net-id must be provided).")) @utils.arg( '--nic', metavar="", action='append', dest='nics', default=[], start_version='2.37', end_version='2.41', help=_("Create a NIC on the server. " "Specify option multiple times to create multiple nics unless " "using the special 'auto' or 'none' values. " "auto: automatically allocate network resources if none are " "available. This cannot be specified with any other nic value and " "cannot be specified multiple times. " "none: do not attach a NIC at all. This cannot be specified " "with any other nic value and cannot be specified multiple times. " "net-id: attach NIC to network with a specific UUID. " "net-name: attach NIC to network with this name " "(either port-id or net-id or net-name must be provided), " "v4-fixed-ip: IPv4 fixed address for NIC (optional), " "v6-fixed-ip: IPv6 fixed address for NIC (optional), " "port-id: attach NIC to port with this UUID " "(either port-id or net-id must be provided).")) @utils.arg( '--nic', metavar="", action='append', dest='nics', default=[], start_version='2.42', help=_("Create a NIC on the server. " "Specify option multiple times to create multiple nics unless " "using the special 'auto' or 'none' values. " "auto: automatically allocate network resources if none are " "available. This cannot be specified with any other nic value and " "cannot be specified multiple times. " "none: do not attach a NIC at all. This cannot be specified " "with any other nic value and cannot be specified multiple times. " "net-id: attach NIC to network with a specific UUID. " "net-name: attach NIC to network with this name " "(either port-id or net-id or net-name must be provided), " "v4-fixed-ip: IPv4 fixed address for NIC (optional), " "v6-fixed-ip: IPv6 fixed address for NIC (optional), " "port-id: attach NIC to port with this UUID " "tag: interface metadata tag (optional) " "(either port-id or net-id must be provided).")) @utils.arg( '--config-drive', metavar="", dest='config_drive', default=False, help=_("Enable config drive.")) @utils.arg( '--poll', dest='poll', action="store_true", default=False, help=_('Report the new server boot progress until it completes.')) @utils.arg( '--admin-pass', dest='admin_pass', metavar='', default=None, help=_('Admin password for the instance.')) @utils.arg( '--access-ip-v4', dest='access_ip_v4', metavar='', default=None, help=_('Alternative access IPv4 of the instance.')) @utils.arg( '--access-ip-v6', dest='access_ip_v6', metavar='', default=None, help=_('Alternative access IPv6 of the instance.')) @utils.arg( '--description', metavar='', dest='description', default=None, help=_('Description for the server.'), start_version="2.19") @utils.arg( '--tags', metavar='', default=None, help=_('Tags for the server.' 'Tags must be separated by commas: --tags '), start_version="2.52") def do_boot(cs, args): """Boot a new server.""" boot_args, boot_kwargs = _boot(cs, args) extra_boot_kwargs = utils.get_resource_manager_extra_kwargs(do_boot, args) boot_kwargs.update(extra_boot_kwargs) server = cs.servers.create(*boot_args, **boot_kwargs) _print_server(cs, args, server) if args.poll: _poll_for_status(cs.servers.get, server.id, 'building', ['active']) def do_cloudpipe_list(cs, _args): """DEPRECATED Print a list of all cloudpipe instances.""" print(CLOUDPIPE_DEPRECATION_WARNING, file=sys.stderr) cloudpipes = cs.cloudpipe.list() columns = ['Project Id', "Public IP", "Public Port", "Internal IP"] utils.print_list(cloudpipes, columns) @utils.arg( 'project', metavar='', help=_('UUID of the project to create the cloudpipe for.')) def do_cloudpipe_create(cs, args): """DEPRECATED Create a cloudpipe instance for the given project.""" print(CLOUDPIPE_DEPRECATION_WARNING, file=sys.stderr) cs.cloudpipe.create(args.project) @utils.arg('address', metavar='', help=_('New IP Address.')) @utils.arg('port', metavar='', help=_('New Port.')) def do_cloudpipe_configure(cs, args): """DEPRECATED Update the VPN IP/port of a cloudpipe instance.""" print(CLOUDPIPE_DEPRECATION_WARNING, file=sys.stderr) cs.cloudpipe.update(args.address, args.port) def _poll_for_status(poll_fn, obj_id, action, final_ok_states, poll_period=5, show_progress=True, status_field="status", silent=False): """Block while an action is being performed, periodically printing progress. """ def print_progress(progress): if show_progress: msg = (_('\rServer %(action)s... %(progress)s%% complete') % dict(action=action, progress=progress)) else: msg = _('\rServer %(action)s...') % dict(action=action) sys.stdout.write(msg) sys.stdout.flush() if not silent: print() while True: obj = poll_fn(obj_id) status = getattr(obj, status_field) if status: status = status.lower() progress = getattr(obj, 'progress', None) or 0 if status in final_ok_states: if not silent: print_progress(100) print(_("\nFinished")) break elif status == "error": if not silent: print(_("\nError %s server") % action) raise exceptions.ResourceInErrorState(obj) elif status == "deleted": if not silent: print(_("\nDeleted %s server") % action) raise exceptions.InstanceInDeletedState(obj.fault["message"]) if not silent: print_progress(progress) time.sleep(poll_period) def _expand_dict_attr(collection, attr): """Expand item attribute whose value is a dict. Take a collection of items where the named attribute is known to have a dictionary value and replace the named attribute with multiple attributes whose names are the keys of the dictionary namespaced with the original attribute name. """ for item in collection: field = getattr(item, attr) delattr(item, attr) for subkey in field.keys(): setattr(item, attr + ':' + subkey, field[subkey]) def _translate_keys(collection, convert): for item in collection: keys = item.__dict__.keys() item_dict = item.to_dict() for from_key, to_key in convert: if from_key in keys and to_key not in keys: setattr(item, to_key, item_dict[from_key]) def _translate_extended_states(collection): power_states = [ 'NOSTATE', # 0x00 'Running', # 0x01 '', # 0x02 'Paused', # 0x03 'Shutdown', # 0x04 '', # 0x05 'Crashed', # 0x06 'Suspended' # 0x07 ] for item in collection: try: setattr(item, 'power_state', power_states[getattr(item, 'power_state')]) except AttributeError: setattr(item, 'power_state', "N/A") try: getattr(item, 'task_state') except AttributeError: setattr(item, 'task_state', "N/A") def _translate_flavor_keys(collection): _translate_keys(collection, [('ram', 'memory_mb')]) def _print_flavor_extra_specs(flavor): try: return flavor.get_keys() except exceptions.NotFound: return "N/A" def _print_flavor_list(flavors, show_extra_specs=False): _translate_flavor_keys(flavors) headers = [ 'ID', 'Name', 'Memory_MB', 'Disk', 'Ephemeral', 'Swap', 'VCPUs', 'RXTX_Factor', 'Is_Public', ] if show_extra_specs: formatters = {'extra_specs': _print_flavor_extra_specs} headers.append('extra_specs') else: formatters = {} utils.print_list(flavors, headers, formatters) @utils.arg( '--extra-specs', dest='extra_specs', action='store_true', default=False, help=_('Get extra-specs of each flavor.')) @utils.arg( '--all', dest='all', action='store_true', default=False, help=_('Display all flavors (Admin only).')) @utils.arg( '--marker', dest='marker', metavar='', default=None, help=_('The last flavor ID of the previous page; displays list of flavors' ' after "marker".')) @utils.arg( '--min-disk', dest='min_disk', metavar='', default=None, help=_('Filters the flavors by a minimum disk space, in GiB.')) @utils.arg( '--min-ram', dest='min_ram', metavar='', default=None, help=_('Filters the flavors by a minimum RAM, in MB.')) @utils.arg( '--limit', dest='limit', metavar='', type=int, default=None, help=_("Maximum number of flavors to display. If limit is bigger than " "'CONF.api.max_limit' option of Nova API, limit " "'CONF.api.max_limit' will be used instead.")) @utils.arg( '--sort-key', dest='sort_key', metavar='', default=None, help=_('Flavors list sort key.')) @utils.arg( '--sort-dir', dest='sort_dir', metavar='', default=None, help=_('Flavors list sort direction.')) def do_flavor_list(cs, args): """Print a list of available 'flavors' (sizes of servers).""" if args.all: flavors = cs.flavors.list(is_public=None, min_disk=args.min_disk, min_ram=args.min_ram, sort_key=args.sort_key, sort_dir=args.sort_dir) else: flavors = cs.flavors.list(marker=args.marker, min_disk=args.min_disk, min_ram=args.min_ram, sort_key=args.sort_key, sort_dir=args.sort_dir, limit=args.limit) _print_flavor_list(flavors, args.extra_specs) @utils.arg( 'flavor', metavar='', help=_("Name or ID of the flavor to delete.")) def do_flavor_delete(cs, args): """Delete a specific flavor""" flavorid = _find_flavor(cs, args.flavor) cs.flavors.delete(flavorid) _print_flavor_list([flavorid]) @utils.arg( 'flavor', metavar='', help=_("Name or ID of flavor.")) def do_flavor_show(cs, args): """Show details about the given flavor.""" flavor = _find_flavor(cs, args.flavor) _print_flavor(flavor) @utils.arg( 'name', metavar='', help=_("Unique name of the new flavor.")) @utils.arg( 'id', metavar='', help=_("Unique ID of the new flavor." " Specifying 'auto' will generated a UUID for the ID.")) @utils.arg( 'ram', metavar='', help=_("Memory size in MB.")) @utils.arg( 'disk', metavar='', help=_("Disk size in GB.")) @utils.arg( '--ephemeral', metavar='', help=_("Ephemeral space size in GB (default 0)."), default=0) @utils.arg( 'vcpus', metavar='', help=_("Number of vcpus")) @utils.arg( '--swap', metavar='', help=_("Swap space size in MB (default 0)."), default=0) @utils.arg( '--rxtx-factor', metavar='', help=_("RX/TX factor (default 1)."), default=1.0) @utils.arg( '--is-public', metavar='', help=_("Make flavor accessible to the public (default true)."), type=lambda v: strutils.bool_from_string(v, True), default=True) def do_flavor_create(cs, args): """Create a new flavor.""" f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id, args.ephemeral, args.swap, args.rxtx_factor, args.is_public) _print_flavor_list([f]) @utils.arg( 'flavor', metavar='', help=_("Name or ID of flavor.")) @utils.arg( 'action', metavar='', choices=['set', 'unset'], help=_("Actions: 'set' or 'unset'.")) @utils.arg( 'metadata', metavar='', nargs='+', action='append', default=[], help=_('Extra_specs to set/unset (only key is necessary on unset).')) def do_flavor_key(cs, args): """Set or unset extra_spec for a flavor.""" flavor = _find_flavor(cs, args.flavor) keypair = _extract_metadata(args) if args.action == 'set': flavor.set_keys(keypair) elif args.action == 'unset': flavor.unset_keys(keypair.keys()) @utils.arg( '--flavor', metavar='', help=_("Filter results by flavor name or ID.")) def do_flavor_access_list(cs, args): """Print access information about the given flavor.""" if args.flavor: flavor = _find_flavor(cs, args.flavor) if flavor.is_public: raise exceptions.CommandError(_("Access list not available " "for public flavors.")) kwargs = {'flavor': flavor} else: raise exceptions.CommandError(_("Unable to get all access lists. " "Specify --flavor")) try: access_list = cs.flavor_access.list(**kwargs) except NotImplementedError as e: raise exceptions.CommandError("%s" % str(e)) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg( 'flavor', metavar='', help=_("Flavor name or ID to add access for the given tenant.")) @utils.arg( 'tenant', metavar='', help=_('Tenant ID to add flavor access for.')) def do_flavor_access_add(cs, args): """Add flavor access for the given tenant.""" flavor = _find_flavor(cs, args.flavor) access_list = cs.flavor_access.add_tenant_access(flavor, args.tenant) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg( 'flavor', metavar='', help=_("Flavor name or ID to remove access for the given tenant.")) @utils.arg( 'tenant', metavar='', help=_('Tenant ID to remove flavor access for.')) def do_flavor_access_remove(cs, args): """Remove flavor access for the given tenant.""" flavor = _find_flavor(cs, args.flavor) access_list = cs.flavor_access.remove_tenant_access(flavor, args.tenant) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) def _extract_metadata(args): metadata = {} for metadatum in args.metadata[0]: # Can only pass the key in on 'delete' # So this doesn't have to have '=' if metadatum.find('=') > -1: (key, value) = metadatum.split('=', 1) else: key = metadatum value = None metadata[key] = value return metadata def _print_image(image): info = image.to_dict() # ignore links, we don't need to present those info.pop('links', None) # try to replace a server entity to just an id server = info.pop('server', None) try: info['server'] = server['id'] except (KeyError, TypeError): pass # break up metadata and display each on its own row metadata = info.pop('metadata', {}) try: for key, value in metadata.items(): _key = 'metadata %s' % key info[_key] = value except AttributeError: pass utils.print_dict(info) def _print_flavor(flavor): info = flavor.to_dict() # ignore links, we don't need to present those info.pop('links') info.update({"extra_specs": _print_flavor_extra_specs(flavor)}) utils.print_dict(info) @utils.arg( '--reservation-id', dest='reservation_id', metavar='', default=None, help=_('Only return servers that match reservation-id.')) @utils.arg( '--ip', dest='ip', metavar='', default=None, help=_('Search with regular expression match by IP address.')) @utils.arg( '--ip6', dest='ip6', metavar='', default=None, help=_('Search with regular expression match by IPv6 address.')) @utils.arg( '--name', dest='name', metavar='', default=None, help=_('Search with regular expression match by name.')) @utils.arg( '--instance-name', dest='instance_name', metavar='', default=None, help=_('Search with regular expression match by server name.')) @utils.arg( '--status', dest='status', metavar='', default=None, help=_('Search by server status.')) @utils.arg( '--flavor', dest='flavor', metavar='', default=None, help=_('Search by flavor name or ID.')) @utils.arg( '--image', dest='image', metavar='', default=None, help=_('Search by image name or ID.')) @utils.arg( '--host', dest='host', metavar='', default=None, help=_('Search servers by hostname to which they are assigned (Admin ' 'only).')) @utils.arg( '--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=int(strutils.bool_from_string( os.environ.get("ALL_TENANTS", 'false'), True)), help=_('Display information from all tenants (Admin only).')) @utils.arg( '--tenant', # nova db searches by project_id dest='tenant', metavar='', nargs='?', help=_('Display information from single tenant (Admin only).')) @utils.arg( '--user', dest='user', metavar='', nargs='?', help=_('Display information from single user (Admin only).')) @utils.arg( '--deleted', dest='deleted', action="store_true", default=False, help=_('Only display deleted servers (Admin only).')) @utils.arg( '--fields', default=None, metavar='', help=_('Comma-separated list of fields to display. ' 'Use the show command to see which fields are available.')) @utils.arg( '--minimal', dest='minimal', action="store_true", default=False, help=_('Get only UUID and name.')) @utils.arg( '--sort', dest='sort', metavar='[:]', help=_('Comma-separated list of sort keys and directions in the form ' 'of [:]. The direction defaults to descending if ' 'not specified.')) @utils.arg( '--marker', dest='marker', metavar='', default=None, help=_('The last server UUID of the previous page; displays list of ' 'servers after "marker".')) @utils.arg( '--limit', dest='limit', metavar='', type=int, default=None, help=_("Maximum number of servers to display. If limit == -1, all servers " "will be displayed. If limit is bigger than 'CONF.api.max_limit' " "option of Nova API, limit 'CONF.api.max_limit' will be used " "instead.")) @utils.arg( '--changes-since', dest='changes_since', metavar='', default=None, help=_("List only servers changed after a certain point of time." "The provided time should be an ISO 8061 formatted time." "ex 2016-03-04T06:27:59Z .")) @utils.arg( '--tags', dest='tags', metavar='', default=None, help=_("The given tags must all be present for a server to be included in " "the list result. Boolean expression in this case is 't1 AND t2'. " "Tags must be separated by commas: --tags "), start_version="2.26") @utils.arg( '--tags-any', dest='tags-any', metavar='', default=None, help=_("If one of the given tags is present the server will be included " "in the list result. Boolean expression in this case is " "'t1 OR t2'. Tags must be separated by commas: " "--tags-any "), start_version="2.26") @utils.arg( '--not-tags', dest='not-tags', metavar='', default=None, help=_("Only the servers that do not have any of the given tags will " "be included in the list results. Boolean expression in this case " "is 'NOT(t1 AND t2)'. Tags must be separated by commas: " "--not-tags "), start_version="2.26") @utils.arg( '--not-tags-any', dest='not-tags-any', metavar='', default=None, help=_("Only the servers that do not have at least one of the given tags " "will be included in the list result. Boolean expression in this " "case is 'NOT(t1 OR t2)'. Tags must be separated by commas: " "--not-tags-any "), start_version="2.26") def do_list(cs, args): """List servers.""" imageid = None flavorid = None if args.image: imageid = _find_image(cs, args.image).id if args.flavor: flavorid = _find_flavor(cs, args.flavor).id # search by tenant or user only works with all_tenants if args.tenant or args.user: args.all_tenants = 1 search_opts = { 'all_tenants': args.all_tenants, 'reservation_id': args.reservation_id, 'ip': args.ip, 'ip6': args.ip6, 'name': args.name, 'image': imageid, 'flavor': flavorid, 'status': args.status, 'tenant_id': args.tenant, 'user_id': args.user, 'host': args.host, 'deleted': args.deleted, 'instance_name': args.instance_name, 'changes-since': args.changes_since} for arg in ('tags', "tags-any", 'not-tags', 'not-tags-any'): if arg in args: search_opts[arg] = getattr(args, arg) filters = {'security_groups': utils.format_security_groups} # In microversion 2.47 we started embedding flavor info in server details. have_embedded_flavor_info = ( cs.api_version >= api_versions.APIVersion('2.47')) # If we don't have embedded flavor info then we only report the flavor id # rather than looking up the rest of the information. if not have_embedded_flavor_info: filters['flavor'] = lambda f: f['id'] id_col = 'ID' detailed = not args.minimal sort_keys = [] sort_dirs = [] if args.sort: for sort in args.sort.split(','): sort_key, _sep, sort_dir = sort.partition(':') if not sort_dir: sort_dir = 'desc' elif sort_dir not in ('asc', 'desc'): raise exceptions.CommandError(_( 'Unknown sort direction: %s') % sort_dir) sort_keys.append(sort_key) sort_dirs.append(sort_dir) if search_opts['changes-since']: try: timeutils.parse_isotime(search_opts['changes-since']) except ValueError: raise exceptions.CommandError(_('Invalid changes-since value: %s') % search_opts['changes-since']) servers = cs.servers.list(detailed=detailed, search_opts=search_opts, sort_keys=sort_keys, sort_dirs=sort_dirs, marker=args.marker, limit=args.limit) convert = [('OS-EXT-SRV-ATTR:host', 'host'), ('OS-EXT-STS:task_state', 'task_state'), ('OS-EXT-SRV-ATTR:instance_name', 'instance_name'), ('OS-EXT-STS:power_state', 'power_state'), ('hostId', 'host_id')] _translate_keys(servers, convert) _translate_extended_states(servers) formatters = {} cols = [] fmts = {} # For detailed lists, if we have embedded flavor information then replace # the "flavor" attribute with more detailed information. if detailed and have_embedded_flavor_info: _expand_dict_attr(servers, 'flavor') if servers: cols, fmts = _get_list_table_columns_and_formatters( args.fields, servers, exclude_fields=('id',), filters=filters) if args.minimal: columns = [ id_col, 'Name'] elif cols: columns = [id_col] + cols formatters.update(fmts) else: columns = [ id_col, 'Name', 'Status', 'Task State', 'Power State', 'Networks' ] # If getting the data for all tenants, print # Tenant ID as well if search_opts['all_tenants']: columns.insert(2, 'Tenant ID') if search_opts['changes-since']: columns.append('Updated') formatters['Networks'] = utils.format_servers_list_networks sortby_index = 1 if args.sort: sortby_index = None utils.print_list(servers, columns, formatters, sortby_index=sortby_index) 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. If exclude_fields is specified, any field both in fields and exclude_fields will be ignored. :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: novaclient.exceptions.CommandError """ if not fields: return [], {} if not objs: obj = None elif isinstance(objs, list): obj = objs[0] else: obj = objs columns = [] formatters = {} 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 = utils.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 @utils.arg( '--hard', dest='reboot_type', action='store_const', const=servers.REBOOT_HARD, default=servers.REBOOT_SOFT, help=_('Perform a hard reboot (instead of a soft one). ' 'Note: Ironic does not currently support soft reboot; ' 'consequently, bare metal nodes will always do a hard ' 'reboot, regardless of the use of this option.')) @utils.arg( 'server', metavar='', nargs='+', help=_('Name or ID of server(s).')) @utils.arg( '--poll', dest='poll', action="store_true", default=False, help=_('Poll until reboot is complete.')) def do_reboot(cs, args): """Reboot a server.""" servers = [_find_server(cs, s) for s in args.server] utils.do_action_on_many( lambda s: s.reboot(args.reboot_type), servers, _("Request to reboot server %s has been accepted."), _("Unable to reboot the specified server(s).")) if args.poll: utils.do_action_on_many( lambda s: _poll_for_status(cs.servers.get, s.id, 'rebooting', ['active'], show_progress=False), servers, _("Wait for server %s reboot."), _("Wait for specified server(s) failed.")) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('image', metavar='', help=_("Name or ID of new image.")) @utils.arg( '--rebuild-password', dest='rebuild_password', metavar='', default=False, help=_("Set the provided admin password on the rebuilt server.")) @utils.arg( '--poll', dest='poll', action="store_true", default=False, help=_('Report the server rebuild progress until it completes.')) @utils.arg( '--minimal', dest='minimal', action="store_true", default=False, help=_('Skips flavor/image lookups when showing servers.')) @utils.arg( '--preserve-ephemeral', action="store_true", default=False, help=_('Preserve the default ephemeral storage partition on rebuild.')) @utils.arg( '--name', metavar='', default=None, help=_('Name for the new server.')) @utils.arg( '--description', metavar='', dest='description', default=None, help=_('New description for the server.'), start_version="2.19") @utils.arg( '--meta', metavar="", action='append', default=[], help=_("Record arbitrary key/value metadata to /meta_data.json " "on the metadata server. Can be specified multiple times.")) @utils.arg( '--file', metavar="", action='append', dest='files', default=[], help=_("Store arbitrary files from locally to " "on the new server. You may store up to 5 files.")) def do_rebuild(cs, args): """Shutdown, re-image, and re-boot a server.""" server = _find_server(cs, args.server) image = _find_image(cs, args.image) if args.rebuild_password is not False: _password = args.rebuild_password else: _password = None kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args) kwargs['preserve_ephemeral'] = args.preserve_ephemeral kwargs['name'] = args.name if 'description' in args: kwargs['description'] = args.description meta = _meta_parsing(args.meta) kwargs['meta'] = meta files = {} for f in args.files: try: dst, src = f.split('=', 1) with open(src, 'r') as s: files[dst] = s.read() except IOError as e: raise exceptions.CommandError(_("Can't open '%(src)s': %(exc)s") % {'src': src, 'exc': e}) except ValueError: raise exceptions.CommandError(_("Invalid file argument '%s'. " "File arguments must be of the " "form '--file " "'") % f) kwargs['files'] = files server = server.rebuild(image, _password, **kwargs) _print_server(cs, args, server) if args.poll: _poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active']) @utils.arg( 'server', metavar='', help=_('Name (old name) or ID of server.')) @utils.arg( '--name', metavar='', dest='name', default=None, help=_('New name for the server.')) @utils.arg( '--description', metavar='', dest='description', default=None, help=_('New description for the server. If it equals to empty string ' '(i.g. ""), the server description will be removed.'), start_version="2.19") def do_update(cs, args): """Update the name or the description for a server.""" update_kwargs = {} if args.name: update_kwargs["name"] = args.name # NOTE(andreykurilin): `do_update` method is used by `do_rename` method, # which do not have description argument at all. When `do_rename` will be # removed after deprecation period, feel free to change the check below to: # `if args.description:` if "description" in args and args.description is not None: update_kwargs["description"] = args.description _find_server(cs, args.server).update(**update_kwargs) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'flavor', metavar='', help=_("Name or ID of new flavor.")) @utils.arg( '--poll', dest='poll', action="store_true", default=False, help=_('Report the server resize progress until it completes.')) def do_resize(cs, args): """Resize a server.""" server = _find_server(cs, args.server) flavor = _find_flavor(cs, args.flavor) kwargs = utils.get_resource_manager_extra_kwargs(do_resize, args) server.resize(flavor, **kwargs) if args.poll: _poll_for_status(cs.servers.get, server.id, 'resizing', ['active', 'verify_resize']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_resize_confirm(cs, args): """Confirm a previous resize.""" _find_server(cs, args.server).confirm_resize() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_resize_revert(cs, args): """Revert a previous resize (and return to the previous VM).""" _find_server(cs, args.server).revert_resize() @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( '--poll', dest='poll', action="store_true", default=False, help=_('Report the server migration progress until it completes.')) def do_migrate(cs, args): """Migrate a server. The new host will be selected by the scheduler.""" server = _find_server(cs, args.server) server.migrate() if args.poll: _poll_for_status(cs.servers.get, server.id, 'migrating', ['active', 'verify_resize']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_pause(cs, args): """Pause a server.""" _find_server(cs, args.server).pause() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unpause(cs, args): """Unpause a server.""" _find_server(cs, args.server).unpause() @utils.arg( '--all-tenants', action='store_const', const=1, default=0, help=_('Stop server(s) in another tenant by name (Admin only).')) @utils.arg( 'server', metavar='', nargs='+', help=_('Name or ID of server(s).')) def do_stop(cs, args): """Stop the server(s).""" find_args = {'all_tenants': args.all_tenants} utils.do_action_on_many( lambda s: _find_server(cs, s, **find_args).stop(), args.server, _("Request to stop server %s has been accepted."), _("Unable to stop the specified server(s).")) @utils.arg( '--all-tenants', action='store_const', const=1, default=0, help=_('Start server(s) in another tenant by name (Admin only).')) @utils.arg( 'server', metavar='', nargs='+', help=_('Name or ID of server(s).')) def do_start(cs, args): """Start the server(s).""" find_args = {'all_tenants': args.all_tenants} utils.do_action_on_many( lambda s: _find_server(cs, s, **find_args).start(), args.server, _("Request to start server %s has been accepted."), _("Unable to start the specified server(s).")) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_lock(cs, args): """Lock a server. A normal (non-admin) user will not be able to execute actions on a locked server. """ _find_server(cs, args.server).lock() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unlock(cs, args): """Unlock a server.""" _find_server(cs, args.server).unlock() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_suspend(cs, args): """Suspend a server.""" _find_server(cs, args.server).suspend() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_resume(cs, args): """Resume a server.""" _find_server(cs, args.server).resume() @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( '--password', metavar='', dest='password', help=_('The admin password to be set in the rescue environment.')) @utils.arg( '--image', metavar='', dest='image', help=_('The image to rescue with.')) def do_rescue(cs, args): """Reboots a server into rescue mode, which starts the machine from either the initial image or a specified image, attaching the current boot disk as secondary. """ kwargs = {} if args.image: kwargs['image'] = _find_image(cs, args.image) if args.password: kwargs['password'] = args.password utils.print_dict(_find_server(cs, args.server).rescue(**kwargs)[1]) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unrescue(cs, args): """Restart the server from normal boot disk again.""" _find_server(cs, args.server).unrescue() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_shelve(cs, args): """Shelve a server.""" _find_server(cs, args.server).shelve() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_shelve_offload(cs, args): """Remove a shelved server from the compute node.""" _find_server(cs, args.server).shelve_offload() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unshelve(cs, args): """Unshelve a server.""" _find_server(cs, args.server).unshelve() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_diagnostics(cs, args): """Retrieve server diagnostics.""" server = _find_server(cs, args.server) utils.print_dict(cs.servers.diagnostics(server)[1], wrap=80) @utils.arg( 'server', metavar='', help=_('Name or ID of a server for which the network cache should ' 'be refreshed from neutron (Admin only).')) def do_refresh_network(cs, args): """Refresh server network information.""" server = _find_server(cs, args.server) cs.server_external_events.create([{'server_uuid': server.id, 'name': 'network-changed'}]) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_set_password(cs, args): """ Change the admin password for a server. """ server = _find_server(cs, args.server) p1 = getpass.getpass('New password: ') p2 = getpass.getpass('Again: ') if p1 != p2: raise exceptions.CommandError(_("Passwords do not match.")) server.change_password(p1) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('name', metavar='', help=_('Name of snapshot.')) @utils.arg( '--metadata', metavar="", action='append', default=[], help=_("Record arbitrary key/value metadata to /meta_data.json " "on the metadata server. Can be specified multiple times.")) @utils.arg( '--show', dest='show', action="store_true", default=False, help=_('Print image info.')) @utils.arg( '--poll', dest='poll', action="store_true", default=False, help=_('Report the snapshot progress and poll until image creation is ' 'complete.')) def do_image_create(cs, args): """Create a new image by taking a snapshot of a running server.""" server = _find_server(cs, args.server) meta = _meta_parsing(args.metadata) or None image_uuid = cs.servers.create_image(server, args.name, meta) if args.poll: _poll_for_status(cs.glance.find_image, image_uuid, 'snapshotting', ['active']) # NOTE(sirp): A race-condition exists between when the image finishes # uploading and when the servers's `task_state` is cleared. To account # for this, we need to poll a second time to ensure the `task_state` is # cleared before returning, ensuring that a snapshot taken immediately # after this function returns will succeed. # # A better long-term solution will be to separate 'snapshotting' and # 'image-uploading' in Nova and clear the task-state once the VM # snapshot is complete but before the upload begins. task_state_field = "OS-EXT-STS:task_state" if hasattr(server, task_state_field): _poll_for_status(cs.servers.get, server.id, 'image_snapshot', [None], status_field=task_state_field, show_progress=False, silent=True) if args.show: _print_image(_find_image(cs, image_uuid)) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('name', metavar='', help=_('Name of the backup image.')) @utils.arg( 'backup_type', metavar='', help=_('The backup type, like "daily" or "weekly".')) @utils.arg( 'rotation', metavar='', help=_('Int parameter representing how many backups to keep ' 'around.')) def do_backup(cs, args): """Backup a server by creating a 'backup' type snapshot.""" result = _find_server(cs, args.server).backup(args.name, args.backup_type, args.rotation) # Microversion >= 2.45 will return a DictWithMeta that has the image_id # in it for the backup snapshot image. if cs.api_version >= api_versions.APIVersion('2.45'): _print_image(_find_image(cs, result['image_id'])) @utils.arg( 'server', metavar='', help=_("Name or ID of server.")) @utils.arg( 'action', metavar='', choices=['set', 'delete'], help=_("Actions: 'set' or 'delete'.")) @utils.arg( 'metadata', metavar='', nargs='+', action='append', default=[], help=_('Metadata to set or delete (only key is necessary on delete).')) def do_meta(cs, args): """Set or delete metadata on a server.""" server = _find_server(cs, args.server) metadata = _extract_metadata(args) if args.action == 'set': cs.servers.set_meta(server, metadata) elif args.action == 'delete': cs.servers.delete_meta(server, sorted(metadata.keys(), reverse=True)) def _print_server(cs, args, server=None, wrap=0): # By default when searching via name we will do a # findall(name=blah) and due a REST /details which is not the same # as a .get() and doesn't get the information about flavors and # images. This fix it as we redo the call with the id which does a # .get() to get all information. if not server: server = _find_server(cs, args.server) minimal = getattr(args, "minimal", False) networks = server.networks info = server.to_dict() for network_label, address_list in networks.items(): info['%s network' % network_label] = ', '.join(address_list) flavor = info.get('flavor', {}) if cs.api_version >= api_versions.APIVersion('2.47'): # The "flavor" field is a JSON representation of a dict containing the # flavor information used at boot. if minimal: # To retain something similar to the previous behaviour, keep the # 'flavor' field name but just output the original name. info['flavor'] = flavor['original_name'] else: # Replace the "flavor" field with individual namespaced fields. del info['flavor'] for key in flavor.keys(): info['flavor:' + key] = flavor[key] else: # Prior to microversion 2.47 we just have the ID of the flavor so we # need to retrieve the flavor information (which may have changed # since the instance was booted). flavor_id = flavor.get('id', '') if minimal: info['flavor'] = flavor_id else: try: info['flavor'] = '%s (%s)' % (_find_flavor(cs, flavor_id).name, flavor_id) except Exception: info['flavor'] = '%s (%s)' % (_("Flavor not found"), flavor_id) if 'security_groups' in info: # when we have multiple nics the info will include the # security groups N times where N == number of nics. Be nice # and only display it once. info['security_groups'] = ', '.join( sorted(set(group['name'] for group in info['security_groups']))) image = info.get('image', {}) if image: image_id = image.get('id', '') if minimal: info['image'] = image_id else: try: info['image'] = '%s (%s)' % (_find_image(cs, image_id).name, image_id) except Exception: info['image'] = '%s (%s)' % (_("Image not found"), image_id) else: # Booted from volume info['image'] = _("Attempt to boot from volume - no image supplied") info.pop('links', None) info.pop('addresses', None) utils.print_dict(info, wrap=wrap) @utils.arg( '--minimal', dest='minimal', action="store_true", default=False, help=_('Skips flavor/image lookups when showing servers.')) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( '--wrap', dest='wrap', metavar='', type=int, default=0, help=_('Wrap the output to a specified length, or 0 to disable.')) def do_show(cs, args): """Show details about the given server.""" _print_server(cs, args, wrap=args.wrap) @utils.arg( '--all-tenants', action='store_const', const=1, default=0, help=_('Delete server(s) in another tenant by name (Admin only).')) @utils.arg( 'server', metavar='', nargs='+', help=_('Name or ID of server(s).')) def do_delete(cs, args): """Immediately shut down and delete specified server(s).""" find_args = {'all_tenants': args.all_tenants} utils.do_action_on_many( lambda s: _find_server(cs, s, **find_args).delete(), args.server, _("Request to delete server %s has been accepted."), _("Unable to delete the specified server(s).")) def _find_server(cs, server, raise_if_notfound=True, **find_args): """Get a server by name or ID. :param cs: NovaClient's instance :param server: identifier of server :param raise_if_notfound: raise an exception if server is not found :param find_args: argument to search server """ if raise_if_notfound: return utils.find_resource(cs.servers, server, **find_args) else: try: return utils.find_resource(cs.servers, server, wrap_exception=False) except exceptions.NoUniqueMatch as e: raise exceptions.CommandError(six.text_type(e)) except exceptions.NotFound: # The server can be deleted return server def _find_image(cs, image): """Get an image by name or ID.""" try: return cs.glance.find_image(image) except (exceptions.NotFound, exceptions.NoUniqueMatch) as e: raise exceptions.CommandError(six.text_type(e)) def _find_flavor(cs, flavor): """Get a flavor by name, ID, or RAM size.""" try: return utils.find_resource(cs.flavors, flavor, is_public=None) except exceptions.NotFound: return cs.flavors.find(ram=flavor) def _find_network_id(cs, net_name): """Get unique network ID from network name from neutron""" try: return cs.neutron.find_network(net_name).id except (exceptions.NotFound, exceptions.NoUniqueMatch) as e: raise exceptions.CommandError(six.text_type(e)) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'network_id', metavar='', help=_('Network ID.')) def do_add_fixed_ip(cs, args): """DEPRECATED Add new IP address on a network to server.""" emit_fixed_floating_deprecation_warning('add-fixed-ip') server = _find_server(cs, args.server) server.add_fixed_ip(args.network_id) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('address', metavar='
', help=_('IP Address.')) def do_remove_fixed_ip(cs, args): """DEPRECATED Remove an IP address from a server.""" emit_fixed_floating_deprecation_warning('remove-fixed-ip') server = _find_server(cs, args.server) server.remove_fixed_ip(args.address) def _print_volume(volume): utils.print_dict(volume.to_dict()) def _translate_availability_zone_keys(collection): _translate_keys(collection, [('zoneName', 'name'), ('zoneState', 'status')]) def _translate_volume_attachments_keys(collection): _translate_keys(collection, [('serverId', 'server_id'), ('volumeId', 'volume_id')]) @utils.arg( 'server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'volume', metavar='', help=_('ID of the volume to attach.')) @utils.arg( 'device', metavar='', default=None, nargs='?', help=_('Name of the device e.g. /dev/vdb. ' 'Use "auto" for autoassign (if supported). ' 'Libvirt driver will use default device name.')) @utils.arg( '--tag', metavar='', default=None, help=_('Tag for the attached volume.'), start_version="2.49") def do_volume_attach(cs, args): """Attach a volume to a server.""" if args.device == 'auto': args.device = None update_kwargs = {} if 'tag' in args and args.tag: update_kwargs['tag'] = args.tag volume = cs.volumes.create_server_volume(_find_server(cs, args.server).id, args.volume, args.device, **update_kwargs) _print_volume(volume) @utils.arg( 'server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'src_volume', metavar='', help=_('ID of the source (original) volume.')) @utils.arg( 'dest_volume', metavar='', help=_('ID of the destination volume.')) def do_volume_update(cs, args): """Update the attachment on the server. Migrates the data from an attached volume to the specified available volume and swaps out the active attachment to the new volume. """ cs.volumes.update_server_volume(_find_server(cs, args.server).id, args.src_volume, args.dest_volume) @utils.arg( 'server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'attachment_id', metavar='', help=_('ID of the volume to detach.')) def do_volume_detach(cs, args): """Detach a volume from a server.""" cs.volumes.delete_server_volume(_find_server(cs, args.server).id, args.attachment_id) @utils.arg( 'server', metavar='', help=_('Name or ID of server.')) def do_volume_attachments(cs, args): """List all the volumes attached to a server.""" volumes = cs.volumes.get_server_volumes(_find_server(cs, args.server).id) _translate_volume_attachments_keys(volumes) utils.print_list(volumes, ['ID', 'DEVICE', 'SERVER ID', 'VOLUME ID']) @api_versions.wraps('2.0', '2.5') def console_dict_accessor(cs, data): return data['console'] @api_versions.wraps('2.6') def console_dict_accessor(cs, data): return data['remote_console'] class Console(object): def __init__(self, console_dict): self.type = console_dict['type'] self.url = console_dict['url'] def print_console(cs, data): utils.print_list([Console(console_dict_accessor(cs, data))], ['Type', 'Url']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'console_type', metavar='', help=_('Type of vnc console ("novnc" or "xvpvnc").')) def do_get_vnc_console(cs, args): """Get a vnc console to a server.""" server = _find_server(cs, args.server) data = server.get_vnc_console(args.console_type) print_console(cs, data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'console_type', metavar='', help=_('Type of spice console ("spice-html5").')) def do_get_spice_console(cs, args): """Get a spice console to a server.""" server = _find_server(cs, args.server) data = server.get_spice_console(args.console_type) print_console(cs, data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'console_type', metavar='', help=_('Type of rdp console ("rdp-html5").')) def do_get_rdp_console(cs, args): """Get a rdp console to a server.""" server = _find_server(cs, args.server) data = server.get_rdp_console(args.console_type) print_console(cs, data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( '--console-type', default='serial', help=_('Type of serial console, default="serial".')) def do_get_serial_console(cs, args): """Get a serial console to a server.""" if args.console_type not in ('serial',): raise exceptions.CommandError( _("Invalid parameter value for 'console_type', " "currently supported 'serial'.")) server = _find_server(cs, args.server) data = server.get_serial_console(args.console_type) print_console(cs, data) @api_versions.wraps('2.8') @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_get_mks_console(cs, args): """Get an MKS console to a server.""" server = _find_server(cs, args.server) data = server.get_mks_console() print_console(cs, data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'private_key', metavar='', help=_('Private key (used locally to decrypt password) (Optional). ' 'When specified, the command displays the clear (decrypted) VM ' 'password. When not specified, the ciphered VM password is ' 'displayed.'), nargs='?', default=None) def do_get_password(cs, args): """Get the admin password for a server. This operation calls the metadata service to query metadata information and does not read password information from the server itself. """ server = _find_server(cs, args.server) data = server.get_password(args.private_key) print(data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_clear_password(cs, args): """Clear the admin password for a server from the metadata server. This action does not actually change the instance server password. """ server = _find_server(cs, args.server) server.clear_password() def _print_floating_ip_list(floating_ips): convert = [('instance_id', 'server_id')] _translate_keys(floating_ips, convert) utils.print_list(floating_ips, ['Id', 'IP', 'Server Id', 'Fixed IP', 'Pool']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( '--length', metavar='', default=None, help=_('Length in lines to tail.')) def do_console_log(cs, args): """Get console log output of a server.""" server = _find_server(cs, args.server) data = server.get_console_output(length=args.length) print(data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('address', metavar='
', help=_('IP Address.')) @utils.arg( '--fixed-address', metavar='', default=None, help=_('Fixed IP Address to associate with.')) def do_floating_ip_associate(cs, args): """DEPRECATED Associate a floating IP address to a server.""" emit_fixed_floating_deprecation_warning('floating-ip-associate') _associate_floating_ip(cs, args) def _associate_floating_ip(cs, args): server = _find_server(cs, args.server) server.add_floating_ip(args.address, args.fixed_address) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('address', metavar='
', help=_('IP Address.')) def do_floating_ip_disassociate(cs, args): """DEPRECATED Disassociate a floating IP address from a server.""" emit_fixed_floating_deprecation_warning('floating-ip-disassociate') _disassociate_floating_ip(cs, args) def _disassociate_floating_ip(cs, args): server = _find_server(cs, args.server) server.remove_floating_ip(args.address) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'secgroup', metavar='', help=_('Name or ID of Security Group.')) def do_add_secgroup(cs, args): """Add a Security Group to a server.""" server = _find_server(cs, args.server) server.add_security_group(args.secgroup) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'secgroup', metavar='', help=_('Name of Security Group.')) def do_remove_secgroup(cs, args): """Remove a Security Group from a server.""" server = _find_server(cs, args.server) server.remove_security_group(args.secgroup) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_list_secgroup(cs, args): """List Security Group(s) of a server.""" server = _find_server(cs, args.server) groups = server.list_security_group() _print_secgroups(groups) def _print_secgroups(secgroups): utils.print_list(secgroups, ['Id', 'Name', 'Description']) @api_versions.wraps("2.0", "2.1") def _keypair_create(cs, args, name, pub_key): return cs.keypairs.create(name, pub_key) @api_versions.wraps("2.2", "2.9") def _keypair_create(cs, args, name, pub_key): return cs.keypairs.create(name, pub_key, key_type=args.key_type) @api_versions.wraps("2.10") def _keypair_create(cs, args, name, pub_key): return cs.keypairs.create(name, pub_key, key_type=args.key_type, user_id=args.user) @utils.arg('name', metavar='', help=_('Name of key.')) @utils.arg( '--pub-key', metavar='', default=None, help=_('Path to a public ssh key.')) @utils.arg( '--key-type', metavar='', default='ssh', help=_('Keypair type. Can be ssh or x509.'), start_version="2.2") @utils.arg( '--user', metavar='', default=None, help=_('ID of user to whom to add key-pair (Admin only).'), start_version="2.10") def do_keypair_add(cs, args): """Create a new key pair for use with servers.""" name = args.name pub_key = args.pub_key if pub_key: if pub_key == '-': pub_key = sys.stdin.read() else: try: with open(os.path.expanduser(pub_key)) as f: pub_key = f.read() except IOError as e: raise exceptions.CommandError( _("Can't open or read '%(key)s': %(exc)s") % {'key': pub_key, 'exc': e} ) keypair = _keypair_create(cs, args, name, pub_key) if not pub_key: private_key = keypair.private_key print(private_key) @api_versions.wraps("2.0", "2.9") @utils.arg('name', metavar='', help=_('Keypair name to delete.')) def do_keypair_delete(cs, args): """Delete keypair given by its name.""" name = _find_keypair(cs, args.name) cs.keypairs.delete(name) @api_versions.wraps("2.10") @utils.arg('name', metavar='', help=_('Keypair name to delete.')) @utils.arg( '--user', metavar='', default=None, help=_('ID of key-pair owner (Admin only).')) def do_keypair_delete(cs, args): """Delete keypair given by its name.""" cs.keypairs.delete(args.name, args.user) @api_versions.wraps("2.0", "2.1") def _get_keypairs_list_columns(cs, args): return ['Name', 'Fingerprint'] @api_versions.wraps("2.2") def _get_keypairs_list_columns(cs, args): return ['Name', 'Type', 'Fingerprint'] @api_versions.wraps("2.0", "2.9") def do_keypair_list(cs, args): """Print a list of keypairs for a user""" keypairs = cs.keypairs.list() columns = _get_keypairs_list_columns(cs, args) utils.print_list(keypairs, columns) @api_versions.wraps("2.10", "2.34") @utils.arg( '--user', metavar='', default=None, help=_('List key-pairs of specified user ID (Admin only).')) def do_keypair_list(cs, args): """Print a list of keypairs for a user""" keypairs = cs.keypairs.list(args.user) columns = _get_keypairs_list_columns(cs, args) utils.print_list(keypairs, columns) @api_versions.wraps("2.35") @utils.arg( '--user', metavar='', default=None, help=_('List key-pairs of specified user ID (Admin only).')) @utils.arg( '--marker', dest='marker', metavar='', default=None, help=_('The last keypair of the previous page; displays list of keypairs ' 'after "marker".')) @utils.arg( '--limit', dest='limit', metavar='', type=int, default=None, help=_("Maximum number of keypairs to display. If limit is bigger than " "'CONF.api.max_limit' option of Nova API, limit " "'CONF.api.max_limit' will be used instead.")) def do_keypair_list(cs, args): """Print a list of keypairs for a user""" keypairs = cs.keypairs.list(args.user, args.marker, args.limit) columns = _get_keypairs_list_columns(cs, args) utils.print_list(keypairs, columns) def _print_keypair(keypair): kp = keypair.to_dict() pk = kp.pop('public_key') utils.print_dict(kp) print(_("Public key: %s") % pk) @api_versions.wraps("2.0", "2.9") @utils.arg( 'keypair', metavar='', help=_("Name of keypair.")) def do_keypair_show(cs, args): """Show details about the given keypair.""" keypair = _find_keypair(cs, args.keypair) _print_keypair(keypair) @api_versions.wraps("2.10") @utils.arg( 'keypair', metavar='', help=_("Name of keypair.")) @utils.arg( '--user', metavar='', default=None, help=_('ID of key-pair owner (Admin only).')) def do_keypair_show(cs, args): """Show details about the given keypair.""" keypair = cs.keypairs.get(args.keypair, args.user) _print_keypair(keypair) def _find_keypair(cs, keypair): """Get a keypair by name.""" return utils.find_resource(cs.keypairs, keypair) def _print_absolute_limits(limits): """Prints absolute limits.""" class Limit(object): def __init__(self, name, used, max, other): self.name = name self.used = used self.max = max self.other = other limit_map = { 'maxServerMeta': {'name': 'Server Meta', 'type': 'max'}, 'maxPersonality': {'name': 'Personality', 'type': 'max'}, 'maxPersonalitySize': {'name': 'Personality Size', 'type': 'max'}, 'maxImageMeta': {'name': 'ImageMeta', 'type': 'max'}, 'maxTotalKeypairs': {'name': 'Keypairs', 'type': 'max'}, 'totalCoresUsed': {'name': 'Cores', 'type': 'used'}, 'maxTotalCores': {'name': 'Cores', 'type': 'max'}, 'totalRAMUsed': {'name': 'RAM', 'type': 'used'}, 'maxTotalRAMSize': {'name': 'RAM', 'type': 'max'}, 'totalInstancesUsed': {'name': 'Instances', 'type': 'used'}, 'maxTotalInstances': {'name': 'Instances', 'type': 'max'}, 'totalFloatingIpsUsed': {'name': 'FloatingIps', 'type': 'used'}, 'maxTotalFloatingIps': {'name': 'FloatingIps', 'type': 'max'}, 'totalSecurityGroupsUsed': {'name': 'SecurityGroups', 'type': 'used'}, 'maxSecurityGroups': {'name': 'SecurityGroups', 'type': 'max'}, 'maxSecurityGroupRules': {'name': 'SecurityGroupRules', 'type': 'max'}, 'maxServerGroups': {'name': 'ServerGroups', 'type': 'max'}, 'totalServerGroupsUsed': {'name': 'ServerGroups', 'type': 'used'}, 'maxServerGroupMembers': {'name': 'ServerGroupMembers', 'type': 'max'}, } max = {} used = {} other = {} limit_names = [] columns = ['Name', 'Used', 'Max'] for l in limits: map = limit_map.get(l.name, {'name': l.name, 'type': 'other'}) name = map['name'] if map['type'] == 'max': max[name] = l.value elif map['type'] == 'used': used[name] = l.value else: other[name] = l.value columns.append('Other') if name not in limit_names: limit_names.append(name) limit_names.sort() limit_list = [] for name in limit_names: l = Limit(name, used.get(name, "-"), max.get(name, "-"), other.get(name, "-")) limit_list.append(l) utils.print_list(limit_list, columns) def _print_rate_limits(limits): """print rate limits.""" columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available'] utils.print_list(limits, columns) @utils.arg( '--tenant', # nova db searches by project_id dest='tenant', metavar='', nargs='?', help=_('Display information from single tenant (Admin only).')) @utils.arg( '--reserved', dest='reserved', action='store_true', default=False, help=_('Include reservations count.')) def do_limits(cs, args): """Print rate and absolute limits.""" limits = cs.limits.get(args.reserved, args.tenant) _print_rate_limits(limits.rate) _print_absolute_limits(limits.absolute) def _get_usage_marker(usage): marker = None if hasattr(usage, 'server_usages') and usage.server_usages: marker = usage.server_usages[-1]['instance_id'] return marker def _get_usage_list_marker(usage_list): marker = None if usage_list: marker = _get_usage_marker(usage_list[-1]) return marker def _merge_usage(usage, next_usage): usage.server_usages.extend(next_usage.server_usages) usage.total_hours += next_usage.total_hours usage.total_memory_mb_usage += next_usage.total_memory_mb_usage usage.total_vcpus_usage += next_usage.total_vcpus_usage usage.total_local_gb_usage += next_usage.total_local_gb_usage def _merge_usage_list(usages, next_usage_list): for next_usage in next_usage_list: if next_usage.tenant_id in usages: _merge_usage(usages[next_usage.tenant_id], next_usage) else: usages[next_usage.tenant_id] = next_usage @utils.arg( '--start', metavar='', help=_('Usage range start date ex 2012-01-20. (default: 4 weeks ago)'), default=None) @utils.arg( '--end', metavar='', help=_('Usage range end date, ex 2012-01-20. (default: tomorrow)'), default=None) def do_usage_list(cs, args): """List usage data for all tenants.""" dateformat = "%Y-%m-%d" rows = ["Tenant ID", "Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"] now = timeutils.utcnow() if args.start: start = datetime.datetime.strptime(args.start, dateformat) else: start = now - datetime.timedelta(weeks=4) if args.end: end = datetime.datetime.strptime(args.end, dateformat) else: end = now + datetime.timedelta(days=1) def simplify_usage(u): simplerows = [x.lower().replace(" ", "_") for x in rows] setattr(u, simplerows[0], u.tenant_id) setattr(u, simplerows[1], "%d" % len(u.server_usages)) setattr(u, simplerows[2], "%.2f" % u.total_memory_mb_usage) setattr(u, simplerows[3], "%.2f" % u.total_vcpus_usage) setattr(u, simplerows[4], "%.2f" % u.total_local_gb_usage) if cs.api_version < api_versions.APIVersion('2.40'): usage_list = cs.usage.list(start, end, detailed=True) else: # If the number of instances used to calculate the usage is greater # than CONF.api.max_limit, the usage will be split across multiple # requests and the responses will need to be merged back together. usages = collections.OrderedDict() usage_list = cs.usage.list(start, end, detailed=True) _merge_usage_list(usages, usage_list) marker = _get_usage_list_marker(usage_list) while marker: next_usage_list = cs.usage.list( start, end, detailed=True, marker=marker) marker = _get_usage_list_marker(next_usage_list) if marker: _merge_usage_list(usages, next_usage_list) usage_list = list(usages.values()) print(_("Usage from %(start)s to %(end)s:") % {'start': start.strftime(dateformat), 'end': end.strftime(dateformat)}) for usage in usage_list: simplify_usage(usage) utils.print_list(usage_list, rows) @utils.arg( '--start', metavar='', help=_('Usage range start date ex 2012-01-20. (default: 4 weeks ago)'), default=None) @utils.arg( '--end', metavar='', help=_('Usage range end date, ex 2012-01-20. (default: tomorrow)'), default=None) @utils.arg( '--tenant', metavar='', default=None, help=_('UUID of tenant to get usage for.')) def do_usage(cs, args): """Show usage data for a single tenant.""" dateformat = "%Y-%m-%d" rows = ["Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"] now = timeutils.utcnow() if args.start: start = datetime.datetime.strptime(args.start, dateformat) else: start = now - datetime.timedelta(weeks=4) if args.end: end = datetime.datetime.strptime(args.end, dateformat) else: end = now + datetime.timedelta(days=1) def simplify_usage(u): simplerows = [x.lower().replace(" ", "_") for x in rows] setattr(u, simplerows[0], "%d" % len(u.server_usages)) setattr(u, simplerows[1], "%.2f" % u.total_memory_mb_usage) setattr(u, simplerows[2], "%.2f" % u.total_vcpus_usage) setattr(u, simplerows[3], "%.2f" % u.total_local_gb_usage) if args.tenant: tenant_id = args.tenant else: if isinstance(cs.client, client.SessionClient): auth = cs.client.auth tenant_id = auth.get_auth_ref(cs.client.session).project_id else: tenant_id = cs.client.tenant_id if cs.api_version < api_versions.APIVersion('2.40'): usage = cs.usage.get(tenant_id, start, end) else: # If the number of instances used to calculate the usage is greater # than CONF.api.max_limit, the usage will be split across multiple # requests and the responses will need to be merged back together. usage = cs.usage.get(tenant_id, start, end) marker = _get_usage_marker(usage) while marker: next_usage = cs.usage.get(tenant_id, start, end, marker=marker) marker = _get_usage_marker(next_usage) if marker: _merge_usage(usage, next_usage) print(_("Usage from %(start)s to %(end)s:") % {'start': start.strftime(dateformat), 'end': end.strftime(dateformat)}) if getattr(usage, 'total_vcpus_usage', None): simplify_usage(usage) utils.print_list([usage], rows) else: print(_('None')) @utils.arg( 'pk_filename', metavar='', nargs='?', default='pk.pem', help=_('Filename for the private key. [Default: pk.pem]')) @utils.arg( 'cert_filename', metavar='', nargs='?', default='cert.pem', help=_('Filename for the X.509 certificate. [Default: cert.pem]')) def do_x509_create_cert(cs, args): """DEPRECATED Create x509 cert for a user in tenant.""" print(CERT_DEPRECATION_WARNING, file=sys.stderr) if os.path.exists(args.pk_filename): raise exceptions.CommandError(_("Unable to write privatekey - %s " "exists.") % args.pk_filename) if os.path.exists(args.cert_filename): raise exceptions.CommandError(_("Unable to write x509 cert - %s " "exists.") % args.cert_filename) certs = cs.certs.create() try: old_umask = os.umask(0o377) with open(args.pk_filename, 'w') as private_key: private_key.write(certs.private_key) print(_("Wrote private key to %s") % args.pk_filename) finally: os.umask(old_umask) with open(args.cert_filename, 'w') as cert: cert.write(certs.data) print(_("Wrote x509 certificate to %s") % args.cert_filename) @utils.arg( 'filename', metavar='', nargs='?', default='cacert.pem', help=_('Filename to write the x509 root cert.')) def do_x509_get_root_cert(cs, args): """DEPRECATED Fetch the x509 root cert.""" print(CERT_DEPRECATION_WARNING, file=sys.stderr) if os.path.exists(args.filename): raise exceptions.CommandError(_("Unable to write x509 root cert - \ %s exists.") % args.filename) with open(args.filename, 'w') as cert: cacert = cs.certs.get() cert.write(cacert.data) print(_("Wrote x509 root cert to %s") % args.filename) @utils.arg( '--hypervisor', metavar='', default=None, help=_('Type of hypervisor.')) def do_agent_list(cs, args): """List all builds.""" result = cs.agents.list(args.hypervisor) columns = ["Agent_id", "Hypervisor", "OS", "Architecture", "Version", 'Md5hash', 'Url'] utils.print_list(result, columns) @utils.arg('os', metavar='', help=_('Type of OS.')) @utils.arg( 'architecture', metavar='', help=_('Type of architecture.')) @utils.arg('version', metavar='', help=_('Version.')) @utils.arg('url', metavar='', help=_('URL.')) @utils.arg('md5hash', metavar='', help=_('MD5 hash.')) @utils.arg( 'hypervisor', metavar='', default='xen', help=_('Type of hypervisor.')) def do_agent_create(cs, args): """Create new agent build.""" result = cs.agents.create(args.os, args.architecture, args.version, args.url, args.md5hash, args.hypervisor) utils.print_dict(result.to_dict()) @utils.arg('id', metavar='', help=_('ID of the agent-build.')) def do_agent_delete(cs, args): """Delete existing agent build.""" cs.agents.delete(args.id) @utils.arg('id', metavar='', help=_('ID of the agent-build.')) @utils.arg('version', metavar='', help=_('Version.')) @utils.arg('url', metavar='', help=_('URL')) @utils.arg('md5hash', metavar='', help=_('MD5 hash.')) def do_agent_modify(cs, args): """Modify existing agent build.""" result = cs.agents.update(args.id, args.version, args.url, args.md5hash) utils.print_dict(result.to_dict()) def _find_aggregate(cs, aggregate): """Get an aggregate by name or ID.""" return utils.find_resource(cs.aggregates, aggregate) def do_aggregate_list(cs, args): """Print a list of all aggregates.""" aggregates = cs.aggregates.list() columns = ['Id', 'Name', 'Availability Zone'] if cs.api_version >= api_versions.APIVersion('2.41'): columns.append('UUID') utils.print_list(aggregates, columns) @utils.arg('name', metavar='', help=_('Name of aggregate.')) @utils.arg( 'availability_zone', metavar='', default=None, nargs='?', help=_('The availability zone of the aggregate (optional).')) def do_aggregate_create(cs, args): """Create a new aggregate with the specified details.""" aggregate = cs.aggregates.create(args.name, args.availability_zone) _print_aggregate_details(cs, aggregate) @utils.arg( 'aggregate', metavar='', help=_('Name or ID of aggregate to delete.')) def do_aggregate_delete(cs, args): """Delete the aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) cs.aggregates.delete(aggregate) print(_("Aggregate %s has been successfully deleted.") % aggregate.id) @utils.arg( 'aggregate', metavar='', help=_('Name or ID of aggregate to update.')) @utils.arg( '--name', dest='name', help=_('New name for aggregate.')) @utils.arg( '--availability-zone', metavar='', dest='availability_zone', help=_('New availability zone for aggregate.')) def do_aggregate_update(cs, args): """Update the aggregate's name and optionally availability zone.""" aggregate = _find_aggregate(cs, args.aggregate) updates = {} if args.name: updates["name"] = args.name if args.availability_zone: updates["availability_zone"] = args.availability_zone aggregate = cs.aggregates.update(aggregate.id, updates) print(_("Aggregate %s has been successfully updated.") % aggregate.id) _print_aggregate_details(cs, aggregate) @utils.arg( 'aggregate', metavar='', help=_('Name or ID of aggregate to update.')) @utils.arg( 'metadata', metavar='', nargs='+', action='append', default=[], help=_('Metadata to add/update to aggregate. ' 'Specify only the key to delete a metadata item.')) def do_aggregate_set_metadata(cs, args): """Update the metadata associated with the aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) metadata = _extract_metadata(args) currentmetadata = getattr(aggregate, 'metadata', {}) if set(metadata.items()) & set(currentmetadata.items()): raise exceptions.CommandError(_("metadata already exists")) for key, value in metadata.items(): if value is None and key not in currentmetadata: raise exceptions.CommandError(_("metadata key %s does not exist" " hence can not be deleted") % key) aggregate = cs.aggregates.set_metadata(aggregate.id, metadata) print(_("Metadata has been successfully updated for aggregate %s.") % aggregate.id) _print_aggregate_details(cs, aggregate) @utils.arg( 'aggregate', metavar='', help=_('Name or ID of aggregate.')) @utils.arg( 'host', metavar='', help=_('The host to add to the aggregate.')) def do_aggregate_add_host(cs, args): """Add the host to the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) aggregate = cs.aggregates.add_host(aggregate.id, args.host) print(_("Host %(host)s has been successfully added for aggregate " "%(aggregate_id)s ") % {'host': args.host, 'aggregate_id': aggregate.id}) _print_aggregate_details(cs, aggregate) @utils.arg( 'aggregate', metavar='', help=_('Name or ID of aggregate.')) @utils.arg( 'host', metavar='', help=_('The host to remove from the aggregate.')) def do_aggregate_remove_host(cs, args): """Remove the specified host from the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) aggregate = cs.aggregates.remove_host(aggregate.id, args.host) print(_("Host %(host)s has been successfully removed from aggregate " "%(aggregate_id)s ") % {'host': args.host, 'aggregate_id': aggregate.id}) _print_aggregate_details(cs, aggregate) @utils.arg( 'aggregate', metavar='', help=_('Name or ID of aggregate.')) def do_aggregate_show(cs, args): """Show details of the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) _print_aggregate_details(cs, aggregate) def _print_aggregate_details(cs, aggregate): columns = ['Id', 'Name', 'Availability Zone', 'Hosts', 'Metadata'] if cs.api_version >= api_versions.APIVersion('2.41'): columns.append('UUID') def parser_metadata(fields): return utils.pretty_choice_dict(getattr(fields, 'metadata', {}) or {}) def parser_hosts(fields): return utils.pretty_choice_list(getattr(fields, 'hosts', [])) formatters = { 'Metadata': parser_metadata, 'Hosts': parser_hosts, } utils.print_list([aggregate], columns, formatters=formatters) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'host', metavar='', default=None, nargs='?', help=_('Destination host name.')) @utils.arg( '--block-migrate', action='store_true', dest='block_migrate', default=False, help=_('True in case of block_migration. (Default=False:live_migration)'), start_version="2.0", end_version="2.24") @utils.arg( '--block-migrate', action='store_true', dest='block_migrate', default="auto", help=_('True in case of block_migration. (Default=auto:live_migration)'), start_version="2.25") @utils.arg( '--disk-over-commit', action='store_true', dest='disk_over_commit', default=False, help=_('Allow overcommit. (Default=False)'), start_version="2.0", end_version="2.24") @utils.arg( '--force', dest='force', action='store_true', default=False, help=_('Force to not verify the scheduler if a host is provided.'), start_version='2.30') def do_live_migration(cs, args): """Migrate running server to a new machine.""" update_kwargs = {} if 'disk_over_commit' in args: update_kwargs['disk_over_commit'] = args.disk_over_commit if 'force' in args and args.force: update_kwargs['force'] = args.force _find_server(cs, args.server).live_migrate(args.host, args.block_migrate, **update_kwargs) @api_versions.wraps("2.22") @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('migration', metavar='', help=_('ID of migration.')) def do_live_migration_force_complete(cs, args): """Force on-going live migration to complete.""" server = _find_server(cs, args.server) cs.server_migrations.live_migrate_force_complete(server, args.migration) @api_versions.wraps("2.23") @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_server_migration_list(cs, args): """Get the migrations list of specified server.""" server = _find_server(cs, args.server) migrations = cs.server_migrations.list(server) fields = ['Id', 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute', 'Dest Host', 'Status', 'Server UUID', 'Created At', 'Updated At'] format_name = ["Total Memory Bytes", "Processed Memory Bytes", "Remaining Memory Bytes", "Total Disk Bytes", "Processed Disk Bytes", "Remaining Disk Bytes"] format_key = ["memory_total_bytes", "memory_processed_bytes", "memory_remaining_bytes", "disk_total_bytes", "disk_processed_bytes", "disk_remaining_bytes"] formatters = map(lambda field: utils.make_field_formatter(field)[1], format_key) formatters = dict(zip(format_name, formatters)) utils.print_list(migrations, fields + format_name, formatters) @api_versions.wraps("2.23") @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('migration', metavar='', help=_('ID of migration.')) def do_server_migration_show(cs, args): """Get the migration of specified server.""" server = _find_server(cs, args.server) migration = cs.server_migrations.get(server, args.migration) utils.print_dict(migration.to_dict()) @api_versions.wraps("2.24") @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('migration', metavar='', help=_('ID of migration.')) def do_live_migration_abort(cs, args): """Abort an on-going live migration.""" server = _find_server(cs, args.server) cs.server_migrations.live_migration_abort(server, args.migration) @utils.arg( '--all-tenants', action='store_const', const=1, default=0, help=_('Reset state server(s) in another tenant by name (Admin only).')) @utils.arg( 'server', metavar='', nargs='+', help=_('Name or ID of server(s).')) @utils.arg( '--active', action='store_const', dest='state', default='error', const='active', help=_('Request the server be reset to "active" state instead ' 'of "error" state (the default).')) def do_reset_state(cs, args): """Reset the state of a server.""" failure_flag = False find_args = {'all_tenants': args.all_tenants} for server in args.server: try: _find_server(cs, server, **find_args).reset_state(args.state) msg = "Reset state for server %s succeeded; new state is %s" print(msg % (server, args.state)) except Exception as e: failure_flag = True msg = "Reset state for server %s failed: %s" % (server, e) print(msg) if failure_flag: msg = "Unable to reset the state for the specified server(s)." raise exceptions.CommandError(msg) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_reset_network(cs, args): """Reset network of a server.""" _find_server(cs, args.server).reset_network() @utils.arg( '--host', metavar='', default=None, help=_('Name of host.')) @utils.arg( '--binary', metavar='', default=None, help=_('Service binary.')) def do_service_list(cs, args): """Show a list of all running services. Filter by host & binary.""" result = cs.services.list(host=args.host, binary=args.binary) columns = ["Id", "Binary", "Host", "Zone", "Status", "State", "Updated_at", "Disabled Reason"] if cs.api_version >= api_versions.APIVersion('2.11'): columns.append("Forced down") utils.print_list(result, columns) # Before microversion 2.53, the service was identified using it's host/binary # values. @api_versions.wraps('2.0', '2.52') @utils.arg('host', metavar='', help=_('Name of host.')) # TODO(mriedem): Eventually just hard-code the binary to "nova-compute". @utils.arg('binary', metavar='', help=_('Service binary. The only ' 'meaningful binary is "nova-compute". (Deprecated)'), default='nova-compute', nargs='?') def do_service_enable(cs, args): """Enable the service.""" result = cs.services.enable(args.host, args.binary) utils.print_list([result], ['Host', 'Binary', 'Status']) # Starting in microversion 2.53, the service is identified by UUID ID. @api_versions.wraps('2.53') @utils.arg('id', metavar='', help=_('ID of the service as a UUID.')) def do_service_enable(cs, args): """Enable the service.""" result = cs.services.enable(args.id) utils.print_list([result], ['ID', 'Host', 'Binary', 'Status']) # Before microversion 2.53, the service was identified using it's host/binary # values. @api_versions.wraps('2.0', '2.52') @utils.arg('host', metavar='', help=_('Name of host.')) # TODO(mriedem): Eventually just hard-code the binary to "nova-compute". @utils.arg('binary', metavar='', help=_('Service binary. The only ' 'meaningful binary is "nova-compute". (Deprecated)'), default='nova-compute', nargs='?') @utils.arg( '--reason', metavar='', help=_('Reason for disabling service.')) def do_service_disable(cs, args): """Disable the service.""" if args.reason: result = cs.services.disable_log_reason(args.host, args.binary, args.reason) utils.print_list([result], ['Host', 'Binary', 'Status', 'Disabled Reason']) else: result = cs.services.disable(args.host, args.binary) utils.print_list([result], ['Host', 'Binary', 'Status']) # Starting in microversion 2.53, the service is identified by UUID ID. @api_versions.wraps('2.53') @utils.arg('id', metavar='', help=_('ID of the service as a UUID.')) @utils.arg( '--reason', metavar='', help=_('Reason for disabling the service.')) def do_service_disable(cs, args): """Disable the service.""" if args.reason: result = cs.services.disable_log_reason(args.id, args.reason) utils.print_list( [result], ['ID', 'Host', 'Binary', 'Status', 'Disabled Reason']) else: result = cs.services.disable(args.id) utils.print_list([result], ['ID', 'Host', 'Binary', 'Status']) # Before microversion 2.53, the service was identified using it's host/binary # values. @api_versions.wraps("2.11", "2.52") @utils.arg('host', metavar='', help=_('Name of host.')) # TODO(mriedem): Eventually just hard-code the binary to "nova-compute". @utils.arg('binary', metavar='', help=_('Service binary. The only ' 'meaningful binary is "nova-compute". (Deprecated)'), default='nova-compute', nargs='?') @utils.arg( '--unset', dest='force_down', help=_("Unset the force state down of service."), action='store_false', default=True) def do_service_force_down(cs, args): """Force service to down.""" result = cs.services.force_down(args.host, args.binary, args.force_down) utils.print_list([result], ['Host', 'Binary', 'Forced down']) # Starting in microversion 2.53, the service is identified by UUID ID. @api_versions.wraps('2.53') @utils.arg('id', metavar='', help=_('ID of the service as a UUID.')) @utils.arg( '--unset', dest='force_down', help=_("Unset the forced_down state of the service."), action='store_false', default=True) def do_service_force_down(cs, args): """Force service to down.""" result = cs.services.force_down(args.id, args.force_down) utils.print_list([result], ['ID', 'Host', 'Binary', 'Forced down']) # Before microversion 2.53, the service was identified using it's host/binary # values. @api_versions.wraps('2.0', '2.52') @utils.arg('id', metavar='', help=_('ID of service as an integer. Note that this may not ' 'uniquely identify a service in a multi-cell deployment.')) def do_service_delete(cs, args): """Delete the service by integer ID.""" cs.services.delete(args.id) # Starting in microversion 2.53, the service is identified by UUID ID. @api_versions.wraps('2.53') @utils.arg('id', metavar='', help=_('ID of service as a UUID.')) def do_service_delete(cs, args): """Delete the service by UUID ID.""" cs.services.delete(args.id) @utils.arg('host', metavar='', help=_('Name of host.')) def do_host_describe(cs, args): """DEPRECATED Describe a specific host.""" emit_hosts_deprecation_warning('host-describe', 'hypervisor-show') result = cs.hosts.get(args.host) columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"] utils.print_list(result, columns) @utils.arg( '--zone', metavar='', default=None, help=_('Filters the list, returning only those hosts in the availability ' 'zone .')) def do_host_list(cs, args): """DEPRECATED List all hosts by service.""" emit_hosts_deprecation_warning('host-list', 'hypervisor-list') columns = ["host_name", "service", "zone"] result = cs.hosts.list(args.zone) utils.print_list(result, columns) @utils.arg('host', metavar='', help=_('Name of host.')) @utils.arg( '--status', metavar='', default=None, dest='status', help=_('Either enable or disable a host.')) @utils.arg( '--maintenance', metavar='', default=None, dest='maintenance', help=_('Either put or resume host to/from maintenance.')) def do_host_update(cs, args): """DEPRECATED Update host settings.""" if args.status == 'enable': emit_hosts_deprecation_warning('host-update', 'service-enable') elif args.status == 'disable': emit_hosts_deprecation_warning('host-update', 'service-disable') else: emit_hosts_deprecation_warning('host-update') updates = {} columns = ["HOST"] if args.status: updates['status'] = args.status columns.append("status") if args.maintenance: updates['maintenance_mode'] = args.maintenance columns.append("maintenance_mode") result = cs.hosts.update(args.host, updates) utils.print_list([result], columns) @utils.arg('host', metavar='', help=_('Name of host.')) @utils.arg( '--action', metavar='', dest='action', choices=['startup', 'shutdown', 'reboot'], help=_('A power action: startup, reboot, or shutdown.')) def do_host_action(cs, args): """DEPRECATED Perform a power action on a host.""" emit_hosts_deprecation_warning('host-action') result = cs.hosts.host_action(args.host, args.action) utils.print_list([result], ['HOST', 'power_action']) def _find_hypervisor(cs, hypervisor): """Get a hypervisor by name or ID.""" return utils.find_resource(cs.hypervisors, hypervisor) def _do_hypervisor_list(cs, matching=None, limit=None, marker=None): columns = ['ID', 'Hypervisor hostname', 'State', 'Status'] if matching: utils.print_list(cs.hypervisors.search(matching), columns) else: params = {} if limit is not None: params['limit'] = limit if marker is not None: params['marker'] = marker # Since we're not outputting detail data, choose # detailed=False for server-side efficiency utils.print_list(cs.hypervisors.list(False, **params), columns) @api_versions.wraps("2.0", "2.32") @utils.arg( '--matching', metavar='', default=None, help=_('List hypervisors matching the given (or pattern).')) def do_hypervisor_list(cs, args): """List hypervisors.""" _do_hypervisor_list(cs, matching=args.matching) @api_versions.wraps("2.33") @utils.arg( '--matching', metavar='', default=None, help=_('List hypervisors matching the given (or pattern). ' 'If matching is used limit and marker options will be ignored.')) @utils.arg( '--marker', dest='marker', metavar='', default=None, help=_('The last hypervisor of the previous page; displays list of ' 'hypervisors after "marker".')) @utils.arg( '--limit', dest='limit', metavar='', type=int, default=None, help=_("Maximum number of hypervisors to display. If limit is bigger than " "'CONF.api.max_limit' option of Nova API, limit " "'CONF.api.max_limit' will be used instead.")) def do_hypervisor_list(cs, args): """List hypervisors.""" _do_hypervisor_list( cs, matching=args.matching, limit=args.limit, marker=args.marker) @utils.arg( 'hostname', metavar='', help=_('The hypervisor hostname (or pattern) to search for.')) def do_hypervisor_servers(cs, args): """List servers belonging to specific hypervisors.""" hypers = cs.hypervisors.search(args.hostname, servers=True) class InstanceOnHyper(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) # Massage the result into a list to be displayed instances = [] for hyper in hypers: hyper_host = hyper.hypervisor_hostname hyper_id = hyper.id if hasattr(hyper, 'servers'): instances.extend([InstanceOnHyper(id=serv['uuid'], name=serv['name'], hypervisor_hostname=hyper_host, hypervisor_id=hyper_id) for serv in hyper.servers]) # Output the data utils.print_list(instances, ['ID', 'Name', 'Hypervisor ID', 'Hypervisor Hostname']) @utils.arg( 'hypervisor', metavar='', help=_('Name or ID of the hypervisor. Starting with microversion 2.53 ' 'the ID must be a UUID.')) @utils.arg( '--wrap', dest='wrap', metavar='', default=40, help=_('Wrap the output to a specified length. ' 'Default is 40 or 0 to disable')) def do_hypervisor_show(cs, args): """Display the details of the specified hypervisor.""" hyper = _find_hypervisor(cs, args.hypervisor) utils.print_dict(utils.flatten_dict(hyper.to_dict()), wrap=int(args.wrap)) @utils.arg( 'hypervisor', metavar='', help=_('Name or ID of the hypervisor. Starting with microversion 2.53 ' 'the ID must be a UUID.')) def do_hypervisor_uptime(cs, args): """Display the uptime of the specified hypervisor.""" hyper = _find_hypervisor(cs, args.hypervisor) hyper = cs.hypervisors.uptime(hyper) # Output the uptime information utils.print_dict(hyper.to_dict()) def do_hypervisor_stats(cs, args): """Get hypervisor statistics over all compute nodes.""" stats = cs.hypervisor_stats.statistics() utils.print_dict(stats.to_dict()) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( '--port', dest='port', action='store', type=int, default=22, help=_('Optional flag to indicate which port to use for ssh. ' '(Default=22)')) @utils.arg( '--private', dest='private', action='store_true', default=False, help=argparse.SUPPRESS) @utils.arg( '--address-type', dest='address_type', action='store', type=str, default='floating', help=_('Optional flag to indicate which IP type to use. Possible values ' 'includes fixed and floating (the Default).')) @utils.arg( '--network', metavar='', help=_('Network to use for the ssh.'), default=None) @utils.arg( '--ipv6', dest='ipv6', action='store_true', default=False, help=_('Optional flag to indicate whether to use an IPv6 address ' 'attached to a server. (Defaults to IPv4 address)')) @utils.arg( '--login', metavar='', help=_('Login to use.'), default="root") @utils.arg( '-i', '--identity', dest='identity', help=_('Private key file, same as the -i option to the ssh command.'), default='') @utils.arg( '--extra-opts', dest='extra', help=_('Extra options to pass to ssh. see: man ssh.'), default='') def do_ssh(cs, args): """SSH into a server.""" if '@' in args.server: user, server = args.server.split('@', 1) args.login = user args.server = server addresses = _find_server(cs, args.server).addresses address_type = "fixed" if args.private else args.address_type version = 6 if args.ipv6 else 4 pretty_version = 'IPv%d' % version # Select the network to use. if args.network: network_addresses = addresses.get(args.network) if not network_addresses: msg = _("Server '%(server)s' is not attached to network " "'%(network)s'") raise exceptions.ResourceNotFound( msg % {'server': args.server, 'network': args.network}) else: if len(addresses) > 1: msg = _("Server '%(server)s' is attached to more than one network." " Please pick the network to use.") raise exceptions.CommandError(msg % {'server': args.server}) elif not addresses: msg = _("Server '%(server)s' is not attached to any network.") raise exceptions.CommandError(msg % {'server': args.server}) else: network_addresses = list(six.itervalues(addresses))[0] # Select the address in the selected network. # If the extension is not present, we assume the address to be floating. match = lambda addr: all(( addr.get('version') == version, addr.get('OS-EXT-IPS:type', 'floating') == address_type)) matching_addresses = [address.get('addr') for address in network_addresses if match(address)] if not any(matching_addresses): msg = _("No address that would match network '%(network)s'" " and type '%(address_type)s' of version %(pretty_version)s " "has been found for server '%(server)s'.") raise exceptions.ResourceNotFound(msg % { 'network': args.network, 'address_type': address_type, 'pretty_version': pretty_version, 'server': args.server}) elif len(matching_addresses) > 1: msg = _("More than one %(pretty_version)s %(address_type)s address " "found.") raise exceptions.CommandError(msg % {'pretty_version': pretty_version, 'address_type': address_type}) else: ip_address = matching_addresses[0] identity = '-i %s' % args.identity if len(args.identity) else '' cmd = "ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity, args.login, ip_address, args.extra) logger.debug("Executing cmd '%s'", cmd) os.system(cmd) # NOTE(mriedem): In the 2.50 microversion, the os-quota-class-sets API # will return the server_groups and server_group_members, but no longer # return floating_ips, fixed_ips, security_groups or security_group_members # as those are deprecated as networking service proxies and/or because # nova-network is deprecated. Similar to the 2.36 microversion. _quota_resources = ['instances', 'cores', 'ram', 'floating_ips', 'fixed_ips', 'metadata_items', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', 'key_pairs', 'security_groups', 'security_group_rules', 'server_groups', 'server_group_members'] def _quota_show(quotas): class FormattedQuota(object): def __init__(self, key, value): setattr(self, 'quota', key) setattr(self, 'limit', value) quota_list = [] for resource in _quota_resources: try: quota = FormattedQuota(resource, getattr(quotas, resource)) quota_list.append(quota) except AttributeError: pass columns = ['Quota', 'Limit'] utils.print_list(quota_list, columns) def _quota_update(manager, identifier, args): updates = {} for resource in _quota_resources: val = getattr(args, resource, None) if val is not None: updates[resource] = val if updates: # default value of force is None to make sure this client # will be compatible with old nova server force_update = getattr(args, 'force', None) user_id = getattr(args, 'user', None) if isinstance(manager, quotas.QuotaSetManager): manager.update(identifier, force=force_update, user_id=user_id, **updates) else: manager.update(identifier, **updates) @utils.arg( '--tenant', metavar='', default=None, help=_('ID of tenant to list the quotas for.')) @utils.arg( '--user', metavar='', default=None, help=_('ID of user to list the quotas for.')) @utils.arg( '--detail', action='store_true', default=False, help=_('Show detailed info (limit, reserved, in-use).')) def do_quota_show(cs, args): """List the quotas for a tenant/user.""" if args.tenant: project_id = args.tenant elif isinstance(cs.client, client.SessionClient): auth = cs.client.auth project_id = auth.get_auth_ref(cs.client.session).project_id else: project_id = cs.client.tenant_id _quota_show(cs.quotas.get(project_id, user_id=args.user, detail=args.detail)) @utils.arg( '--tenant', metavar='', default=None, help=_('ID of tenant to list the default quotas for.')) def do_quota_defaults(cs, args): """List the default quotas for a tenant.""" if args.tenant: project_id = args.tenant elif isinstance(cs.client, client.SessionClient): auth = cs.client.auth project_id = auth.get_auth_ref(cs.client.session).project_id else: project_id = cs.client.tenant_id _quota_show(cs.quotas.defaults(project_id)) @api_versions.wraps("2.0", "2.35") @utils.arg( 'tenant', metavar='', help=_('ID of tenant to set the quotas for.')) @utils.arg( '--user', metavar='', default=None, help=_('ID of user to set the quotas for.')) @utils.arg( '--instances', metavar='', type=int, default=None, help=_('New value for the "instances" quota.')) @utils.arg( '--cores', metavar='', type=int, default=None, help=_('New value for the "cores" quota.')) @utils.arg( '--ram', metavar='', type=int, default=None, help=_('New value for the "ram" quota.')) @utils.arg( '--floating-ips', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "floating-ips" quota.')) @utils.arg( '--fixed-ips', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "fixed-ips" quota.')) @utils.arg( '--metadata-items', metavar='', type=int, default=None, help=_('New value for the "metadata-items" quota.')) @utils.arg( '--injected-files', metavar='', type=int, default=None, help=_('New value for the "injected-files" quota.')) @utils.arg( '--injected-file-content-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-content-bytes" quota.')) @utils.arg( '--injected-file-path-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-path-bytes" quota.')) @utils.arg( '--key-pairs', metavar='', type=int, default=None, help=_('New value for the "key-pairs" quota.')) @utils.arg( '--security-groups', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "security-groups" quota.')) @utils.arg( '--security-group-rules', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "security-group-rules" quota.')) @utils.arg( '--server-groups', metavar='', type=int, default=None, help=_('New value for the "server-groups" quota.')) @utils.arg( '--server-group-members', metavar='', type=int, default=None, help=_('New value for the "server-group-members" quota.')) @utils.arg( '--force', dest='force', action="store_true", default=None, help=_('Whether force update the quota even if the already used and ' 'reserved exceeds the new quota.')) def do_quota_update(cs, args): """Update the quotas for a tenant/user.""" _quota_update(cs.quotas, args.tenant, args) # 2.36 does not support updating quota for floating IPs, fixed IPs, security # groups or security group rules. @api_versions.wraps("2.36") @utils.arg( 'tenant', metavar='', help=_('ID of tenant to set the quotas for.')) @utils.arg( '--user', metavar='', default=None, help=_('ID of user to set the quotas for.')) @utils.arg( '--instances', metavar='', type=int, default=None, help=_('New value for the "instances" quota.')) @utils.arg( '--cores', metavar='', type=int, default=None, help=_('New value for the "cores" quota.')) @utils.arg( '--ram', metavar='', type=int, default=None, help=_('New value for the "ram" quota.')) @utils.arg( '--metadata-items', metavar='', type=int, default=None, help=_('New value for the "metadata-items" quota.')) @utils.arg( '--injected-files', metavar='', type=int, default=None, help=_('New value for the "injected-files" quota.')) @utils.arg( '--injected-file-content-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-content-bytes" quota.')) @utils.arg( '--injected-file-path-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-path-bytes" quota.')) @utils.arg( '--key-pairs', metavar='', type=int, default=None, help=_('New value for the "key-pairs" quota.')) @utils.arg( '--server-groups', metavar='', type=int, default=None, help=_('New value for the "server-groups" quota.')) @utils.arg( '--server-group-members', metavar='', type=int, default=None, help=_('New value for the "server-group-members" quota.')) @utils.arg( '--force', dest='force', action="store_true", default=None, help=_('Whether force update the quota even if the already used and ' 'reserved exceeds the new quota.')) def do_quota_update(cs, args): """Update the quotas for a tenant/user.""" _quota_update(cs.quotas, args.tenant, args) @utils.arg( '--tenant', metavar='', required=True, help=_('ID of tenant to delete quota for.')) @utils.arg( '--user', metavar='', help=_('ID of user to delete quota for.')) def do_quota_delete(cs, args): """Delete quota for a tenant/user so their quota will Revert back to default. """ cs.quotas.delete(args.tenant, user_id=args.user) @utils.arg( 'class_name', metavar='', help=_('Name of quota class to list the quotas for.')) def do_quota_class_show(cs, args): """List the quotas for a quota class.""" _quota_show(cs.quota_classes.get(args.class_name)) @api_versions.wraps("2.0", "2.49") @utils.arg( 'class_name', metavar='', help=_('Name of quota class to set the quotas for.')) @utils.arg( '--instances', metavar='', type=int, default=None, help=_('New value for the "instances" quota.')) @utils.arg( '--cores', metavar='', type=int, default=None, help=_('New value for the "cores" quota.')) @utils.arg( '--ram', metavar='', type=int, default=None, help=_('New value for the "ram" quota.')) @utils.arg( '--floating-ips', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "floating-ips" quota.')) @utils.arg( '--fixed-ips', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "fixed-ips" quota.')) @utils.arg( '--metadata-items', metavar='', type=int, default=None, help=_('New value for the "metadata-items" quota.')) @utils.arg( '--injected-files', metavar='', type=int, default=None, help=_('New value for the "injected-files" quota.')) @utils.arg( '--injected-file-content-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-content-bytes" quota.')) @utils.arg( '--injected-file-path-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-path-bytes" quota.')) @utils.arg( '--key-pairs', metavar='', type=int, default=None, help=_('New value for the "key-pairs" quota.')) @utils.arg( '--security-groups', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "security-groups" quota.')) @utils.arg( '--security-group-rules', metavar='', type=int, default=None, action=shell.DeprecatedAction, help=_('New value for the "security-group-rules" quota.')) @utils.arg( '--server-groups', metavar='', type=int, default=None, help=_('New value for the "server-groups" quota.')) @utils.arg( '--server-group-members', metavar='', type=int, default=None, help=_('New value for the "server-group-members" quota.')) def do_quota_class_update(cs, args): """Update the quotas for a quota class.""" _quota_update(cs.quota_classes, args.class_name, args) # 2.50 does not support updating quota class values for floating IPs, # fixed IPs, security groups or security group rules. @api_versions.wraps("2.50") @utils.arg( 'class_name', metavar='', help=_('Name of quota class to set the quotas for.')) @utils.arg( '--instances', metavar='', type=int, default=None, help=_('New value for the "instances" quota.')) @utils.arg( '--cores', metavar='', type=int, default=None, help=_('New value for the "cores" quota.')) @utils.arg( '--ram', metavar='', type=int, default=None, help=_('New value for the "ram" quota.')) @utils.arg( '--metadata-items', metavar='', type=int, default=None, help=_('New value for the "metadata-items" quota.')) @utils.arg( '--injected-files', metavar='', type=int, default=None, help=_('New value for the "injected-files" quota.')) @utils.arg( '--injected-file-content-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-content-bytes" quota.')) @utils.arg( '--injected-file-path-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-path-bytes" quota.')) @utils.arg( '--key-pairs', metavar='', type=int, default=None, help=_('New value for the "key-pairs" quota.')) @utils.arg( '--server-groups', metavar='', type=int, default=None, help=_('New value for the "server-groups" quota.')) @utils.arg( '--server-group-members', metavar='', type=int, default=None, help=_('New value for the "server-group-members" quota.')) def do_quota_class_update(cs, args): """Update the quotas for a quota class.""" _quota_update(cs.quota_classes, args.class_name, args) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( 'host', metavar='', nargs='?', help=_("Name or ID of the target host. " "If no host is specified, the scheduler will choose one.")) @utils.arg( '--password', dest='password', metavar='', help=_("Set the provided admin password on the evacuated server. Not" " applicable if the server is on shared storage.")) @utils.arg( '--on-shared-storage', dest='on_shared_storage', action="store_true", default=False, help=_('Specifies whether server files are located on shared storage.'), start_version='2.0', end_version='2.13') @utils.arg( '--force', dest='force', action='store_true', default=False, help=_('Force to not verify the scheduler if a host is provided.'), start_version='2.29') def do_evacuate(cs, args): """Evacuate server from failed host.""" server = _find_server(cs, args.server) on_shared_storage = getattr(args, 'on_shared_storage', None) force = getattr(args, 'force', None) update_kwargs = {} if on_shared_storage is not None: update_kwargs['on_shared_storage'] = on_shared_storage if force: update_kwargs['force'] = force res = server.evacuate(host=args.host, password=args.password, **update_kwargs)[1] if isinstance(res, dict): utils.print_dict(res) def _print_interfaces(interfaces): columns = ['Port State', 'Port ID', 'Net ID', 'IP addresses', 'MAC Addr'] class FormattedInterface(object): def __init__(self, interface): for col in columns: key = col.lower().replace(" ", "_") if hasattr(interface, key): setattr(self, key, getattr(interface, key)) self.ip_addresses = ",".join([fip['ip_address'] for fip in interface.fixed_ips]) utils.print_list([FormattedInterface(i) for i in interfaces], columns) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_interface_list(cs, args): """List interfaces attached to a server.""" server = _find_server(cs, args.server) res = server.interface_list() if isinstance(res, list): _print_interfaces(res) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg( '--port-id', metavar='', help=_('Port ID.'), dest="port_id") @utils.arg( '--net-id', metavar='', help=_('Network ID'), default=None, dest="net_id") @utils.arg( '--fixed-ip', metavar='', help=_('Requested fixed IP.'), default=None, dest="fixed_ip") @utils.arg( '--tag', metavar='', default=None, dest="tag", help=_('Tag for the attached interface.'), start_version="2.49") def do_interface_attach(cs, args): """Attach a network interface to a server.""" server = _find_server(cs, args.server) update_kwargs = {} if 'tag' in args and args.tag: update_kwargs['tag'] = args.tag res = server.interface_attach(args.port_id, args.net_id, args.fixed_ip, **update_kwargs) if isinstance(res, dict): utils.print_dict(res) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('port_id', metavar='', help=_('Port ID.')) def do_interface_detach(cs, args): """Detach a network interface from a server.""" server = _find_server(cs, args.server) res = server.interface_detach(args.port_id) if isinstance(res, dict): utils.print_dict(res) @api_versions.wraps("2.17") @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_trigger_crash_dump(cs, args): """Trigger crash dump in an instance.""" server = _find_server(cs, args.server) server.trigger_crash_dump() def _treeizeAvailabilityZone(zone): """Build a tree view for availability zones.""" AvailabilityZone = availability_zones.AvailabilityZone az = AvailabilityZone(zone.manager, zone.to_dict(), zone._loaded) result = [] # Zone tree view item az.zoneName = zone.zoneName az.zoneState = ('available' if zone.zoneState['available'] else 'not available') az.set_info('zoneName', az.zoneName) az.set_info('zoneState', az.zoneState) result.append(az) if zone.hosts is not None: zone_hosts = sorted(zone.hosts.items(), key=lambda x: x[0]) for (host, services) in zone_hosts: # Host tree view item az = AvailabilityZone(zone.manager, zone.to_dict(), zone._loaded) az.zoneName = '|- %s' % host az.zoneState = '' az.set_info('zoneName', az.zoneName) az.set_info('zoneState', az.zoneState) result.append(az) for (svc, state) in services.items(): # Service tree view item az = AvailabilityZone(zone.manager, zone.to_dict(), zone._loaded) az.zoneName = '| |- %s' % svc az.zoneState = '%s %s %s' % ( 'enabled' if state['active'] else 'disabled', ':-)' if state['available'] else 'XXX', state['updated_at']) az.set_info('zoneName', az.zoneName) az.set_info('zoneState', az.zoneState) result.append(az) return result @utils.service_type('compute') def do_availability_zone_list(cs, _args): """List all the availability zones.""" try: availability_zones = cs.availability_zones.list() except exceptions.Forbidden as e: # policy doesn't allow probably try: availability_zones = cs.availability_zones.list(detailed=False) except Exception: raise e result = [] for zone in availability_zones: result += _treeizeAvailabilityZone(zone) _translate_availability_zone_keys(result) utils.print_list(result, ['Name', 'Status'], sortby_index=None) @api_versions.wraps("2.0", "2.12") def _print_server_group_details(cs, server_group): columns = ['Id', 'Name', 'Policies', 'Members', 'Metadata'] utils.print_list(server_group, columns) @api_versions.wraps("2.13") def _print_server_group_details(cs, server_group): # noqa columns = ['Id', 'Name', 'Project Id', 'User Id', 'Policies', 'Members', 'Metadata'] utils.print_list(server_group, columns) @utils.arg( '--limit', dest='limit', metavar='', type=int, default=None, help=_("Maximum number of server groups to display. If limit is bigger " "than 'CONF.api.max_limit' option of Nova API, limit " "'CONF.api.max_limit' will be used instead.")) @utils.arg( '--offset', dest='offset', metavar='', type=int, default=None, help=_('The offset of groups list to display; use with limit to ' 'return a slice of server groups.')) @utils.arg( '--all-projects', dest='all_projects', action='store_true', default=False, help=_('Display server groups from all projects (Admin only).')) def do_server_group_list(cs, args): """Print a list of all server groups.""" server_groups = cs.server_groups.list(all_projects=args.all_projects, limit=args.limit, offset=args.offset) _print_server_group_details(cs, server_groups) @utils.arg('name', metavar='', help=_('Server group name.')) @utils.arg( 'policy', metavar='', nargs='+', help=_('Policies for the server groups.')) def do_server_group_create(cs, args): """Create a new server group with the specified details.""" server_group = cs.server_groups.create(name=args.name, policies=args.policy) _print_server_group_details(cs, [server_group]) @utils.arg( 'id', metavar='', nargs='+', help=_("Unique ID(s) of the server group to delete.")) def do_server_group_delete(cs, args): """Delete specific server group(s).""" failure_count = 0 for sg in args.id: try: cs.server_groups.delete(sg) print(_("Server group %s has been successfully deleted.") % sg) except Exception as e: failure_count += 1 print(_("Delete for server group %(sg)s failed: %(e)s") % {'sg': sg, 'e': e}) if failure_count == len(args.id): raise exceptions.CommandError(_("Unable to delete any of the " "specified server groups.")) @utils.arg( 'id', metavar='', help=_("Unique ID of the server group to get.")) def do_server_group_get(cs, args): """Get a specific server group.""" server_group = cs.server_groups.get(args.id) _print_server_group_details(cs, [server_group]) def do_version_list(cs, args): """List all API versions.""" result = cs.versions.list() if 'min_version' in dir(result[0]): columns = ["Id", "Status", "Updated", "Min Version", "Version"] else: columns = ["Id", "Status", "Updated"] print(_("Client supported API versions:")) print(_("Minimum version %(v)s") % {'v': novaclient.API_MIN_VERSION.get_string()}) print(_("Maximum version %(v)s") % {'v': novaclient.API_MAX_VERSION.get_string()}) print(_("\nServer supported API versions:")) utils.print_list(result, columns) @api_versions.wraps("2.0", "2.11") def _print_virtual_interface_list(cs, interface_list): columns = ['Id', 'Mac address'] utils.print_list(interface_list, columns) @api_versions.wraps("2.12") def _print_virtual_interface_list(cs, interface_list): columns = ['Id', 'Mac address', 'Network ID'] formatters = {"Network ID": lambda o: o.net_id} utils.print_list(interface_list, columns, formatters) @utils.arg('server', metavar='', help=_('ID of server.')) def do_virtual_interface_list(cs, args): """DEPRECATED Show virtual interface info about the given server.""" print(_('WARNING: Command virtual-interface-list is deprecated and will ' 'be removed in the first major release after the Nova server ' '16.0.0 Pike release. There is no replacement or alternative for ' 'this command. Specify --os-compute-api-version less than 2.44 ' 'to continue using this command until it is removed.'), file=sys.stderr) server = _find_server(cs, args.server) interface_list = cs.virtual_interfaces.list(base.getid(server)) _print_virtual_interface_list(cs, interface_list) @api_versions.wraps("2.26") @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_server_tag_list(cs, args): """Get list of tags from a server.""" server = _find_server(cs, args.server) tags = server.tag_list() formatters = {'Tag': lambda o: o} utils.print_list(tags, ['Tag'], formatters=formatters) @api_versions.wraps("2.26") @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('tag', metavar='', nargs='+', help=_('Tag(s) to add.')) def do_server_tag_add(cs, args): """Add one or more tags to a server.""" server = _find_server(cs, args.server) utils.do_action_on_many( lambda t: server.add_tag(t), args.tag, _("Request to add tag %s to specified server has been accepted."), _("Unable to add tag %s to the specified server.")) @api_versions.wraps("2.26") @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('tags', metavar='', nargs='+', help=_('Tag(s) to set.')) def do_server_tag_set(cs, args): """Set list of tags to a server.""" server = _find_server(cs, args.server) server.set_tags(args.tags) @api_versions.wraps("2.26") @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('tag', metavar='', nargs='+', help=_('Tag(s) to delete.')) def do_server_tag_delete(cs, args): """Delete one or more tags from a server.""" server = _find_server(cs, args.server) utils.do_action_on_many( lambda t: server.delete_tag(t), args.tag, _("Request to delete tag %s from specified server has been accepted."), _("Unable to delete tag %s from specified server.")) @api_versions.wraps("2.26") @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_server_tag_delete_all(cs, args): """Delete all tags from a server.""" server = _find_server(cs, args.server) server.delete_all_tags() @utils.arg( 'cell', metavar='', help=_('Name of the cell.')) def do_cell_show(cs, args): """Show details of a given cell.""" cell = cs.cells.get(args.cell) utils.print_dict(cell.to_dict()) @utils.arg( '--cell', metavar='', help=_("Name of the cell to get the capacities."), default=None) def do_cell_capacities(cs, args): """Get cell capacities for all cells or a given cell.""" cell = cs.cells.capacities(args.cell) print(_("Ram Available: %s MB") % cell.capacities['ram_free']['total_mb']) utils.print_dict(cell.capacities['ram_free']['units_by_mb'], dict_property='Ram(MB)', dict_value="Units") print(_("\nDisk Available: %s MB") % cell.capacities['disk_free']['total_mb']) utils.print_dict(cell.capacities['disk_free']['units_by_mb'], dict_property='Disk(MB)', dict_value="Units") @utils.arg('server', metavar='', help='Name or ID of server.') def do_force_delete(cs, args): """Force delete a server.""" utils.find_resource(cs.servers, args.server).force_delete() @utils.arg('server', metavar='', help='Name or ID of server.') def do_restore(cs, args): """Restore a soft-deleted server.""" utils.find_resource(cs.servers, args.server, deleted=True).restore() class EvacuateHostResponse(base.Resource): pass def _server_evacuate(cs, server, args): success = True error_message = "" try: if api_versions.APIVersion("2.29") <= cs.api_version: # if microversion >= 2.29 force = getattr(args, 'force', None) cs.servers.evacuate(server=server['uuid'], host=args.target_host, force=force) elif api_versions.APIVersion("2.14") <= cs.api_version: # if microversion 2.14 - 2.28 cs.servers.evacuate(server=server['uuid'], host=args.target_host) else: # else microversion 2.0 - 2.13 on_shared_storage = getattr(args, 'on_shared_storage', None) cs.servers.evacuate(server=server['uuid'], host=args.target_host, on_shared_storage=on_shared_storage) except Exception as e: success = False error_message = _("Error while evacuating instance: %s") % e return EvacuateHostResponse(base.Manager, {"server_uuid": server['uuid'], "evacuate_accepted": success, "error_message": error_message}) @utils.arg('host', metavar='', help='The hypervisor hostname (or pattern) to search for. ' 'WARNING: Use a fully qualified domain name if you only ' 'want to evacuate from a specific host.') @utils.arg( '--target_host', metavar='', default=None, help=_('Name of target host. If no host is specified the scheduler will ' 'select a target.')) @utils.arg( '--on-shared-storage', dest='on_shared_storage', action="store_true", default=False, help=_('Specifies whether all instances files are on shared storage'), start_version='2.0', end_version='2.13') @utils.arg( '--force', dest='force', action='store_true', default=False, help=_('Force to not verify the scheduler if a host is provided.'), start_version='2.29') def do_host_evacuate(cs, args): """Evacuate all instances from failed host.""" hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] for hyper in hypervisors: if hasattr(hyper, 'servers'): for server in hyper.servers: response.append(_server_evacuate(cs, server, args)) utils.print_list(response, ["Server UUID", "Evacuate Accepted", "Error Message"]) def _server_live_migrate(cs, server, args): class HostEvacuateLiveResponse(object): def __init__(self, server_uuid, live_migration_accepted, error_message): self.server_uuid = server_uuid self.live_migration_accepted = live_migration_accepted self.error_message = error_message success = True error_message = "" update_kwargs = {} try: # API >= 2.30 if 'force' in args and args.force: update_kwargs['force'] = args.force # API 2.0->2.24 if 'disk_over_commit' in args: update_kwargs['disk_over_commit'] = args.disk_over_commit cs.servers.live_migrate(server['uuid'], args.target_host, args.block_migrate, **update_kwargs) except Exception as e: success = False error_message = _("Error while live migrating instance: %s") % e return HostEvacuateLiveResponse(server['uuid'], success, error_message) @utils.arg('host', metavar='', help='The hypervisor hostname (or pattern) to search for. ' 'WARNING: Use a fully qualified domain name if you only ' 'want to live migrate from a specific host.') @utils.arg( '--target-host', metavar='', default=None, help=_('Name of target host.')) @utils.arg( '--block-migrate', action='store_true', default=False, help=_('Enable block migration. (Default=False)'), start_version="2.0", end_version="2.24") @utils.arg( '--block-migrate', action='store_true', default="auto", help=_('Enable block migration. (Default=auto)'), start_version="2.25") @utils.arg( '--disk-over-commit', action='store_true', default=False, help=_('Enable disk overcommit.'), start_version="2.0", end_version="2.24") @utils.arg( '--max-servers', type=int, dest='max_servers', metavar='', help='Maximum number of servers to live migrate simultaneously') @utils.arg( '--force', dest='force', action='store_true', default=False, help=_('Force to not verify the scheduler if a host is provided.'), start_version='2.30') def do_host_evacuate_live(cs, args): """Live migrate all instances of the specified host to other available hosts. """ hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] migrating = 0 for hyper in hypervisors: for server in getattr(hyper, 'servers', []): response.append(_server_live_migrate(cs, server, args)) migrating += 1 if args.max_servers is not None and migrating >= args.max_servers: break utils.print_list(response, ["Server UUID", "Live Migration Accepted", "Error Message"]) class HostServersMigrateResponse(base.Resource): pass def _server_migrate(cs, server): success = True error_message = "" try: cs.servers.migrate(server['uuid']) except Exception as e: success = False error_message = _("Error while migrating instance: %s") % e return HostServersMigrateResponse(base.Manager, {"server_uuid": server['uuid'], "migration_accepted": success, "error_message": error_message}) @utils.arg('host', metavar='', help='The hypervisor hostname (or pattern) to search for. ' 'WARNING: Use a fully qualified domain name if you only ' 'want to cold migrate from a specific host.') def do_host_servers_migrate(cs, args): """Cold migrate all instances off the specified host to other available hosts. """ hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] for hyper in hypervisors: if hasattr(hyper, 'servers'): for server in hyper.servers: response.append(_server_migrate(cs, server)) utils.print_list(response, ["Server UUID", "Migration Accepted", "Error Message"]) @utils.arg( 'server', metavar='', help=_('Name or UUID of the server to show actions for.'), start_version="2.0", end_version="2.20") @utils.arg( 'server', metavar='', help=_('Name or UUID of the server to show actions for. Only UUID can be ' 'used to show actions for a deleted server.'), start_version="2.21") @utils.arg( 'request_id', metavar='', help=_('Request ID of the action to get.')) def do_instance_action(cs, args): """Show an action.""" if cs.api_version < api_versions.APIVersion("2.21"): server = _find_server(cs, args.server) else: server = _find_server(cs, args.server, raise_if_notfound=False) action_resource = cs.instance_action.get(server, args.request_id) action = action_resource.to_dict() if 'events' in action: action['events'] = pprint.pformat(action['events']) utils.print_dict(action) @utils.arg( 'server', metavar='', help=_('Name or UUID of the server to list actions for.'), start_version="2.0", end_version="2.20") @utils.arg( 'server', metavar='', help=_('Name or UUID of the server to list actions for. Only UUID can be ' 'used to list actions on a deleted server.'), start_version="2.21") def do_instance_action_list(cs, args): """List actions on a server.""" if cs.api_version < api_versions.APIVersion("2.21"): server = _find_server(cs, args.server) else: server = _find_server(cs, args.server, raise_if_notfound=False) actions = cs.instance_action.list(server) utils.print_list(actions, ['Action', 'Request_ID', 'Message', 'Start_Time'], sortby_index=3) def do_list_extensions(cs, _args): """ List all the os-api extensions that are available. """ extensions = cs.list_extensions.show_all() fields = ["Name", "Summary", "Alias", "Updated"] utils.print_list(extensions, fields) @utils.arg('host', metavar='', help='The hypervisor hostname (or pattern) to search for. ' 'WARNING: Use a fully qualified domain name if you only ' 'want to update metadata for servers on a specific host.') @utils.arg( 'action', metavar='', choices=['set', 'delete'], help=_("Actions: 'set' or 'delete'")) @utils.arg( 'metadata', metavar='', nargs='+', action='append', default=[], help=_('Metadata to set or delete (only key is necessary on delete)')) def do_host_meta(cs, args): """Set or Delete metadata on all instances of a host.""" hypervisors = cs.hypervisors.search(args.host, servers=True) for hyper in hypervisors: metadata = _extract_metadata(args) if hasattr(hyper, 'servers'): for server in hyper.servers: if args.action == 'set': cs.servers.set_meta(server['uuid'], metadata) elif args.action == 'delete': cs.servers.delete_meta(server['uuid'], metadata.keys()) def _print_migrations(cs, migrations): fields = ['Source Node', 'Dest Node', 'Source Compute', 'Dest Compute', 'Dest Host', 'Status', 'Instance UUID', 'Old Flavor', 'New Flavor', 'Created At', 'Updated At'] def old_flavor(migration): return migration.old_instance_type_id def new_flavor(migration): return migration.new_instance_type_id def migration_type(migration): return migration.migration_type formatters = {'Old Flavor': old_flavor, 'New Flavor': new_flavor} if cs.api_version >= api_versions.APIVersion("2.23"): fields.insert(0, "Id") fields.append("Type") formatters.update({"Type": migration_type}) utils.print_list(migrations, fields, formatters) @utils.arg( '--instance-uuid', dest='instance_uuid', metavar='', help=_('Fetch migrations for the given instance.')) @utils.arg( '--host', dest='host', metavar='', help=_('Fetch migrations for the given host.')) @utils.arg( '--status', dest='status', metavar='', help=_('Fetch migrations for the given status.')) def do_migration_list(cs, args): """Print a list of migrations.""" migrations = cs.migrations.list(args.host, args.status, None, instance_uuid=args.instance_uuid) _print_migrations(cs, migrations) python-novaclient-9.1.1/novaclient/v2/server_migrations.py0000664000175000017500000000555413165151077025155 0ustar jenkinsjenkins00000000000000# Copyright 2016 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient import base class ServerMigration(base.Resource): def __repr__(self): return "" class ServerMigrationsManager(base.ManagerWithFind): resource_class = ServerMigration @api_versions.wraps("2.22") def live_migrate_force_complete(self, server, migration): """ Force on-going live migration to complete :param server: The :class:`Server` (or its ID) :param migration: Migration id that will be forced to complete :returns: An instance of novaclient.base.TupleWithMeta """ body = {'force_complete': None} resp, body = self.api.client.post( '/servers/%s/migrations/%s/action' % (base.getid(server), base.getid(migration)), body=body) return self.convert_into_with_meta(body, resp) @api_versions.wraps("2.23") def get(self, server, migration): """ Get a migration of a specified server :param server: The :class:`Server` (or its ID) :param migration: Migration id that will be gotten. :returns: An instance of novaclient.v2.server_migrations.ServerMigration """ return self._get('/servers/%s/migrations/%s' % (base.getid(server), base.getid(migration)), 'migration') @api_versions.wraps("2.23") def list(self, server): """ Get a migrations list of a specified server :param server: The :class:`Server` (or its ID) :returns: An instance of novaclient.base.ListWithMeta """ return self._list( '/servers/%s/migrations' % (base.getid(server)), "migrations") @api_versions.wraps("2.24") def live_migration_abort(self, server, migration): """ Cancel an ongoing live migration :param server: The :class:`Server` (or its ID) :param migration: Migration id that will be cancelled :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete( '/servers/%s/migrations/%s' % (base.getid(server), base.getid(migration))) python-novaclient-9.1.1/novaclient/v2/__init__.py0000664000175000017500000000121113165151077023134 0ustar jenkinsjenkins00000000000000# # 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 novaclient.v2.client import Client # noqa python-novaclient-9.1.1/novaclient/v2/limits.py0000664000175000017500000000602413165151077022705 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from six.moves.urllib import parse from novaclient import base class Limits(base.Resource): """A collection of RateLimit and AbsoluteLimit objects.""" def __repr__(self): return "" @property def absolute(self): for (name, value) in self._info['absolute'].items(): yield AbsoluteLimit(name, value) @property def rate(self): for group in self._info['rate']: uri = group['uri'] regex = group['regex'] for rate in group['limit']: yield RateLimit(rate['verb'], uri, regex, rate['value'], rate['remaining'], rate['unit'], rate['next-available']) class RateLimit(object): """Data model that represents a flattened view of a single rate limit.""" def __init__(self, verb, uri, regex, value, remain, unit, next_available): self.verb = verb self.uri = uri self.regex = regex self.value = value self.remain = remain self.unit = unit self.next_available = next_available def __eq__(self, other): return self.uri == other.uri \ and self.regex == other.regex \ and self.value == other.value \ and self.verb == other.verb \ and self.remain == other.remain \ and self.unit == other.unit \ and self.next_available == other.next_available def __repr__(self): return "" % (self.verb, self.uri) class AbsoluteLimit(object): """Data model that represents a single absolute limit.""" def __init__(self, name, value): self.name = name self.value = value def __eq__(self, other): return self.value == other.value and self.name == other.name def __repr__(self): return "" % (self.name) class LimitsManager(base.Manager): """Manager object used to interact with limits resource.""" resource_class = Limits def get(self, reserved=False, tenant_id=None): """ Get a specific extension. :rtype: :class:`Limits` """ opts = {} if reserved: opts['reserved'] = 1 if tenant_id: opts['tenant_id'] = tenant_id query_string = "?%s" % parse.urlencode(opts) if opts else "" return self._get("/limits%s" % query_string, "limits") python-novaclient-9.1.1/novaclient/v2/versions.py0000664000175000017500000001001713165151077023251 0ustar jenkinsjenkins00000000000000# 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. """ version interface """ from six.moves import urllib from novaclient import base from novaclient import exceptions as exc class Version(base.Resource): """ Compute REST API information """ def __repr__(self): return "" class VersionManager(base.ManagerWithFind): resource_class = Version def _get_current(self): """Returns info about current version.""" # TODO(sdague): we've now got to make up to 3 HTTP requests to # determine what version we are running, due to differences in # deployments and versions. We really need to cache the # results of this per endpoint and keep the results of it for # some reasonable TTL (like 24 hours) to reduce our round trip # traffic. try: # Assume that the value of get_endpoint() is something # we can get the version of. This is a 404 for Nova < # Mitaka if the service catalog contains project_id. # # TODO(sdague): add microversion for when this will # change url = "%s" % self.api.client.get_endpoint() return self._get(url, "version") except exc.NotFound: # If that's a 404, we can instead try hacking together # an endpoint root url by chopping off the last 2 /s. # This is kind of gross, but we've had this baked in # so long people got used to this hard coding. # # NOTE(sdague): many service providers don't really # implement GET / in the expected way, if we do a GET # /v2 that's actually a 300 redirect to # /v2/... because of how paste works. So adding the # end slash is really important. url = "%s/" % url.rsplit("/", 1)[0] return self._get(url, "version") def get_current(self): try: return self._get_current() except exc.Unauthorized: # NOTE(sdague): RAX's repose configuration blocks access to the # versioned endpoint, which is definitely non-compliant behavior. # However, there is no defcore test for this yet. Remove this code # block once we land things in defcore. return None def list(self): """List all versions.""" endpoint = self.api.client.get_endpoint() url = urllib.parse.urlparse(endpoint) # NOTE(andreykurilin): endpoint URL has at least 3 formats: # 1. the classic (legacy) endpoint: # http://{host}:{optional_port}/v{2 or 2.1}/{project-id} # 2. starting from microversion 2.18 project-id is not included: # http://{host}:{optional_port}/v{2 or 2.1} # 3. under wsgi: # http://{host}:{optional_port}/compute/v{2 or 2.1} if (url.path.endswith("v2") or "/v2/" in url.path or url.path.endswith("v2.1") or "/v2.1/" in url.path): # this way should handle all 3 possible formats path = url.path[:url.path.rfind("/v2")] version_url = '%s://%s%s' % (url.scheme, url.netloc, path) else: # NOTE(andreykurilin): probably, it is one of the next cases: # * https://compute.example.com/ # * https://example.com/compute # leave as is without cropping. version_url = endpoint return self._list(version_url, "versions") python-novaclient-9.1.1/novaclient/v2/agents.py0000664000175000017500000000450513165151077022667 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ agent interface """ from novaclient import base class Agent(base.Resource): def __repr__(self): return "" % self.agent def _add_details(self, info): dico = 'resource' in info and info['resource'] or info for (k, v) in dico.items(): setattr(self, k, v) class AgentsManager(base.ManagerWithFind): resource_class = Agent def list(self, hypervisor=None): """List all agent builds.""" url = "/os-agents" if hypervisor: url = "/os-agents?hypervisor=%s" % hypervisor return self._list(url, "agents") def _build_update_body(self, version, url, md5hash): return {'para': {'version': version, 'url': url, 'md5hash': md5hash}} def update(self, id, version, url, md5hash): """Update an existing agent build.""" body = self._build_update_body(version, url, md5hash) return self._update('/os-agents/%s' % id, body, 'agent') def create(self, os, architecture, version, url, md5hash, hypervisor): """Create a new agent build.""" body = {'agent': {'hypervisor': hypervisor, 'os': os, 'architecture': architecture, 'version': version, 'url': url, 'md5hash': md5hash}} return self._create('/os-agents', body, 'agent') def delete(self, id): """ Deletes an existing agent build. :param id: The agent's id to delete :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete('/os-agents/%s' % id) python-novaclient-9.1.1/novaclient/v2/usage.py0000664000175000017500000001164013165151077022510 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Usage interface. """ import oslo_utils from novaclient import api_versions from novaclient import base class Usage(base.Resource): """ Usage contains information about a tenant's physical resource usage """ def __repr__(self): return "" def get(self): fmt = '%Y-%m-%dT%H:%M:%S.%f' if self.start and self.stop and self.tenant_id: # set_loaded() first ... so if we have to bail, we know we tried. self.set_loaded(True) start = oslo_utils.timeutils.parse_strtime(self.start, fmt=fmt) stop = oslo_utils.timeutils.parse_strtime(self.stop, fmt=fmt) new = self.manager.get(self.tenant_id, start, stop) if new: self._add_details(new._info) self.append_request_ids(new.request_ids) class UsageManager(base.ManagerWithFind): """ Manage :class:`Usage` resources. """ resource_class = Usage usage_prefix = 'os-simple-tenant-usage' def _usage_query(self, start, end, marker=None, limit=None, detailed=None): query = "?start=%s&end=%s" % (start.isoformat(), end.isoformat()) if limit: query = "%s&limit=%s" % (query, int(limit)) if marker: query = "%s&marker=%s" % (query, marker) if detailed is not None: query = "%s&detailed=%s" % (query, int(bool(detailed))) return query @api_versions.wraps("2.0", "2.39") def list(self, start, end, detailed=False): """ Get usage for all tenants :param start: :class:`datetime.datetime` Start date in UTC :param end: :class:`datetime.datetime` End date in UTC :param detailed: Whether to include information about each instance whose usage is part of the report :rtype: list of :class:`Usage`. """ query_string = self._usage_query(start, end, detailed=detailed) url = '/%s%s' % (self.usage_prefix, query_string) return self._list(url, 'tenant_usages') @api_versions.wraps("2.40") def list(self, start, end, detailed=False, marker=None, limit=None): """ Get usage for all tenants :param start: :class:`datetime.datetime` Start date in UTC :param end: :class:`datetime.datetime` End date in UTC :param detailed: Whether to include information about each instance whose usage is part of the report :param marker: Begin returning usage data for instances that appear later in the instance list than that represented by this instance UUID (optional). :param limit: Maximum number of instances to include in the usage (optional). :rtype: list of :class:`Usage`. """ query_string = self._usage_query(start, end, marker, limit, detailed) url = '/%s%s' % (self.usage_prefix, query_string) return self._list(url, 'tenant_usages') @api_versions.wraps("2.0", "2.39") def get(self, tenant_id, start, end): """ Get usage for a specific tenant. :param tenant_id: Tenant ID to fetch usage for :param start: :class:`datetime.datetime` Start date in UTC :param end: :class:`datetime.datetime` End date in UTC :rtype: :class:`Usage` """ query_string = self._usage_query(start, end) url = '/%s/%s%s' % (self.usage_prefix, tenant_id, query_string) return self._get(url, 'tenant_usage') @api_versions.wraps("2.40") def get(self, tenant_id, start, end, marker=None, limit=None): """ Get usage for a specific tenant. :param tenant_id: Tenant ID to fetch usage for :param start: :class:`datetime.datetime` Start date in UTC :param end: :class:`datetime.datetime` End date in UTC :param marker: Begin returning usage data for instances that appear later in the instance list than that represented by this instance UUID (optional). :param limit: Maximum number of instances to include in the usage (optional). :rtype: :class:`Usage` """ query_string = self._usage_query(start, end, marker, limit) url = '/%s/%s%s' % (self.usage_prefix, tenant_id, query_string) return self._get(url, 'tenant_usage') python-novaclient-9.1.1/novaclient/v2/cells.py0000664000175000017500000000255113165151077022507 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import base class Cell(base.Resource): def __repr__(self): return "" % self.name class CellsManager(base.Manager): resource_class = Cell def get(self, cell_name): """ Get a cell. :param cell_name: Name of the :class:`Cell` to get. :rtype: :class:`Cell` """ return self._get("/os-cells/%s" % cell_name, "cell") def capacities(self, cell_name=None): """ Get capacities for a cell. :param cell_name: Name of the :class:`Cell` to get capacities for. :rtype: :class:`Cell` """ path = ["%s/capacities" % cell_name, "capacities"][cell_name is None] return self._get("/os-cells/%s" % path, "cell") python-novaclient-9.1.1/novaclient/v2/instance_action.py0000664000175000017500000000252013165151077024542 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import base class InstanceAction(base.Resource): pass class InstanceActionManager(base.ManagerWithFind): resource_class = InstanceAction def get(self, server, request_id): """ Get details of an action performed on an instance. :param request_id: The request_id of the action to get. """ return self._get("/servers/%s/os-instance-actions/%s" % (base.getid(server), request_id), 'instanceAction') def list(self, server): """ Get a list of actions performed on a server. """ return self._list('/servers/%s/os-instance-actions' % base.getid(server), 'instanceActions') python-novaclient-9.1.1/novaclient/v2/servers.py0000664000175000017500000022052513165151100023064 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Server interface. """ import base64 import warnings from oslo_utils import encodeutils import six from six.moves.urllib import parse from novaclient import api_versions from novaclient import base from novaclient import crypto from novaclient import exceptions from novaclient.i18n import _ REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD' CONSOLE_TYPE_ACTION_MAPPING = { 'novnc': 'os-getVNCConsole', 'xvpvnc': 'os-getVNCConsole', 'spice-html5': 'os-getSPICEConsole', 'rdp-html5': 'os-getRDPConsole', 'serial': 'os-getSerialConsole' } CONSOLE_TYPE_PROTOCOL_MAPPING = { 'novnc': 'vnc', 'xvpvnc': 'vnc', 'spice-html5': 'spice', 'rdp-html5': 'rdp', 'serial': 'serial', 'webmks': 'mks' } ADD_REMOVE_FIXED_FLOATING_DEPRECATION_WARNING = _( 'The %s server action API is deprecated as of the 2.44 microversion. This ' 'API binding will be removed in the first major release after the Nova ' '16.0.0 Pike release. Use python-neutronclient or openstacksdk instead.' ) class Server(base.Resource): HUMAN_ID = True def __repr__(self): return '' % getattr(self, 'name', 'unknown-name') def delete(self): """ Delete (i.e. shut down and delete the image) this server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.delete(self) @api_versions.wraps("2.0", "2.18") def update(self, name=None): """ Update the name and the description for this server. :param name: Update the server's name. :returns: :class:`Server` """ return self.manager.update(self, name=name) @api_versions.wraps("2.19") def update(self, name=None, description=None): """ Update the name and the description for this server. :param name: Update the server's name. :param description: Update the server's description. :returns: :class:`Server` """ update_kwargs = {"name": name} if description is not None: update_kwargs["description"] = description return self.manager.update(self, **update_kwargs) def get_console_output(self, length=None): """ Get text console log output from Server. :param length: The number of lines you would like to retrieve (as int) """ return self.manager.get_console_output(self, length) def get_vnc_console(self, console_type): """ Get vnc console for a Server. :param console_type: Type of console ('novnc' or 'xvpvnc') """ return self.manager.get_vnc_console(self, console_type) def get_spice_console(self, console_type): """ Get spice console for a Server. :param console_type: Type of console ('spice-html5') """ return self.manager.get_spice_console(self, console_type) def get_rdp_console(self, console_type): """ Get rdp console for a Server. :param console_type: Type of console ('rdp-html5') """ return self.manager.get_rdp_console(self, console_type) def get_serial_console(self, console_type): """ Get serial console for a Server. :param console_type: Type of console ('serial') """ return self.manager.get_serial_console(self, console_type) def get_mks_console(self): """ Get mks console for a Server. """ return self.manager.get_mks_console(self) def get_console_url(self, console_type): """ Retrieve a console of a particular protocol and console_type :param console_type: Type of console """ return self.manager.get_console_url(self, console_type) def get_password(self, private_key=None): """ Get password for a Server. Returns the clear password of an instance if private_key is provided, returns the ciphered password otherwise. :param private_key: Path to private key file for decryption (optional) """ return self.manager.get_password(self, private_key) def clear_password(self): """ Get password for a Server. """ return self.manager.clear_password(self) def add_fixed_ip(self, network_id): """ Add an IP address on a network. :param network_id: The ID of the network the IP should be on. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.add_fixed_ip(self, network_id) def add_floating_ip(self, address, fixed_address=None): """ Add floating IP to an instance :param address: The IP address or FloatingIP to add to the instance :param fixed_address: The fixedIP address the FloatingIP is to be associated with (optional) :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.add_floating_ip(self, address, fixed_address) def remove_floating_ip(self, address): """ Remove floating IP from an instance :param address: The IP address or FloatingIP to remove :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.remove_floating_ip(self, address) def stop(self): """ Stop -- Stop the running server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.stop(self) def force_delete(self): """ Force delete -- Force delete a server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.force_delete(self) def restore(self): """ Restore -- Restore a server in 'soft-deleted' state. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.restore(self) def start(self): """ Start -- Start the paused server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.start(self) def pause(self): """ Pause -- Pause the running server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.pause(self) def unpause(self): """ Unpause -- Unpause the paused server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.unpause(self) def lock(self): """ Lock -- Lock the instance from certain operations. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.lock(self) def unlock(self): """ Unlock -- Remove instance lock. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.unlock(self) def suspend(self): """ Suspend -- Suspend the running server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.suspend(self) def resume(self): """ Resume -- Resume the suspended server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.resume(self) def rescue(self, password=None, image=None): """ Rescue -- Rescue the problematic server. :param password: The admin password to be set in the rescue instance. :param image: The :class:`Image` to rescue with. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.rescue(self, password, image) def unrescue(self): """ Unrescue -- Unrescue the rescued server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.unrescue(self) def shelve(self): """ Shelve -- Shelve the server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.shelve(self) def shelve_offload(self): """ Shelve_offload -- Remove a shelved server from the compute node. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.shelve_offload(self) def unshelve(self): """ Unshelve -- Unshelve the server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.unshelve(self) def diagnostics(self): """Diagnostics -- Retrieve server diagnostics.""" return self.manager.diagnostics(self) def migrate(self): """ Migrate a server to a new host. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.migrate(self) def remove_fixed_ip(self, address): """ Remove an IP address. :param address: The IP address to remove. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.remove_fixed_ip(self, address) def change_password(self, password): """ Update the admin password for a server. :param password: string to set as the admin password on the server :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.change_password(self, password) def reboot(self, reboot_type=REBOOT_SOFT): """ Reboot the server. :param reboot_type: either :data:`REBOOT_SOFT` for a software-level reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.reboot(self, reboot_type) def rebuild(self, image, password=None, preserve_ephemeral=False, **kwargs): """ Rebuild -- shut down and then re-image -- this server. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as the admin password on the rebuilt server. :param preserve_ephemeral: If True, request that any ephemeral device be preserved when rebuilding the instance. Defaults to False. """ return self.manager.rebuild(self, image, password=password, preserve_ephemeral=preserve_ephemeral, **kwargs) def resize(self, flavor, **kwargs): """ Resize the server's resources. :param flavor: the :class:`Flavor` (or its ID) to resize to. :returns: An instance of novaclient.base.TupleWithMeta Until a resize event is confirmed with :meth:`confirm_resize`, the old server will be kept around and you'll be able to roll back to the old flavor quickly with :meth:`revert_resize`. All resizes are automatically confirmed after 24 hours. """ return self.manager.resize(self, flavor, **kwargs) def create_image(self, image_name, metadata=None): """ Create an image based on this server. :param image_name: The name to assign the newly create image. :param metadata: Metadata to assign to the image. """ return self.manager.create_image(self, image_name, metadata) def backup(self, backup_name, backup_type, rotation): """ Backup a server instance. :param backup_name: Name of the backup image :param backup_type: The backup type, like 'daily' or 'weekly' :param rotation: Int parameter representing how many backups to keep around. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.backup(self, backup_name, backup_type, rotation) def confirm_resize(self): """ Confirm that the resize worked, thus removing the original server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.confirm_resize(self) def revert_resize(self): """ Revert a previous resize, switching back to the old server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.revert_resize(self) @property def networks(self): """ Generate a simplified list of addresses """ networks = {} try: for network_label, address_list in self.addresses.items(): networks[network_label] = [a['addr'] for a in address_list] return networks except Exception: return {} @api_versions.wraps("2.0", "2.24") def live_migrate(self, host=None, block_migration=False, disk_over_commit=None): """ Migrates a running instance to a new machine. :param host: destination host name. :param block_migration: if True, do block_migration, the default value is False and None will be mapped to False :param disk_over_commit: if True, allow disk over commit, the default value is None which is mapped to False :returns: An instance of novaclient.base.TupleWithMeta """ if block_migration is None: block_migration = False if disk_over_commit is None: disk_over_commit = False return self.manager.live_migrate(self, host, block_migration, disk_over_commit) @api_versions.wraps("2.25", "2.29") def live_migrate(self, host=None, block_migration=None): """ Migrates a running instance to a new machine. :param host: destination host name. :param block_migration: if True, do block_migration, the default value is None which is mapped to 'auto'. :returns: An instance of novaclient.base.TupleWithMeta """ if block_migration is None: block_migration = "auto" return self.manager.live_migrate(self, host, block_migration) @api_versions.wraps("2.30") def live_migrate(self, host=None, block_migration=None, force=None): """ Migrates a running instance to a new machine. :param host: destination host name. :param block_migration: if True, do block_migration, the default value is None which is mapped to 'auto'. :param force: force to bypass the scheduler if host is provided. :returns: An instance of novaclient.base.TupleWithMeta """ if block_migration is None: block_migration = "auto" return self.manager.live_migrate(self, host, block_migration, force) def reset_state(self, state='error'): """ Reset the state of an instance to active or error. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.reset_state(self, state) def reset_network(self): """ Reset network of an instance. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.reset_network(self) def add_security_group(self, security_group): """ Add a security group to an instance. :param security_group: The name of security group to add :returns: An instance of novaclient.base.DictWithMeta """ return self.manager.add_security_group(self, security_group) def remove_security_group(self, security_group): """ Remove a security group from an instance. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.remove_security_group(self, security_group) def list_security_group(self): """ List security group(s) of an instance. """ return self.manager.list_security_group(self) @api_versions.wraps("2.0", "2.13") def evacuate(self, host=None, on_shared_storage=True, password=None): """ Evacuate an instance from failed host to specified host. :param host: Name of the target host :param on_shared_storage: Specifies whether instance files located on shared storage. :param password: string to set as admin password on the evacuated server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.evacuate(self, host, on_shared_storage, password) @api_versions.wraps("2.14", "2.28") def evacuate(self, host=None, password=None): """ Evacuate an instance from failed host to specified host. :param host: Name of the target host :param password: string to set as admin password on the evacuated server. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.evacuate(self, host, password) @api_versions.wraps("2.29") def evacuate(self, host=None, password=None, force=None): """ Evacuate an instance from failed host to specified host. :param host: Name of the target host :param password: string to set as admin password on the evacuated server. :param force: forces to bypass the scheduler if host is provided. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.evacuate(self, host, password, force) def interface_list(self): """ List interfaces attached to an instance. """ return self.manager.interface_list(self) @api_versions.wraps("2.0", "2.48") def interface_attach(self, port_id, net_id, fixed_ip): """ Attach a network interface to an instance. """ return self.manager.interface_attach(self, port_id, net_id, fixed_ip) @api_versions.wraps("2.49") def interface_attach(self, port_id, net_id, fixed_ip, tag=None): """ Attach a network interface to an instance with an optional tag. """ return self.manager.interface_attach(self, port_id, net_id, fixed_ip, tag) def interface_detach(self, port_id): """ Detach a network interface from an instance. """ return self.manager.interface_detach(self, port_id) def trigger_crash_dump(self): """Trigger crash dump in an instance""" return self.manager.trigger_crash_dump(self) @api_versions.wraps('2.26') def tag_list(self): """ Get list of tags from an instance. """ return self.manager.tag_list(self) @api_versions.wraps('2.26') def delete_tag(self, tag): """ Remove single tag from an instance. """ return self.manager.delete_tag(self, tag) @api_versions.wraps('2.26') def delete_all_tags(self): """ Remove all tags from an instance. """ return self.manager.delete_all_tags(self) @api_versions.wraps('2.26') def set_tags(self, tags): """ Set list of tags to an instance. """ return self.manager.set_tags(self, tags) @api_versions.wraps('2.26') def add_tag(self, tag): """ Add single tag to an instance. """ return self.manager.add_tag(self, tag) class NetworkInterface(base.Resource): @property def id(self): return self.port_id def __repr__(self): return '' % self.id class SecurityGroup(base.Resource): def __str__(self): return str(self.id) class ServerManager(base.BootingManagerWithFind): resource_class = Server def _boot(self, resource_url, response_key, name, image, flavor, meta=None, files=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, admin_pass=None, disk_config=None, access_ip_v4=None, access_ip_v6=None, description=None, tags=None, **kwargs): """ Create (boot) a new server. """ body = {"server": { "name": name, "imageRef": str(base.getid(image)) if image else '', "flavorRef": str(base.getid(flavor)), }} if userdata: if hasattr(userdata, 'read'): userdata = userdata.read() # NOTE(melwitt): Text file data is converted to bytes prior to # base64 encoding. The utf-8 encoding will fail for binary files. if six.PY3: try: userdata = userdata.encode("utf-8") except AttributeError: # In python 3, 'bytes' object has no attribute 'encode' pass else: try: userdata = encodeutils.safe_encode(userdata) except UnicodeDecodeError: pass userdata_b64 = base64.b64encode(userdata).decode('utf-8') body["server"]["user_data"] = userdata_b64 if meta: body["server"]["metadata"] = meta if reservation_id: body["server"]["reservation_id"] = reservation_id if key_name: body["server"]["key_name"] = key_name if scheduler_hints: body['os:scheduler_hints'] = scheduler_hints if config_drive: body["server"]["config_drive"] = config_drive if admin_pass: body["server"]["adminPass"] = admin_pass if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["min_count"] = min_count body["server"]["max_count"] = max_count if security_groups: body["server"]["security_groups"] = [{'name': sg} for sg in security_groups] # Files are a slight bit tricky. They're passed in a "personality" # list to the POST. Each item is a dict giving a file name and the # base64-encoded contents of the file. We want to allow passing # either an open file *or* some contents as files here. if files: personality = body['server']['personality'] = [] for filepath, file_or_string in sorted(files.items(), key=lambda x: x[0]): if hasattr(file_or_string, 'read'): data = file_or_string.read() else: data = file_or_string if six.PY3 and isinstance(data, str): data = data.encode('utf-8') cont = base64.b64encode(data).decode('utf-8') personality.append({ 'path': filepath, 'contents': cont, }) if availability_zone: body["server"]["availability_zone"] = availability_zone # Block device mappings are passed as a list of dictionaries if block_device_mapping: body['server']['block_device_mapping'] = \ self._parse_block_device_mapping(block_device_mapping) elif block_device_mapping_v2: # Following logic can't be removed because it will leaves # a valid boot with both --image and --block-device # failed , see bug 1433609 for more info if image: bdm_dict = {'uuid': base.getid(image), 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True} block_device_mapping_v2.insert(0, bdm_dict) body['server']['block_device_mapping_v2'] = block_device_mapping_v2 if nics is not None: # With microversion 2.37+ nics can be an enum of 'auto' or 'none' # or a list of dicts. if isinstance(nics, six.string_types): all_net_data = nics else: # NOTE(tr3buchet): nics can be an empty list all_net_data = [] for nic_info in nics: net_data = {} # if value is empty string, do not send value in body if nic_info.get('net-id'): net_data['uuid'] = nic_info['net-id'] if (nic_info.get('v4-fixed-ip') and nic_info.get('v6-fixed-ip')): raise base.exceptions.CommandError(_( "Only one of 'v4-fixed-ip' and 'v6-fixed-ip' " "may be provided.")) elif nic_info.get('v4-fixed-ip'): net_data['fixed_ip'] = nic_info['v4-fixed-ip'] elif nic_info.get('v6-fixed-ip'): net_data['fixed_ip'] = nic_info['v6-fixed-ip'] if nic_info.get('port-id'): net_data['port'] = nic_info['port-id'] if nic_info.get('tag'): net_data['tag'] = nic_info['tag'] all_net_data.append(net_data) body['server']['networks'] = all_net_data if disk_config is not None: body['server']['OS-DCF:diskConfig'] = disk_config if access_ip_v4 is not None: body['server']['accessIPv4'] = access_ip_v4 if access_ip_v6 is not None: body['server']['accessIPv6'] = access_ip_v6 if description: body['server']['description'] = description if tags: body['server']['tags'] = tags return self._create(resource_url, body, response_key, return_raw=return_raw, **kwargs) def get(self, server): """ Get a server. :param server: ID of the :class:`Server` to get. :rtype: :class:`Server` """ return self._get("/servers/%s" % base.getid(server), "server") def list(self, detailed=True, search_opts=None, marker=None, limit=None, sort_keys=None, sort_dirs=None): """ Get a list of servers. :param detailed: Whether to return detailed server info (optional). :param search_opts: Search options to filter out servers which don't match the search_opts (optional). The search opts format is a dictionary of key / value pairs that will be appended to the query string. For a complete list of keys see: http://developer.openstack.org/api-ref-compute-v2.1.html#listServers :param marker: Begin returning servers that appear later in the server list than that represented by this server id (optional). :param limit: Maximum number of servers to return (optional). :param sort_keys: List of sort keys :param sort_dirs: List of sort directions :rtype: list of :class:`Server` Examples: client.servers.list() - returns detailed list of servers client.servers.list(search_opts={'status': 'ERROR'}) - returns list of servers in error state. client.servers.list(limit=10) - returns only 10 servers """ if search_opts is None: search_opts = {} qparams = {} for opt, val in search_opts.items(): if val: if isinstance(val, six.text_type): val = val.encode('utf-8') qparams[opt] = val detail = "" if detailed: detail = "/detail" result = base.ListWithMeta([], None) while True: if marker: qparams['marker'] = marker if limit and limit != -1: qparams['limit'] = limit # Transform the dict to a sequence of two-element tuples in fixed # order, then the encoded string will be consistent in Python 2&3. if qparams or sort_keys or sort_dirs: # sort keys and directions are unique since the same parameter # key is repeated for each associated value # (ie, &sort_key=key1&sort_key=key2&sort_key=key3) items = list(qparams.items()) if sort_keys: items.extend(('sort_key', sort_key) for sort_key in sort_keys) if sort_dirs: items.extend(('sort_dir', sort_dir) for sort_dir in sort_dirs) new_qparams = sorted(items, key=lambda x: x[0]) query_string = "?%s" % parse.urlencode(new_qparams) else: query_string = "" servers = self._list("/servers%s%s" % (detail, query_string), "servers") result.extend(servers) result.append_request_ids(servers.request_ids) if not servers or limit != -1: break marker = result[-1].id return result @api_versions.wraps('2.0', '2.43') def add_fixed_ip(self, server, network_id): """ DEPRECATED Add an IP address on a network. :param server: The :class:`Server` (or its ID) to add an IP to. :param network_id: The ID of the network the IP should be on. :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn(ADD_REMOVE_FIXED_FLOATING_DEPRECATION_WARNING % 'addFixedIP', DeprecationWarning) return self._action('addFixedIp', server, {'networkId': network_id}) @api_versions.wraps('2.0', '2.43') def remove_fixed_ip(self, server, address): """ DEPRECATED Remove an IP address. :param server: The :class:`Server` (or its ID) to add an IP to. :param address: The IP address to remove. :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn(ADD_REMOVE_FIXED_FLOATING_DEPRECATION_WARNING % 'removeFixedIP', DeprecationWarning) return self._action('removeFixedIp', server, {'address': address}) @api_versions.wraps('2.0', '2.43') def add_floating_ip(self, server, address, fixed_address=None): """ DEPRECATED Add a floating IP to an instance :param server: The :class:`Server` (or its ID) to add an IP to. :param address: The FloatingIP or string floating address to add. :param fixed_address: The FixedIP the floatingIP should be associated with (optional) :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn(ADD_REMOVE_FIXED_FLOATING_DEPRECATION_WARNING % 'addFloatingIP', DeprecationWarning) address = address.ip if hasattr(address, 'ip') else address if fixed_address: if hasattr(fixed_address, 'ip'): fixed_address = fixed_address.ip return self._action('addFloatingIp', server, {'address': address, 'fixed_address': fixed_address}) else: return self._action('addFloatingIp', server, {'address': address}) @api_versions.wraps('2.0', '2.43') def remove_floating_ip(self, server, address): """ DEPRECATED Remove a floating IP address. :param server: The :class:`Server` (or its ID) to remove an IP from. :param address: The FloatingIP or string floating address to remove. :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn(ADD_REMOVE_FIXED_FLOATING_DEPRECATION_WARNING % 'removeFloatingIP', DeprecationWarning) address = address.ip if hasattr(address, 'ip') else address return self._action('removeFloatingIp', server, {'address': address}) def get_vnc_console(self, server, console_type): """ Get a vnc console for an instance :param server: The :class:`Server` (or its ID) to get console for. :param console_type: Type of vnc console to get ('novnc' or 'xvpvnc') :returns: An instance of novaclient.base.DictWithMeta """ return self.get_console_url(server, console_type) def get_spice_console(self, server, console_type): """ Get a spice console for an instance :param server: The :class:`Server` (or its ID) to get console for. :param console_type: Type of spice console to get ('spice-html5') :returns: An instance of novaclient.base.DictWithMeta """ return self.get_console_url(server, console_type) def get_rdp_console(self, server, console_type): """ Get a rdp console for an instance :param server: The :class:`Server` (or its ID) to get console for. :param console_type: Type of rdp console to get ('rdp-html5') :returns: An instance of novaclient.base.DictWithMeta """ return self.get_console_url(server, console_type) def get_serial_console(self, server, console_type): """ Get a serial console for an instance :param server: The :class:`Server` (or its ID) to get console for. :param console_type: Type of serial console to get ('serial') :returns: An instance of novaclient.base.DictWithMeta """ return self.get_console_url(server, console_type) def _get_protocol(self, console_type): protocol = CONSOLE_TYPE_PROTOCOL_MAPPING.get(console_type) if not protocol: raise exceptions.UnsupportedConsoleType(console_type) return protocol @api_versions.wraps('2.0', '2.5') def get_console_url(self, server, console_type): """ Retrieve a console url of a server. :param server: server to get console url for :param console_type: type can be novnc, xvpvnc, spice-html5, rdp-html5 and serial. """ action = CONSOLE_TYPE_ACTION_MAPPING.get(console_type) if not action: raise exceptions.UnsupportedConsoleType(console_type) return self._action(action, server, {'type': console_type}) @api_versions.wraps('2.8') def get_mks_console(self, server): """ Get a mks console for an instance :param server: The :class:`Server` (or its ID) to get console for. :returns: An instance of novaclient.base.DictWithMeta """ return self.get_console_url(server, 'webmks') @api_versions.wraps('2.6') def get_console_url(self, server, console_type): """ Retrieve a console url of a server. :param server: server to get console url for :param console_type: type can be novnc/xvpvnc for protocol vnc; spice-html5 for protocol spice; rdp-html5 for protocol rdp; serial for protocol serial. webmks for protocol mks (since version 2.8). """ if self.api_version < api_versions.APIVersion('2.8'): if console_type == 'webmks': raise exceptions.UnsupportedConsoleType(console_type) protocol = self._get_protocol(console_type) body = {'remote_console': {'protocol': protocol, 'type': console_type}} url = '/servers/%s/remote-consoles' % base.getid(server) resp, body = self.api.client.post(url, body=body) return self.convert_into_with_meta(body, resp) def get_password(self, server, private_key=None): """ Get admin password of an instance Returns the admin password of an instance in the clear if private_key is provided, returns the ciphered password otherwise. Requires that openssl is installed and in the path :param server: The :class:`Server` (or its ID) for which the admin password is to be returned :param private_key: The private key to decrypt password (optional) :returns: An instance of novaclient.base.StrWithMeta or novaclient.base.BytesWithMeta or novaclient.base.UnicodeWithMeta """ resp, body = self.api.client.get("/servers/%s/os-server-password" % base.getid(server)) ciphered_pw = body.get('password', '') if body else '' if private_key and ciphered_pw: try: ciphered_pw = crypto.decrypt_password(private_key, ciphered_pw) except Exception as exc: ciphered_pw = '%sFailed to decrypt:\n%s' % (exc, ciphered_pw) return self.convert_into_with_meta(ciphered_pw, resp) def clear_password(self, server): """ Clear the admin password of an instance Remove the admin password for an instance from the metadata server. :param server: The :class:`Server` (or its ID) for which the admin password is to be cleared """ return self._delete("/servers/%s/os-server-password" % base.getid(server)) def stop(self, server): """ Stop the server. :param server: The :class:`Server` (or its ID) to stop :returns: An instance of novaclient.base.TupleWithMeta """ resp, body = self._action_return_resp_and_body('os-stop', server, None) return base.TupleWithMeta((resp, body), resp) def force_delete(self, server): """ Force delete the server. :param server: The :class:`Server` (or its ID) to force delete :returns: An instance of novaclient.base.TupleWithMeta """ resp, body = self._action_return_resp_and_body('forceDelete', server, None) return base.TupleWithMeta((resp, body), resp) def restore(self, server): """ Restore soft-deleted server. :param server: The :class:`Server` (or its ID) to restore :returns: An instance of novaclient.base.TupleWithMeta """ resp, body = self._action_return_resp_and_body('restore', server, None) return base.TupleWithMeta((resp, body), resp) def start(self, server): """ Start the server. :param server: The :class:`Server` (or its ID) to start :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('os-start', server, None) def pause(self, server): """ Pause the server. :param server: The :class:`Server` (or its ID) to pause :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('pause', server, None) def unpause(self, server): """ Unpause the server. :param server: The :class:`Server` (or its ID) to unpause :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('unpause', server, None) def lock(self, server): """ Lock the server. :param server: The :class:`Server` (or its ID) to lock :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('lock', server, None) def unlock(self, server): """ Unlock the server. :param server: The :class:`Server` (or its ID) to unlock :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('unlock', server, None) def suspend(self, server): """ Suspend the server. :param server: The :class:`Server` (or its ID) to suspend :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('suspend', server, None) def resume(self, server): """ Resume the server. :param server: The :class:`Server` (or its ID) to resume :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('resume', server, None) def rescue(self, server, password=None, image=None): """ Rescue the server. :param server: The :class:`Server` to rescue. :param password: The admin password to be set in the rescue instance. :param image: The :class:`Image` to rescue with. :returns: An instance of novaclient.base.TupleWithMeta """ info = {} if password: info['adminPass'] = password if image: info['rescue_image_ref'] = base.getid(image) resp, body = self._action_return_resp_and_body('rescue', server, info or None) return base.TupleWithMeta((resp, body), resp) def unrescue(self, server): """ Unrescue the server. :param server: The :class:`Server` (or its ID) to unrescue :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('unrescue', server, None) def shelve(self, server): """ Shelve the server. :param server: The :class:`Server` (or its ID) to shelve :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('shelve', server, None) def shelve_offload(self, server): """ Remove a shelved instance from the compute node. :param server: The :class:`Server` (or its ID) to shelve offload :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('shelveOffload', server, None) def unshelve(self, server): """ Unshelve the server. :param server: The :class:`Server` (or its ID) to unshelve :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('unshelve', server, None) def ips(self, server): """ Return IP Addresses associated with the server. Often a cheaper call then getting all the details for a server. :param server: The :class:`Server` (or its ID) for which the IP adresses are to be returned :returns: An instance of novaclient.base.DictWithMeta """ resp, body = self.api.client.get("/servers/%s/ips" % base.getid(server)) return base.DictWithMeta(body['addresses'], resp) def diagnostics(self, server): """ Retrieve server diagnostics. :param server: The :class:`Server` (or its ID) for which diagnostics to be returned :returns: An instance of novaclient.base.TupleWithMeta """ resp, body = self.api.client.get("/servers/%s/diagnostics" % base.getid(server)) return base.TupleWithMeta((resp, body), resp) def _validate_create_nics(self, nics): # nics are required with microversion 2.37+ and can be a string or list if self.api_version > api_versions.APIVersion('2.36'): if not nics: raise ValueError('nics are required after microversion 2.36') elif nics and not isinstance(nics, list): raise ValueError('nics must be a list') def create(self, name, image, flavor, meta=None, files=None, reservation_id=None, min_count=None, max_count=None, security_groups=None, userdata=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, disk_config=None, admin_pass=None, access_ip_v4=None, access_ip_v6=None, **kwargs): # TODO(anthony): indicate in doc string if param is an extension # and/or optional """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. Both keys and values must be <=255 characters. :param files: A dict of files to overwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param reservation_id: a UUID for the set of servers being requested. :param min_count: (optional extension) The minimum number of servers to launch. :param max_count: (optional extension) The maximum number of servers to launch. :param security_groups: A list of security group names :param userdata: user data to pass to be exposed by the metadata server this can be a file type object as well or a string. :param key_name: (optional extension) name of previously created keypair to inject into the instance. :param availability_zone: Name of the availability zone for instance placement. :param block_device_mapping: (optional extension) A dict of block device mappings for this server. :param block_device_mapping_v2: (optional extension) A dict of block device mappings for this server. :param nics: An ordered list of nics (dicts) to be added to this server, with information about connected networks, fixed IPs, port etc. Beginning in microversion 2.37 this field is required and also supports a single string value of 'auto' or 'none'. The 'auto' value means the Compute service will automatically allocate a network for the project if one is not available. This is the same behavior as not passing anything for nics before microversion 2.37. The 'none' value tells the Compute service to not allocate any networking for the server. :param scheduler_hints: (optional extension) arbitrary key-value pairs specified by the client to help boot an instance :param config_drive: (optional extension) value for config drive either boolean, or volume-id :param disk_config: (optional extension) control how the disk is partitioned when the server is created. possible values are 'AUTO' or 'MANUAL'. :param admin_pass: (optional extension) add a user supplied admin password. :param access_ip_v4: (optional extension) add alternative access ip v4 :param access_ip_v6: (optional extension) add alternative access ip v6 :param description: optional description of the server (allowed since microversion 2.19) :param tags: A list of arbitrary strings to be added to the server as tags (allowed since microversion 2.52) """ if not min_count: min_count = 1 if not max_count: max_count = min_count if min_count > max_count: min_count = max_count boot_args = [name, image, flavor] descr_microversion = api_versions.APIVersion("2.19") if "description" in kwargs and self.api_version < descr_microversion: raise exceptions.UnsupportedAttribute("description", "2.19") self._validate_create_nics(nics) tags_microversion = api_versions.APIVersion("2.32") if self.api_version < tags_microversion: if nics: for nic_info in nics: if nic_info.get("tag"): raise ValueError("Setting interface tags is " "unsupported before microversion " "2.32") if block_device_mapping_v2: for bdm in block_device_mapping_v2: if bdm.get("tag"): raise ValueError("Setting block device tags is " "unsupported before microversion " "2.32") boot_tags_microversion = api_versions.APIVersion("2.52") if "tags" in kwargs and self.api_version < boot_tags_microversion: raise exceptions.UnsupportedAttribute("tags", "2.52") boot_kwargs = dict( meta=meta, files=files, userdata=userdata, reservation_id=reservation_id, min_count=min_count, max_count=max_count, security_groups=security_groups, key_name=key_name, availability_zone=availability_zone, scheduler_hints=scheduler_hints, config_drive=config_drive, disk_config=disk_config, admin_pass=admin_pass, access_ip_v4=access_ip_v4, access_ip_v6=access_ip_v6, **kwargs) if block_device_mapping: resource_url = "/os-volumes_boot" boot_kwargs['block_device_mapping'] = block_device_mapping elif block_device_mapping_v2: resource_url = "/os-volumes_boot" boot_kwargs['block_device_mapping_v2'] = block_device_mapping_v2 else: resource_url = "/servers" if nics: boot_kwargs['nics'] = nics response_key = "server" return self._boot(resource_url, response_key, *boot_args, **boot_kwargs) @api_versions.wraps("2.0", "2.18") def update(self, server, name=None): """ Update the name for a server. :param server: The :class:`Server` (or its ID) to update. :param name: Update the server's name. """ if name is None: return body = { "server": { "name": name, }, } return self._update("/servers/%s" % base.getid(server), body, "server") @api_versions.wraps("2.19") def update(self, server, name=None, description=None): """ Update the name or the description for a server. :param server: The :class:`Server` (or its ID) to update. :param name: Update the server's name. :param description: Update the server's description. If it equals to empty string(i.g. ""), the server description will be removed. """ if name is None and description is None: return body = {"server": {}} if name: body["server"]["name"] = name if description == "": body["server"]["description"] = None elif description: body["server"]["description"] = description return self._update("/servers/%s" % base.getid(server), body, "server") def change_password(self, server, password): """ Update the password for a server. :param server: The :class:`Server` (or its ID) for which the admin password is to be changed :returns: An instance of novaclient.base.TupleWithMeta """ return self._action("changePassword", server, {"adminPass": password}) def delete(self, server): """ Delete (i.e. shut down and delete the image) this server. :param server: The :class:`Server` (or its ID) to delete :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete("/servers/%s" % base.getid(server)) def reboot(self, server, reboot_type=REBOOT_SOFT): """ Reboot a server. :param server: The :class:`Server` (or its ID) to share onto. :param reboot_type: either :data:`REBOOT_SOFT` for a software-level reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('reboot', server, {'type': reboot_type}) def rebuild(self, server, image, password=None, disk_config=None, preserve_ephemeral=False, name=None, meta=None, files=None, **kwargs): """ Rebuild -- shut down and then re-image -- a server. :param server: The :class:`Server` (or its ID) to share onto. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as password on the rebuilt server. :param disk_config: partitioning mode to use on the rebuilt server. Valid values are 'AUTO' or 'MANUAL' :param preserve_ephemeral: If True, request that any ephemeral device be preserved when rebuilding the instance. Defaults to False. :param name: Something to name the server. :param meta: A dict of arbitrary key/value metadata to store for this server. Both keys and values must be <=255 characters. :param files: A dict of files to overwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param description: optional description of the server (allowed since microversion 2.19) :returns: :class:`Server` """ descr_microversion = api_versions.APIVersion("2.19") if "description" in kwargs and self.api_version < descr_microversion: raise exceptions.UnsupportedAttribute("description", "2.19") body = {'imageRef': base.getid(image)} if password is not None: body['adminPass'] = password if disk_config is not None: body['OS-DCF:diskConfig'] = disk_config if preserve_ephemeral is not False: body['preserve_ephemeral'] = True if name is not None: body['name'] = name if "description" in kwargs: body["description"] = kwargs["description"] if meta: body['metadata'] = meta if files: personality = body['personality'] = [] for filepath, file_or_string in sorted(files.items(), key=lambda x: x[0]): if hasattr(file_or_string, 'read'): data = file_or_string.read() else: data = file_or_string cont = base64.b64encode(data.encode('utf-8')).decode('utf-8') personality.append({ 'path': filepath, 'contents': cont, }) resp, body = self._action_return_resp_and_body('rebuild', server, body, **kwargs) return Server(self, body['server'], resp=resp) def migrate(self, server): """ Migrate a server to a new host. :param server: The :class:`Server` (or its ID). :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('migrate', server) def resize(self, server, flavor, disk_config=None, **kwargs): """ Resize a server's resources. :param server: The :class:`Server` (or its ID) to share onto. :param flavor: the :class:`Flavor` (or its ID) to resize to. :param disk_config: partitioning mode to use on the rebuilt server. Valid values are 'AUTO' or 'MANUAL' :returns: An instance of novaclient.base.TupleWithMeta Until a resize event is confirmed with :meth:`confirm_resize`, the old server will be kept around and you'll be able to roll back to the old flavor quickly with :meth:`revert_resize`. All resizes are automatically confirmed after 24 hours. """ info = {'flavorRef': base.getid(flavor)} if disk_config is not None: info['OS-DCF:diskConfig'] = disk_config return self._action('resize', server, info=info, **kwargs) def confirm_resize(self, server): """ Confirm that the resize worked, thus removing the original server. :param server: The :class:`Server` (or its ID) to share onto. :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('confirmResize', server) def revert_resize(self, server): """ Revert a previous resize, switching back to the old server. :param server: The :class:`Server` (or its ID) to share onto. :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('revertResize', server) def create_image(self, server, image_name, metadata=None): """ Snapshot a server. :param server: The :class:`Server` (or its ID) to share onto. :param image_name: Name to give the snapshot image :param metadata: Metadata to give newly-created image entity :returns: An instance of novaclient.base.StrWithMeta (The snapshot image's UUID) """ body = {'name': image_name, 'metadata': metadata or {}} resp, body = self._action_return_resp_and_body('createImage', server, body) # The 2.45 microversion returns the image_id in the response body, # not as a location header. if self.api_version >= api_versions.APIVersion('2.45'): image_uuid = body['image_id'] else: location = resp.headers['location'] image_uuid = location.split('/')[-1] return base.StrWithMeta(image_uuid, resp) def backup(self, server, backup_name, backup_type, rotation): """ Backup a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param backup_name: Name of the backup image :param backup_type: The backup type, like 'daily' or 'weekly' :param rotation: Int parameter representing how many backups to keep around. :returns: An instance of novaclient.base.TupleWithMeta if the request microversion is < 2.45, otherwise novaclient.base.DictWithMeta. """ body = {'name': backup_name, 'backup_type': backup_type, 'rotation': rotation} return self._action('createBackup', server, body) def set_meta(self, server, metadata): """ Set a server's metadata :param server: The :class:`Server` to add metadata to :param metadata: A dict of metadata to be added to the server """ body = {'metadata': metadata} return self._create("/servers/%s/metadata" % base.getid(server), body, "metadata") def set_meta_item(self, server, key, value): """ Updates an item of server metadata :param server: The :class:`Server` to add metadata to :param key: metadata key to update :param value: string value """ body = {'meta': {key: value}} return self._update("/servers/%s/metadata/%s" % (base.getid(server), key), body) def get_console_output(self, server, length=None): """ Get text console log output from Server. :param server: The :class:`Server` (or its ID) whose console output you would like to retrieve. :param length: The number of tail loglines you would like to retrieve. :returns: An instance of novaclient.base.StrWithMeta or novaclient.base.UnicodeWithMeta """ resp, body = self._action_return_resp_and_body('os-getConsoleOutput', server, {'length': length}) return self.convert_into_with_meta(body['output'], resp) def delete_meta(self, server, keys): """ Delete metadata from a server :param server: The :class:`Server` to add metadata to :param keys: A list of metadata keys to delete from the server :returns: An instance of novaclient.base.TupleWithMeta """ result = base.TupleWithMeta((), None) for k in keys: ret = self._delete("/servers/%s/metadata/%s" % (base.getid(server), k)) result.append_request_ids(ret.request_ids) return result @api_versions.wraps('2.0', '2.24') def live_migrate(self, server, host, block_migration, disk_over_commit): """ Migrates a running instance to a new machine. :param server: instance id which comes from nova list. :param host: destination host name. :param block_migration: if True, do block_migration. :param disk_over_commit: if True, allow disk overcommit. :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('os-migrateLive', server, {'host': host, 'block_migration': block_migration, 'disk_over_commit': disk_over_commit}) @api_versions.wraps('2.25', '2.29') def live_migrate(self, server, host, block_migration): """ Migrates a running instance to a new machine. :param server: instance id which comes from nova list. :param host: destination host name. :param block_migration: if True, do block_migration, can be set as 'auto' :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('os-migrateLive', server, {'host': host, 'block_migration': block_migration}) @api_versions.wraps('2.30') def live_migrate(self, server, host, block_migration, force=None): """ Migrates a running instance to a new machine. :param server: instance id which comes from nova list. :param host: destination host name. :param block_migration: if True, do block_migration, can be set as 'auto' :param force: forces to bypass the scheduler if host is provided. :returns: An instance of novaclient.base.TupleWithMeta """ body = {'host': host, 'block_migration': block_migration} if force: body['force'] = force return self._action('os-migrateLive', server, body) def reset_state(self, server, state='error'): """ Reset the state of an instance to active or error. :param server: ID of the instance to reset the state of. :param state: Desired state; either 'active' or 'error'. Defaults to 'error'. :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('os-resetState', server, dict(state=state)) def reset_network(self, server): """ Reset network of an instance. :param server: The :class:`Server` for network is to be reset :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('resetNetwork', server) def add_security_group(self, server, security_group): """ Add a Security Group to an instance :param server: ID of the instance. :param security_group: The name of security group to add. :returns: An instance of novaclient.base.DictWithMeta """ return self._action('addSecurityGroup', server, {'name': security_group}) def remove_security_group(self, server, security_group): """ Remove a Security Group to an instance :param server: ID of the instance. :param security_group: The name of security group to remove. :returns: An instance of novaclient.base.TupleWithMeta """ return self._action('removeSecurityGroup', server, {'name': security_group}) def list_security_group(self, server): """ List Security Group(s) of an instance :param server: ID of the instance. """ return self._list('/servers/%s/os-security-groups' % base.getid(server), 'security_groups', SecurityGroup) @api_versions.wraps("2.0", "2.13") def evacuate(self, server, host=None, on_shared_storage=True, password=None): """ Evacuate a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param host: Name of the target host. :param on_shared_storage: Specifies whether instance files located on shared storage :param password: string to set as password on the evacuated server. :returns: An instance of novaclient.base.TupleWithMeta """ body = {'onSharedStorage': on_shared_storage} if host is not None: body['host'] = host if password is not None: body['adminPass'] = password resp, body = self._action_return_resp_and_body('evacuate', server, body) return base.TupleWithMeta((resp, body), resp) @api_versions.wraps("2.14", "2.28") def evacuate(self, server, host=None, password=None): """ Evacuate a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param host: Name of the target host. :param password: string to set as password on the evacuated server. :returns: An instance of novaclient.base.TupleWithMeta """ body = {} if host is not None: body['host'] = host if password is not None: body['adminPass'] = password resp, body = self._action_return_resp_and_body('evacuate', server, body) return base.TupleWithMeta((resp, body), resp) @api_versions.wraps("2.29") def evacuate(self, server, host=None, password=None, force=None): """ Evacuate a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param host: Name of the target host. :param password: string to set as password on the evacuated server. :param force: forces to bypass the scheduler if host is provided. :returns: An instance of novaclient.base.TupleWithMeta """ body = {} if host is not None: body['host'] = host if password is not None: body['adminPass'] = password if force: body['force'] = force resp, body = self._action_return_resp_and_body('evacuate', server, body) return base.TupleWithMeta((resp, body), resp) def interface_list(self, server): """ List attached network interfaces :param server: The :class:`Server` (or its ID) to query. """ return self._list('/servers/%s/os-interface' % base.getid(server), 'interfaceAttachments', obj_class=NetworkInterface) @api_versions.wraps("2.0", "2.48") def interface_attach(self, server, port_id, net_id, fixed_ip): """ Attach a network_interface to an instance. :param server: The :class:`Server` (or its ID) to attach to. :param port_id: The port to attach. """ body = {'interfaceAttachment': {}} if port_id: body['interfaceAttachment']['port_id'] = port_id if net_id: body['interfaceAttachment']['net_id'] = net_id if fixed_ip: body['interfaceAttachment']['fixed_ips'] = [ {'ip_address': fixed_ip}] return self._create('/servers/%s/os-interface' % base.getid(server), body, 'interfaceAttachment') @api_versions.wraps("2.49") def interface_attach(self, server, port_id, net_id, fixed_ip, tag=None): """ Attach a network_interface to an instance. :param server: The :class:`Server` (or its ID) to attach to. :param port_id: The port to attach. The port_id and net_id parameters are mutually exclusive. :param net_id: The ID of the network to attach. :param fixed_ip: The fixed IP addresses. If the fixed_ip is specified, the net_id has to be specified at the same time. :param tag: The tag. """ body = {'interfaceAttachment': {}} if port_id: body['interfaceAttachment']['port_id'] = port_id if net_id: body['interfaceAttachment']['net_id'] = net_id if fixed_ip: body['interfaceAttachment']['fixed_ips'] = [ {'ip_address': fixed_ip}] if tag: body['interfaceAttachment']['tag'] = tag return self._create('/servers/%s/os-interface' % base.getid(server), body, 'interfaceAttachment') def interface_detach(self, server, port_id): """ Detach a network_interface from an instance. :param server: The :class:`Server` (or its ID) to detach from. :param port_id: The port to detach. :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete('/servers/%s/os-interface/%s' % (base.getid(server), port_id)) @api_versions.wraps("2.17") def trigger_crash_dump(self, server): """Trigger crash dump in an instance""" return self._action("trigger_crash_dump", server) def _action(self, action, server, info=None, **kwargs): """ Perform a server "action" -- reboot/rebuild/resize/etc. """ resp, body = self._action_return_resp_and_body(action, server, info=info, **kwargs) return self.convert_into_with_meta(body, resp) def _action_return_resp_and_body(self, action, server, info=None, **kwargs): """ Perform a server "action" and return response headers and body """ body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/servers/%s/action' % base.getid(server) return self.api.client.post(url, body=body) @api_versions.wraps('2.26') def tag_list(self, server): """ Get list of tags from an instance. """ resp, body = self.api.client.get( "/servers/%s/tags" % base.getid(server)) return base.ListWithMeta(body['tags'], resp) @api_versions.wraps('2.26') def delete_tag(self, server, tag): """ Remove single tag from an instance. """ return self._delete("/servers/%s/tags/%s" % (base.getid(server), tag)) @api_versions.wraps('2.26') def delete_all_tags(self, server): """ Remove all tags from an instance. """ return self._delete("/servers/%s/tags" % base.getid(server)) @api_versions.wraps('2.26') def set_tags(self, server, tags): """ Set list of tags to an instance. """ body = {"tags": tags} return self._update("/servers/%s/tags" % base.getid(server), body) @api_versions.wraps('2.26') def add_tag(self, server, tag): """ Add single tag to an instance. """ return self._update( "/servers/%s/tags/%s" % (base.getid(server), tag), None) python-novaclient-9.1.1/novaclient/v2/certs.py0000664000175000017500000000341213165151077022522 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ DEPRECATED Certificate interface. """ import warnings from novaclient import base from novaclient.i18n import _ CERT_DEPRECATION_WARNING = ( _('The nova-cert service is deprecated. This API binding will be removed ' 'in the first major release after the Nova server 16.0.0 Pike release.') ) class Certificate(base.Resource): """DEPRECATED""" def __repr__(self): return ("" % (len(self.private_key) if self.private_key else 0, len(self.data))) class CertificateManager(base.Manager): """DEPRECATED Manage :class:`Certificate` resources.""" resource_class = Certificate def create(self): """DEPRECATED Create a x509 certificate for a user in tenant.""" warnings.warn(CERT_DEPRECATION_WARNING, DeprecationWarning) return self._create('/os-certificates', {}, 'certificate') def get(self): """DEPRECATED Get root certificate.""" warnings.warn(CERT_DEPRECATION_WARNING, DeprecationWarning) return self._get("/os-certificates/root", 'certificate') python-novaclient-9.1.1/novaclient/v2/services.py0000664000175000017500000001253013165151077023226 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ service interface """ from six.moves import urllib from novaclient import api_versions from novaclient import base class Service(base.Resource): def __repr__(self): return "" % self.id def _add_details(self, info): dico = 'resource' in info and info['resource'] or info for (k, v) in dico.items(): setattr(self, k, v) class ServiceManager(base.ManagerWithFind): resource_class = Service def list(self, host=None, binary=None): """ Get a list of services. :param host: destination host name. """ url = "/os-services" filters = [] if host: filters.append(("host", host)) if binary: filters.append(("binary", binary)) if filters: url = "%s?%s" % (url, urllib.parse.urlencode(filters)) return self._list(url, "services") @api_versions.wraps("2.0", "2.10") def _update_body(self, host, binary, disabled_reason=None): body = {"host": host, "binary": binary} if disabled_reason is not None: body["disabled_reason"] = disabled_reason return body @api_versions.wraps("2.11") def _update_body(self, host, binary, disabled_reason=None, force_down=None): body = {"host": host, "binary": binary} if disabled_reason is not None: body["disabled_reason"] = disabled_reason if force_down is not None: body["forced_down"] = force_down return body @api_versions.wraps('2.0', '2.52') def enable(self, host, binary): """Enable the service specified by hostname and binary.""" body = self._update_body(host, binary) return self._update("/os-services/enable", body, "service") @api_versions.wraps('2.53') def enable(self, service_uuid): """Enable the service specified by the service UUID ID. :param service_uuid: The UUID ID of the service to enable. """ return self._update( "/os-services/%s" % service_uuid, {'status': 'enabled'}, "service") @api_versions.wraps('2.0', '2.52') def disable(self, host, binary): """Disable the service specified by hostname and binary.""" body = self._update_body(host, binary) return self._update("/os-services/disable", body, "service") @api_versions.wraps('2.53') def disable(self, service_uuid): """Disable the service specified by the service UUID ID. :param service_uuid: The UUID ID of the service to disable. """ return self._update("/os-services/%s" % service_uuid, {'status': 'disabled'}, "service") @api_versions.wraps('2.0', '2.52') def disable_log_reason(self, host, binary, reason): """Disable the service with reason.""" body = self._update_body(host, binary, reason) return self._update("/os-services/disable-log-reason", body, "service") @api_versions.wraps('2.53') def disable_log_reason(self, service_uuid, reason): """Disable the service with a reason. :param service_uuid: The UUID ID of the service to disable. :param reason: The reason for disabling a service. The minimum length is 1 and the maximum length is 255. """ body = { 'status': 'disabled', 'disabled_reason': reason } return self._update("/os-services/%s" % service_uuid, body, "service") def delete(self, service_id): """Delete a service. :param service_id: Before microversion 2.53, this must be an integer id and may not uniquely the service in a multi-cell deployment. Starting with microversion 2.53 this must be a UUID. """ return self._delete("/os-services/%s" % service_id) @api_versions.wraps("2.11", "2.52") def force_down(self, host, binary, force_down=None): """Force service state to down specified by hostname and binary.""" body = self._update_body(host, binary, force_down=force_down) return self._update("/os-services/force-down", body, "service") @api_versions.wraps("2.53") def force_down(self, service_uuid, force_down): """Update the service's ``forced_down`` field specified by the service UUID ID. :param service_uuid: The UUID ID of the service. :param force_down: Whether or not this service was forced down manually by an administrator. This value is useful to know that some 3rd party has verified the service should be marked down. """ return self._update("/os-services/%s" % service_uuid, {'forced_down': force_down}, "service") python-novaclient-9.1.1/novaclient/v2/hypervisors.py0000664000175000017500000001150113165151077023775 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Hypervisors interface """ from six.moves.urllib import parse from novaclient import api_versions from novaclient import base from novaclient import utils class Hypervisor(base.Resource): NAME_ATTR = 'hypervisor_hostname' def __repr__(self): return "" % self.id class HypervisorManager(base.ManagerWithFind): resource_class = Hypervisor is_alphanum_id_allowed = True def _list_base(self, detailed=True, marker=None, limit=None): path = '/os-hypervisors' if detailed: path += '/detail' params = {} if limit is not None: params['limit'] = int(limit) if marker is not None: params['marker'] = str(marker) path += utils.prepare_query_string(params) return self._list(path, 'hypervisors') @api_versions.wraps("2.0", "2.32") def list(self, detailed=True): """ Get a list of hypervisors. :param detailed: Include a detailed response. """ return self._list_base(detailed=detailed) @api_versions.wraps("2.33") def list(self, detailed=True, marker=None, limit=None): """ Get a list of hypervisors. :param detailed: Include a detailed response. :param marker: Begin returning hypervisors that appear later in the hypervisors list than that represented by this hypervisor ID. Starting with microversion 2.53 the marker must be a UUID hypervisor ID. (optional). :param limit: maximum number of hypervisors to return (optional). """ return self._list_base(detailed=detailed, marker=marker, limit=limit) def search(self, hypervisor_match, servers=False): """ Get a list of matching hypervisors. :param hypervisor_match: The hypervisor host name or a portion of it. The hypervisor hosts are selected with the host name matching this pattern. :param servers: If True, server information is also retrieved. """ # Starting with microversion 2.53, the /servers and /search routes are # deprecated and we get the same results using GET /os-hypervisors # using query parameters for the hostname pattern and servers. if self.api_version >= api_versions.APIVersion('2.53'): url = ('/os-hypervisors?hypervisor_hostname_pattern=%s' % parse.quote(hypervisor_match, safe='')) if servers: url += '&with_servers=True' else: target = 'servers' if servers else 'search' url = ('/os-hypervisors/%s/%s' % (parse.quote(hypervisor_match, safe=''), target)) return self._list(url, 'hypervisors') def get(self, hypervisor): """ Get a specific hypervisor. :param hypervisor: Either a Hypervisor object or an ID. Starting with microversion 2.53 the ID must be a UUID value. """ return self._get("/os-hypervisors/%s" % base.getid(hypervisor), "hypervisor") def uptime(self, hypervisor): """ Get the uptime for a specific hypervisor. :param hypervisor: Either a Hypervisor object or an ID. Starting with microversion 2.53 the ID must be a UUID value. """ return self._get("/os-hypervisors/%s/uptime" % base.getid(hypervisor), "hypervisor") def statistics(self): """ Get hypervisor statistics over all compute nodes. Kept for backwards compatibility, new code should call hypervisor_stats.statistics() instead of hypervisors.statistics() """ return self.api.hypervisor_stats.statistics() class HypervisorStats(base.Resource): def __repr__(self): return ("" % (self.count, "s" if self.count != 1 else "")) class HypervisorStatsManager(base.Manager): resource_class = HypervisorStats def statistics(self): """ Get hypervisor statistics over all compute nodes. """ return self._get("/os-hypervisors/statistics", "hypervisor_statistics") python-novaclient-9.1.1/novaclient/v2/cloudpipe.py0000664000175000017500000000511713165151077023372 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """DEPRECATED Cloudpipe interface.""" import warnings from novaclient import base from novaclient.i18n import _ DEPRECATION_WARNING = ( _('The os-cloudpipe Nova API has been removed. This API binding will be ' 'removed in the first major release after the Nova server 16.0.0 Pike ' 'release.') ) class Cloudpipe(base.Resource): """A cloudpipe instance is a VPN attached to a project's VLAN.""" def __repr__(self): return "" % self.project_id def delete(self): """ DEPRECATED Delete the own cloudpipe instance :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn(DEPRECATION_WARNING) return self.manager.delete(self) class CloudpipeManager(base.ManagerWithFind): """DEPRECATED""" resource_class = Cloudpipe def create(self, project): """DEPRECATED Launch a cloudpipe instance. :param project: UUID of the project (tenant) for the cloudpipe """ warnings.warn(DEPRECATION_WARNING) body = {'cloudpipe': {'project_id': project}} return self._create('/os-cloudpipe', body, 'instance_id', return_raw=True) def list(self): """DEPRECATED Get a list of cloudpipe instances.""" warnings.warn(DEPRECATION_WARNING) return self._list('/os-cloudpipe', 'cloudpipes') def update(self, address, port): """DEPRECATED Configure cloudpipe parameters for the project. Update VPN address and port for all networks associated with the project defined by authentication :param address: IP address :param port: Port number :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn(DEPRECATION_WARNING) body = {'configure_project': {'vpn_ip': address, 'vpn_port': port}} return self._update("/os-cloudpipe/configure-project", body) python-novaclient-9.1.1/novaclient/v2/server_groups.py0000664000175000017500000000632013165151077024310 0ustar jenkinsjenkins00000000000000# Copyright (c) 2014 VMware, 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. """ Server group interface. """ from six.moves.urllib import parse from novaclient import base class ServerGroup(base.Resource): """ A server group. """ def __repr__(self): return '' % self.id def delete(self): """ Delete this server group. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.delete(self.id) class ServerGroupsManager(base.ManagerWithFind): """ Manage :class:`ServerGroup` resources. """ resource_class = ServerGroup def list(self, all_projects=False, limit=None, offset=None): """Get a list of all server groups. :param all_projects: Lists server groups for all projects. (optional) :param limit: Maximum number of server groups to return. (optional) :param offset: Use with `limit` to return a slice of server groups. `offset` is where to start in the groups list. (optional) :returns: list of :class:`ServerGroup`. """ qparams = {} if all_projects: qparams['all_projects'] = bool(all_projects) if limit: qparams['limit'] = int(limit) if offset: qparams['offset'] = int(offset) qparams = sorted(qparams.items(), key=lambda x: x[0]) query_string = "?%s" % parse.urlencode(qparams) if qparams else "" return self._list('/os-server-groups%s' % query_string, 'server_groups') def get(self, id): """Get a specific server group. :param id: The ID of the :class:`ServerGroup` to get. :rtype: :class:`ServerGroup` """ return self._get('/os-server-groups/%s' % id, 'server_group') def delete(self, id): """Delete a specific server group. :param id: The ID of the :class:`ServerGroup` to delete. :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete('/os-server-groups/%s' % id) def create(self, name, policies): """Create (allocate) a server group. :param name: The name of the server group. :param policies: Policy name or a list of exactly one policy name to associate with the server group. :rtype: list of :class:`ServerGroup` """ policies = policies if isinstance(policies, list) else [policies] body = {'server_group': {'name': name, 'policies': policies}} return self._create('/os-server-groups', body, 'server_group') python-novaclient-9.1.1/novaclient/v2/assisted_volume_snapshots.py0000664000175000017500000000340113165151077026710 0ustar jenkinsjenkins00000000000000# Copyright (C) 2013, Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Assisted volume snapshots - to be used by Cinder and not end users. """ import json from novaclient import base class Snapshot(base.Resource): def __repr__(self): return "" % self.id def delete(self): """ Delete this snapshot. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.delete(self) class AssistedSnapshotManager(base.Manager): resource_class = Snapshot def create(self, volume_id, create_info): body = {'snapshot': {'volume_id': volume_id, 'create_info': create_info}} return self._create('/os-assisted-volume-snapshots', body, 'snapshot') def delete(self, snapshot, delete_info): """ Delete a specified assisted volume snapshot. :param snapshot: an assisted volume snapshot to delete :param delete_info: Information for snapshot deletion :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete("/os-assisted-volume-snapshots/%s?delete_info=%s" % (base.getid(snapshot), json.dumps(delete_info))) python-novaclient-9.1.1/novaclient/v2/list_extensions.py0000664000175000017500000000214313165151077024634 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import base class ListExtResource(base.Resource): @property def summary(self): descr = self.description.strip() if not descr: return '??' lines = descr.split("\n") if len(lines) == 1: return lines[0] else: return lines[0] + "..." class ListExtManager(base.Manager): resource_class = ListExtResource def show_all(self): return self._list("/extensions", 'extensions') python-novaclient-9.1.1/novaclient/v2/flavors.py0000664000175000017500000001764013165151077023066 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Flavor interface. """ from oslo_utils import strutils from six.moves.urllib import parse from novaclient import base from novaclient import exceptions from novaclient.i18n import _ from novaclient import utils class Flavor(base.Resource): """A flavor is an available hardware configuration for a server.""" HUMAN_ID = True def __repr__(self): return "" % self.name @property def ephemeral(self): """Provide a user-friendly accessor to OS-FLV-EXT-DATA:ephemeral.""" return self._info.get("OS-FLV-EXT-DATA:ephemeral", 'N/A') @property def is_public(self): """Provide a user-friendly accessor to os-flavor-access:is_public.""" return self._info.get("os-flavor-access:is_public", 'N/A') def get_keys(self): """ Get extra specs from a flavor. :returns: An instance of novaclient.base.DictWithMeta """ resp, body = self.manager.api.client.get( "/flavors/%s/os-extra_specs" % base.getid(self)) return self.manager.convert_into_with_meta(body["extra_specs"], resp) def set_keys(self, metadata): """Set extra specs on a flavor. :param metadata: A dict of key/value pairs to be set """ utils.validate_flavor_metadata_keys(metadata.keys()) body = {'extra_specs': metadata} return self.manager._create( "/flavors/%s/os-extra_specs" % base.getid(self), body, "extra_specs", return_raw=True) def unset_keys(self, keys): """Unset extra specs on a flavor. :param keys: A list of keys to be unset :returns: An instance of novaclient.base.TupleWithMeta """ result = base.TupleWithMeta((), None) for k in keys: ret = self.manager._delete( "/flavors/%s/os-extra_specs/%s" % (base.getid(self), k)) result.append_request_ids(ret.request_ids) return result def delete(self): """ Delete this flavor. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.delete(self) class FlavorManager(base.ManagerWithFind): """Manage :class:`Flavor` resources.""" resource_class = Flavor is_alphanum_id_allowed = True def list(self, detailed=True, is_public=True, marker=None, min_disk=None, min_ram=None, limit=None, sort_key=None, sort_dir=None): """Get a list of all flavors. :param detailed: Whether flavor needs to be return with details (optional). :param is_public: Filter flavors with provided access type (optional). None means give all flavors and only admin has query access to all flavor types. :param marker: Begin returning flavors that appear later in the flavor list than that represented by this flavor id (optional). :param min_disk: Filters the flavors by a minimum disk space, in GiB. :param min_ram: Filters the flavors by a minimum RAM, in MB. :param limit: maximum number of flavors to return (optional). :param sort_key: Flavors list sort key (optional). :param sort_dir: Flavors list sort direction (optional). :returns: list of :class:`Flavor`. """ qparams = {} # is_public is ternary - None means give all flavors. # By default Nova assumes True and gives admins public flavors # and flavors from their own projects only. if marker: qparams['marker'] = str(marker) if min_disk: qparams['minDisk'] = int(min_disk) if min_ram: qparams['minRam'] = int(min_ram) if limit: qparams['limit'] = int(limit) if sort_key: qparams['sort_key'] = str(sort_key) if sort_dir: qparams['sort_dir'] = str(sort_dir) if not is_public: qparams['is_public'] = is_public qparams = sorted(qparams.items(), key=lambda x: x[0]) query_string = "?%s" % parse.urlencode(qparams) if qparams else "" detail = "" if detailed: detail = "/detail" return self._list("/flavors%s%s" % (detail, query_string), "flavors") def get(self, flavor): """Get a specific flavor. :param flavor: The ID of the :class:`Flavor` to get. :returns: :class:`Flavor` """ return self._get("/flavors/%s" % base.getid(flavor), "flavor") def delete(self, flavor): """Delete a specific flavor. :param flavor: The ID of the :class:`Flavor` to get. :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete("/flavors/%s" % base.getid(flavor)) def _build_body(self, name, ram, vcpus, disk, id, swap, ephemeral, rxtx_factor, is_public): return { "flavor": { "name": name, "ram": ram, "vcpus": vcpus, "disk": disk, "id": id, "swap": swap, "OS-FLV-EXT-DATA:ephemeral": ephemeral, "rxtx_factor": rxtx_factor, "os-flavor-access:is_public": is_public, } } def create(self, name, ram, vcpus, disk, flavorid="auto", ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True): """Create a flavor. :param name: Descriptive name of the flavor :param ram: Memory in MB for the flavor :param vcpus: Number of VCPUs for the flavor :param disk: Size of local disk in GB :param flavorid: ID for the flavor (optional). You can use the reserved value ``"auto"`` to have Nova generate a UUID for the flavor in cases where you cannot simply pass ``None``. :param swap: Swap space in MB :param rxtx_factor: RX/TX factor :returns: :class:`Flavor` """ try: ram = int(ram) except (TypeError, ValueError): raise exceptions.CommandError(_("Ram must be an integer.")) try: vcpus = int(vcpus) except (TypeError, ValueError): raise exceptions.CommandError(_("VCPUs must be an integer.")) try: disk = int(disk) except (TypeError, ValueError): raise exceptions.CommandError(_("Disk must be an integer.")) if flavorid == "auto": flavorid = None try: swap = int(swap) except (TypeError, ValueError): raise exceptions.CommandError(_("Swap must be an integer.")) try: ephemeral = int(ephemeral) except (TypeError, ValueError): raise exceptions.CommandError(_("Ephemeral must be an integer.")) try: rxtx_factor = float(rxtx_factor) except (TypeError, ValueError): raise exceptions.CommandError(_("rxtx_factor must be a float.")) try: is_public = strutils.bool_from_string(is_public, True) except Exception: raise exceptions.CommandError(_("is_public must be a boolean.")) body = self._build_body(name, ram, vcpus, disk, flavorid, swap, ephemeral, rxtx_factor, is_public) return self._create("/flavors", body, "flavor") python-novaclient-9.1.1/novaclient/v2/aggregates.py0000664000175000017500000000727013165151077023521 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Aggregate interface.""" from novaclient import base class Aggregate(base.Resource): """An aggregates is a collection of compute hosts.""" def __repr__(self): return "" % self.id def update(self, values): """Update the name and/or availability zone.""" return self.manager.update(self, values) def add_host(self, host): return self.manager.add_host(self, host) def remove_host(self, host): return self.manager.remove_host(self, host) def set_metadata(self, metadata): return self.manager.set_metadata(self, metadata) def delete(self): """ Delete the own aggregate. :returns: An instance of novaclient.base.TupleWithMeta """ return self.manager.delete(self) class AggregateManager(base.ManagerWithFind): resource_class = Aggregate def list(self): """Get a list of os-aggregates.""" return self._list('/os-aggregates', 'aggregates') def create(self, name, availability_zone): """Create a new aggregate.""" body = {'aggregate': {'name': name, 'availability_zone': availability_zone}} return self._create('/os-aggregates', body, 'aggregate') def get(self, aggregate): """Get details of the specified aggregate.""" return self._get('/os-aggregates/%s' % (base.getid(aggregate)), "aggregate") # NOTE:(dtroyer): utils.find_resource() uses manager.get() but we need to # keep the API backward compatible def get_details(self, aggregate): """Get details of the specified aggregate.""" return self.get(aggregate) def update(self, aggregate, values): """Update the name and/or availability zone.""" body = {'aggregate': values} return self._update("/os-aggregates/%s" % base.getid(aggregate), body, "aggregate") def add_host(self, aggregate, host): """Add a host into the Host Aggregate.""" body = {'add_host': {'host': host}} return self._create("/os-aggregates/%s/action" % base.getid(aggregate), body, "aggregate") def remove_host(self, aggregate, host): """Remove a host from the Host Aggregate.""" body = {'remove_host': {'host': host}} return self._create("/os-aggregates/%s/action" % base.getid(aggregate), body, "aggregate") def set_metadata(self, aggregate, metadata): """Set aggregate metadata, replacing the existing metadata.""" body = {'set_metadata': {'metadata': metadata}} return self._create("/os-aggregates/%s/action" % base.getid(aggregate), body, "aggregate") def delete(self, aggregate): """ Delete the specified aggregate. :param aggregate: The aggregate to delete :returns: An instance of novaclient.base.TupleWithMeta """ return self._delete('/os-aggregates/%s' % (base.getid(aggregate))) python-novaclient-9.1.1/novaclient/v2/server_external_events.py0000664000175000017500000000237013165151077026200 0ustar jenkinsjenkins00000000000000# Copyright (C) 2014, 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. """ External event triggering for servers, not to be used by users. """ from novaclient import base class Event(base.Resource): def __repr__(self): return "" % self.name class ServerExternalEventManager(base.Manager): resource_class = Event def create(self, events): """Create one or more server events. :param:events: A list of dictionaries containing 'server_uuid', 'name', 'status', and 'tag' (which may be absent) """ body = {'events': events} return self._create('/os-server-external-events', body, 'events', return_raw=True) python-novaclient-9.1.1/novaclient/v2/networks.py0000664000175000017500000000421613165151077023261 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Network interface. """ from novaclient import base from novaclient import exceptions from novaclient.i18n import _ class Network(base.Resource): """ A network as defined in the Networking (Neutron) API. """ HUMAN_ID = True NAME_ATTR = "name" def __repr__(self): return "" % self.name class NeutronManager(base.Manager): """A manager for name -> id lookups for neutron networks. This uses neutron directly from service catalog. Do not use it for anything else besides that. You have been warned. """ resource_class = Network def find_network(self, name): """Find a network by name (user provided input).""" with self.alternate_service_type( 'network', allowed_types=('network',)): matches = self._list('/v2.0/networks?name=%s' % name, 'networks') num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % ( self.resource_class.__name__, name) raise exceptions.NotFound(404, msg) elif num_matches > 1: msg = (_("Multiple %(class)s matches found for " "'%(name)s', use an ID to be more specific.") % {'class': self.resource_class.__name__.lower(), 'name': name}) raise exceptions.NoUniqueMatch(msg) else: matches[0].append_request_ids(matches.request_ids) return matches[0] python-novaclient-9.1.1/novaclient/v2/hosts.py0000664000175000017500000000714213165151077022546 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ DEPRECATED host interface """ import warnings from novaclient import api_versions from novaclient import base from novaclient.i18n import _ HOSTS_DEPRECATION_WARNING = ( _('The os-hosts API is deprecated. This API binding will be removed ' 'in the first major release after the Nova server 16.0.0 Pike release.') ) class Host(base.Resource): """DEPRECATED""" def __repr__(self): return "" % self.host def _add_details(self, info): dico = 'resource' in info and info['resource'] or info for (k, v) in dico.items(): setattr(self, k, v) @api_versions.wraps("2.0", "2.42") def update(self, values): return self.manager.update(self.host, values) @api_versions.wraps("2.0", "2.42") def startup(self): return self.manager.host_action(self.host, 'startup') @api_versions.wraps("2.0", "2.42") def shutdown(self): return self.manager.host_action(self.host, 'shutdown') @api_versions.wraps("2.0", "2.42") def reboot(self): return self.manager.host_action(self.host, 'reboot') @property def host_name(self): return self.host @host_name.setter def host_name(self, value): # A host from hosts.list() has the attribute "host_name" instead of # "host." This sets "host" if that's the case. Even though it doesn't # exactly mirror the response format, it enables users to work with # host objects from list and non-list operations interchangeably. self.host = value class HostManager(base.ManagerWithFind): resource_class = Host @api_versions.wraps("2.0", "2.42") def get(self, host): """ DEPRECATED Describes cpu/memory/hdd info for host. :param host: destination host name. """ warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning) return self._list("/os-hosts/%s" % host, "host") @api_versions.wraps("2.0", "2.42") def update(self, host, values): """DEPRECATED Update status or maintenance mode for the host.""" warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning) return self._update("/os-hosts/%s" % host, values) @api_versions.wraps("2.0", "2.42") def host_action(self, host, action): """ DEPRECATED Perform an action on a host. :param host: The host to perform an action :param action: The action to perform :returns: An instance of novaclient.base.TupleWithMeta """ warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning) url = '/os-hosts/%s/%s' % (host, action) resp, body = self.api.client.get(url) return base.TupleWithMeta((resp, body), resp) @api_versions.wraps("2.0", "2.42") def list(self, zone=None): warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning) url = '/os-hosts' if zone: url = '/os-hosts?zone=%s' % zone return self._list(url, "hosts") list_all = list python-novaclient-9.1.1/novaclient/v2/client.py0000664000175000017500000002652513165151077022672 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from novaclient import client from novaclient import exceptions from novaclient.i18n import _ from novaclient.v2 import agents from novaclient.v2 import aggregates from novaclient.v2 import assisted_volume_snapshots from novaclient.v2 import availability_zones from novaclient.v2 import cells from novaclient.v2 import certs from novaclient.v2 import cloudpipe from novaclient.v2 import contrib from novaclient.v2 import flavor_access from novaclient.v2 import flavors from novaclient.v2 import hosts from novaclient.v2 import hypervisors from novaclient.v2 import images from novaclient.v2 import instance_action from novaclient.v2 import keypairs from novaclient.v2 import limits from novaclient.v2 import list_extensions from novaclient.v2 import migrations from novaclient.v2 import networks from novaclient.v2 import quota_classes from novaclient.v2 import quotas from novaclient.v2 import server_external_events from novaclient.v2 import server_groups from novaclient.v2 import server_migrations from novaclient.v2 import servers from novaclient.v2 import services from novaclient.v2 import usage from novaclient.v2 import versions from novaclient.v2 import virtual_interfaces from novaclient.v2 import volumes class Client(object): """Top-level object to access the OpenStack Compute API. .. warning:: All scripts and projects should not initialize this class directly. It should be done via `novaclient.client.Client` interface. """ def __init__(self, api_version=None, auth=None, auth_token=None, auth_url=None, cacert=None, cert=None, direct_use=True, endpoint_override=None, endpoint_type='publicURL', extensions=None, http_log_debug=False, insecure=False, logger=None, os_cache=False, password=None, project_domain_id=None, project_domain_name=None, project_id=None, project_name=None, region_name=None, service_name=None, service_type='compute', session=None, timeout=None, timings=False, user_domain_id=None, user_domain_name=None, user_id=None, username=None, **kwargs): """Initialization of Client object. :param api_version: Compute API version :type api_version: novaclient.api_versions.APIVersion :param str auth: Auth :param str auth_token: Auth token :param str auth_url: Auth URL :param str cacert: ca-certificate :param str cert: certificate :param bool direct_use: Inner variable of novaclient. Do not use it outside novaclient. It's restricted. :param str endpoint_override: Bypass URL :param str endpoint_type: Endpoint Type :param str extensions: Extensions :param bool http_log_debug: Enable debugging for HTTP connections :param bool insecure: Allow insecure :param logging.Logger logger: Logger instance to be used for all logging stuff :param str password: User password :param bool os_cache: OS cache :param str project_domain_id: ID of project domain :param str project_domain_name: Name of project domain :param str project_id: Project/Tenant ID :param str project_name: Project/Tenant name :param str region_name: Region Name :param str service_name: Service Name :param str service_type: Service Type :param str session: Session :param float timeout: API timeout, None or 0 disables :param bool timings: Timings :param str user_domain_id: ID of user domain :param str user_domain_name: Name of user domain :param str user_id: User ID :param str username: Username """ if direct_use: raise exceptions.Forbidden( 403, _("'novaclient.v2.client.Client' is not designed to be " "initialized directly. It is inner class of " "novaclient. You should use " "'novaclient.client.Client' instead. Related lp " "bug-report: 1493576")) # NOTE(cyeoh): In the novaclient context (unlike Nova) the # project_id is not the same as the tenant_id. Here project_id # is a name (what the Nova API often refers to as a project or # tenant name) and tenant_id is a UUID (what the Nova API # often refers to as a project_id or tenant_id). self.project_id = project_id self.project_name = project_name self.user_id = user_id self.flavors = flavors.FlavorManager(self) self.flavor_access = flavor_access.FlavorAccessManager(self) self.glance = images.GlanceManager(self) self.limits = limits.LimitsManager(self) self.servers = servers.ServerManager(self) self.versions = versions.VersionManager(self) # extensions self.agents = agents.AgentsManager(self) self.cloudpipe = cloudpipe.CloudpipeManager(self) self.certs = certs.CertificateManager(self) self.volumes = volumes.VolumeManager(self) self.keypairs = keypairs.KeypairManager(self) self.neutron = networks.NeutronManager(self) self.quota_classes = quota_classes.QuotaClassSetManager(self) self.quotas = quotas.QuotaSetManager(self) self.usage = usage.UsageManager(self) self.virtual_interfaces = \ virtual_interfaces.VirtualInterfaceManager(self) self.aggregates = aggregates.AggregateManager(self) self.hosts = hosts.HostManager(self) self.hypervisors = hypervisors.HypervisorManager(self) self.hypervisor_stats = hypervisors.HypervisorStatsManager(self) self.services = services.ServiceManager(self) self.os_cache = os_cache self.availability_zones = \ availability_zones.AvailabilityZoneManager(self) self.server_groups = server_groups.ServerGroupsManager(self) self.server_migrations = \ server_migrations.ServerMigrationsManager(self) # V2.0 extensions: # NOTE(andreykurilin): tenant_networks extension is # deprecated now, which is why it is not initialized by default. self.assisted_volume_snapshots = \ assisted_volume_snapshots.AssistedSnapshotManager(self) self.cells = cells.CellsManager(self) self.instance_action = instance_action.InstanceActionManager(self) self.list_extensions = list_extensions.ListExtManager(self) self.migrations = migrations.MigrationManager(self) self.server_external_events = \ server_external_events.ServerExternalEventManager(self) self.logger = logger or logging.getLogger(__name__) # Add in any extensions... if extensions: for extension in extensions: # do not import extensions from contrib directory twice. if extension.name in contrib.V2_0_EXTENSIONS: # NOTE(andreykurilin): this message looks more like # warning or note, but it is not critical, so let's do # not flood "warning" logging level and use just debug.. self.logger.debug("Nova 2.0 extenstion '%s' is auto-loaded" " by default. You do not need to specify" " it manually.", extension.name) continue if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) self.client = client._construct_http_client( api_version=api_version, auth=auth, auth_token=auth_token, auth_url=auth_url, cacert=cacert, cert=cert, endpoint_override=endpoint_override, endpoint_type=endpoint_type, http_log_debug=http_log_debug, insecure=insecure, logger=self.logger, os_cache=self.os_cache, password=password, project_domain_id=project_domain_id, project_domain_name=project_domain_name, project_id=project_id, project_name=project_name, region_name=region_name, service_name=service_name, service_type=service_type, session=session, timeout=timeout, timings=timings, user_domain_id=user_domain_id, user_domain_name=user_domain_name, user_id=user_id, username=username, **kwargs) @property def api_version(self): return self.client.api_version @api_version.setter def api_version(self, value): self.client.api_version = value @property def projectid(self): self.logger.warning(_("Property 'projectid' is deprecated since " "Ocata. Use 'project_name' instead.")) return self.project_name @property def tenant_id(self): self.logger.warning(_("Property 'tenant_id' is deprecated since " "Ocata. Use 'project_id' instead.")) return self.project_id def __enter__(self): self.logger.warning(_("NovaClient instance can't be used as a " "context manager since Ocata (deprecated " "behaviour) since it is redundant in case of " "SessionClient.")) return self def __exit__(self, t, v, tb): # do not do anything pass def set_management_url(self, url): self.logger.warning( _("Method `set_management_url` is deprecated since Ocata. " "Use `endpoint_override` argument instead while initializing " "novaclient's instance.")) self.client.set_management_url(url) def get_timings(self): return self.client.get_timings() def reset_timings(self): self.client.reset_timings() def authenticate(self): """Authenticate against the server. Normally this is called automatically when you first access the API, but you can call this method to force authentication right now. Returns on success; raises :exc:`exceptions.Unauthorized` if the credentials are wrong. """ self.logger.warning(_( "Method 'authenticate' is deprecated since Ocata.")) python-novaclient-9.1.1/novaclient/v2/availability_zones.py0000664000175000017500000000312013165151077025266 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Availability Zone interface """ from novaclient import base class AvailabilityZone(base.Resource): """An availability zone object.""" NAME_ATTR = 'display_name' def __repr__(self): return "" % self.zoneName class AvailabilityZoneManager(base.ManagerWithFind): """Manage :class:`AvailabilityZone` resources.""" resource_class = AvailabilityZone return_parameter_name = "availabilityZoneInfo" def list(self, detailed=True): """Get a list of all availability zones. :param detailed: If True, list availability zones with details. :returns: list of :class:`AvailabilityZone` """ if detailed is True: return self._list("/os-availability-zone/detail", self.return_parameter_name) else: return self._list("/os-availability-zone", self.return_parameter_name) python-novaclient-9.1.1/novaclient/v2/quotas.py0000664000175000017500000000520213165151077022715 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import base class QuotaSet(base.Resource): @property def id(self): """QuotaSet does not have a 'id' attribute but base.Resource needs it to self-refresh and QuotaSet is indexed by tenant_id. """ return self.tenant_id def update(self, *args, **kwargs): return self.manager.update(self.tenant_id, *args, **kwargs) class QuotaSetManager(base.Manager): resource_class = QuotaSet def get(self, tenant_id, user_id=None, detail=False): url = '/os-quota-sets/%(tenant_id)s' if detail: url += '/detail' if hasattr(tenant_id, 'tenant_id'): tenant_id = tenant_id.tenant_id if user_id: params = {'tenant_id': tenant_id, 'user_id': user_id} url += '?user_id=%(user_id)s' else: params = {'tenant_id': tenant_id} return self._get(url % params, "quota_set") def update(self, tenant_id, **kwargs): user_id = kwargs.pop('user_id', None) body = {'quota_set': kwargs} for key in list(body['quota_set']): if body['quota_set'][key] is None: body['quota_set'].pop(key) if user_id: url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) else: url = '/os-quota-sets/%s' % tenant_id return self._update(url, body, 'quota_set') def defaults(self, tenant_id): return self._get('/os-quota-sets/%s/defaults' % tenant_id, 'quota_set') def delete(self, tenant_id, user_id=None): """ Delete quota for a tenant or for a user. :param tenant_id: A tenant for which quota is to be deleted :param user_id: A user for which quota is to be deleted :returns: An instance of novaclient.base.TupleWithMeta """ if user_id: url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) else: url = '/os-quota-sets/%s' % tenant_id return self._delete(url) python-novaclient-9.1.1/novaclient/v2/quota_classes.py0000664000175000017500000000645213165151077024257 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient import api_versions from novaclient import base class QuotaClassSet(base.Resource): def update(self, *args, **kwargs): return self.manager.update(self.id, *args, **kwargs) class QuotaClassSetManager(base.Manager): resource_class = QuotaClassSet def get(self, class_name): return self._get("/os-quota-class-sets/%s" % (class_name), "quota_class_set") def _update_body(self, **kwargs): return {'quota_class_set': kwargs} # NOTE(mriedem): Before 2.50 the resources you could update was just a # kwargs dict and not validated on the client-side, only on the API server # side. @api_versions.wraps("2.0", "2.49") def update(self, class_name, **kwargs): body = self._update_body(**kwargs) for key in list(body['quota_class_set']): if body['quota_class_set'][key] is None: body['quota_class_set'].pop(key) return self._update('/os-quota-class-sets/%s' % (class_name), body, 'quota_class_set') # NOTE(mriedem): 2.50 does strict validation of the resources you can # specify since the network-related resources are blocked in 2.50. @api_versions.wraps("2.50") def update(self, class_name, instances=None, cores=None, ram=None, metadata_items=None, injected_files=None, injected_file_content_bytes=None, injected_file_path_bytes=None, key_pairs=None, server_groups=None, server_group_members=None): resources = {} if instances is not None: resources['instances'] = instances if cores is not None: resources['cores'] = cores if ram is not None: resources['ram'] = ram if metadata_items is not None: resources['metadata_items'] = metadata_items if injected_files is not None: resources['injected_files'] = injected_files if injected_file_content_bytes is not None: resources['injected_file_content_bytes'] = ( injected_file_content_bytes) if injected_file_path_bytes is not None: resources['injected_file_path_bytes'] = injected_file_path_bytes if key_pairs is not None: resources['key_pairs'] = key_pairs if server_groups is not None: resources['server_groups'] = server_groups if server_group_members is not None: resources['server_group_members'] = server_group_members body = {'quota_class_set': resources} return self._update('/os-quota-class-sets/%s' % class_name, body, 'quota_class_set') python-novaclient-9.1.1/novaclient/v2/flavor_access.py0000664000175000017500000000473413165151077024224 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Flavor access interface.""" from novaclient import base from novaclient.i18n import _ class FlavorAccess(base.Resource): def __repr__(self): return ("" % (self.flavor_id, self.tenant_id)) class FlavorAccessManager(base.ManagerWithFind): """Manage :class:`FlavorAccess` resources.""" resource_class = FlavorAccess def list(self, **kwargs): # NOTE(mriedem): This looks a bit weird, you would normally expect this # method to just take a flavor arg, but it used to erroneously accept # flavor or tenant, but never actually implemented support for listing # flavor access by tenant. We leave the interface unchanged though for # backward compatibility. if kwargs.get('flavor'): return self._list('/flavors/%s/os-flavor-access' % base.getid(kwargs['flavor']), 'flavor_access') raise NotImplementedError(_('Unknown list options.')) def add_tenant_access(self, flavor, tenant): """Add a tenant to the given flavor access list.""" info = {'tenant': tenant} return self._action('addTenantAccess', flavor, info) def remove_tenant_access(self, flavor, tenant): """Remove a tenant from the given flavor access list.""" info = {'tenant': tenant} return self._action('removeTenantAccess', flavor, info) def _action(self, action, flavor, info, **kwargs): """Perform a flavor action.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/flavors/%s/action' % base.getid(flavor) resp, body = self.api.client.post(url, body=body) items = [self.resource_class(self, res) for res in body['flavor_access']] return base.ListWithMeta(items, resp) python-novaclient-9.1.1/novaclient/v2/contrib/0000775000175000017500000000000013165151230022457 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/novaclient/v2/contrib/migrations.py0000664000175000017500000000140313165151077025214 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ migration interface """ from novaclient.v2 import contrib from novaclient.v2 import migrations Migration = migrations.Migration MigrationManager = migrations.MigrationManager contrib.warn() python-novaclient-9.1.1/novaclient/v2/contrib/host_evacuate_live.py0000664000175000017500000000127713165151077026722 0ustar jenkinsjenkins00000000000000# Copyright 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.v2 import contrib contrib.warn(alternative=False) python-novaclient-9.1.1/novaclient/v2/contrib/__init__.py0000664000175000017500000000413013165151077024577 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import inspect import warnings from novaclient.i18n import _ # NOTE(andreykurilin): "tenant_networks" extension excluded # here deliberately. It was deprecated separately from deprecation # extension mechanism and I prefer to not auto-load it by default # (V2_0_EXTENSIONS is designed for such behaviour). V2_0_EXTENSIONS = { 'assisted_volume_snapshots': 'novaclient.v2.assisted_volume_snapshots', 'cells': 'novaclient.v2.cells', 'instance_action': 'novaclient.v2.instance_action', 'list_extensions': 'novaclient.v2.list_extensions', 'migrations': 'novaclient.v2.migrations', 'server_external_events': 'novaclient.v2.server_external_events', } def warn(alternative=True): """Prints warning msg for contrib modules.""" frm = inspect.stack()[1] module_name = inspect.getmodule(frm[0]).__name__ if module_name.startswith("novaclient.v2.contrib."): if alternative: new_module_name = module_name.replace("contrib.", "") msg = _("Module `%(module)s` is deprecated as of OpenStack " "Ocata in favor of `%(new_module)s` and will be " "removed after OpenStack Pike.") % { "module": module_name, "new_module": new_module_name} if not alternative: msg = _("Module `%s` is deprecated as of OpenStack Ocata " "All shell commands were moved to " "`novaclient.v2.shell` and will be automatically " "loaded.") % module_name warnings.warn(msg) python-novaclient-9.1.1/novaclient/v2/contrib/host_evacuate.py0000664000175000017500000000142013165151077025671 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient.v2 import contrib from novaclient.v2 import shell EvacuateHostResponse = shell.EvacuateHostResponse contrib.warn(alternative=False) python-novaclient-9.1.1/novaclient/v2/contrib/cells.py0000664000175000017500000000140113165151077024140 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient.v2 import cells from novaclient.v2 import contrib Cell = cells.Cell CellsManager = cells.CellsManager contrib.warn() python-novaclient-9.1.1/novaclient/v2/contrib/instance_action.py0000664000175000017500000000142513165151077026205 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient.v2 import contrib from novaclient.v2 import instance_action InstanceActionManager = instance_action.InstanceActionManager contrib.warn() python-novaclient-9.1.1/novaclient/v2/contrib/assisted_volume_snapshots.py0000664000175000017500000000173613165151077030361 0ustar jenkinsjenkins00000000000000# Copyright (C) 2013, Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Assisted volume snapshots - to be used by Cinder and not end users. """ from novaclient.v2 import assisted_volume_snapshots from novaclient.v2 import contrib AssistedSnapshotManager = assisted_volume_snapshots.AssistedSnapshotManager Snapshot = assisted_volume_snapshots.Snapshot manager_class = AssistedSnapshotManager name = 'assisted_volume_snapshots' contrib.warn() python-novaclient-9.1.1/novaclient/v2/contrib/host_servers_migrate.py0000664000175000017500000000143413165151077027302 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient.v2 import contrib from novaclient.v2 import shell HostServersMigrateResponse = shell.HostServersMigrateResponse contrib.warn(alternative=False) python-novaclient-9.1.1/novaclient/v2/contrib/list_extensions.py0000664000175000017500000000147413165151077026302 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from novaclient.v2 import contrib from novaclient.v2 import list_extensions ListExtResource = list_extensions.ListExtResource ListExtManager = list_extensions.ListExtManager contrib.warn() python-novaclient-9.1.1/novaclient/v2/contrib/server_external_events.py0000664000175000017500000000172113165151077027637 0ustar jenkinsjenkins00000000000000# Copyright (C) 2014, 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. """ External event triggering for servers, not to be used by users. """ from novaclient.v2 import contrib from novaclient.v2 import server_external_events Event = server_external_events.Event ServerExternalEventManager = server_external_events.ServerExternalEventManager manager_class = ServerExternalEventManager name = 'server_external_events' contrib.warn() python-novaclient-9.1.1/novaclient/v2/contrib/metadata_extensions.py0000664000175000017500000000127513165151077027106 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient.v2 import contrib contrib.warn(alternative=False) python-novaclient-9.1.1/novaclient/v2/contrib/deferred_delete.py0000664000175000017500000000121013165151077026136 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from novaclient.v2 import contrib contrib.warn(alternative=False) python-novaclient-9.1.1/novaclient/exceptions.py0000664000175000017500000002142313165151077023236 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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. """ class UnsupportedVersion(Exception): """Indicates that the user is trying to use an unsupported version of the API. """ pass class UnsupportedConsoleType(Exception): """Indicates that the user is trying to use an unsupported console type when retrieving console urls of servers. """ def __init__(self, console_type): self.message = 'Unsupported console_type "%s"' % console_type class UnsupportedAttribute(AttributeError): """Indicates that the user is trying to transmit the argument to a method, which is not supported by selected version. """ def __init__(self, argument_name, start_version, end_version=None): if end_version: self.message = ( "'%(name)s' argument is only allowed for microversions " "%(start)s - %(end)s." % {"name": argument_name, "start": start_version, "end": end_version}) else: self.message = ( "'%(name)s' argument is only allowed since microversion " "%(start)s." % {"name": argument_name, "start": start_version}) class CommandError(Exception): pass class AuthorizationFailure(Exception): pass class NoUniqueMatch(Exception): pass class NoTokenLookupException(Exception): """This form of authentication does not support looking up endpoints from an existing token. """ pass class EndpointNotFound(Exception): """Could not find Service or Region in Service Catalog.""" pass class AmbiguousEndpoints(Exception): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): self.endpoints = endpoints def __str__(self): return "AmbiguousEndpoints: %s" % repr(self.endpoints) class ConnectionRefused(Exception): """ Connection refused: the server refused the connection. """ def __init__(self, response=None): self.response = response def __str__(self): return "ConnectionRefused: %s" % repr(self.response) class ResourceInErrorState(Exception): """Resource is in the error state.""" def __init__(self, obj): msg = "`%s` resource is in the error state" % obj.__class__.__name__ fault_msg = getattr(obj, "fault", {}).get("message") if fault_msg: msg += "due to '%s'" % fault_msg self.message = "%s." % msg class VersionNotFoundForAPIMethod(Exception): msg_fmt = "API version '%(vers)s' is not supported on '%(method)s' method." def __init__(self, version, method): self.version = version self.method = method def __str__(self): return self.msg_fmt % {"vers": self.version, "method": self.method} class InstanceInDeletedState(Exception): """Instance is in the deleted state.""" pass class ClientException(Exception): """ The base exception class for all exceptions this library raises. """ message = 'Unknown Error' def __init__(self, code, message=None, details=None, request_id=None, url=None, method=None): self.code = code self.message = message or self.__class__.message self.details = details self.request_id = request_id self.url = url self.method = method def __str__(self): formatted_string = "%s (HTTP %s)" % (self.message, self.code) if self.request_id: formatted_string += " (Request-ID: %s)" % self.request_id return formatted_string class RetryAfterException(ClientException): """ The base exception class for ClientExceptions that use Retry-After header. """ def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RetryAfterException, self).__init__(*args, **kwargs) class BadRequest(ClientException): """ HTTP 400 - Bad request: you sent some malformed data. """ http_status = 400 message = "Bad request" class Unauthorized(ClientException): """ HTTP 401 - Unauthorized: bad credentials. """ http_status = 401 message = "Unauthorized" class Forbidden(ClientException): """ HTTP 403 - Forbidden: your credentials don't give you access to this resource. """ http_status = 403 message = "Forbidden" class NotFound(ClientException): """ HTTP 404 - Not found """ http_status = 404 message = "Not found" class MethodNotAllowed(ClientException): """ HTTP 405 - Method Not Allowed """ http_status = 405 message = "Method Not Allowed" class NotAcceptable(ClientException): """ HTTP 406 - Not Acceptable """ http_status = 406 message = "Not Acceptable" class Conflict(ClientException): """ HTTP 409 - Conflict """ http_status = 409 message = "Conflict" class OverLimit(RetryAfterException): """ HTTP 413 - Over limit: you're over the API limits for this time period. """ http_status = 413 message = "Over limit" class RateLimit(RetryAfterException): """ HTTP 429 - Rate limit: you've sent too many requests for this time period. """ http_status = 429 message = "Rate limit" # NotImplemented is a python keyword. class HTTPNotImplemented(ClientException): """ HTTP 501 - Not Implemented: the server does not support this operation. """ http_status = 501 message = "Not Implemented" # In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__() # so we can do this: # _code_map = dict((c.http_status, c) # for c in ClientException.__subclasses__()) # # Instead, we have to hardcode it: _error_classes = [BadRequest, Unauthorized, Forbidden, NotFound, MethodNotAllowed, NotAcceptable, Conflict, OverLimit, RateLimit, HTTPNotImplemented] _code_map = dict((c.http_status, c) for c in _error_classes) class InvalidUsage(RuntimeError): """This function call is invalid in the way you are using this client. Due to the transition to using keystoneauth some function calls are no longer available. You should make a similar call to the session object instead. """ pass def from_response(response, body, url, method=None): """ Return an instance of an ClientException or subclass based on a requests response. Usage:: resp, body = requests.request(...) if resp.status_code != 200: raise exception_from_response(resp, rest.text) """ cls = _code_map.get(response.status_code, ClientException) kwargs = { 'code': response.status_code, 'method': method, 'url': url, 'request_id': None, } if response.headers: kwargs['request_id'] = response.headers.get('x-compute-request-id') if (issubclass(cls, RetryAfterException) and 'retry-after' in response.headers): kwargs['retry_after'] = response.headers.get('retry-after') if body: message = "n/a" details = "n/a" if hasattr(body, 'keys'): # NOTE(mriedem): WebOb<1.6.0 will return a nested dict structure # where the error keys to the message/details/code. WebOb>=1.6.0 # returns just a response body as a single dict, not nested, # so we have to handle both cases (since we can't trust what we're # given with content_type: application/json either way. if 'message' in body: # WebOb 1.6.0 case message = body.get('message') details = body.get('details') else: # WebOb<1.6.0 where we assume there is a single error message # key to the body that has the message and details. error = body[list(body)[0]] message = error.get('message') details = error.get('details') kwargs['message'] = message kwargs['details'] = details return cls(**kwargs) class ResourceNotFound(Exception): """Error in getting the resource.""" pass python-novaclient-9.1.1/novaclient/base.py0000664000175000017500000004657513165151077022006 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Base utilities to build API operation managers and objects on top of. """ import abc import contextlib import copy import hashlib import os import threading from oslo_utils import reflection from oslo_utils import strutils from requests import Response import six from novaclient import exceptions from novaclient import utils def getid(obj): """Get object's ID or object. Abstracts the common pattern of allowing both an object or an object's ID as a parameter when dealing with relationships. """ try: return obj.id except AttributeError: return obj # TODO(aababilov): call run_hooks() in HookableMixin's child classes class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): """Add a new hook of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param hook_func: hook function """ if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): """Run all hooks of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param args: args to be passed to every hook function :param kwargs: kwargs to be passed to every hook function """ hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) class RequestIdMixin(object): """Wrapper class to expose x-openstack-request-id to the caller. """ def request_ids_setup(self): self.x_openstack_request_ids = [] @property def request_ids(self): return self.x_openstack_request_ids def append_request_ids(self, resp): """Add request_ids as an attribute to the object :param resp: Response object or list of Response objects """ if isinstance(resp, list): # Add list of request_ids if response is of type list. for resp_obj in resp: self._append_request_id(resp_obj) elif resp is not None: # Add request_ids if response contains single object. self._append_request_id(resp) def _append_request_id(self, resp): if isinstance(resp, Response): # Extract 'x-openstack-request-id' from headers if # response is a Response object. request_id = (resp.headers.get('x-openstack-request-id') or resp.headers.get('x-compute-request-id')) else: # If resp is of type string or None. request_id = resp if request_id not in self.x_openstack_request_ids: self.x_openstack_request_ids.append(request_id) class Resource(RequestIdMixin): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ HUMAN_ID = False NAME_ATTR = 'name' def __init__(self, manager, info, loaded=False, resp=None): """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 :param resp: Response or list of Response objects """ self.manager = manager self._info = info self._add_details(info) self._loaded = loaded self.request_ids_setup() self.append_request_ids(resp) def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k not in ['manager', 'x_openstack_request_ids']) info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) @property def api_version(self): return self.manager.api_version @property def human_id(self): """Human-readable ID which can be used for bash completion. """ if self.HUMAN_ID: name = getattr(self, self.NAME_ATTR, None) if name is not None: return strutils.to_slug(name) return None 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) # The 'request_ids' attribute has been added, # so store the request id to it instead of _info self.append_request_ids(new.request_ids) 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): # Using not of '==' implementation because the not of # __eq__, when it returns NotImplemented, is returning False. return not self == other def is_loaded(self): return self._loaded def set_loaded(self, val): self._loaded = val def set_info(self, key, value): self._info[key] = value def to_dict(self): return copy.deepcopy(self._info) class Manager(HookableMixin): """Manager for API service. Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None cache_lock = threading.RLock() def __init__(self, api): self.api = api @property def client(self): return self.api.client @property def api_version(self): return self.api.api_version def _list(self, url, response_key, obj_class=None, body=None): if body: resp, body = self.api.client.post(url, body=body) else: resp, body = self.api.client.get(url) if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... if isinstance(data, dict): try: data = data['values'] except KeyError: pass with self.completion_cache('human_id', obj_class, mode="w"): with self.completion_cache('uuid', obj_class, mode="w"): items = [obj_class(self, res, loaded=True) for res in data if res] return ListWithMeta(items, resp) @contextlib.contextmanager def alternate_service_type(self, default, allowed_types=()): original_service_type = self.api.client.service_type if original_service_type in allowed_types: yield else: self.api.client.service_type = default try: yield finally: self.api.client.service_type = original_service_type @contextlib.contextmanager def completion_cache(self, cache_type, obj_class, mode): """The completion cache for bash autocompletion. The completion cache store items that can be used for bash autocompletion, like UUIDs or human-friendly IDs. A resource listing will clear and repopulate the cache. A resource create will append to the cache. Delete is not handled because listings are assumed to be performed often enough to keep the cache reasonably up-to-date. """ # NOTE(wryan): This lock protects read and write access to the # completion caches with self.cache_lock: base_dir = utils.env('NOVACLIENT_UUID_CACHE_DIR', default="~/.novaclient") # NOTE(sirp): Keep separate UUID caches for each username + # endpoint pair username = utils.env('OS_USERNAME', 'NOVA_USERNAME') url = utils.env('OS_URL', 'NOVA_URL') uniqifier = hashlib.md5(username.encode('utf-8') + url.encode('utf-8')).hexdigest() cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier)) try: os.makedirs(cache_dir, 0o755) except OSError: # NOTE(kiall): This is typically either permission denied while # attempting to create the directory, or the # directory already exists. Either way, don't # fail. pass resource = obj_class.__name__.lower() filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-')) path = os.path.join(cache_dir, filename) cache_attr = "_%s_cache" % cache_type try: setattr(self, cache_attr, open(path, mode)) except IOError: # NOTE(kiall): This is typically a permission denied while # attempting to write the cache file. pass try: yield finally: cache = getattr(self, cache_attr, None) if cache: cache.close() delattr(self, cache_attr) def write_to_completion_cache(self, cache_type, val): cache = getattr(self, "_%s_cache" % cache_type, None) if cache: cache.write("%s\n" % val) def _get(self, url, response_key): resp, body = self.api.client.get(url) if response_key is not None: content = body[response_key] else: content = body return self.resource_class(self, content, loaded=True, resp=resp) def _create(self, url, body, response_key, return_raw=False, **kwargs): self.run_hooks('modify_body_for_create', body, **kwargs) resp, body = self.api.client.post(url, body=body) if return_raw: return self.convert_into_with_meta(body[response_key], resp) with self.completion_cache('human_id', self.resource_class, mode="a"): with self.completion_cache('uuid', self.resource_class, mode="a"): return self.resource_class(self, body[response_key], resp=resp) def _delete(self, url): resp, body = self.api.client.delete(url) return self.convert_into_with_meta(body, resp) def _update(self, url, body, response_key=None, **kwargs): self.run_hooks('modify_body_for_update', body, **kwargs) resp, body = self.api.client.put(url, body=body) if body: if response_key: return self.resource_class(self, body[response_key], resp=resp) else: return self.resource_class(self, body, resp=resp) else: return StrWithMeta(body, resp) def convert_into_with_meta(self, item, resp): if isinstance(item, six.string_types): if six.PY2 and isinstance(item, six.text_type): return UnicodeWithMeta(item, resp) else: return StrWithMeta(item, resp) elif isinstance(item, six.binary_type): return BytesWithMeta(item, resp) elif isinstance(item, list): return ListWithMeta(item, resp) elif isinstance(item, tuple): return TupleWithMeta(item, resp) elif item is None: return TupleWithMeta((), resp) else: return DictWithMeta(item, resp) @six.add_metaclass(abc.ABCMeta) class ManagerWithFind(Manager): """Like a `Manager`, but with additional `find()`/`findall()` methods.""" @abc.abstractmethod def list(self): pass def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``.""" matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num_matches > 1: raise exceptions.NoUniqueMatch else: matches[0].append_request_ids(matches.request_ids) return matches[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``.""" found = ListWithMeta([], None) searches = kwargs.items() detailed = True list_kwargs = {} list_argspec = reflection.get_callable_args(self.list) if 'detailed' in list_argspec: detailed = ("human_id" not in kwargs and "name" not in kwargs and "display_name" not in kwargs) list_kwargs['detailed'] = detailed if 'is_public' in list_argspec and 'is_public' in kwargs: is_public = kwargs['is_public'] list_kwargs['is_public'] = is_public if is_public is None: tmp_kwargs = kwargs.copy() del tmp_kwargs['is_public'] searches = tmp_kwargs.items() if 'search_opts' in list_argspec: # pass search_opts in to do server side based filtering. # TODO(jogo) not all search_opts support regex, find way to # identify when to use regex and when to use string matching. # volumes does not support regex while servers does. So when # doing findall on servers some client side filtering is still # needed. if "human_id" in kwargs: list_kwargs['search_opts'] = {"name": kwargs["human_id"]} elif "name" in kwargs: list_kwargs['search_opts'] = {"name": kwargs["name"]} elif "display_name" in kwargs: list_kwargs['search_opts'] = {"name": kwargs["display_name"]} if "all_tenants" in kwargs: all_tenants = kwargs['all_tenants'] list_kwargs['search_opts']['all_tenants'] = all_tenants searches = [(k, v) for k, v in searches if k != 'all_tenants'] if "deleted" in kwargs: deleted = kwargs['deleted'] list_kwargs['search_opts']['deleted'] = deleted searches = [(k, v) for k, v in searches if k != 'deleted'] listing = self.list(**list_kwargs) found.append_request_ids(listing.request_ids) for obj in listing: try: if all(getattr(obj, attr) == value for (attr, value) in searches): if detailed: found.append(obj) else: detail = self.get(obj.id) found.append(detail) found.append_request_ids(detail.request_ids) except AttributeError: continue return found class BootingManagerWithFind(ManagerWithFind): """Like a `ManagerWithFind`, but has the ability to boot servers.""" def _parse_block_device_mapping(self, block_device_mapping): """Parses legacy block device mapping.""" # FIXME(andreykurilin): make it work with block device mapping v2 bdm = [] for device_name, mapping in block_device_mapping.items(): # # The mapping is in the format: # :[]:[]:[] # bdm_dict = {'device_name': device_name} mapping_parts = mapping.split(':') source_id = mapping_parts[0] if len(mapping_parts) == 1: bdm_dict['volume_id'] = source_id elif len(mapping_parts) > 1: source_type = mapping_parts[1] if source_type.startswith('snap'): bdm_dict['snapshot_id'] = source_id else: bdm_dict['volume_id'] = source_id if len(mapping_parts) > 2 and mapping_parts[2]: bdm_dict['volume_size'] = str(int(mapping_parts[2])) if len(mapping_parts) > 3: bdm_dict['delete_on_termination'] = mapping_parts[3] bdm.append(bdm_dict) return bdm class ListWithMeta(list, RequestIdMixin): def __init__(self, values, resp): super(ListWithMeta, self).__init__(values) self.request_ids_setup() self.append_request_ids(resp) class DictWithMeta(dict, RequestIdMixin): def __init__(self, values, resp): super(DictWithMeta, self).__init__(values) self.request_ids_setup() self.append_request_ids(resp) class TupleWithMeta(tuple, RequestIdMixin): def __new__(cls, values, resp): return super(TupleWithMeta, cls).__new__(cls, values) def __init__(self, values, resp): self.request_ids_setup() self.append_request_ids(resp) class StrWithMeta(str, RequestIdMixin): def __new__(cls, value, resp): return super(StrWithMeta, cls).__new__(cls, value) def __init__(self, values, resp): self.request_ids_setup() self.append_request_ids(resp) class BytesWithMeta(six.binary_type, RequestIdMixin): def __new__(cls, value, resp): return super(BytesWithMeta, cls).__new__(cls, value) def __init__(self, values, resp): self.request_ids_setup() self.append_request_ids(resp) if six.PY2: class UnicodeWithMeta(six.text_type, RequestIdMixin): def __new__(cls, value, resp): return super(UnicodeWithMeta, cls).__new__(cls, value) def __init__(self, values, resp): self.request_ids_setup() self.append_request_ids(resp) python-novaclient-9.1.1/novaclient/client.py0000664000175000017500000003500413165151077022333 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, 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. """ OpenStack Client interface. Handles the REST calls and responses. """ import itertools import pkgutil import warnings from keystoneauth1 import adapter from keystoneauth1 import identity from keystoneauth1 import session as ksession from oslo_utils import importutils import pkg_resources osprofiler_profiler = importutils.try_import("osprofiler.profiler") osprofiler_web = importutils.try_import("osprofiler.web") import novaclient from novaclient import api_versions from novaclient import exceptions from novaclient import extension as ext from novaclient.i18n import _ from novaclient import utils # TODO(jichenjc): when an extension in contrib is moved to core extension, # Add the name into the following list, then after last patch merged, # remove the whole function extensions_ignored_name = ["__init__"] class SessionClient(adapter.LegacyJsonAdapter): client_name = 'python-novaclient' client_version = novaclient.__version__ def __init__(self, *args, **kwargs): self.times = [] self.timings = kwargs.pop('timings', False) self.api_version = kwargs.pop('api_version', None) self.api_version = self.api_version or api_versions.APIVersion() super(SessionClient, self).__init__(*args, **kwargs) def request(self, url, method, **kwargs): kwargs.setdefault('headers', kwargs.get('headers', {})) api_versions.update_headers(kwargs["headers"], self.api_version) # NOTE(dbelova): 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()) # NOTE(jamielennox): The standard call raises errors from # keystoneauth1, where we need to raise the novaclient errors. raise_exc = kwargs.pop('raise_exc', True) with utils.record_time(self.times, self.timings, method, url): resp, body = super(SessionClient, self).request(url, method, raise_exc=False, **kwargs) # TODO(andreykurilin): uncomment this line, when we will be able to # check only nova-related calls # api_versions.check_headers(resp, self.api_version) if raise_exc and resp.status_code >= 400: raise exceptions.from_response(resp, body, url, method) return resp, body def get_timings(self): return self.times def reset_timings(self): self.times = [] @property def management_url(self): self.logger.warning( _("Property `management_url` is deprecated for SessionClient. " "Use `endpoint_override` instead.")) return self.endpoint_override @management_url.setter def management_url(self, value): self.logger.warning( _("Property `management_url` is deprecated for SessionClient. " "It should be set via `endpoint_override` variable while class" " initialization.")) self.endpoint_override = value def _construct_http_client(api_version=None, auth=None, auth_token=None, auth_url=None, cacert=None, cert=None, endpoint_override=None, endpoint_type='publicURL', http_log_debug=False, insecure=False, logger=None, os_cache=False, password=None, project_domain_id=None, project_domain_name=None, project_id=None, project_name=None, region_name=None, service_name=None, service_type='compute', session=None, timeout=None, timings=False, user_agent='python-novaclient', user_domain_id=None, user_domain_name=None, user_id=None, username=None, **kwargs): if not session: if not auth and auth_token: auth = identity.Token(auth_url=auth_url, token=auth_token, project_id=project_id, project_name=project_name, project_domain_id=project_domain_id, project_domain_name=project_domain_name) elif not auth: auth = identity.Password(username=username, user_id=user_id, password=password, project_id=project_id, project_name=project_name, auth_url=auth_url, project_domain_id=project_domain_id, project_domain_name=project_domain_name, user_domain_id=user_domain_id, user_domain_name=user_domain_name) session = ksession.Session(auth=auth, verify=(cacert or not insecure), timeout=timeout, cert=cert, user_agent=user_agent) return SessionClient(api_version=api_version, auth=auth, endpoint_override=endpoint_override, interface=endpoint_type, logger=logger, region_name=region_name, service_name=service_name, service_type=service_type, session=session, timings=timings, user_agent=user_agent, **kwargs) def discover_extensions(*args, **kwargs): """Returns the list of extensions, which can be discovered by python path and by entry-point 'novaclient.extension'. """ # TODO(mriedem): Remove support for 'only_contrib' in Queens. if 'only_contrib' in kwargs and kwargs['only_contrib']: warnings.warn(_('Discovering extensions only by contrib path is no ' 'longer supported since all contrib extensions ' 'have either been made required or removed. The ' 'only_contrib argument is deprecated and will be ' 'removed in a future release.')) return [] chain = itertools.chain(_discover_via_python_path(), _discover_via_entry_points()) return [ext.Extension(name, module) for name, module in chain] def _discover_via_python_path(): for (module_loader, name, _ispkg) in pkgutil.iter_modules(): if name.endswith('_python_novaclient_ext'): # NOTE(sdague): needed for python 2.x compatibility. if not hasattr(module_loader, 'load_module'): module_loader = module_loader.find_module(name) module = module_loader.load_module(name) if hasattr(module, 'extension_name'): name = module.extension_name yield name, module def _discover_via_entry_points(): for ep in pkg_resources.iter_entry_points('novaclient.extension'): name = ep.name module = ep.load() yield name, module def _get_client_class_and_version(version): if not isinstance(version, api_versions.APIVersion): version = api_versions.get_api_version(version) else: api_versions.check_major_version(version) if version.is_latest(): raise exceptions.UnsupportedVersion( _("The version should be explicit, not latest.")) return version, importutils.import_class( "novaclient.v%s.client.Client" % version.ver_major) def get_client_class(version): """Returns Client class based on given version.""" warnings.warn(_("'get_client_class' is deprecated. " "Please use `novaclient.client.Client` instead.")) _api_version, client_class = _get_client_class_and_version(version) return client_class def _check_arguments(kwargs, release, deprecated_name, right_name=None): """Process deprecation of arguments. Checks presence of deprecated argument in kwargs, prints proper warning message, renames key to right one it needed. """ if deprecated_name in kwargs: if right_name: if right_name in kwargs: msg = _("The '%(old)s' argument is deprecated in " "%(release)s and its use may result in errors " "in future releases. As '%(new)s' is provided, " "the '%(old)s' argument will be ignored.") % { "old": deprecated_name, "release": release, "new": right_name} kwargs.pop(deprecated_name) else: msg = _("The '%(old)s' argument is deprecated in " "%(release)s and its use may result in errors in " "future releases. Use '%(right)s' instead.") % { "old": deprecated_name, "release": release, "right": right_name} kwargs[right_name] = kwargs.pop(deprecated_name) else: msg = _("The '%(old)s' argument is deprecated in %(release)s " "and its use may result in errors in future " "releases.") % { "old": deprecated_name, "release": release} # just ignore it kwargs.pop(deprecated_name) warnings.warn(msg) def Client(version, username=None, password=None, project_id=None, auth_url=None, **kwargs): """Initialize client object based on given version. HOW-TO: The simplest way to create a client instance is initialization with your credentials:: >>> from novaclient import client >>> nova = client.Client(VERSION, USERNAME, PASSWORD, ... PROJECT_ID, AUTH_URL) Here ``VERSION`` can be a string or ``novaclient.api_versions.APIVersion`` obj. If you prefer string value, you can use ``1.1`` (deprecated now), ``2`` or ``2.X`` (where X is a microversion). Alternatively, you can create a client instance using the keystoneauth session API. See "The novaclient Python API" page at python-novaclient's doc. """ if password: kwargs["password"] = password if project_id: kwargs["project_id"] = project_id _check_arguments(kwargs, "Ocata", "auth_plugin") _check_arguments(kwargs, "Ocata", "auth_system") if "no_cache" in kwargs: _check_arguments(kwargs, "Ocata", "no_cache", right_name="os_cache") # os_cache is not a fully compatible with no_cache, so we need to # apply this custom processing kwargs["os_cache"] = not kwargs["os_cache"] _check_arguments(kwargs, "Ocata", "bypass_url", right_name="endpoint_override") _check_arguments(kwargs, "Ocata", "api_key", right_name="password") # NOTE(andreykurilin): OpenStack projects use two variables with one # meaning: 'endpoint_type' and 'interface'. 'endpoint_type' is an old # name which was used by most OpenStack clients. Later it was replaced by # 'interface' in keystone and later some other clients switched to new # variable name too. In case of novaclient, there is no need to switch to # 'interface' variable name due too several reasons: # - novaclient uses 'endpoint_type' variable name long time ago and # there is no real reasons to switch to new name; # - 'interface' argument is used in several shell subcommands # (for example in `nova floating-ip-bulk-create`), so we will need to # modify these subcommands to not conflict with global flag # 'interface' # Actually, novaclient did not accept 'interface' before, but since we # allow additional arguments(kwargs), someone can use this variable name # and face issue about unexpected behavior. _check_arguments(kwargs, "Ocata", "interface", right_name="endpoint_type") _check_arguments(kwargs, "Ocata", "tenant_name", right_name="project_name") _check_arguments(kwargs, "Ocata", "tenant_id", right_name="project_id") _check_arguments(kwargs, "Ocata", "proxy_tenant_id") _check_arguments(kwargs, "Ocata", "proxy_token") _check_arguments(kwargs, "Ocata", "connection_pool") _check_arguments(kwargs, "Ocata", "volume_service_name") api_version, client_class = _get_client_class_and_version(version) kwargs.pop("direct_use", None) profile = kwargs.pop("profile", None) if osprofiler_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 # nova-api nova.conf, otherwise the latter will fail to check the # request signature and will skip initialization of osprofiler on # the server side. osprofiler_profiler.init(profile) return client_class(api_version=api_version, auth_url=auth_url, direct_use=False, username=username, **kwargs) python-novaclient-9.1.1/novaclient/api_versions.py0000664000175000017500000004046113165151077023561 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import logging import os import pkgutil import re import traceback import warnings from oslo_utils import strutils import novaclient from novaclient import exceptions from novaclient.i18n import _ LOG = logging.getLogger(__name__) if not LOG.handlers: LOG.addHandler(logging.StreamHandler()) LEGACY_HEADER_NAME = "X-OpenStack-Nova-API-Version" HEADER_NAME = "OpenStack-API-Version" SERVICE_TYPE = "compute" _SUBSTITUTIONS = {} _type_error_msg = _("'%(other)s' should be an instance of '%(cls)s'") class APIVersion(object): """This class represents an API Version Request. This class provides convenience methods for manipulation and comparison of version numbers that we need to do to implement microversions. """ def __init__(self, version_str=None): """Create an API version object. :param version_str: String representation of APIVersionRequest. Correct format is 'X.Y', where 'X' and 'Y' are int values. None value should be used to create Null APIVersionRequest, which is equal to 0.0 """ self.ver_major = 0 self.ver_minor = 0 if version_str is not None: match = re.match(r"^([1-9]\d*)\.([1-9]\d*|0|latest)$", version_str) if match: self.ver_major = int(match.group(1)) if match.group(2) == "latest": # NOTE(andreykurilin): Infinity allows to easily determine # latest version and doesn't require any additional checks # in comparison methods. self.ver_minor = float("inf") else: self.ver_minor = int(match.group(2)) else: msg = _("Invalid format of client version '%s'. " "Expected format 'X.Y', where X is a major part and Y " "is a minor part of version.") % version_str raise exceptions.UnsupportedVersion(msg) def __str__(self): """Debug/Logging representation of object.""" if self.is_latest(): return "Latest API Version Major: %s" % self.ver_major return ("API Version Major: %s, Minor: %s" % (self.ver_major, self.ver_minor)) def __repr__(self): if self.is_null(): return "" else: return "" % self.get_string() def is_null(self): return self.ver_major == 0 and self.ver_minor == 0 def is_latest(self): return self.ver_minor == float("inf") def __lt__(self, other): if not isinstance(other, APIVersion): raise TypeError(_type_error_msg % {"other": other, "cls": self.__class__}) return ((self.ver_major, self.ver_minor) < (other.ver_major, other.ver_minor)) def __eq__(self, other): if not isinstance(other, APIVersion): raise TypeError(_type_error_msg % {"other": other, "cls": self.__class__}) return ((self.ver_major, self.ver_minor) == (other.ver_major, other.ver_minor)) def __gt__(self, other): if not isinstance(other, APIVersion): raise TypeError(_type_error_msg % {"other": other, "cls": self.__class__}) return ((self.ver_major, self.ver_minor) > (other.ver_major, other.ver_minor)) def __le__(self, other): return self < other or self == other def __ne__(self, other): return not self.__eq__(other) def __ge__(self, other): return self > other or self == other def matches(self, min_version, max_version): """Matches the version object. Returns whether the version object represents a version greater than or equal to the minimum version and less than or equal to the maximum version. :param min_version: Minimum acceptable version. :param max_version: Maximum acceptable version. :returns: boolean If min_version is null then there is no minimum limit. If max_version is null then there is no maximum limit. If self is null then raise ValueError """ if self.is_null(): raise ValueError(_("Null APIVersion doesn't support 'matches'.")) if max_version.is_null() and min_version.is_null(): return True elif max_version.is_null(): return min_version <= self elif min_version.is_null(): return self <= max_version else: return min_version <= self <= max_version def get_string(self): """Version string representation. Converts object to string representation which if used to create an APIVersion object results in the same version. """ if self.is_null(): raise ValueError( _("Null APIVersion cannot be converted to string.")) elif self.is_latest(): return "%s.%s" % (self.ver_major, "latest") return "%s.%s" % (self.ver_major, self.ver_minor) class VersionedMethod(object): def __init__(self, name, start_version, end_version, func): """Versioning information for a single method :param name: Name of the method :param start_version: Minimum acceptable version :param end_version: Maximum acceptable_version :param func: Method to call Minimum and maximums are inclusive """ self.name = name self.start_version = start_version self.end_version = end_version self.func = func def __str__(self): return ("Version Method %s: min: %s, max: %s" % (self.name, self.start_version, self.end_version)) def __repr__(self): return "" % self.name def get_available_major_versions(): # NOTE(andreykurilin): available clients version should not be # hardcoded, so let's discover them. matcher = re.compile(r"v[0-9]*$") submodules = pkgutil.iter_modules([os.path.dirname(__file__)]) available_versions = [name[1:] for loader, name, ispkg in submodules if matcher.search(name)] return available_versions def check_major_version(api_version): """Checks major part of ``APIVersion`` obj is supported. :raises novaclient.exceptions.UnsupportedVersion: if major part is not supported """ available_versions = get_available_major_versions() if (not api_version.is_null() and str(api_version.ver_major) not in available_versions): if len(available_versions) == 1: msg = _("Invalid client version '%(version)s'. " "Major part should be '%(major)s'") % { "version": api_version.get_string(), "major": available_versions[0]} else: msg = _("Invalid client version '%(version)s'. " "Major part must be one of: '%(major)s'") % { "version": api_version.get_string(), "major": ", ".join(available_versions)} raise exceptions.UnsupportedVersion(msg) def get_api_version(version_string): """Returns checked APIVersion object""" version_string = str(version_string) if strutils.is_int_like(version_string): version_string = "%s.0" % version_string api_version = APIVersion(version_string) check_major_version(api_version) return api_version def _get_server_version_range(client): version = client.versions.get_current() if not hasattr(version, 'version') or not version.version: return APIVersion(), APIVersion() return APIVersion(version.min_version), APIVersion(version.version) def discover_version(client, requested_version): """Discover most recent version supported by API and client. Checks ``requested_version`` and returns the most recent version supported by both the API and the client. :param client: client object :param requested_version: requested version represented by APIVersion obj :returns: APIVersion """ server_start_version, server_end_version = _get_server_version_range( client) if (not requested_version.is_latest() and requested_version != APIVersion('2.0')): if server_start_version.is_null() and server_end_version.is_null(): raise exceptions.UnsupportedVersion( _("Server doesn't support microversions")) if not requested_version.matches(server_start_version, server_end_version): raise exceptions.UnsupportedVersion( _("The specified version isn't supported by server. The valid " "version range is '%(min)s' to '%(max)s'") % { "min": server_start_version.get_string(), "max": server_end_version.get_string()}) return requested_version if requested_version == APIVersion('2.0'): if (server_start_version == APIVersion('2.1') or (server_start_version.is_null() and server_end_version.is_null())): return APIVersion('2.0') else: raise exceptions.UnsupportedVersion( _("The server isn't backward compatible with Nova V2 REST " "API")) if server_start_version.is_null() and server_end_version.is_null(): return APIVersion('2.0') elif novaclient.API_MIN_VERSION > server_end_version: raise exceptions.UnsupportedVersion( _("Server version is too old. The client valid version range is " "'%(client_min)s' to '%(client_max)s'. The server valid version " "range is '%(server_min)s' to '%(server_max)s'.") % { 'client_min': novaclient.API_MIN_VERSION.get_string(), 'client_max': novaclient.API_MAX_VERSION.get_string(), 'server_min': server_start_version.get_string(), 'server_max': server_end_version.get_string()}) elif novaclient.API_MAX_VERSION < server_start_version: raise exceptions.UnsupportedVersion( _("Server version is too new. The client valid version range is " "'%(client_min)s' to '%(client_max)s'. The server valid version " "range is '%(server_min)s' to '%(server_max)s'.") % { 'client_min': novaclient.API_MIN_VERSION.get_string(), 'client_max': novaclient.API_MAX_VERSION.get_string(), 'server_min': server_start_version.get_string(), 'server_max': server_end_version.get_string()}) elif novaclient.API_MAX_VERSION <= server_end_version: return novaclient.API_MAX_VERSION elif server_end_version < novaclient.API_MAX_VERSION: return server_end_version def update_headers(headers, api_version): """Set microversion headers if api_version is not null""" if not api_version.is_null(): version_string = api_version.get_string() if api_version.ver_minor != 0: headers[LEGACY_HEADER_NAME] = version_string if api_version.ver_minor >= 27: headers[HEADER_NAME] = '%s %s' % (SERVICE_TYPE, version_string) def check_headers(response, api_version): """Checks that microversion header is in response.""" if api_version.ver_minor > 0: if (api_version.ver_minor < 27 and LEGACY_HEADER_NAME not in response.headers): _warn_missing_microversion_header(LEGACY_HEADER_NAME) elif (api_version.ver_minor >= 27 and HEADER_NAME not in response.headers): _warn_missing_microversion_header(HEADER_NAME) def _add_substitution(versioned_method): _SUBSTITUTIONS.setdefault(versioned_method.name, []) _SUBSTITUTIONS[versioned_method.name].append(versioned_method) def _get_function_name(func): # NOTE(andreykurilin): Based on the facts: # - Python 2 does not have __qualname__ property as Python 3 has; # - we cannot use im_class here, since we need to obtain name of # function in `wraps` decorator during class initialization # ("im_class" property does not exist at that moment) # we need to write own logic to obtain the full function name which # include module name, owner name(optional) and just function name. filename, _lineno, _name, line = traceback.extract_stack()[-4] module, _file_extension = os.path.splitext(filename) module = module.replace("/", ".") if module.endswith(func.__module__): return "%s.[%s].%s" % (func.__module__, line, func.__name__) else: return "%s.%s" % (func.__module__, func.__name__) def get_substitutions(func_name, api_version=None): if hasattr(func_name, "__id__"): func_name = func_name.__id__ substitutions = _SUBSTITUTIONS.get(func_name, []) if api_version and not api_version.is_null(): return [m for m in substitutions if api_version.matches(m.start_version, m.end_version)] return sorted(substitutions, key=lambda m: m.start_version) # FIXME(mriedem): This breaks any ManagerWithFind.list method that has a # 'detailed' kwarg since the ManagerWithFind.findall won't find the correct # argspec from the wrapped list method. def wraps(start_version, end_version=None): start_version = APIVersion(start_version) if end_version: end_version = APIVersion(end_version) else: end_version = APIVersion("%s.latest" % start_version.ver_major) def decor(func): func.versioned = True name = _get_function_name(func) versioned_method = VersionedMethod(name, start_version, end_version, func) _add_substitution(versioned_method) @functools.wraps(func) def substitution(obj, *args, **kwargs): methods = get_substitutions(name, obj.api_version) if not methods: raise exceptions.VersionNotFoundForAPIMethod( obj.api_version.get_string(), name) return methods[-1].func(obj, *args, **kwargs) # Let's share "arguments" with original method and substitution to # allow put cliutils.arg and wraps decorators in any order if not hasattr(func, 'arguments'): func.arguments = [] substitution.arguments = func.arguments # NOTE(andreykurilin): The way to obtain function's name in Python 2 # bases on traceback(see _get_function_name for details). Since the # right versioned method is used in several places, one object # can have different names. Let's generate name of function one time # and use __id__ property in all other places. substitution.__id__ = name return substitution return decor def _warn_missing_microversion_header(header_name): """Log a warning about missing microversion response header.""" LOG.warning(_( "Your request was processed by a Nova API which does not support " "microversions (%s header is missing from response). " "Warning: Response may be incorrect."), header_name) def deprecated_after(version): decorator = wraps('2.0', version) def wrapper(fn): @functools.wraps(fn) def wrapped(*a, **k): decorated = decorator(fn) if hasattr(fn, '__module__'): mod = fn.__module__ else: mod = a[0].__module__ warnings.warn('The %s module is deprecated ' 'and will be removed.' % mod, DeprecationWarning) return decorated(*a, **k) return wrapped return wrapper python-novaclient-9.1.1/novaclient/utils.py0000664000175000017500000003256213165151100022206 0ustar jenkinsjenkins00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import json import os import re import textwrap import time import uuid from oslo_serialization import jsonutils from oslo_utils import encodeutils import prettytable import six from six.moves.urllib import parse from novaclient import exceptions from novaclient.i18n import _ VALID_KEY_REGEX = re.compile(r"[\w\.\- :]+$", re.UNICODE) 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 get_service_type(f): """Retrieves service type from function.""" return getattr(f, 'service_type', None) 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 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 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 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 add_resource_manager_extra_kwargs_hook(f, hook): """Add hook to bind CLI arguments to ResourceManager calls. The `do_foo` calls in shell.py will receive CLI args and then in turn pass them through to the ResourceManager. Before passing through the args, the hooks registered here will be called, giving us a chance to add extra kwargs (taken from the command-line) to what's passed to the ResourceManager. """ if not hasattr(f, 'resource_manager_kwargs_hooks'): f.resource_manager_kwargs_hooks = [] names = [h.__name__ for h in f.resource_manager_kwargs_hooks] if hook.__name__ not in names: f.resource_manager_kwargs_hooks.append(hook) def get_resource_manager_extra_kwargs(f, args, allow_conflicts=False): """Return extra_kwargs by calling resource manager kwargs hooks.""" hooks = getattr(f, "resource_manager_kwargs_hooks", []) extra_kwargs = {} for hook in hooks: hook_kwargs = hook(args) hook_name = hook.__name__ conflicting_keys = set(hook_kwargs.keys()) & set(extra_kwargs.keys()) if conflicting_keys and not allow_conflicts: msg = (_("Hook '%(hook_name)s' is attempting to redefine " "attributes '%(conflicting_keys)s'") % {'hook_name': hook_name, 'conflicting_keys': conflicting_keys}) raise exceptions.NoUniqueMatch(msg) extra_kwargs.update(hook_kwargs) return extra_kwargs def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) def pretty_choice_dict(d): """Returns a formatted dict as 'key=value'.""" return pretty_choice_list(['%s=%s' % (k, d[k]) for k in sorted(d.keys())]) def print_list(objs, fields, formatters={}, sortby_index=None): if sortby_index is None: sortby = None else: sortby = fields[sortby_index] mixed_case_fields = ['serverId'] pt = prettytable.PrettyTable([f for f in fields], caching=False) pt.align = 'l' for o in objs: row = [] for field in fields: if field in formatters: row.append(formatters[field](o)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') if data is None: data = '-' # '\r' would break the table, so remove it. data = six.text_type(data).replace("\r", "") row.append(data) pt.add_row(row) if sortby is not None: result = encodeutils.safe_encode(pt.get_string(sortby=sortby)) else: result = encodeutils.safe_encode(pt.get_string()) if six.PY3: result = result.decode() print(result) def _flatten(data, prefix=None): """Flatten a dict, using name as a prefix for the keys of dict. >>> _flatten('cpu_info', {'arch':'x86_64'}) [('cpu_info_arch': 'x86_64')] """ if isinstance(data, dict): for key, value in data.items(): new_key = '%s_%s' % (prefix, key) if prefix else key if isinstance(value, (dict, list)) and value: for item in _flatten(value, new_key): yield item else: yield new_key, value else: yield prefix, data def flatten_dict(data): """Return a new dict whose sub-dicts have been merged into the original. Each of the parents keys are prepended to the child's to prevent collisions. Any string elements will be JSON parsed before flattening. >>> flatten_dict({'service': {'host':'cloud9@compute-068', 'id': 143}}) {'service_host': colud9@compute-068', 'service_id': 143} """ data = data.copy() # Try and decode any nested JSON structures. for key, value in data.items(): if isinstance(value, six.string_types): try: data[key] = json.loads(value) except ValueError: pass return dict(_flatten(data)) def print_dict(d, dict_property="Property", dict_value="Value", wrap=0): pt = prettytable.PrettyTable([dict_property, dict_value], caching=False) pt.align = 'l' for k, v in sorted(d.items()): # convert dict to str to check length if isinstance(v, (dict, list)): v = jsonutils.dumps(v, ensure_ascii=False) 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 or '\r' in v): # '\r' would break the table, so remove it. if '\r' in v: v = v.replace('\r', '') lines = v.strip().split(r'\n') col1 = k for line in lines: pt.add_row([col1, line]) col1 = '' else: if v is None: v = '-' pt.add_row([k, v]) result = encodeutils.safe_encode(pt.get_string()) if six.PY3: result = result.decode() print(result) def find_resource(manager, name_or_id, wrap_exception=True, **find_args): """Helper for the _find_* methods.""" # for str id which is not uuid (for Flavor, Keypair and hypervsior in cells # environments search currently) if getattr(manager, 'is_alphanum_id_allowed', False): try: return manager.get(name_or_id) except exceptions.NotFound: pass # first try to get entity as uuid try: tmp_id = encodeutils.safe_encode(name_or_id) if six.PY3: tmp_id = tmp_id.decode() uuid.UUID(tmp_id) return manager.get(tmp_id) except (TypeError, ValueError, exceptions.NotFound): pass # then try to get entity as name try: try: resource = getattr(manager, 'resource_class', None) name_attr = resource.NAME_ATTR if resource else 'name' kwargs = {name_attr: name_or_id} kwargs.update(find_args) return manager.find(**kwargs) except exceptions.NotFound: pass # then try to find entity by human_id try: return manager.find(human_id=name_or_id, **find_args) except exceptions.NotFound: pass except exceptions.NoUniqueMatch: msg = (_("Multiple %(class)s matches found for '%(name)s', use an ID " "to be more specific.") % {'class': manager.resource_class.__name__.lower(), 'name': name_or_id}) if wrap_exception: raise exceptions.CommandError(msg) raise exceptions.NoUniqueMatch(msg) # finally try to get entity as integer id try: return manager.get(int(name_or_id)) except (TypeError, ValueError, exceptions.NotFound): msg = (_("No %(class)s with a name or ID of '%(name)s' exists.") % {'class': manager.resource_class.__name__.lower(), 'name': name_or_id}) if wrap_exception: raise exceptions.CommandError(msg) raise exceptions.NotFound(404, msg) def format_servers_list_networks(server): output = [] for (network, addresses) in server.networks.items(): if len(addresses) == 0: continue addresses_csv = ', '.join(addresses) group = "%s=%s" % (network, addresses_csv) output.append(group) return '; '.join(output) def format_security_groups(groups): return ', '.join(group['name'] for group in groups) 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 safe_issubclass(*args): """Like issubclass, but will just return False if not a class.""" try: if issubclass(*args): return True except TypeError: pass return False def do_action_on_many(action, resources, success_msg, error_msg): """Helper to run an action on many resources.""" failure_flag = False for resource in resources: try: action(resource) print(success_msg % resource) except Exception as e: failure_flag = True print(encodeutils.safe_encode(six.text_type(e))) if failure_flag: raise exceptions.CommandError(error_msg) def is_integer_like(val): """Returns validation of a value as an integer.""" try: int(val) return True except (TypeError, ValueError, AttributeError): return False def validate_flavor_metadata_keys(keys): for key in keys: valid_name = VALID_KEY_REGEX.match(key) if not valid_name: msg = _('Invalid key: "%s". Keys may only contain letters, ' 'numbers, spaces, underscores, periods, colons and ' 'hyphens.') raise exceptions.CommandError(msg % key) @contextlib.contextmanager def record_time(times, enabled, *args): """Record the time of a specific action. :param times: A list of tuples holds time data. :type times: list :param enabled: Whether timing is enabled. :type enabled: bool :param *args: Other data to be stored besides time data, these args will be joined to a string. """ if not enabled: yield else: start = time.time() yield end = time.time() times.append((' '.join(args), start, end)) def prepare_query_string(params): """Convert dict params to query string""" params = sorted(params.items(), key=lambda x: x[0]) return '?%s' % parse.urlencode(params) if params else '' python-novaclient-9.1.1/python_novaclient.egg-info/0000775000175000017500000000000013165151230023563 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/python_novaclient.egg-info/requires.txt0000664000175000017500000000032313165151227026167 0ustar jenkinsjenkins00000000000000pbr!=2.1.0,>=2.0.0 keystoneauth1>=3.0.1 iso8601>=0.1.11 oslo.i18n!=3.15.2,>=2.1.0 oslo.serialization!=2.19.1,>=1.10.0 oslo.utils>=3.20.0 PrettyTable<0.8,>=0.7.1 simplejson>=2.2.0 six>=1.9.0 Babel!=2.4.0,>=2.3.4 python-novaclient-9.1.1/python_novaclient.egg-info/pbr.json0000664000175000017500000000005613165151227025250 0ustar jenkinsjenkins00000000000000{"git_version": "a1c0074", "is_release": true}python-novaclient-9.1.1/python_novaclient.egg-info/top_level.txt0000664000175000017500000000001313165151227026315 0ustar jenkinsjenkins00000000000000novaclient python-novaclient-9.1.1/python_novaclient.egg-info/dependency_links.txt0000664000175000017500000000000113165151227027637 0ustar jenkinsjenkins00000000000000 python-novaclient-9.1.1/python_novaclient.egg-info/not-zip-safe0000664000175000017500000000000113165151217026016 0ustar jenkinsjenkins00000000000000 python-novaclient-9.1.1/python_novaclient.egg-info/entry_points.txt0000664000175000017500000000006013165151227027063 0ustar jenkinsjenkins00000000000000[console_scripts] nova = novaclient.shell:main python-novaclient-9.1.1/python_novaclient.egg-info/PKG-INFO0000664000175000017500000000556513165151227024701 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-novaclient Version: 9.1.1 Summary: Client library for OpenStack Compute API Home-page: http://docs.openstack.org/developer/python-novaclient Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: Apache License, Version 2.0 Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/python-novaclient.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on ============================================ Python bindings to the OpenStack Compute API ============================================ .. image:: https://img.shields.io/pypi/v/python-novaclient.svg :target: https://pypi.python.org/pypi/python-novaclient/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/python-novaclient.svg :target: https://pypi.python.org/pypi/python-novaclient/ :alt: Downloads This is a client for the OpenStack Compute API. It provides a Python API (the ``novaclient`` module) and a command-line script (``nova``). Each implements 100% of the OpenStack Compute API. * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Launchpad project`_ - release management * `Blueprints`_ - feature specifications * `Bugs`_ - issue tracking * `Source`_ * `Specs`_ * `How to Contribute`_ .. _PyPi: https://pypi.python.org/pypi/python-novaclient .. _Online Documentation: http://docs.openstack.org/python-novaclient .. _Launchpad project: https://launchpad.net/python-novaclient .. _Blueprints: https://blueprints.launchpad.net/python-novaclient .. _Bugs: https://bugs.launchpad.net/python-novaclient .. _Source: https://git.openstack.org/cgit/openstack/python-novaclient .. _How to Contribute: http://docs.openstack.org/infra/manual/developers.html .. _Specs: http://specs.openstack.org/openstack/nova-specs/ Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 python-novaclient-9.1.1/python_novaclient.egg-info/SOURCES.txt0000664000175000017500000002706613165151230025462 0ustar jenkinsjenkins00000000000000.coveragerc .mailmap .testr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel.cfg bindep.txt requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt tox.ini doc/Makefile doc/source/conf.py doc/source/index.rst doc/source/cli/index.rst doc/source/cli/nova.rst doc/source/contributor/index.rst doc/source/contributor/testing.rst doc/source/reference/deprecation-policy.rst doc/source/reference/index.rst doc/source/reference/api/index.rst doc/source/user/index.rst doc/source/user/shell.rst novaclient/__init__.py novaclient/api_versions.py novaclient/base.py novaclient/client.py novaclient/crypto.py novaclient/exceptions.py novaclient/extension.py novaclient/i18n.py novaclient/shell.py novaclient/utils.py novaclient/tests/__init__.py novaclient/tests/functional/README.rst novaclient/tests/functional/__init__.py novaclient/tests/functional/base.py novaclient/tests/functional/clouds.yaml.sample novaclient/tests/functional/test_auth.py novaclient/tests/functional/api/__init__.py novaclient/tests/functional/api/test_servers.py novaclient/tests/functional/hooks/post_test_hook.sh novaclient/tests/functional/v2/__init__.py novaclient/tests/functional/v2/fake_crypto.py novaclient/tests/functional/v2/test_aggregates.py novaclient/tests/functional/v2/test_consoles.py novaclient/tests/functional/v2/test_device_tagging.py novaclient/tests/functional/v2/test_extended_attributes.py novaclient/tests/functional/v2/test_flavor_access.py novaclient/tests/functional/v2/test_hypervisors.py novaclient/tests/functional/v2/test_image_meta.py novaclient/tests/functional/v2/test_instance_action.py novaclient/tests/functional/v2/test_instances.py novaclient/tests/functional/v2/test_keypairs.py novaclient/tests/functional/v2/test_networks.py novaclient/tests/functional/v2/test_os_services.py novaclient/tests/functional/v2/test_quota_classes.py novaclient/tests/functional/v2/test_quotas.py novaclient/tests/functional/v2/test_resize.py novaclient/tests/functional/v2/test_server_groups.py novaclient/tests/functional/v2/test_servers.py novaclient/tests/functional/v2/test_trigger_crash_dump.py novaclient/tests/functional/v2/test_usage.py novaclient/tests/functional/v2/legacy/__init__.py novaclient/tests/functional/v2/legacy/test_consoles.py novaclient/tests/functional/v2/legacy/test_extended_attributes.py novaclient/tests/functional/v2/legacy/test_flavor_access.py novaclient/tests/functional/v2/legacy/test_hypervisors.py novaclient/tests/functional/v2/legacy/test_instances.py novaclient/tests/functional/v2/legacy/test_keypairs.py novaclient/tests/functional/v2/legacy/test_os_services.py novaclient/tests/functional/v2/legacy/test_quotas.py novaclient/tests/functional/v2/legacy/test_readonly_nova.py novaclient/tests/functional/v2/legacy/test_server_groups.py novaclient/tests/functional/v2/legacy/test_servers.py novaclient/tests/functional/v2/legacy/test_usage.py novaclient/tests/unit/__init__.py novaclient/tests/unit/fake_actions_module.py novaclient/tests/unit/fakes.py novaclient/tests/unit/idfake.pem novaclient/tests/unit/test_api_versions.py novaclient/tests/unit/test_base.py novaclient/tests/unit/test_client.py novaclient/tests/unit/test_discover.py novaclient/tests/unit/test_exceptions.py novaclient/tests/unit/test_shell.py novaclient/tests/unit/test_utils.py novaclient/tests/unit/utils.py novaclient/tests/unit/fixture_data/__init__.py novaclient/tests/unit/fixture_data/agents.py novaclient/tests/unit/fixture_data/aggregates.py novaclient/tests/unit/fixture_data/availability_zones.py novaclient/tests/unit/fixture_data/base.py novaclient/tests/unit/fixture_data/certs.py novaclient/tests/unit/fixture_data/client.py novaclient/tests/unit/fixture_data/cloudpipe.py novaclient/tests/unit/fixture_data/floatingips.py novaclient/tests/unit/fixture_data/hosts.py novaclient/tests/unit/fixture_data/hypervisors.py novaclient/tests/unit/fixture_data/images.py novaclient/tests/unit/fixture_data/keypairs.py novaclient/tests/unit/fixture_data/limits.py novaclient/tests/unit/fixture_data/quotas.py novaclient/tests/unit/fixture_data/server_groups.py novaclient/tests/unit/fixture_data/server_migrations.py novaclient/tests/unit/fixture_data/servers.py novaclient/tests/unit/v2/__init__.py novaclient/tests/unit/v2/fakes.py novaclient/tests/unit/v2/test_agents.py novaclient/tests/unit/v2/test_aggregates.py novaclient/tests/unit/v2/test_assisted_volume_snapshots.py novaclient/tests/unit/v2/test_availability_zone.py novaclient/tests/unit/v2/test_cells.py novaclient/tests/unit/v2/test_certs.py novaclient/tests/unit/v2/test_client.py novaclient/tests/unit/v2/test_cloudpipe.py novaclient/tests/unit/v2/test_flavor_access.py novaclient/tests/unit/v2/test_flavors.py novaclient/tests/unit/v2/test_hosts.py novaclient/tests/unit/v2/test_hypervisors.py novaclient/tests/unit/v2/test_images.py novaclient/tests/unit/v2/test_instance_actions.py novaclient/tests/unit/v2/test_keypairs.py novaclient/tests/unit/v2/test_limits.py novaclient/tests/unit/v2/test_list_extensions.py novaclient/tests/unit/v2/test_migrations.py novaclient/tests/unit/v2/test_quota_classes.py novaclient/tests/unit/v2/test_quotas.py novaclient/tests/unit/v2/test_server_external_events.py novaclient/tests/unit/v2/test_server_groups.py novaclient/tests/unit/v2/test_server_migrations.py novaclient/tests/unit/v2/test_servers.py novaclient/tests/unit/v2/test_services.py novaclient/tests/unit/v2/test_shell.py novaclient/tests/unit/v2/test_usage.py novaclient/tests/unit/v2/test_versions.py novaclient/tests/unit/v2/test_volumes.py novaclient/tests/unit/v2/testfile.txt novaclient/v2/__init__.py novaclient/v2/agents.py novaclient/v2/aggregates.py novaclient/v2/assisted_volume_snapshots.py novaclient/v2/availability_zones.py novaclient/v2/cells.py novaclient/v2/certs.py novaclient/v2/client.py novaclient/v2/cloudpipe.py novaclient/v2/flavor_access.py novaclient/v2/flavors.py novaclient/v2/hosts.py novaclient/v2/hypervisors.py novaclient/v2/images.py novaclient/v2/instance_action.py novaclient/v2/keypairs.py novaclient/v2/limits.py novaclient/v2/list_extensions.py novaclient/v2/migrations.py novaclient/v2/networks.py novaclient/v2/quota_classes.py novaclient/v2/quotas.py novaclient/v2/server_external_events.py novaclient/v2/server_groups.py novaclient/v2/server_migrations.py novaclient/v2/servers.py novaclient/v2/services.py novaclient/v2/shell.py novaclient/v2/usage.py novaclient/v2/versions.py novaclient/v2/virtual_interfaces.py novaclient/v2/volumes.py novaclient/v2/contrib/__init__.py novaclient/v2/contrib/assisted_volume_snapshots.py novaclient/v2/contrib/cells.py novaclient/v2/contrib/deferred_delete.py novaclient/v2/contrib/host_evacuate.py novaclient/v2/contrib/host_evacuate_live.py novaclient/v2/contrib/host_servers_migrate.py novaclient/v2/contrib/instance_action.py novaclient/v2/contrib/list_extensions.py novaclient/v2/contrib/metadata_extensions.py novaclient/v2/contrib/migrations.py novaclient/v2/contrib/server_external_events.py python_novaclient.egg-info/PKG-INFO python_novaclient.egg-info/SOURCES.txt python_novaclient.egg-info/dependency_links.txt python_novaclient.egg-info/entry_points.txt python_novaclient.egg-info/not-zip-safe python_novaclient.egg-info/pbr.json python_novaclient.egg-info/requires.txt python_novaclient.egg-info/top_level.txt releasenotes/notes/.placeholder releasenotes/notes/add-osprofiler-support-cc9dd228242e9919.yaml releasenotes/notes/add-user-agent-string-db77210dfd3ec671.yaml releasenotes/notes/bp-deprecate-image-meta-proxy-api-1483b75cf73b021e.yaml releasenotes/notes/clarify-project-id-variable-5832698315000438.yaml releasenotes/notes/deprecate-baremetal-d67f58a2986b3565.yaml releasenotes/notes/deprecate-certs-1558d8e3b7888938.yaml releasenotes/notes/deprecate-cloudpipe-670202797fdf97b6.yaml releasenotes/notes/deprecate-connection-pool-arg-cef35346d5ebf40c.yaml releasenotes/notes/deprecate-network-cli-f0a539528be594d3.yaml releasenotes/notes/deprecate-no-cache-arg-7814806b4f79c1b9.yaml releasenotes/notes/deprecate-proxy-args-a3f4e224f7664ff8.yaml releasenotes/notes/deprecate-service-binary-arg-2d5c446f5a2409a7.yaml releasenotes/notes/deprecate-volume-service-name-arg-4c65e8866f9624dd.yaml releasenotes/notes/deprecate_cell_name_arg-eb34cb7c43cfcb89.yaml releasenotes/notes/deprecate_contrib_extensions-0ec70c070b09eedb.yaml releasenotes/notes/drop-deprecated-aggregate-update-args-17bd019f4be34b18.yaml releasenotes/notes/fix-booting-with-multiple-nics-c6e5885b948d35ba.yaml releasenotes/notes/fix-tag-attribute-disappearing-25483a80f548ef35.yaml releasenotes/notes/fix-token-auth-6c48c63a759f51d5.yaml releasenotes/notes/fixed-ListExtResource-given-in-place-of-ListExtManager-a759a27079d16a44.yaml releasenotes/notes/global_request_id-26f4e4301f84d403.yaml releasenotes/notes/image-api-deprecation-41944dc6fc024918.yaml releasenotes/notes/instance-uuid-flag-in-migration-list-5d2fed7657d3def5.yaml releasenotes/notes/keystoneauth-8ec1e6be14cdbae3.yaml releasenotes/notes/log-request-id-ce106497e0520fad.yaml releasenotes/notes/make-console-public-0c776bfda240cd9d.yaml releasenotes/notes/microversion-2.37-d03da96406a45e67.yaml releasenotes/notes/microversion-v2_28-abf653ae5cf5c4a9.yaml releasenotes/notes/microversion-v2_31-3e1a16eb5eb53f59.yaml releasenotes/notes/microversion-v2_33-10d12ea3b25839e8.yaml releasenotes/notes/microversion-v2_34-a9c5601811152964.yaml releasenotes/notes/microversion-v2_35-537619a43278fbb5.yaml releasenotes/notes/microversion-v2_38-0618fe2b3c7f96f9.yaml releasenotes/notes/microversion-v2_40-484adba0806b08bf.yaml releasenotes/notes/microversion-v2_41-6df7a5a66a9ded35.yaml releasenotes/notes/microversion-v2_43-76db2ac463b431e4.yaml releasenotes/notes/microversion-v2_44-d60c8834e436ad3d.yaml releasenotes/notes/microversion-v2_45-1bfcae3914280534.yaml releasenotes/notes/microversion-v2_47-4aa54fbbd519e421.yaml releasenotes/notes/microversion-v2_49-56bde596ee13366d.yaml releasenotes/notes/microversion-v2_50-4f484658d66d01aa.yaml releasenotes/notes/microversion-v2_52-2fe81b3bf2e4b4ea.yaml releasenotes/notes/microversion-v2_53-3463b546a38c5f84.yaml releasenotes/notes/no-glance-proxy-5c13001a4b13e8ce.yaml releasenotes/notes/no-neutron-proxy-18fd54febe939a6b.yaml releasenotes/notes/pike-rm-deprecated-img-d58e9ae2d774cbfc.yaml releasenotes/notes/pike-rm-deprecated-net-272aeb62b329a5bc.yaml releasenotes/notes/remove-auth-system-b2cd247b8a312b72.yaml releasenotes/notes/remove-deprecated-option-in-3.3.0-82a413157838570d.yaml releasenotes/notes/remove-deprecated-option-in-9.0.0-bc76629d28f1d4c4.yaml releasenotes/notes/remove-py26-support-f31379e86f40d975.yaml releasenotes/notes/remove_api_v_1_1-88b3f18ce1423b46.yaml releasenotes/notes/rename-apikey-to-password-735588d841efa49e.yaml releasenotes/notes/rename-bypass-url-42cd96956a6bc6dc.yaml releasenotes/notes/restrict-args-for-novaclient-ep-491098c3634365be.yaml releasenotes/notes/restrict-direct-use-of-v2client-c8e1ee2afefec5a1.yaml releasenotes/notes/restrict-interface-parameter-e5fe166f39ba0935.yaml releasenotes/notes/return-request-id-to-caller-52c5423794b33f8b.yaml releasenotes/notes/rm-baremetal-cli-api-fbc8c242d48cd2fb.yaml releasenotes/notes/rm-deprecated-commands-options-ocata-00f249810e5bdf97.yaml releasenotes/notes/switch-to-sessionclient-aa49d16599fea570.yaml releasenotes/notes/volume-cli-removal-ffcb94421a356042.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/liberty.rst releasenotes/source/mitaka.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po tools/nova.bash_completion tools/nova.zsh_completion tools/pretty_tox.sh tools/tox_install.sh tools/with_venv.shpython-novaclient-9.1.1/run_tests.sh0000775000175000017500000000152513165151100020712 0ustar jenkinsjenkins00000000000000#!/bin/bash set -eu cat </dev/null)}) _nova_opts=(${nbc:#-*}) _nova_flags=(${(M)nbc:#-*}) _nova_opt_exp=${${nbc:#-*}// /|} cur=$words[CURRENT] prev=$words[(( CURRENT - 1 ))] _checkcomp(){ for word in $words[@]; do if [[ -n ${_nova_opts[(r)$word]} ]]; then return 0 fi done return 1 } echo $_nova_opts[@] |grep --color nova if [[ "$prev" != "help" ]] && _checkcomp; then COMPLETION_CACHE=(~/.novaclient/*/*-cache) cflags=($_nova_flags[@] ${(ps: :)$(cat $COMPLETION_CACHE 2>/dev/null)}) compadd "$@" -d $cflags[@] else compadd "$@" -d $_nova_opts[@] fi python-novaclient-9.1.1/tools/with_venv.sh0000775000175000017500000000012413165151100022027 0ustar jenkinsjenkins00000000000000#!/bin/bash TOOLS=`dirname $0` VENV=$TOOLS/../.venv source $VENV/bin/activate && $@ python-novaclient-9.1.1/tools/pretty_tox.sh0000775000175000017500000000065213165151077022262 0ustar jenkinsjenkins00000000000000#!/usr/bin/env bash set -o pipefail TESTRARGS=$1 # --until-failure is not compatible with --subunit see: # # https://bugs.launchpad.net/testrepository/+bug/1411804 # # this work around exists until that is addressed if [[ "$TESTARGS" =~ "until-failure" ]]; then python setup.py testr --slowest --testr-args="$TESTRARGS" else python setup.py testr --slowest --testr-args="--subunit $TESTRARGS" | subunit-trace -f fi python-novaclient-9.1.1/tools/tox_install.sh0000775000175000017500000000201213165151077022371 0ustar jenkinsjenkins00000000000000#!/usr/bin/env bash # Client constraint file contains this client version pin that is in conflict # with installing the client from source. We should remove the version pin in # the constraints file before applying it for from-source installation. CONSTRAINTS_FILE=$1 shift 1 set -e # NOTE(tonyb): Place this in the tox enviroment's log dir so it will get # published to logs.openstack.org for easy debugging. localfile="$VIRTUAL_ENV/log/upper-constraints.txt" if [[ $CONSTRAINTS_FILE != http* ]]; then CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE fi # NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep curl $CONSTRAINTS_FILE --insecure --progress-bar --output $localfile pip install -c$localfile openstack-requirements # This is the main purpose of the script: Allow local installation of # the current repo. It is listed in constraints file and thus any # install will be constrained and we need to unconstrain it. edit-constraints $localfile -- $CLIENT_NAME pip install -c$localfile -U $* exit $? python-novaclient-9.1.1/tools/nova.bash_completion0000664000175000017500000000156513165151077023541 0ustar jenkinsjenkins00000000000000_nova_opts="" # lazy init _nova_flags="" # lazy init _nova_opts_exp="" # lazy init _nova() { local cur prev nbc cflags COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [ "x$_nova_opts" == "x" ] ; then nbc="`nova bash-completion | sed -e "s/ *-h */ /" -e "s/ *-i */ /"`" _nova_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/ */ /g"`" _nova_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/ */ /g"`" _nova_opts_exp="`echo "$_nova_opts" | tr ' ' '|'`" fi if [[ " ${COMP_WORDS[@]} " =~ " "($_nova_opts_exp)" " && "$prev" != "help" ]] ; then COMPLETION_CACHE=~/.novaclient/*/*-cache cflags="$_nova_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) else COMPREPLY=($(compgen -W "${_nova_opts}" -- ${cur})) fi return 0 } complete -F _nova nova python-novaclient-9.1.1/setup.py0000664000175000017500000000200613165151077020047 0ustar jenkinsjenkins00000000000000# 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-novaclient-9.1.1/bindep.txt0000664000175000017500000000144013165151100020323 0ustar jenkinsjenkins00000000000000# This is a cross-platform list tracking distribution packages needed by tests; # see http://docs.openstack.org/infra/bindep/ for additional information. build-essential [platform:dpkg] dbus-devel [platform:rpm] dbus-glib-devel [platform:rpm] gettext language-pack-en [platform:ubuntu] libdbus-1-dev [platform:dpkg] libdbus-glib-1-dev [platform:dpkg] libffi-dev [platform:dpkg] libffi-devel [platform:rpm] libssl-dev [platform:ubuntu-xenial] libuuid-devel [platform:rpm] locales [platform:debian] python-dev [platform:dpkg] python-devel [platform:rpm] python3-all-dev [platform:ubuntu !platform:ubuntu-precise] python3-dev [platform:dpkg] python3-devel [platform:fedora] python3.4 [platform:ubuntu-trusty] python3.5 [platform:ubuntu-xenial] python34-devel [platform:centos] uuid-dev [platform:dpkg] python-novaclient-9.1.1/doc/0000775000175000017500000000000013165151230017073 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/doc/Makefile0000664000175000017500000000616013165151100020532 0ustar jenkinsjenkins00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXSOURCE = source PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE) .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-novaclient.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-novaclient.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." python-novaclient-9.1.1/doc/source/0000775000175000017500000000000013165151230020373 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/doc/source/conf.py0000664000175000017500000001076013165151100021672 0ustar jenkinsjenkins00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # python-novaclient documentation build configuration file import os import sys BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, ROOT) sys.path.insert(0, BASE_DIR) # TODO(stephenfin): This looks like something that pbr's autodoc integration # could be doing for us. Investigate. def gen_ref(ver, title, names): refdir = os.path.join(BASE_DIR, "reference", "api") pkg = "novaclient" if ver: pkg = "%s.%s" % (pkg, ver) refdir = os.path.join(refdir, ver) if not os.path.exists(refdir): os.makedirs(refdir) # we don't want to write index files for top-level directories - only # sub-directories if ver: idxpath = os.path.join(refdir, "index.rst") with open(idxpath, "w") as idx: idx.write(("%(title)s\n" "%(signs)s\n" "\n" ".. toctree::\n" " :maxdepth: 1\n" "\n") % {"title": title, "signs": "=" * len(title)}) for name in names: idx.write(" %s\n" % name) for name in names: rstpath = os.path.join(refdir, "%s.rst" % name) with open(rstpath, "w") as rst: rst.write(("%(title)s\n" "%(signs)s\n" "\n" ".. automodule:: %(pkg)s.%(name)s\n" " :members:\n" " :undoc-members:\n" " :show-inheritance:\n" " :noindex:\n") % {"title": name.capitalize(), "signs": "=" * len(name), "pkg": pkg, "name": name}) def get_module_names(): names = os.listdir(os.path.join(ROOT, 'novaclient', 'v2')) exclude = ['shell.py', '__init__.py'] for name in names: if name.endswith('.py') and name not in exclude: yield name.strip('.py') gen_ref(None, "Exceptions", ["exceptions"]) gen_ref("v2", "Version 2 API", sorted(get_module_names())) # -- 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', 'openstackdocstheme', ] autoclass_content = 'both' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # openstackdocstheme options repository_name = 'openstack/python-novaclient' bug_project = 'python-novaclient' bug_tag = 'doc' project = 'python-novaclient' copyright = 'OpenStack Contributors' # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'openstackdocs' # -- Options for openstackdocstheme ------------------------------------------- repository_name = 'openstack/python-novaclient' bug_project = 'python-novaclient' bug_tag = '' # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('cli/nova', 'nova', 'OpenStack Nova command line client', ['OpenStack Contributors'], 1), ] python-novaclient-9.1.1/doc/source/index.rst0000664000175000017500000000156113165151077022250 0ustar jenkinsjenkins00000000000000=========================================== Python bindings to the OpenStack Nova API =========================================== This is a client for OpenStack Nova API. There's :doc:`a Python API ` (the :mod:`novaclient` module), and a :doc:`command-line script ` (installed as :program:`nova`). Each implements the entire OpenStack Nova API. You'll need credentials for an OpenStack cloud that implements the Compute API, such as TryStack, HP, or Rackspace, in order to use the nova client. .. seealso:: You may want to read the `OpenStack Compute API Guide`__ to get an idea of the concepts. By understanding the concepts this library should make more sense. __ https://developer.openstack.org/api-guide/compute/index.html .. toctree:: :maxdepth: 2 user/index reference/index cli/index contributor/index python-novaclient-9.1.1/doc/source/cli/0000775000175000017500000000000013165151230021142 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/doc/source/cli/index.rst0000664000175000017500000000012613165151077023013 0ustar jenkinsjenkins00000000000000=============== CLI Reference =============== .. toctree:: :maxdepth: 2 nova python-novaclient-9.1.1/doc/source/cli/nova.rst0000664000175000017500000000271013165151077022650 0ustar jenkinsjenkins00000000000000====== nova ====== SYNOPSIS ======== `nova` [options] [command-options] `nova help` `nova help` DESCRIPTION =========== `nova` is a command line client for controlling OpenStack Nova, the cloud computing fabric controller. It implements 100% of the Nova API, allowing management of instances, images, quotas and much more. Before you can issue commands with `nova`, you must ensure that your environment contains the necessary variables so that you can prove to the CLI who you are and what credentials you have to issue the commands. See `Getting Credentials for a CLI` section of `OpenStack CLI Guide` for more info. See `OpenStack Nova CLI Guide` for a full-fledged guide. OPTIONS ======= To get a list of available commands and options run:: nova help To get usage and options of a command run:: nova help EXAMPLES ======== Get information about boot command:: nova help boot List available images:: nova image-list List available flavors:: nova flavor-list Launch an instance:: nova boot myserver --image some-image --flavor 2 View instance information:: nova show myserver List instances:: nova list Terminate an instance:: nova delete myserver SEE ALSO ======== OpenStack Nova CLI Guide: http://docs.openstack.org/cli-reference/nova.html BUGS ==== Nova client is hosted in Launchpad so you can view current bugs at https://bugs.launchpad.net/python-novaclient/. python-novaclient-9.1.1/doc/source/contributor/0000775000175000017500000000000013165151230022745 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/doc/source/contributor/index.rst0000664000175000017500000000067013165151100024605 0ustar jenkinsjenkins00000000000000=================== Contributor Guide =================== Code is hosted at `git.openstack.org`__. Submit bugs to the Nova project on `Launchpad`__. Submit code to the `openstack/python-novaclient` project using `Gerrit`__. __ https://git.openstack.org/cgit/openstack/python-novaclient __ https://launchpad.net/nova __ http://docs.openstack.org/infra/manual/developers.html#development-workflow .. toctree:: :maxdepth: 2 testing python-novaclient-9.1.1/doc/source/contributor/testing.rst0000664000175000017500000000154313165151100025153 0ustar jenkinsjenkins00000000000000========= Testing ========= The preferred way to run the unit tests is using ``tox``. There are multiple test targets that can be run to validate the code. ``tox -e pep8`` Style guidelines enforcement. ``tox -e py27`` Traditional unit testing. ``tox -e functional`` Live functional testing against an existing OpenStack instance. Functional testing assumes the existence of a `clouds.yaml` file as supported by `os-client-config `__ It assumes the existence of a cloud named `devstack` that behaves like a normal DevStack installation with a demo and an admin user/tenant - or clouds named `functional_admin` and `functional_nonadmin`. Refer to `Consistent Testing Interface`__ for more details. __ http://git.openstack.org/cgit/openstack/governance/tree/reference/project-testing-interface.rst python-novaclient-9.1.1/doc/source/reference/0000775000175000017500000000000013165151230022331 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/doc/source/reference/index.rst0000664000175000017500000000012613165151077024202 0ustar jenkinsjenkins00000000000000Reference ========= .. toctree:: :maxdepth: 1 api/index deprecation-policy python-novaclient-9.1.1/doc/source/reference/deprecation-policy.rst0000664000175000017500000000271513165151077026673 0ustar jenkinsjenkins00000000000000Deprecating commands ==================== There are times when commands need to be deprecated due to rename or removal. The process for command deprecation is: 1. Push up a change for review which deprecates the command(s). - The change should print a deprecation warning to ``stderr`` each time a deprecated command is used. - That warning message should include a rough timeline for when the command will be removed and what should be used instead, if anything. - The description in the help text for the deprecated command should mark that it is deprecated. - The change should include a release note with the ``deprecations`` section filled out. - The deprecation cycle is typically the first client release *after* the next *full* nova server release so that there is at least six months of deprecation. 2. Once the change is approved, have a member of the `nova-release`_ team release a new version of `python-novaclient`. .. _nova-release: https://review.openstack.org/#/admin/groups/147,members 3. Example: ``_ This change was made while the nova 12.0.0 Liberty release was in development. The current version of `python-novaclient` at the time was 2.25.0. Once the change was merged, `python-novaclient` 2.26.0 was released. Since there was less than six months before 12.0.0 would be released, the deprecation cycle ran through the 13.0.0 nova server release. python-novaclient-9.1.1/doc/source/reference/api/0000775000175000017500000000000013165151230023102 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/doc/source/reference/api/index.rst0000664000175000017500000000723513165151100024746 0ustar jenkinsjenkins00000000000000================================== The :mod:`novaclient` Python API ================================== .. module:: novaclient :synopsis: A client for the OpenStack Nova API. .. currentmodule:: novaclient Usage ----- First create a client instance with your credentials:: >>> from novaclient import client >>> nova = client.Client(VERSION, USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Here ``VERSION`` can be a string or ``novaclient.api_versions.APIVersion`` obj. If you prefer string value, you can use ``1.1`` (deprecated now), ``2`` or ``2.X`` (where X is a microversion). Alternatively, you can create a client instance using the keystoneauth session API:: >>> from keystoneauth1 import loading >>> from keystoneauth1 import session >>> from novaclient import client >>> loader = loading.get_plugin_loader('password') >>> auth = loader.load_from_options(auth_url=AUTH_URL, ... username=USERNAME, ... password=PASSWORD, ... project_id=PROJECT_ID) >>> sess = session.Session(auth=auth) >>> nova = client.Client(VERSION, session=sess) If you have PROJECT_NAME instead of a PROJECT_ID, use the project_name parameter. Similarly, if your cloud uses keystone v3 and you have a DOMAIN_NAME or DOMAIN_ID, provide it as `user_domain_(name|id)` and if you are using a PROJECT_NAME also provide the domain information as `project_domain_(name|id)`. novaclient adds 'python-novaclient' and its version to the user-agent string that keystoneauth produces. If you are creating an application using novaclient and want to register a name and version in the user-agent string, pass those to the Session:: >>> sess = session.Session( ... auth=auth, app_name'nodepool', app_version'1.2.3') If you are making a library that consumes novaclient but is not an end-user application, you can append a (name, version) tuple to the session's `additional_user_agent` property:: >>> sess = session.Session(auth=auth) >>> sess.additional_user_agent.append(('shade', '1.2.3')) For more information on this keystoneauth API, see `Using Sessions`_. .. _Using Sessions: http://docs.openstack.org/developer/keystoneauth/using-sessions.html It is also possible to use an instance as a context manager in which case there will be a session kept alive for the duration of the with statement:: >>> from novaclient import client >>> with client.Client(VERSION, USERNAME, PASSWORD, ... PROJECT_ID, AUTH_URL) as nova: ... nova.servers.list() ... nova.flavors.list() ... It is also possible to have a permanent (process-long) connection pool, by passing a connection_pool=True:: >>> from novaclient import client >>> nova = client.Client(VERSION, USERNAME, PASSWORD, PROJECT_ID, ... AUTH_URL, connection_pool=True) Then call methods on its managers:: >>> nova.servers.list() [] >>> nova.flavors.list() [, , , , , , ] >>> fl = nova.flavors.find(ram=512) >>> nova.servers.create("my-server", flavor=fl) .. warning:: Direct initialization of ``novaclient.v2.client.Client`` object can cause you to "shoot yourself in the foot". See launchpad bug-report `1493576`_ for more details. .. _1493576: https://launchpad.net/bugs/1493576 Reference --------- For more information, see the reference: .. toctree:: :maxdepth: 2 v2/index exceptions python-novaclient-9.1.1/doc/source/user/0000775000175000017500000000000013165151230021351 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/doc/source/user/shell.rst0000664000175000017500000000322013165151077023220 0ustar jenkinsjenkins00000000000000=================================== The :program:`nova` Shell Utility =================================== .. program:: nova .. highlight:: bash The :program:`nova` shell utility interacts with OpenStack Nova API from the command line. It supports the entirety of the OpenStack Nova API. First, you'll need an OpenStack Nova account and an API key. You get this by using the `nova-manage` command in OpenStack Nova. You'll need to provide :program:`nova` with your OpenStack username and API key. You can do this with the `--os-username`, `--os-password` and `--os-tenant-id` options, but it's easier to just set them as environment variables by setting some environment variables: .. envvar:: OS_USERNAME Your OpenStack Nova username. .. envvar:: OS_PASSWORD Your password. .. envvar:: OS_TENANT_NAME Project for work. .. envvar:: OS_AUTH_URL The OpenStack API server URL. .. envvar:: OS_COMPUTE_API_VERSION The OpenStack API version. .. envvar:: OS_REGION_NAME The Keystone region name. Defaults to the first region if multiple regions are available. For example, in Bash you'd use:: export OS_USERNAME=yourname export OS_PASSWORD=yadayadayada export OS_TENANT_NAME=myproject export OS_AUTH_URL=http://:5000/v3/ export OS_COMPUTE_API_VERSION=2.1 From there, all shell commands take the form:: nova [arguments...] Run :program:`nova help` to get a full list of all possible commands, and run :program:`nova help ` to get detailed help for that command. Reference --------- For more information, see the reference: .. toctree:: :maxdepth: 2 /cli/nova python-novaclient-9.1.1/doc/source/user/index.rst0000664000175000017500000000011613165151077023221 0ustar jenkinsjenkins00000000000000============ User Guide ============ .. toctree:: :maxdepth: 2 shell python-novaclient-9.1.1/CONTRIBUTING.rst0000664000175000017500000000107213165151100020763 0ustar jenkinsjenkins00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps documented at: http://docs.openstack.org/infra/manual/developers.html#development-workflow Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/python-novaclient python-novaclient-9.1.1/.coveragerc0000664000175000017500000000014513165151077020460 0ustar jenkinsjenkins00000000000000[run] branch = True source = novaclient omit = novaclient/openstack/* [report] ignore_errors = True python-novaclient-9.1.1/.mailmap0000664000175000017500000000267313165151077017770 0ustar jenkinsjenkins00000000000000Antony Messerli root Chris Behrens comstud Joe Gordon Johannes Erdfelt jerdfelt Andy Smith termie hwbi Nikolay Sokolov Nokolay Sokolov Nikolay Sokolov Nokolay Sokolov zhangguoqing python-novaclient-9.1.1/AUTHORS0000664000175000017500000004250113165151227017406 0ustar jenkinsjenkins00000000000000Aaron Lee Aaron Rosen Aarti Kriplani Abhishek Chanda Abhishek Kekane Abhishek Lahiri Abhishek Talwar Adam Gandelman Adam Spiers Akihiro MOTOKI Akshil Verma Ala Rezmerita Alan Pevec Alessandro Pilotti Alessandro Pilotti Alessio Ababilov Alessio Ababilov Alex Gaynor Alex Glikson Alex Meade Alexis Lee Alvaro Lopez Garcia Amandeep Anand Shanmugam Andrea Rosa Andreas Jaeger Andreas Jaeger Andrew Bogott Andrew Laski Andrew Lazarev Andrey Brindeyev Andrey Kurilin Andrey Kurilin Andrey Pavlov Andrey Volkov Andy Hill Andy McCrae Andy Smith Anita Kuno Ankit Agrawal Anna Babich Anthony Young Antony Messerli Arata Notsu Armando Migliaccio Artom Lifshitz Arundhati Surpur Arvinder Singh Atsushi SAKAI Augustina Ragwitz Avishay Traeger Balazs Gibizer Bartosz Górski Bartosz Górski Bartosz Górski Ben Nemec Ben Nemec Bin Zhou Bo Wang Boris Pavlovic Brian Elliott Brian Lamar Brian Waldon Cao ShuFeng Cao Shufeng Carlos Goncalves Cedric Brandily Chang Bo Guo ChangBo Guo(gcb) Chaozhe.Chen Chmouel Boudjnah Chmouel Boudjnah Chris Behrens Chris Buccella Chris Dent Chris Friesen Chris Jones Chris Krelle Chris St. Pierre Chris Yeoh Christian Berendt Christoph Gysin Christopher J Schaefer Christopher MacGown Christopher Yeoh Chuck Chuck Carmack Chuck Short Chung Chih, Hung Clark Boylan Claudiu Belu Clenimar Filemon Clint Byrum Cole Robinson Cyril Roelandt Dan Prince Dan Smith Dan Smith Dan Wendlandt Daniel P. Berrange Daniel Wallace Dao Cong Tien Davanum Srinivas Davanum Srinivas Dave Walker (Daviey) Dave Wilde David Hu David Kranz David Moreau Simard David Scannell David Wittman Dean Troyer Diana Clarke Dinesh Bhor Dirk Mueller Dominik Heidler Doug Hellmann Doug Hellmann Ed Leafe EdLeafe Eldar Nugaev Eli Qiao Emanuele Rocca Eoghan Glynn Eric Brown Eric Guo Eric Harney Eugeniya Kudryashova Feodor Tersin Feodor Tersin Flaper Fesp Flavio Percoco Florent Flament François Charlier Gabriel Hurley Gary Kotton Gaurav Gupta Ghanshyam Ghe Rivero Gleb Stepanov Gordon Chung Gábor Antal Haiwei Xu Hans Lindgren Haomeng, Wang He Jie Xu Hengqing Hu Hironori Shiina Huanxuan Ao Hugh Saunders Ian Cordasco Ian Cordasco Ian Wienand Ihar Hrachyshka Ikuo Kumagai Ilya Alekseyev Istvan Imre JUN JIE NAN Jake Dahn Jake Yip Jakub Ruzicka James E. Blair James Meredith James Penick Jamie Lennox Jamie Lennox Janonymous Jaroslav Henner Jason Dunsmore Jason Kölker Jason Straw Javeme Jay Lau Jay Pipes Jeffrey Guan Jens Rosenboom Jeremy Liu Jeremy Stanley Jesse Andrews Jiajun Liu Jianing YANG Jim Rollenhagen Jimmy McCrory Joao Targino Joe Gordon Joe Heck Johannes Erdfelt John Garbutt John Tran Josh Kearney Joshua Harlow Juan Manuel Olle Julie Pichon Julien Danjou Jyotsna Katie McLaughlin Kaushik Chandrashekar Ken'ichi Ohmichi Ken'ichi Ohmichi Kevin L. Mitchell Kevin L. Mitchell Kevin_Zheng Kiall Mac Innes Kieran Spear Kirill Shileev Kravchenko Pavel Kristi Nikolla Kui Shi Kurt Taylor Kyrylo Romanenko Laurens Van Houtven <_@lvh.cc> Leandro I. Costantino Leo Toyoda Liang Chen LiuNanke Lorin Hochstein Lorin Hochstein Luong Anh Tuan Lvov Maxim M. David Bennett Mahesh Panchaksharaiah Major Hayden Mark Doffman Mark McClain Mark McLoughlin Masaki Matsushita Masayuki Igawa Matt Dietz Matt Fischer Matt Riedemann Matt Riedemann Matt Stephenson Matt Thompson Matthew Farrellee Matthew Gilliard Matthew Treinish Melanie Witt Michael Basnight Michael Davies Michael Still Michal Dulko Miguel Grinberg Mike Lundy Mitsuhiko Yamazaki Mitsuhiro Tanino Monty Taylor Morgan Fainberg Nachi Ueno Natsuki Maruyama Nicholas Mistry Nick Shobe Nicolas Simonds Nikola Dipanov Nikolay Sokolov Noorul Islam K M Ollie Leahy Ondřej Nový OpenStack Release Bot Paul Voccio Paul Voccio Pavel Kholkin Pavel Shkitin Pavlo Shchelokovskyy Pawel Koniszewski Pedro Navarro Peng Yong Pete Savage Phil Day Pádraig Brady Qin Zhao Radomir Dopieralski Radoslav Gerganov Rafael Rivero Rafi Khardalian Rajiv Kumar Rakesh H S Ralf Haferkamp Ramaraja Ramaraja Ramachandran Rami Vaknin Ravi Shekhar Jethani Richard Jones Rick Harris Ritesh Paiboina Robie Basak Rohan Rhishikesh Kanade Roman Podoliaka Roman Podolyaka Roman Rader Ronald Bradford Rui Chen Rui Zang Russell Bryant Russell Cloran Russell Sim Ryan Hallisey Ryan Wilson Sahid Orentino Ferdjaoui Sahid Orentino Ferdjaoui Sandy Walsh SandyWalsh Sanjay Kumar Singh Sarah Ulmer Sascha Peilicke Sascha Peilicke Sascha Peilicke Satheesh Ulaganathan Sathish Nagappan Scott Devoid Scott Moser Sean Dague Sean Dague Sean McCully Sergey Lukjanov Sergey Nikitin Sergio Cazzolato Shane Wang ShaoHe Feng Shrirang Phadke Shuangtai Tian Simon Leinen Solly Ross Stanislaw Pitucha Stanisław Pitucha Stef T Stephen Finucane Steven Kaufer Sujitha Sulochan Acharya Sumanth Nagadavalli Sushil Kumar Sven Anderson Svetlana Shturm Swapnil Kulkarni (coolsvap) Sylvain Bauza Takashi NATSUME Takashi Sogabe Thiago Paiva Thierry Carrez Thomas Bechtold Thomas Schreiber TianTian Gao Tihomir Trifonov Timofey Durakov Tom Cammann Tomi Juvonen Tomofumi Hayashi Tony Breeds Tony Xu Tovin Seven Trey Morris Ubuntu Ukesh Kumar Vasudevan Unmesh Gurjar Unmesh Gurjar Vasyl Khomenko Veronica Musso Victor Morales Victor Stinner Victoria Martínez de la Cruz Vincent Hou Vincent Untz Vishvananda Ishaya Vitaliy Kolosov Wang Wei William Wolf Wu Wenxiang Xavier Queralt Xiao Chen Xiao Hanyu Xiaowei Qian Yaguang Tang Yaguang Tang Your Name Yufang Zhang Yunhong, Jiang Yuriy Taraday Yusuke Ide Yuuichi Fujioka Zane Bitter Zhengguang Zhenguo Niu Zhenzan Zhou Zhi Yan Liu ZhiQiang Fan ZhiQiang Fan Zhihai Zhihai Song Zhiteng Huang Zhongyue Luo ZhuChunzhan Ziad Sawalha abhishek.talwar annegentle bhagyashris chen-li chenaidong1 chenxing dineshbhor fujioka yuuichi gengchc2 gengjh gtt116 guoqingzhang henriquetruta hgangwx huangtianhua hwbi igormilovanovic int32bit ivan-zhu jakedahn jeremyfreudberg jichenjc john-griffith jonnary kylin7-sg lei zhang liaonanhai libing licanwei liu-sheng liuan liuqing liyingjun liyingjun lizheming lrqrun lzyeval masumotok melanie witt melwitt meretiko neetu nidhimittalhada ozg rahulram raiesmh08 rajiv.kumar ricolin ricolin rsritesh sampathP sathish-nagappan shihanzhang shu-mutou sonu.kumar sridhargaddam tengqm unicell vagrant venkatamahesh vnathan wangxiyuan wingwj xiexs yamini sardana yangyapeng yatin karel yatinkarel yuyafei zhang-jinnan zhangdaolong zhangjl zhangtralon zhangyanzi zhengyao1 zhiyanliu Édouard Thuleau python-novaclient-9.1.1/releasenotes/0000775000175000017500000000000013165151230021017 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/0000775000175000017500000000000013165151230022317 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/liberty.rst0000664000175000017500000000021313165151077024530 0ustar jenkinsjenkins00000000000000============================== Liberty Series Release Notes ============================== .. release-notes:: :branch: stable/liberty python-novaclient-9.1.1/releasenotes/source/_templates/0000775000175000017500000000000013165151230024454 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013165151077026736 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/conf.py0000664000175000017500000002056713165151077023641 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # # Nova Client Release Notes documentation build configuration file, created by # sphinx-quickstart on Mon Nov 23 20:38:38 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'reno.sphinxext', 'openstackdocstheme', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Nova Client Release Notes' copyright = u'2015, Nova developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # import pbr.version nova_version = pbr.version.VersionInfo('python-novaclient') # The short X.Y version. version = nova_version.canonical_version_string() # The full version, including alpha/beta/rc tags. release = nova_version.version_string_with_vcs() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'NovaClientReleaseNotestdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'PythonNovaClient.tex', u'Nova Client Release Notes Documentation', u'Nova developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pythonnovaclient', u'Nova Client Release Notes Documentation', [u'Nova developers'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'PythonNovaClient', u'Nova Client Release Notes Documentation', u'Nova developers', 'PythonNovaClient', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] python-novaclient-9.1.1/releasenotes/source/index.rst0000664000175000017500000000043213165151100024153 0ustar jenkinsjenkins00000000000000Welcome to Nova Client Release Notes documentation! =================================================== Contents ======== .. toctree:: :maxdepth: 2 unreleased ocata newton mitaka liberty Indices and tables ================== * :ref:`genindex` * :ref:`search` python-novaclient-9.1.1/releasenotes/source/unreleased.rst0000664000175000017500000000015313165151077025210 0ustar jenkinsjenkins00000000000000============================ Current Series Release Notes ============================ .. release-notes:: python-novaclient-9.1.1/releasenotes/source/mitaka.rst0000664000175000017500000000022313165151077024325 0ustar jenkinsjenkins00000000000000=================================== Mitaka Series Release Notes =================================== .. release-notes:: :branch: stable/mitaka python-novaclient-9.1.1/releasenotes/source/_static/0000775000175000017500000000000013165151230023745 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/_static/.placeholder0000664000175000017500000000000013165151077026227 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/ocata.rst0000664000175000017500000000022113165151077024144 0ustar jenkinsjenkins00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: stable/ocata python-novaclient-9.1.1/releasenotes/source/locale/0000775000175000017500000000000013165151230023556 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/locale/fr/0000775000175000017500000000000013165151230024165 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/locale/fr/LC_MESSAGES/0000775000175000017500000000000013165151230025752 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po0000664000175000017500000001146713165151077031025 0ustar jenkinsjenkins00000000000000# Gérald LONLAS , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: Nova Client Release Notes 7.1.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-03-24 05:03+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-10-22 06:13+0000\n" "Last-Translator: Gérald LONLAS \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" msgid "3.0.0" msgstr "3.0.0" msgid "3.3.0" msgstr "3.3.0" msgid "4.0.0" msgstr "4.0.0" msgid "4.1.0" msgstr "4.1.0" msgid "5.0.0" msgstr "5.0.0" msgid "5.1.0" msgstr "5.1.0" msgid "6.0.0" msgstr "6.0.0" msgid ":ref:`genindex`" msgstr ":ref:`genindex`" msgid ":ref:`search`" msgstr ":ref:`search`" msgid "Contents" msgstr "Contenu" msgid "Current Series Release Notes" msgstr "Note de la release actuelle" msgid "Deprecation Notes" msgstr "Notes dépréciées " msgid "Fixed IPs" msgstr "IP fixes" msgid "Floating IPs" msgstr "IP flottantes" msgid "Indices and tables" msgstr "Index et table des matières" msgid "Known Issues" msgstr "Problèmes connus" msgid "Liberty Series Release Notes" msgstr "Note de release pour Liberty" msgid "Mitaka Series Release Notes" msgstr "Note de release pour Mitaka" msgid "New Features" msgstr "Nouvelles fonctionnalités" msgid "Newton Series Release Notes" msgstr "Note de release pour Newton" msgid "Security Group Rules" msgstr "Règles de groupe de sécurité" msgid "Security Groups" msgstr "Groupes de sécurité" msgid "Upgrade Notes" msgstr "Notes de mises à jours" msgid "Welcome to Nova Client Release Notes documentation!" msgstr "Bienvenue dans la documentation de la note de Release du Client Nova" msgid "absolute-limits" msgstr "absolute-limits" msgid "add-floating-ip" msgstr "add-floating-ip" msgid "aggregate-details" msgstr "aggregate-details" msgid "credentials" msgstr "informations d'identification" msgid "dns-create" msgstr "dns-create" msgid "dns-create-private-domain" msgstr "dns-create-private-domain" msgid "dns-create-public-domain" msgstr "dns-create-public-domain" msgid "dns-delete" msgstr "dns-delete" msgid "dns-delete-domain" msgstr "dns-delete-domain" msgid "dns-domains" msgstr "dns-domains" msgid "dns-list" msgstr "dns-list" msgid "endpoints" msgstr "points de terminaison" msgid "fixed-ip-get" msgstr "fixed-ip-get" msgid "fixed-ip-reserve" msgstr "fixed-ip-reserve" msgid "fixed-ip-unreserve" msgstr "fixed-ip-unreserve" msgid "floating-ip-bulk-create" msgstr "floating-ip-bulk-create" msgid "floating-ip-bulk-delete" msgstr "floating-ip-bulk-delete" msgid "floating-ip-bulk-list" msgstr "floating-ip-bulk-list" msgid "floating-ip-create" msgstr "floating-ip-create" msgid "floating-ip-delete" msgstr "floating-ip-delete" msgid "floating-ip-list" msgstr "floating-ip-list" msgid "floating-ip-pool-list" msgstr "floating-ip-pool-list" msgid "network-associate-host" msgstr "network-associate-host" msgid "network-associate-project" msgstr "network-associate-project" msgid "network-create" msgstr "network-create" msgid "network-delete" msgstr "network-delete" msgid "network-disassociate" msgstr "network-disassociate" msgid "network-list" msgstr "network-list" msgid "network-show" msgstr "network-show" msgid "nova baremetal-interface-list" msgstr "nova baremetal-interface-list" msgid "nova baremetal-node-list" msgstr "nova baremetal-node-list" msgid "nova baremetal-node-show" msgstr "nova baremetal-node-show" msgid "nova image-delete" msgstr "nova image-delete" msgid "nova image-list" msgstr "nova image-list" msgid "nova image-meta" msgstr "nova image-meta" msgid "nova image-show" msgstr "nova image-show" msgid "rate-limits" msgstr "rate-limits" msgid "remove-floating-ip" msgstr "remove-floating-ip" msgid "rename" msgstr "rename" msgid "root-password" msgstr "root-password" msgid "secgroup-add-default-rule" msgstr "secgroup-add-default-rule" msgid "secgroup-add-group-rule" msgstr "secgroup-add-group-rule" msgid "secgroup-add-rule" msgstr "secgroup-add-rule" msgid "secgroup-create" msgstr "secgroup-create" msgid "secgroup-delete" msgstr "secgroup-delete" msgid "secgroup-delete-default-rule" msgstr "secgroup-delete-default-rule" msgid "secgroup-delete-group-rule" msgstr "secgroup-delete-group-rule" msgid "secgroup-delete-rule" msgstr "secgroup-delete-rule" msgid "secgroup-list" msgstr "secgroup-list" msgid "secgroup-list-default-rules" msgstr "secgroup-list-default-rules" msgid "secgroup-list-rules" msgstr "secgroup-list-rules" msgid "secgroup-update" msgstr "secgroup-update" msgid "tenant-network-create" msgstr "tenant-network-create" msgid "tenant-network-delete" msgstr "tenant-network-delete" msgid "tenant-network-list" msgstr "tenant-network-list" msgid "tenant-network-show" msgstr "tenant-network-show" python-novaclient-9.1.1/releasenotes/source/newton.rst0000664000175000017500000000022313165151077024371 0ustar jenkinsjenkins00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: stable/newton python-novaclient-9.1.1/releasenotes/notes/0000775000175000017500000000000013165151230022147 5ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/notes/fix-booting-with-multiple-nics-c6e5885b948d35ba.yaml0000664000175000017500000000021613165151077033257 0ustar jenkinsjenkins00000000000000--- fixes: - Fix an ability to boot server with multiple nics which was broken with microversion 2.42 (fix tag attribute disappearing). python-novaclient-9.1.1/releasenotes/notes/microversion-v2_44-d60c8834e436ad3d.yaml0000664000175000017500000000126113165151077030636 0ustar jenkinsjenkins00000000000000--- deprecations: - | The following CLIs and their backing API bindings are deprecated and capped at microversion 2.44: * ``nova add-fixed-ip``: use python-neutronclient or openstacksdk * ``nova remove-fixed-ip``: use python-neutronclient or openstacksdk * ``nova floating-ip-associate``: use python-neutronclient or openstacksdk * ``nova floating-ip-disassociate``: use python-neutronclient or openstacksdk * ``nova virtual-interface-list``: there is no replacement as this is only implemented for nova-network which is deprecated The CLIs and API bindings will be removed in the first major release after Nova 16.0.0 Pike is released. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_43-76db2ac463b431e4.yaml0000664000175000017500000000103113165151077030617 0ustar jenkinsjenkins00000000000000--- deprecations: - | The following CLIs and their backing API bindings are deprecated and capped at microversion 2.43: * ``nova host-describe`` - superseded by ``nova hypervisor-show`` * ``nova host-list`` - superseded by ``nova hypervisor-list`` * ``nova host-update`` - superseded by ``nova service-enable`` and ``nova service-disable`` * ``nova host-action`` - no alternative by design The CLIs and API bindings will be removed in the first major release after Nova 16.0.0 Pike is released. python-novaclient-9.1.1/releasenotes/notes/global_request_id-26f4e4301f84d403.yaml0000664000175000017500000000026313165151077030602 0ustar jenkinsjenkins00000000000000--- features: - | A new ``global_request_id`` parameter is accepted on the client constructor, which will then pass ``X-OpenStack-Request-ID`` on all requests made. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_45-1bfcae3914280534.yaml0000664000175000017500000000116213165151077030546 0ustar jenkinsjenkins00000000000000--- features: - | Support was added for microversion 2.45. This changes how the ``createImage`` and ``createBackup`` server action APIs return the created snapshot image ID in the response. With microversion 2.45 and later, the image ID is return in a json dict response body with an ``image_id`` key and uuid value. The old ``Location`` response header is no longer returned in microversion 2.45 or later. There are no changes to the ``nova image-create`` CLI. However, the ``nova backup`` CLI will print out the backup snapshot image information with microversion 2.45 or greater now. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_49-56bde596ee13366d.yaml0000664000175000017500000000034613165151077030657 0ustar jenkinsjenkins00000000000000--- features: - | Added support for microversion 2.49 that enables users to attach tagged interfaces and volumes. A new ``--tag`` option is added to ``nova volume-attach`` and ``nova interface-attach`` commands. python-novaclient-9.1.1/releasenotes/notes/deprecate-network-cli-f0a539528be594d3.yaml0000664000175000017500000000361713165151077031407 0ustar jenkinsjenkins00000000000000--- upgrade: - | The ability to update the following network-related resources via the ``nova quota-update`` and ``nova quota-class-update`` commands is now deprecated: * Fixed IPs * Floating IPs * Security Groups * Security Group Rules By default the quota and limits CLIs will not update or show those resources using microversion >= 2.36. You can still use them, however, by specifying ``--os-compute-api-version 2.35``. Quota information for network resources should be retrieved from python-neutronclient or python-openstackclient. deprecations: - | The following commands are now deprecated: * dns-create * dns-create-private-domain * dns-create-public-domain * dns-delete * dns-delete-domain * dns-domains * dns-list * fixed-ip-get * fixed-ip-reserve * fixed-ip-unreserve * floating-ip-create * floating-ip-delete * floating-ip-list * floating-ip-pool-list * floating-ip-bulk-create * floating-ip-bulk-delete * floating-ip-bulk-list * network-create * network-delete * network-disassociate * network-associate-host * network-associate-project * network-list * network-show * scrub * secgroup-create * secgroup-delete * secgroup-list * secgroup-update * secgroup-add-group-rule * secgroup-delete-group-rule * secgroup-add-rule * secgroup-delete-rule * secgroup-list-rules * secgroup-list-default-rules * secgroup-add-default-rule * secgroup-delete-default-rule * tenant-network-create * tenant-network-delete * tenant-network-list * tenant-network-show With the 2.36 microversion these will fail in the API. The CLI will fallback to passing the 2.35 microversion to ease the transition. Network resource information should be retrieved from python-neutronclient or python-openstackclient. python-novaclient-9.1.1/releasenotes/notes/return-request-id-to-caller-52c5423794b33f8b.yaml0000664000175000017500000000251113165151077032403 0ustar jenkinsjenkins00000000000000--- prelude: > Methods in manager classes and resource classes return wrapper classes that wrap values returned originally. For example, a wrapper class for list, a wrapper class for dict, a wrapper class for str and so on. The wrapper classes have a 'request_ids' property for request IDs returned from Nova (nova-api). So the caller can get the Nova's request IDs, then output them to logs with its own request ID. The function to output them to the logs will be implemented in other projects (cinder, heat, etc.). features: - Methods in manager classes and resource classes return wrapper classes that wrap values returned originally. For example, a wrapper class for list, a wrapper class for dict, a wrapper class for str and so on. The wrapper classes have a 'request_ids' property for request IDs returned from Nova (nova-api). So the caller can get the Nova's request IDs, then output them to logs with its own request ID. The function to output them to the logs will be implemented in other projects (cinder, heat, etc.). upgrade: - In case that methods return a response object and body originally and body is None, the methods return the wrapper class for tuple as 'body' instead of the wrapper class for None. The wrapper class for None has not been added. python-novaclient-9.1.1/releasenotes/notes/deprecate-certs-1558d8e3b7888938.yaml0000664000175000017500000000040413165151077030145 0ustar jenkinsjenkins00000000000000--- deprecations: - | The ``nova x509-create-cert`` and ``nova x509-get-root-cert`` commands and ``novaclient.v2.certs`` API binding are now deprecated and will be removed in the first major release after the Nova server 16.0.0 Pike release. python-novaclient-9.1.1/releasenotes/notes/add-user-agent-string-db77210dfd3ec671.yaml0000664000175000017500000000040113165151077031436 0ustar jenkinsjenkins00000000000000--- features: - novaclient now adds information about itself to the keystoneauth user-agent. Adding information about wrapping libraries or consuming applications can be found at https://docs.openstack.org/developer/python-novaclient/api.html python-novaclient-9.1.1/releasenotes/notes/add-osprofiler-support-cc9dd228242e9919.yaml0000664000175000017500000000230513165151077031641 0ustar jenkinsjenkins00000000000000--- prelude: > OSprofiler support was added to the client. That makes possible to trigger Nova operation trace generation from the CLI. features: - A new ``--profile`` option was added to allow Nova profiling from the CLI. If the user wishes to trace a nova boot request he or she needs to type the following command -- ``nova --profile boot --image --flavor ``, where ``secret_key`` should match one of the keys defined in nova.conf. As a result of this operation additional information regarding ``trace_id`` will be printed, that can be used to generate human-friendly html report -- ``osprofiler trace show --html --out trace.html``. To enable profiling, user needs to have osprofiler installed in the local environment via ``pip install osprofiler``. security: - OSprofiler support, that was added during the Ocata release cycle, requires passing of trace information between various OpenStack services. This information is signed by one of the HMAC keys defined in nova.conf file. That means that only someone who knows this key is able to send the proper header to trigger profiling. python-novaclient-9.1.1/releasenotes/notes/pike-rm-deprecated-img-d58e9ae2d774cbfc.yaml0000664000175000017500000000051713165151077031733 0ustar jenkinsjenkins00000000000000--- prelude: > Deprecated image commands and python API bindings have been removed. upgrade: - | The following deprecated image commands have been removed:: * nova image-list * nova image-show * nova image-meta * nova image-delete Along with the related python API bindings in ``novaclient.v2.images``. python-novaclient-9.1.1/releasenotes/notes/deprecate-no-cache-arg-7814806b4f79c1b9.yaml0000664000175000017500000000033613165151077031316 0ustar jenkinsjenkins00000000000000--- deprecations: - novaclient.client.Client entry-point accepted two arguments with same meaning (**no_cache** and **os_cache**). Since **os_cache** is more widely used in our code, **no_cache** was deprecated. python-novaclient-9.1.1/releasenotes/notes/deprecate-service-binary-arg-2d5c446f5a2409a7.yaml0000664000175000017500000000077113165151077032630 0ustar jenkinsjenkins00000000000000--- deprecations: - | The ``binary`` argument to the ``nova service-enable``, ``nova service-disable``, and ``nova service-force-down`` commands has been deprecated. The only binary that it makes sense to use is ``nova-compute`` since disabling a service like ``nova-scheduler`` or ``nova-conductor`` does not actually do anything, and starting in the 16.0.0 Pike release the compute API will not be able to look up services other than ``nova-compute`` for these operations. ././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000python-novaclient-9.1.1/releasenotes/notes/fixed-ListExtResource-given-in-place-of-ListExtManager-a759a27079d16a44.yamlpython-novaclient-9.1.1/releasenotes/notes/fixed-ListExtResource-given-in-place-of-ListExtManager-a70000664000175000017500000000072713165151077034702 0ustar jenkinsjenkins00000000000000--- fixes: - | The contents of the list_extensions.py file was moved from contrib to v2 directory in release 7.0.0, and a stub importing the objects from the new location was left in its place for backward compatibility, together with a warning informing about the new location. However, the stub incorrectly assigned the ListExtResource class to the ListExtManager name. This has now been fixed, and ListExtManager is used instead. python-novaclient-9.1.1/releasenotes/notes/restrict-interface-parameter-e5fe166f39ba0935.yaml0000664000175000017500000000021713165151077033044 0ustar jenkinsjenkins00000000000000--- deprecations: - keyword argument **interface** of novaclient.client.Client entry-point was deprecated in favor of **endpoint_type**; python-novaclient-9.1.1/releasenotes/notes/no-neutron-proxy-18fd54febe939a6b.yaml0000664000175000017500000000060113165151077030715 0ustar jenkinsjenkins00000000000000--- upgrade: - | The 2.36 microversion deprecated the network proxy APIs in Nova. Because of this we now go directly to neutron for name to net-id lookups. For nova-net deployements the old proxies will continue to be used. To do this the following is assumed: #. There is a **network** entry in the service catalog. #. The network v2 API is available. python-novaclient-9.1.1/releasenotes/notes/remove-deprecated-option-in-3.3.0-82a413157838570d.yaml0000664000175000017500000000461313165151077032734 0ustar jenkinsjenkins00000000000000--- update: - The following deprecated options have been removed: - create instances: - --num-instance replaced by --min-count and --max-count - --key_name replaced by --key-name - --user_data replaced by --user-data - --availability_zone replaced by -- availability-zone - --security_groups replaced by --sercurity-groups - --block_device_mapping replaced by --block-device-mapping - list servers: - --reservation_id replaced by --reservation-id - --instance_name replaced by --instance-name - --all_tenants replaced by --all-tenants - rebuild instance: - --rebuild_password replaced by --rebuild-password - get serial console: - --console_type replaced by --console-type - create dns private domain: - --availability_zone replaced by --availability-zone - list security groups: - --all_tenants replaced by --all-tenants - add key pairs: - --pub_key replaced by --pub-key - live-migrate servers: - --block_migrate replaced by --block-migrate - --disk_over_commit replaced by --disk-over-commit - update quotas: - --floating_ips replaced by --floating-ips - --metadata_items replaced by --metadata-items - --injected_files replaced by --injected-files - --injected_file_content_bytes replaced by --injected-file-content-bytes - update quota classes: - --floating_ips replaced by --floating-ips - --metadata_items replaced by --metadata-items - --injected_files replaced by --injected-files - --injected_file_content_bytes replaced by --injected-file-content-bytes - create server groups: - --policy - Authentication Options: - --os_username replaced by --os-username - --os_password replaced by --os-password - --os_tenant_name replaced by --os-tenant-name - --os_auth_url replaced by --os-auth-url - --os_region_name replaced by --os-region-name - --os_auth_system replaced by --os-auth-system - --endpoint-type replaced by --os-endpoint-type - Optional arguments: - --service_type replaced by --service-type - --service_name replaced by --service-name - --volume_service_name replaced by --volume-service-name - --os_compute_api_version replaced by --os-compute-api-version - --bypass_url replaced by --bypass-url python-novaclient-9.1.1/releasenotes/notes/.placeholder0000664000175000017500000000000013165151077024431 0ustar jenkinsjenkins00000000000000python-novaclient-9.1.1/releasenotes/notes/deprecate-volume-service-name-arg-4c65e8866f9624dd.yaml0000664000175000017500000000053113165151077033612 0ustar jenkinsjenkins00000000000000--- deprecations: - CLI argument for volume_service_name was deprecated long time ago. All novaclient's methods for communication with Volume API were deprecated and removed. There is no need to leave **volume_service_name** argument of novaclient.client.Client entry-point since it is not used anywhere, so it is removed now. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_47-4aa54fbbd519e421.yaml0000664000175000017500000000155213165151077030712 0ustar jenkinsjenkins00000000000000--- features: - | Added support for microversion 2.47 which returns the flavor details directly embedded in the server details when listing or showing servers. With this change, CLI requests with microversion >= 2.47 will no longer need to do additional queries to get the flavor and flavor extra_specs information. Instead, the flavor information will be output as separate key/value pairs with the keys namespaced with the "flavor:" prefix. As one would expect, these keys can also be specified as output fields when listing servers, like this: ``nova list --fields name,flavor:original_name`` When displaying details of a single server, the ``--minimal`` option will display a ``flavor`` field with a value of the ``original_name`` of the flavor. Prior to this microversion the value was the ``id`` of the flavor. python-novaclient-9.1.1/releasenotes/notes/remove-py26-support-f31379e86f40d975.yaml0000664000175000017500000000011513165151077030746 0ustar jenkinsjenkins00000000000000--- upgrade: - Python 2.6 support has been removed from python-novaclient. python-novaclient-9.1.1/releasenotes/notes/volume-cli-removal-ffcb94421a356042.yaml0000664000175000017500000000043513165151077030714 0ustar jenkinsjenkins00000000000000--- upgrade: - Volume, volume-type and volume-snapshot create/update/delete/list CLIs and python API bindings are removed. Use python-cinderclient or python-openstackclient for CLIs instead. Use python-cinderclient or python-openstacksdk for python API bindings instead. python-novaclient-9.1.1/releasenotes/notes/deprecate-baremetal-d67f58a2986b3565.yaml0000664000175000017500000000067613165151077031043 0ustar jenkinsjenkins00000000000000--- deprecations: - | The following CLIs and python API bindings are now deprecated for removal: * nova baremetal-node-list * nova baremetal-node-show * nova baremetal-interface-list These will be removed in the first major python-novaclient release after the Nova 15.0.0 Ocata release. Use python-ironicclient or python-openstackclient for CLI and python-ironicclient or openstacksdk for python API bindings. python-novaclient-9.1.1/releasenotes/notes/fix-token-auth-6c48c63a759f51d5.yaml0000664000175000017500000000017013165151077030056 0ustar jenkinsjenkins00000000000000--- fixes: - Fix an ability to authenticate using Keystone Token which was broken with novaclient 7.0.0 release. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_38-0618fe2b3c7f96f9.yaml0000664000175000017500000000031513165151077030655 0ustar jenkinsjenkins00000000000000--- upgrade: - Support for microversion 2.38 added. As of microversion 2.38, invalid statuses passed to ``nova list --status invalid_status`` will result in a HTTP 400 Bad Request error response.python-novaclient-9.1.1/releasenotes/notes/remove_api_v_1_1-88b3f18ce1423b46.yaml0000664000175000017500000000015613165151077030320 0ustar jenkinsjenkins00000000000000--- upgrade: - remove version 1.1 API support as we only support v2 and v2.1 API in nova side now. python-novaclient-9.1.1/releasenotes/notes/log-request-id-ce106497e0520fad.yaml0000664000175000017500000000101313165151077030107 0ustar jenkinsjenkins00000000000000--- prelude: > - Log 'x-openstack-request-id' or 'x-compute-request-id' in each API call. If the caller (e.g. heat) uses oslo.log, the caller's request id in oslo.context and the callee's request id can be output in the same log message (same line). features: - Log 'x-openstack-request-id' or 'x-compute-request-id' in each API call. If the caller (e.g. heat) uses oslo.log, the caller's request id in oslo.context and the callee's request id can be output in the same log message (same line). python-novaclient-9.1.1/releasenotes/notes/remove-deprecated-option-in-9.0.0-bc76629d28f1d4c4.yaml0000664000175000017500000000033513165151077033240 0ustar jenkinsjenkins00000000000000--- upgrade: - | The following deprecated options have been removed: - ``--tenant`` (from ``flavor access list``) - ``--cell_name`` (from ``migration list``) - ``--volume-service-name`` (global option) python-novaclient-9.1.1/releasenotes/notes/make-console-public-0c776bfda240cd9d.yaml0000664000175000017500000000052313165151077031250 0ustar jenkinsjenkins00000000000000--- features: - Provides a public unified interface 'get_console_url' for classes 'novaclient.v2.servers.Server' and 'novaclient.v2.servers.ServerManager'. Users (Heat, OpenStack-client and etc.) can call this public interface instead of calling the individual methods to retrieve a console url of a particular protocol. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_35-537619a43278fbb5.yaml0000664000175000017500000000030413165151077030500 0ustar jenkinsjenkins00000000000000--- features: - Added microversion v2.35 that adds pagination support for keypairs with the help of new optional parameters 'limit' and 'marker' which were added to keypair-list command.python-novaclient-9.1.1/releasenotes/notes/clarify-project-id-variable-5832698315000438.yaml0000664000175000017500000000124613165151077032077 0ustar jenkinsjenkins00000000000000--- deprecations: - Keyword argument **tenant_id** of novaclient.client.Client entry-point was deprecated in favor of **project_id**. - Keyword argument **tenant_name** of novaclient.client.Client entry-point was deprecated in favor of **project_name**. other: - The meaning of 'project_id' variable of novaclient.client.Client entry-point was not clear. In different cases it was used as ID or Name of project (in terms of Keystone). The time to identify meaning is come, so now project_id/tenant_id variables specifically mean Project ID (in terms of Keystone) and project_name/tenant_name variables mean Project Name (in terms of Keystone). python-novaclient-9.1.1/releasenotes/notes/microversion-v2_52-2fe81b3bf2e4b4ea.yaml0000664000175000017500000000052213165151077031044 0ustar jenkinsjenkins00000000000000--- features: - | `Microversion 2.52`_ is now supported which adds the ``--tags`` option to the ``nova boot`` command and a ``tags`` kwarg to the ``novaclient.v2.servers.ServerManager.create()`` python API binding method. .. _Microversion 2.52: https://docs.openstack.org/nova/latest/api_microversion_history.html#id47 python-novaclient-9.1.1/releasenotes/notes/deprecate_contrib_extensions-0ec70c070b09eedb.yaml0000664000175000017500000000215113165151077033337 0ustar jenkinsjenkins00000000000000--- prelude: > All extensions of API V2.0 were merged to 2.1, but NovaClient continued to store them as a separate entities. upgrade: - All managers and resources from novaclient.v2.contrib submodules are moved to appropriate submodules of novaclient.v2 (except barametal and tenant_networks, which were deprecated previously) - All shell commands from novaclient.v2.contrib submodules are moved to novaclient.v2.shell module. - novaclient.v2.client.Client imports all modules (which were located in submodules of novaclient.v2.contrib) by-default for api version v2 - Method novaclient.client.discover_extensions returns only barametal and tenant_networks extensions, since they are not included by default. - There are no modules and extensions for "deferred_delete", "host_evacuate", "host_evacuate_live" and "metadata_extensions" anymore. Previously, they contained only shell commands and shell module auto loads them (there is no ability to not do it). deprecations: - All modules of novaclient.v2.contrib are deprecated now and will be removed after OpenStack Pike. ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000python-novaclient-9.1.1/releasenotes/notes/instance-uuid-flag-in-migration-list-5d2fed7657d3def5.yamlpython-novaclient-9.1.1/releasenotes/notes/instance-uuid-flag-in-migration-list-5d2fed7657d3def5.yam0000664000175000017500000000047613165151077034315 0ustar jenkinsjenkins00000000000000--- features: - | A new ``--instance-uuid`` option is added to ``nova migration-list`` command. This is used to query the migration history of a specific server by the migration-list command. Please use ``nova server-migration-list`` command for querying in-progress migrations of a specific server. ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000python-novaclient-9.1.1/releasenotes/notes/rm-deprecated-commands-options-ocata-00f249810e5bdf97.yamlpython-novaclient-9.1.1/releasenotes/notes/rm-deprecated-commands-options-ocata-00f249810e5bdf97.yam0000664000175000017500000000054613165151077034127 0ustar jenkinsjenkins00000000000000--- prelude: > Several deprecated commands have been removed. See the upgrade section for details. upgrade: - | The following deprecated commands have been removed: * absolute-limits * add-floating-ip * aggregate-details * credentials * endpoints * rate-limits * remove-floating-ip * rename * root-password python-novaclient-9.1.1/releasenotes/notes/microversion-v2_41-6df7a5a66a9ded35.yaml0000664000175000017500000000033613165151077031004 0ustar jenkinsjenkins00000000000000--- features: - | Added support for microversion 2.41 which shows the aggregate UUID in CLI output when listing, creating, showing, updating, setting metadata, and adding or removing hosts from an aggregate. python-novaclient-9.1.1/releasenotes/notes/rm-baremetal-cli-api-fbc8c242d48cd2fb.yaml0000664000175000017500000000040613165151077031364 0ustar jenkinsjenkins00000000000000--- upgrade: - | The baremetal CLIs and python API bindings were deprecated in the Newton release and have been removed. Use python-openstackclient or python-ironicclient for CLIs. Use python-ironicclient or openstacksdk for python API bindingspython-novaclient-9.1.1/releasenotes/notes/keystoneauth-8ec1e6be14cdbae3.yaml0000664000175000017500000000056613165151077030305 0ustar jenkinsjenkins00000000000000--- features: - keystoneauth plugins are now supported. upgrade: - novaclient now requires the keystoneauth library. deprecations: - novaclient auth strategy plugins are deprecated. Please use keystoneauth auth plugins instead. - nova credentials is deprecated. Please use openstack token issue - nova endpoints is deprecated. Please use openstack catalog list instead. python-novaclient-9.1.1/releasenotes/notes/switch-to-sessionclient-aa49d16599fea570.yaml0000664000175000017500000000074113165151077032075 0ustar jenkinsjenkins00000000000000--- upgrade: - | When using novaclient as a library (via novaclient.client.Client entry-point), an internal implementation of HTTPClient was used if a session object is not specified. For better user experience we switched to using SessionClient which uses keystoneauth (Keystone folks maintain this library) for all auth stuff. The SessionClient interface is similar to HTTPClient, but there is a small possibility that you will notice a difference. python-novaclient-9.1.1/releasenotes/notes/remove-auth-system-b2cd247b8a312b72.yaml0000664000175000017500000000066613165151077031033 0ustar jenkinsjenkins00000000000000--- prelude: > The ability to use non-Keystone authentication systems has been removed. upgrade: - The ``--os-auth-system`` CLI option and ``OS_AUTH_SYSTEM`` environment variable usage was deprecated in the 3.1.0 release during the Mitaka development series. This release drops the support for using those options to load non-Keystone authentication systems via the ``openstack.client.auth_plugin`` extension point. python-novaclient-9.1.1/releasenotes/notes/bp-deprecate-image-meta-proxy-api-1483b75cf73b021e.yaml0000664000175000017500000000037513165151077033466 0ustar jenkinsjenkins00000000000000--- upgrade: - Starting from microversion 2.39 'image-metadata' proxy API in Nova is deprecated and python API bindings will not work, although 'image-meta' CLI commands will still work, because of the fallback on the CLI to version 2.35. python-novaclient-9.1.1/releasenotes/notes/no-glance-proxy-5c13001a4b13e8ce.yaml0000664000175000017500000000061113165151077030256 0ustar jenkinsjenkins00000000000000--- upgrade: - | The 2.36 microversion deprecated the image proxy API. As such, CLI calls now directly call the image service to get image details, for example, as a convenience to boot a server with an image name rather than the image id. To do this the following is assumed: #. There is an **image** entry in the service catalog. #. The image v2 API is available. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_28-abf653ae5cf5c4a9.yaml0000664000175000017500000000017713165151077031064 0ustar jenkinsjenkins00000000000000--- upgrade: - Support v2.28 microversion - cpu_info property of hypervisor resource is a json now (previously it was text). python-novaclient-9.1.1/releasenotes/notes/microversion-v2_33-10d12ea3b25839e8.yaml0000664000175000017500000000031213165151077030541 0ustar jenkinsjenkins00000000000000--- features: - Added microversion v2.33 that adds pagination support for hypervisors with the help of new optional parameters 'limit' and 'marker' which were added to hypervisor-list command.python-novaclient-9.1.1/releasenotes/notes/rename-apikey-to-password-735588d841efa49e.yaml0000664000175000017500000000030613165151077032232 0ustar jenkinsjenkins00000000000000--- deprecations: - The **api_key** variable of novaclient.client.Client entry-point was deprecated in favor of **password**. Nothing has changed in the case of positional argument usage. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_50-4f484658d66d01aa.yaml0000664000175000017500000000266613165151077030567 0ustar jenkinsjenkins00000000000000--- fixes: - | Adds support for the ``2.50`` microversion which fixes the ``nova quota-class-show`` and ``nova quota-class-update`` commands in the following ways: * The ``server_groups`` and ``server_group_members`` quota resources will now be shown in the output table for ``nova quota-class-show``. * The ``floating_ips``, ``fixed_ips``, ``security_groups`` and ``security_group_rules`` quota resources will no longer be able to be updated using ``nova quota-class-update`` nor will they be shown in the output of ``nova quota-class-show``. Use python-openstackclient or python-neutronclient to work with quotas for network resources. In addition, the ``nova quota-class-update`` CLI was previously incorrectly limiting the ability to update quota class values for ``floating_ips``, ``fixed_ips``, ``security_groups`` and ``security_group_rules`` based on the 2.36 microversion. That has been changed to limit based on the ``2.50`` microversion. upgrade: - | The ``novaclient.v2.quota_classes.QuotaClassSetManager.update`` method now defines specific kwargs starting with microversion ``2.50`` since updating network-related resource quota class values is not supported on the server with microversion ``2.50``. The list of excluded resources is: - ``fixed_ips`` - ``floating_ips`` - ``networks`` - ``security_groups`` - ``security_group_rules`` python-novaclient-9.1.1/releasenotes/notes/microversion-v2_53-3463b546a38c5f84.yaml0000664000175000017500000000340013165151077030500 0ustar jenkinsjenkins00000000000000--- features: - | Added support for `microversion 2.53`_. The following changes were made for the ``services`` commands and python API bindings: - The ``nova service-list`` command and API will have a UUID value for the ``id`` field in the output and response, respectively. - The ``nova service-enable`` command and API will require a UUID service id value to uniquely identify the service rather than a ``host`` and ``binary`` value. The UUID ``id`` field will also be in the command output. - The ``nova service-disable`` command and API will require a UUID service id value to uniquely identify the service rather than a ``host`` and ``binary`` value. The UUID ``id`` field will also be in the command output. - The ``nova service-force-down`` command and API will require a UUID service id value to uniquely identify the service rather than a ``host`` and ``binary`` value. The UUID ``id`` field will also be in the command output. - The ``nova service-delete`` command and API will require a UUID service id value to uniquely identify the service rather than an integer service id value. The following changes were made for the ``hypervisors`` commands and python API bindings: - The ID field in the various ``nova hypervisor-*`` commands and ``Hypervisor.id`` attribute in the API binding will now be a UUID value. - If paging over hypervisors using ``nova hypervisor-list``, the ``--marker`` must be a UUID value. - The ``nova hypervisor-show`` and ``nova hypervisor-uptime`` commands and APIs now take a UUID value for the hypervisor ID. .. _microversion 2.53: https://docs.openstack.org/nova/latest/api_microversion_history.html#id48 ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000python-novaclient-9.1.1/releasenotes/notes/drop-deprecated-aggregate-update-args-17bd019f4be34b18.yamlpython-novaclient-9.1.1/releasenotes/notes/drop-deprecated-aggregate-update-args-17bd019f4be34b18.ya0000664000175000017500000000033613165151077034125 0ustar jenkinsjenkins00000000000000--- upgrade: - | The deprecated `name` and `availability_zone` positional arguments in the ``nova aggregate-update`` command have been removed. Use the ``--name`` and ``--availability-zone`` options instead. python-novaclient-9.1.1/releasenotes/notes/deprecate-cloudpipe-670202797fdf97b6.yaml0000664000175000017500000000055413165151077031063 0ustar jenkinsjenkins00000000000000--- deprecations: - | The os-cloudpipe API has been removed from Nova. As a result, the ``nova cloudpipe-list``, ``nova cloudpipe-create``, and ``nova cloudpipe-configure`` commands and the ``novaclient.v2.cloudpipe`` API bindings are now deprecated, and will be removed in the first major release after the Nova server 16.0.0 Pike release. python-novaclient-9.1.1/releasenotes/notes/image-api-deprecation-41944dc6fc024918.yaml0000664000175000017500000000066613165151077031261 0ustar jenkinsjenkins00000000000000--- deprecations: - | The following CLIs and python API bindings are now deprecated for removal: * nova image-delete * nova image-list * nova image-meta * nova image-show These will be removed in the first major python-novaclient release after the Nova 15.0.0 Ocata release. Use python-glanceclient or python-openstackclient for CLI and python-glanceclient or openstacksdk for python API bindings. python-novaclient-9.1.1/releasenotes/notes/restrict-args-for-novaclient-ep-491098c3634365be.yaml0000664000175000017500000000034113165151077033165 0ustar jenkinsjenkins00000000000000--- upgrade: - | novaclient.client.Client entry-point accepts only 5 positional arguments:: version, username, api_key, project_id, auth_url Using positional arguments for all other options is now an error. python-novaclient-9.1.1/releasenotes/notes/pike-rm-deprecated-net-272aeb62b329a5bc.yaml0000664000175000017500000000456613165151077031570 0ustar jenkinsjenkins00000000000000--- prelude: > Deprecated network-related resource commands and python API bindings have been removed. From this point on, python-novaclient will no longer work with nova-network *except* for the ``nova virtual-interface-list``, ``nova add-fixed-ip`` and ``nova remove-fixed-ip`` commands. upgrade: - | The following deprecated network-related resource commands have been removed:: * nova dns-create * nova dns-create-private-domain * nova dns-create-public-domain * nova dns-delete * nova dns-delete-domain * nova dns-domains * nova dns-list * nova fixed-ip-get * nova fixed-ip-reserve * nova fixed-ip-unreserve * nova floating-ip-create * nova floating-ip-delete * nova floating-ip-list * nova floating-ip-bulk-create * nova floating-ip-bulk-delete * nova floating-ip-bulk-list * nova floating-ip-pool-list * nova net * nova net-create * nova net-delete * nova net-list * nova network-create * nova network-delete * nova network-list * nova network-show * nova network-associate-host * nova-network-associate-project * nova network-disassociate * nova scrub * nova secgroup-create * nova secgroup-delete * nova secgroup-list * nova secgroup-update * nova secgroup-add-rule * nova secgroup-delete-rule * nova secgroup-list-rules * nova secgroup-add-default-rule * nova secgroup-delete-default-rule * nova secgroup-list-default-rules * nova secgroup-add-group-rule * nova secgroup-delete-group-rule * nova tenant-network-create * nova tenant-network-delete * nova tenant-network-list * nova tenant-network-show Along with the following python API bindings:: * novaclient.v2.contrib.tenant_networks * novaclient.v2.fixed_ips * novaclient.v2.floating_ip_dns * novaclient.v2.floating_ip_pools * novaclient.v2.floating_ips * novaclient.v2.floating_ips_bulk * novaclient.v2.fping * novaclient.v2.networks * novaclient.v2.security_group_default_rules * novaclient.v2.security_group_rules * novaclient.v2.security_groups deprecations: - | The ``only_contrib`` parameter for the ``novaclient.client.discover_extensions`` method is deprecated and now results in an empty list returned since all contrib extensions are either required or have been removed.python-novaclient-9.1.1/releasenotes/notes/microversion-v2_31-3e1a16eb5eb53f59.yaml0000664000175000017500000000014713165151077030712 0ustar jenkinsjenkins00000000000000--- upgrade: - Support for microversion 2.31 which fixes a bug in the os-console-auth-tokens API python-novaclient-9.1.1/releasenotes/notes/deprecate-connection-pool-arg-cef35346d5ebf40c.yaml0000664000175000017500000000013613165151077033230 0ustar jenkinsjenkins00000000000000--- deprecations: - The **connection_pool** variable is deprecated now and will be ignored. python-novaclient-9.1.1/releasenotes/notes/rename-bypass-url-42cd96956a6bc6dc.yaml0000664000175000017500000000060713165151077030723 0ustar jenkinsjenkins00000000000000--- deprecations: - The **--bypass-url** CLI argument was deprecated in favor of **--endpoint-override** - The **bypass_url** argument of novaclient.client.Client entry-point was deprecated in favor of **endpoint_override**. fixes: - Inability to use bypass-url with keystone session is fixed. features: - You can use **bypass-url** / **endpoint-override** with Keystone V3. python-novaclient-9.1.1/releasenotes/notes/fix-tag-attribute-disappearing-25483a80f548ef35.yaml0000664000175000017500000000070413165151077033137 0ustar jenkinsjenkins00000000000000--- fixes: - | Microversion 2.42 is related to the following bug. * https://bugs.launchpad.net/nova/+bug/1658571 The following options have been changed as of Microversion 2.42. * Remove ``tag`` attribute in ``--block-device`` option on the server boot (nova boot) between microversion 2.33 and 2.41. * Remove ``tag`` attribute in ``--nic`` option on the server boot (nova boot) between microversion 2.37 and 2.41. python-novaclient-9.1.1/releasenotes/notes/microversion-2.37-d03da96406a45e67.yaml0000664000175000017500000000224613165151077030314 0ustar jenkinsjenkins00000000000000--- features: - | The 2.37 microversion is now supported. This introduces the following changes: * CLI: The **--nic** value for the **nova boot** command now takes two special values, 'auto' and 'none'. If --nic is not specified, the CLI defaults to 'auto'. * Python API: The **nics** kwarg is required when creating a server using the *novaclient.v2.servers.ServerManager.create* API. The **nics** value can be a list of dicts or a string with value 'auto' or 'none'. upgrade: - | With the 2.37 microversion, the **nics** kwarg is required when creating a server using the *novaclient.v2.servers.ServerManager.create* API. The **nics** value can be a list of dicts or an enum string with one of the following values: * **auto**: This tells the Compute service to automatically allocate a network for the project if one is not available and then associate an IP from that network with the server. This is the same behavior as passing nics=None before the 2.37 microversion. * **none**: This tells the Compute service to not allocate any networking for the server. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_40-484adba0806b08bf.yaml0000664000175000017500000000023413165151077030677 0ustar jenkinsjenkins00000000000000--- features: - Added microversion v2.40 which introduces pagination support for usage with the help of new optional parameters 'limit' and 'marker'. python-novaclient-9.1.1/releasenotes/notes/deprecate-proxy-args-a3f4e224f7664ff8.yaml0000664000175000017500000000033113165151077031337 0ustar jenkinsjenkins00000000000000--- deprecations: - The **proxy_tenant_id** and **proxy_token** arguments to the novaclient.client.Client entry-point were never documented nor tested and are now deprecated for removal in a future release. python-novaclient-9.1.1/releasenotes/notes/deprecate_cell_name_arg-eb34cb7c43cfcb89.yaml0000664000175000017500000000041013165151077032263 0ustar jenkinsjenkins00000000000000--- deprecations: - CLI argument ``--cell_name`` for ``nova migration-list`` command is deprecated. Nova API does not have logic for handling cell_name parameter in **os-migrations**, and while the parameter is passed to Nova it has never been used. python-novaclient-9.1.1/releasenotes/notes/restrict-direct-use-of-v2client-c8e1ee2afefec5a1.yaml0000664000175000017500000000032713165151077033670 0ustar jenkinsjenkins00000000000000--- upgrade: - novaclient.v2.client.Client raises an exception in case of direct usage instead of warning message. novaclient.client.Client is a primary interface to initialize the python client for Nova. python-novaclient-9.1.1/releasenotes/notes/microversion-v2_34-a9c5601811152964.yaml0000664000175000017500000000006513165151077030332 0ustar jenkinsjenkins00000000000000--- upgrade: - Support for microversion 2.34 added.python-novaclient-9.1.1/babel.cfg0000664000175000017500000000002013165151077020055 0ustar jenkinsjenkins00000000000000[python: **.py] python-novaclient-9.1.1/README.rst0000664000175000017500000000317213165151100020014 0ustar jenkinsjenkins00000000000000======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/python-novaclient.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on ============================================ Python bindings to the OpenStack Compute API ============================================ .. image:: https://img.shields.io/pypi/v/python-novaclient.svg :target: https://pypi.python.org/pypi/python-novaclient/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/python-novaclient.svg :target: https://pypi.python.org/pypi/python-novaclient/ :alt: Downloads This is a client for the OpenStack Compute API. It provides a Python API (the ``novaclient`` module) and a command-line script (``nova``). Each implements 100% of the OpenStack Compute API. * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Launchpad project`_ - release management * `Blueprints`_ - feature specifications * `Bugs`_ - issue tracking * `Source`_ * `Specs`_ * `How to Contribute`_ .. _PyPi: https://pypi.python.org/pypi/python-novaclient .. _Online Documentation: http://docs.openstack.org/python-novaclient .. _Launchpad project: https://launchpad.net/python-novaclient .. _Blueprints: https://blueprints.launchpad.net/python-novaclient .. _Bugs: https://bugs.launchpad.net/python-novaclient .. _Source: https://git.openstack.org/cgit/openstack/python-novaclient .. _How to Contribute: http://docs.openstack.org/infra/manual/developers.html .. _Specs: http://specs.openstack.org/openstack/nova-specs/ python-novaclient-9.1.1/setup.cfg0000664000175000017500000000256213165151230020154 0ustar jenkinsjenkins00000000000000[metadata] name = python-novaclient summary = Client library for OpenStack Compute API description-file = README.rst license = Apache License, Version 2.0 author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = http://docs.openstack.org/developer/python-novaclient classifier = Development Status :: 5 - Production/Stable Environment :: Console Environment :: OpenStack Intended Audience :: Developers Intended Audience :: Information Technology License :: OSI Approved :: Apache Software License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = novaclient [entry_points] console_scripts = nova = novaclient.shell:main [build_sphinx] builders = html,man all-files = 1 warning-is-error = 1 source-dir = doc/source build-dir = doc/build [upload_sphinx] upload-dir = doc/build/html [compile_catalog] domain = novaclient directory = novaclient/locale [update_catalog] domain = novaclient output_dir = novaclient/locale input_file = novaclient/locale/novaclient.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = novaclient/locale/novaclient.pot [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 python-novaclient-9.1.1/HACKING.rst0000664000175000017500000000267413165151100020131 0ustar jenkinsjenkins00000000000000Nova Client Style Commandments ============================== - Step 1: Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking - Step 2: Read on Nova Client Specific Commandments --------------------------------- None so far Text encoding ------------- - All text within python code should be of type 'unicode'. WRONG: >>> s = 'foo' >>> s 'foo' >>> type(s) RIGHT: >>> u = u'foo' >>> u u'foo' >>> type(u) - Transitions between internal unicode and external strings should always be immediately and explicitly encoded or decoded. - All external text that is not explicitly encoded (database storage, commandline arguments, etc.) should be presumed to be encoded as utf-8. WRONG: mystring = infile.readline() myreturnstring = do_some_magic_with(mystring) outfile.write(myreturnstring) RIGHT: mystring = infile.readline() mytext = s.decode('utf-8') returntext = do_some_magic_with(mytext) returnstring = returntext.encode('utf-8') outfile.write(returnstring) Running Tests ------------- The testing system is based on a combination of tox and testr. If you just want to run the whole suite, run `tox` and all will be fine. However, if you'd like to dig in a bit more, you might want to learn some things about testr itself. A basic walkthrough for OpenStack can be found at http://wiki.openstack.org/testr python-novaclient-9.1.1/PKG-INFO0000664000175000017500000000556513165151230017436 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-novaclient Version: 9.1.1 Summary: Client library for OpenStack Compute API Home-page: http://docs.openstack.org/developer/python-novaclient Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: Apache License, Version 2.0 Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/python-novaclient.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on ============================================ Python bindings to the OpenStack Compute API ============================================ .. image:: https://img.shields.io/pypi/v/python-novaclient.svg :target: https://pypi.python.org/pypi/python-novaclient/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/python-novaclient.svg :target: https://pypi.python.org/pypi/python-novaclient/ :alt: Downloads This is a client for the OpenStack Compute API. It provides a Python API (the ``novaclient`` module) and a command-line script (``nova``). Each implements 100% of the OpenStack Compute API. * License: Apache License, Version 2.0 * `PyPi`_ - package installation * `Online Documentation`_ * `Launchpad project`_ - release management * `Blueprints`_ - feature specifications * `Bugs`_ - issue tracking * `Source`_ * `Specs`_ * `How to Contribute`_ .. _PyPi: https://pypi.python.org/pypi/python-novaclient .. _Online Documentation: http://docs.openstack.org/python-novaclient .. _Launchpad project: https://launchpad.net/python-novaclient .. _Blueprints: https://blueprints.launchpad.net/python-novaclient .. _Bugs: https://bugs.launchpad.net/python-novaclient .. _Source: https://git.openstack.org/cgit/openstack/python-novaclient .. _How to Contribute: http://docs.openstack.org/infra/manual/developers.html .. _Specs: http://specs.openstack.org/openstack/nova-specs/ Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 python-novaclient-9.1.1/tox.ini0000664000175000017500000000507513165151100017644 0ustar jenkinsjenkins00000000000000# noted to use py35 you need virtualenv >= 1.11.4 [tox] envlist = py35,py27,pypy,pep8,docs minversion = 2.0 skipsdist = True [testenv] usedevelop = True # tox is silly... these need to be separated by a newline.... whitelist_externals = find bash passenv = ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike} {opts} {packages} setenv = VIRTUAL_ENV={envdir} BRANCH_NAME=master CLIENT_NAME=python-novaclient deps = -r{toxinidir}/test-requirements.txt commands = find . -type f -name "*.pyc" -delete bash tools/pretty_tox.sh '{posargs}' # there is also secret magic in pretty_tox.sh which lets you run in a fail only # mode. To do this define the TRACE_FAILONLY environmental variable. [testenv:pep8] commands = flake8 {posargs} [testenv:bandit] commands = bandit -r novaclient -n5 -x tests [testenv:venv] commands = {posargs} [testenv:docs] commands = python setup.py build_sphinx [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:functional] basepython = python2.7 passenv = OS_NOVACLIENT_TEST_NETWORK setenv = {[testenv]setenv} OS_TEST_PATH = ./novaclient/tests/functional commands = bash tools/pretty_tox.sh '--concurrency=1 {posargs}' [testenv:functional-py35] basepython = python3.5 passenv = OS_NOVACLIENT_TEST_NETWORK setenv = {[testenv]setenv} OS_TEST_PATH = ./novaclient/tests/functional commands = bash tools/pretty_tox.sh '--concurrency=1 {posargs}' [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' coverage report [flake8] # Following checks should be enabled in the future. # # H404 multi line docstring should start without a leading new line # H405 multi line docstring summary not separated with an empty line # # Following checks are ignored on purpose. # # Additional checks are also ignored on purpose: F811, F821 ignore = F811,F821,H404,H405 show-source = True exclude=.venv,.git,.tox,dist,*lib/python*,*egg,build,doc/source/conf.py,releasenotes [hacking] import_exceptions = novaclient.i18n [testenv:bindep] # Do not install any requirements. We want this to be fast and work even if # system dependencies are missing, since it's used to tell you what system # dependencies are missing! This also means that bindep must be installed # separately, outside of the requirements files. deps = bindep commands = bindep test python-novaclient-9.1.1/ChangeLog0000664000175000017500000024234713165151227020122 0ustar jenkinsjenkins00000000000000CHANGES ======= 9.1.1 ----- * Allow boot server with multiple nics * Update UPPER\_CONSTRAINTS\_FILE for stable/pike * Update .gitreview for stable/pike 9.1.0 ----- * Help text for "--matching" is not clear * Be clear about hypevisors.search used in a few CLIs * Change Service repr to use self.id always * Microversion 2.53 - services and hypervisors using UUIDs * Add 'Forced down' column in serivce-list * Updated from global requirements * Updated from global requirements * Updated from global requirements * Expect id and disabled\_reason in GET /os-services response * Updated from global requirements * Adjust test\_resize\_down\_revert to account for counting quotas * Updated from global requirements * Microversion 2.52 - Support tag when boot * Updated from global requirements * Fix the inappropriate parameter name * Add resize down test which also verifies quota changes * Add functional test for resize-confirm plus quota validation * Add support for the 2.51 microversion * Microversion 2.50 - fix quota class sets resource usage * Updated from global requirements * doc: Switch from oslosphinx to openstackdocstheme * doc: Create directory structure for docs migration * Microversion 2.49 - Virt device tagged attach * Fix cropping the endpoint url * Removed extra word 'method' from the NOTE * Deprecate binary argument in nova service enable/disable/force-down CLIs * Updated from global requirements * Microversion 2.48: Standardization of VM diagnostics * 2.47: Show flavor info in server details * Cleanup duplicated methods * Make --profile load from environment variables 9.0.1 ----- * Revert "client.logger.warning wrongly used in migrations" * Clean up ShellTest unit tests * Fix setting 'global\_request\_id' in SessionClient 9.0.0 ----- * Updated from global requirements * Prevent 2.32 release note from showing up in 9.0.0 release notes * Skip rebuild functional test due to persistent vif plugging timeout * strip the remote prefixes from the release note branch specifiers * 2.46: match nova API version * Help message for aggregate-update is ambiguous * client.logger.warning wrongly used in migrations * Better handle key=value pair not being key=value * Error out if nic auto or none are not alone * Updated from global requirements * Have python-novaclient support global\_request\_id * Updated from global requirements * Remove various deprecated options * Updated from global requirements * Updated from global requirements * Fix help message and description for volume-update * Fix warning for deprecated cert commands * Mark cloudpipe deprecated in novaclient * Fix a typo * Updated from global requirements * Updated from global requirements * Remove 1.1 extension comment * 2.45: createImage/createBackup image\_id is in response body * 2.44: Deprecate multinic/floatingIP actions and os-virtual-interfaces * 2.43: Deprecate novaclient /os-hosts usage * Add \`instance-uuid\` flag to the migration-list * Remove direct dependency on requests * Microversion 2.42 - Fix tag attribute disappearing * doc: Remove cruft from conf.py * Explicitly set 'builders' option * Add novaclient client\_name and client\_version to user-agent * Updated from global requirements * Fix cinder volume leakage * Updated from global requirements * Deprecate certs commands and APIs * Revise \`nova list\` description * Update reno for removed network CLIs 8.0.0 ----- * Remove deprecated network APIs * Update Compute API Guide pointer * Fix 'nova list --fields' error in no instances case * Explicitly specify arguments of server\_groups creation * Remove version 1.1 support * Remove log translations * Updated from global requirements * Imported Translations from Zanata * Set test timout to 300 seconds * Drop deprecated aggregate-update positional args * Remove deprecated floating\_ips APIs * Remove deprecated fixed\_ips APIs * Remove deprecated security\_groups APIs * Remove deprecated security\_group\_rules APIs * Remove deprecated security\_group\_default\_rules APIs * Remove the deprecated fping API * Remove deprecated floating\_ip\_pools API * Remove deprecated floating\_ips\_bulk API * Remove deprecated floating IP DNS domain/entry APIs * Remove deprecated tenant network APIs * Remove deprecated baremetal CLIs and APIs * Remove py34 tox env and pypi classifier * Fix aggregate\_update name and availability\_zone clash * Use Sphinx 1.5 warning-is-error * Remove duplicate methods * Fix ValueError when incorrect metadata passed * Updated from global requirements * Tags and Metadata fields with unicode cannot be correctly displayed * [Fix gate]Update test requirement * Release note for cell\_name deprecation * Remove functional tests for removed commands * Deperecate cell\_name cli arg for migration-list * Updated from global requirements * Grammar typo in the comments for function set\_meta * Fix devstack python-novaclient warning * Remove deprecated network-related resource commands * Remove deprecated image commands/API bindings * Add functional test for "nova boot --image-with" * Updated from global requirements * Updated from global requirements * Update reno for stable/ocata 7.1.0 ----- * Add release not for fixing token auth method * Fix functional tests to deal with multiple networks * ListExtResource given in place of ListExtManager * Pass relevant parameters to Token based authentication * Updated from global requirements * x-openstack-request-id logged twice in logs * Add profiling support to novaclient * Fix help strings * Make \_console() public * Allow multiple tag add/delete from cli * Updated from global requirements * Enable coverage report in console output * Fix "Message object do not support addition" * Add support for showing aggregate UUIDs (v2.41) * Blacklist rather than whitelist autodoc modules 7.0.0 ----- * Clarify some release notes prior to the 7.0.0 release * Replaces uuid.uuid4 with uuidutils.generate\_uuid() * Microversion 2.40 - Simple tenant usage pagination * Fixed the \_\_ne\_\_ implementation in base.Resource * Add some missing modules in API reference * Deprecate volume\_service\_name argument * [proxy-api] microversion 2.39 deprecates image-metadata proxy API * Remove all code related to HTTPClient * Create keystone session instance if not present * Make SessionClient interface similar to HTTPClient * Deprecate connection\_pool variable * Transmit all auth related vars from cli to inner methods * Deprecate proxy\_token and proxy\_tenant\_id args * Clarify meaning of project\_id var * Rename interface to endpoint\_type * Rename api\_key to password * Rename bypass\_url to endpoint\_override * Remove redundant args of \_construct\_http\_client * Introduce helper for checking args deprecation * Sort arguments for client's methods * Updated from global requirements * Remove internal attribute access from shell * Add limit and offset to server-groups list * Restict usage \*args for novaclient.client.Client * Add version pin for image list function * CONF.osapi\_max\_limit -> CONF.api.max\_limit * Replace six.iteritems() with .items() * Usage missing from generated docs * remove variable '\_' from tests * Add min-disk and min-ram filters to list flavors * Fix doc generation errors * Check source\_type and destination\_type when booting with bdm provided * Updated from global requirements * Fix can't process the resource with name 'help' * Use more specific asserts in tests * Revert "Microversion 2.39 - Simple tenant usage pagination" * Updated from global requirements * Microversion 2.39 - Simple tenant usage pagination * Move all extensions from contrib dir * Use upper-constraints when running tox * Correct copy/paste errors in help * Show team and repo badges on README * Fix import statement order * Remove unused test code * Remove unused code * Fix the description of hypervisors.list * Change fake server id as str to fit real server id type * modified the description of service.list * Add version pin for image related function * Updated from global requirements * Bump client microversion to 2.38 * Replace oslo\_utils.timeutils.isotime * Updated from global requirements * Updated from global requirements * Add Python 3.5 classifier and venv * Updated from global requirements * Remove support for non-keystone auth systems * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Make "policy" a mandatory argument for server-group-create * Update docs for instructions on deprecating commands * Remove unused helper volume methods in v2 shell * Remove deprecated commands * Add timezone disclaimer to docstring * Enable release notes translation * Clean up requests-mock usage * Replace requests mocking with requests-mock * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Update reno for stable/newton * Fix incorrect output of "nova show" for long user data * Modify use of assertTrue(A in B) * Updated from global requirements 6.0.0 ----- * Fix test\_trigger\_crash\_dump\_in\_locked\_state\_nonadmin test * Fix 'UnicodeEncodeError' for unicode values * Fixes TypeError on Client instantiation * Updated from global requirements * Updated from global requirements * Replace functions 'Dict.get' and 'del' with 'Dict.pop' * Removed unused 'install\_venv' module * [functional] Do not discover same resources for every test * functional tests fail if cirros image not exist * Updated from global requirements * Pick first image if can't find the specific image * Add support for v2.37 and auto-allocated networking * Cap image API deprecated methods at 2.35 * Cap baremetal python APIs at 2.35 * Deprecate all the nova-network functions in the python API * Deprecate network-\* commands and clamp to microversion 2.35 * [functional] Skip tests if API doesn't support microversion * Updated from global requirements * Store api\_version object in one place 5.1.0 ----- * Add eggs to gitignore list * Use glanceclient for functional tests * Skip nova-network-only tests if using Neutron * Make wait\_for\_server\_os\_boot wait longer * Handle successful response in console functional tests * Use neutron for network name -> id resolution * Look up image names directly in glance * Move other-requirements.txt to bindep.txt * Make novaclient functional tests use pretty tox * Updated from global requirements * Added smaller flavors for novaclient functional tests to use * Split nic parsing out of \_boot method * Fix boot --nic error message for v2.32 boundary * Updated from global requirements * Microversion 2.35 adds keypairs pagination support * Modify flatten method to display an empty dict * remove start\_version arg for keypairs v2.10 shell * Added support for microversion 2.34 * Add support for microversion 2.33 * Fix python35 job failures * Refactor test\_servers APIVersion setup * Updated from global requirements * Updated from global requirements * Remove discover from test-requirements * Change all test URLs to use example.com * Fix deprecation message for --volume-service-name 5.0.0 ----- * Deprecate baremetal API and CLI interfaces * Add a missing i18n support * Correctly handle NOVACLIENT\_INESCURE * Virtual device tagging client support * Update clouds.yaml.sample * Updated from global requirements * base.Resource not define \_\_ne\_\_() built-in function * Remove white space between print and () * Clean up deprecated CLI options * functional: fix a deprecation warning in test\_auth.TestAuthentication * functional: skip test\_auth\_via\_keystone\_vX if X is not available * Raise an exception in v2.client for direct\_use * Add support for microversion 2.31 * Updated from global requirements * Updated from global requirements * [functional] Fix wrong message in server description test * Deprecated the \`--volume-service-name\` option * Make it possible to list quotas with details * List system dependencies for running common tests * make string.letters python3 compatible * Updated from global requirements * Fix the help message for 'get-mks-console' * Add support for microversion 2.30 * Add support for microversion 2.29 * Updated from global requirements * [functional] make tests work with only keystone v3 * Updated from global requirements * Update README to comply with Identity v3 * Extend microversion stuff to support resource classes 4.1.0 ----- * Added functional tests for server tags (microverison 2.26) * Add support for microversion 2.28 * TrivialFix: Added missed value in string formatting * Log request\_id for each api call * Functional tests for server's description * Updated from global requirements * Updated from global requirements * Update to microversion 2.27 * Fix funtional test gate failure caused by keystone client change * Fix nova host-evacuate for v2.14 * Updated from global requirements * Use tempes.lib instead of tempes\_lib * Deprecate --tenant option from flavor-access-list * Added Support of microverison 2.26 * Name and AZ should as be optional param on aggregate-update * Updated from global requirements * Update the home-page with developer documentation * Updated from global requirements * Updated from global requirements * Clean the duplicated columns for "nova network-list" * Updated from global requirements * [Trivial] change some functions in novaclient/utils.py to public * Add info for user\_id in v2.10 * Enhance descriptions for get and clear password * Add a note message for reboot * Decorate FakeHTTPClient with versions * Restrict positional arguments for Client * Use common find\_server from v2.shell 4.0.0 ----- * Updated from global requirements * Deprecate image list/show/delete/update CLIs/APIs * Make functional tests work with v3 keystone * Updated from global requirements * Fix typos in docstrings and comments * Remove busted baremetal CLIs and API bindings * Updated from global requirements * Add default values for domain related options * Remove deprecated volume(snapshot) commands/bindings * Switch to 2.1 default api\_version in v2.Client * [tests] initialize client objects inside setUp * Fix ServerGroup.NAME\_ATTR 3.4.0 ----- * Using glance 'image-list'/'image-show' in boot help message * Fix host-evacuate-live for 2.25 microversion * Use keystoneclient python bindings for testing * Removed unused Oslo Incubator code * Adding tox support for bandit * aggregate-details changed to aggregate-show * Handle error response for webob>=1.6.0 * Validate shutdown value of --block-device * Add changes-since support when list servers * Update reno for stable/mitaka * Wrap interface\_list by proper resource class * nova add-secgroup help updated with secgroup id * Remove additional 'timeout' element * Make it clear that host-servers-migrate is a cold migration * Return a less dramatic message for public flavors * Remove unused code in tests/unit/v2/fakes.py * Fix a typo in novaclient/v2/hosts.py * Remove an unused method in novaclient/shell.py * Overwrite Usage class's get() function * Remove console expectation from NMI tests * Use novaclient/utils directly and remove openstack/common (4/4) * Use novaclient/utils directly and remove openstack/common * Use novaclient/utils directly and remove openstack/common (2/4) * Use novaclient/utils directly and remove openstack/common (1/4) 3.3.0 ----- * The novaclient Python API doc keystoneauth example fixed * [microversion] Bump to 2.25 * Support for abort an ongoing live migration * Add two server-migration commands and bump migration-list command * Use isinstance instead of type * Support for forcing live migration to complete * Adds missing internationalization for help message * [microversions] Enable 2.21 * [microversions] fix help msg for versioned args * Prepare to move extension into core plugin * Use assertIsNone instead of assertEqual(None, \*\*\*) * Functional tests for trigger-crash-dump (microversion 2.17) * Fix string interpolation at logging call * Deprecate run\_test.sh * Updated from global requirements * Add a way to discover only contrib extensions * [functional] Move code for boot vm to base testcase * [microversions] Enable 2.20 * [microversions] Turn off check for header in response * [microversions] Add support for 2.19 * Allow to specify a network for functional tests * Provide user with coherent interface to get request\_ids * [microversions] Skip microversion 2.18 * Fix running functional tests against deployment with insecure SSL * Updated from global requirements * Make \_poll\_for\_status more user-friendly * Fix omission of request\_ids returned to user * Remove unnecessary filter from Resource's \_\_repr\_\_() function * Use # noqa to ignore one line but not whole file * Add release notes for return-request-id-to-caller * Update translation setup * Add return-request-id-to-caller function(5/5) * Add return-request-id-to-caller function(4/5) * Add return-request-id-to-caller function(3/5) * Support to boot a VM with network name * Add return-request-id-to-caller function(2/5) * Add return-request-id-to-caller function(1/5) * Add wrapper classes for return-request-id-to-caller * [microversions] Add support for 2.17 * Allow restore command with name param * Updated from global requirements * [microversions] Extend shell with 2.12 * Remove argparse from requirements * Added support for Nova microversion 2.16 * Functional tests for os-services * Functional tests for flavors with public and non-public access * Updated from global requirements * Updated from global requirements * Updated from global requirements * Fix URLs for CLI Reference and API * Add functional tests launcher for py3 env * [microversions] Add support for 2.15 * Updated from global requirements * Test: Clean v2 client userwarning * Fix broken link in documentation * Fix W503 line break before binary operator * Add a mechanism to call out deprecated options * Updated from global requirements * Replace assertTrue(isinstance()) by optimal assert 3.2.0 ----- * Use assertTrue/False instead of assertEqual(T/F) * Add python 2.7 comment * [microversions] Add support for 2.14 * Fix extension loading from python path on Python 2.7 * Wrong usage of "a/an" * Fix help strings * Validate the fixed ip address passed with --nic * [microversions] Add support for API microversion 2.13 * [microversions] share one object for shell arguments * Put py34 first in the env order of tox * Fixed test\_shell which can't test microversions>=2.4 * Updated from global requirements * document search\_opts parameter * Cleanup needless code from oslo-incubator 3.1.0 ----- * Validation for arguments of list command passed by "--fields" 3.0.0 ----- * Allow command line for virtual-interface-list * Removes MANIFEST.in as it is not needed explicitely by PBR * Updated from global requirements * Drop py33 support * Migrate to keystoneauth from keystoneclient * Deprecated tox -downloadcache option removed * Updated from global requirements * [functional-test] add v2.8 functional test * [functional-tests] fix version of novaclient instance * remove the default arguments "[]" * Change the logic for the client to retrive resources * Fix help message in case of microversions * Updated from global requirements * [microversions] Increase max version to 2.12 * [microversions] Increase max version to 2.11 * [microversions] Add support for 2.10 * [microversions] update test\_versions with implemented versions * encode the url parameters * Fix H404/405 violations in novaclient/v2/[a-f] * Fix H404/405 violations in novaclient/tests/\* * Fix multiline string with missing space * [microversions] Increase max version to 2.9 * [microversions] Add support for 2.8 * Enable pass instance name as parameter in nova cli * Fix H404/405 violations in client.py,base.py,api\_version.py * Help msg about libvirt always default device names * Fix a Typo in Docstring * Updated from global requirements * Remove python 2.6 support from python-novaclient * [microversions] Increase max version to 2.7 * Optimize "open" method with context manager * force releasenotes warnings to be treated as errors * Updated from global requirements * Fix repr of a host from a hosts.list() * Updated from global requirements * Add accessIPv4 and accessIPv6 when create server * Add reno for release notes management * Check response headers for microversions * Not transform to str on potential unicode fields * Updated from global requirements * Print current nova default nova API microversion * improve readme contents * Updated from global requirements * Last sync from oslo-incubator * Fix typo in error message 2.35.0 ------ * Added command for device to cinder volume mapping * Functional tests for os-extended-server-attributes * Remove --tenant suggestion for flavor-access-list * Add v2 support for optional project\_id in version discovery urls * Remove novaclient.v1\_1 module 2.34.0 ------ * Revert "Remove novaclient.v1\_1 module" * Revert "Do not expose exceptions from requests library" * Check flavor option before image checks * Updated from global requirements 2.33.0 ------ * Do not expose exceptions from requests library * make project\_id optional in urls for version discovery * Support the server side remote-console API changes * Correct usage of API\_MAX\_VERSION and DEFAULT\_OS\_COMPUTE\_API\_VERSION * Rely on devstack for clouds.yaml * Refactor parsing metadata to a common function * Updated from global requirements * Updated from global requirements * Use v2.0 in clouds.yaml auth\_url for functional test job * Add sort\_dir/key to flavor list * Add pagination params for flavor list * Increase timeout when testing admin timeout * Fix incorrect help for 'nova flavor-create' 2.32.0 ------ * Revert "Allow display project-id for server groups" * Test that microversions are not skipped * Updated from global requirements * Set DEFAULT\_OS\_COMPUTE\_API\_VERSION to 2.5 2.31.0 ------ * Always send volume\_id when booting with legacy bdm * Change default service\_type for volumes managers * Add --metadata as optional input when do nova image-create * Remove novaclient.v1\_1 module * Use dictionary literal for dictionary creation 2.30.0 ------ * Change ignore-errors to ignore\_errors * Modify nova help list message for --tenant * Split functional tests for legacy(V2.1) and microversions * Specify api\_version for functional tests * Improve help strings * Updated from global requirements * Encode unicode filter arguments for server list * [docs] Fix length of underline * Updated from global requirements * Fix nova bash-completion needs authentication * Show reserved status for a fixed\_ip if available * Fix unicode issue in shell.main() error handling * Remove redundant help command test and commonize * Fix the homepage url in setup.cfg * Fix mistakes in comments * Modify "nova keypair-show" Positional arguments help information 2.29.0 ------ * make sure os\_password is set for auth\_plugins * [BugFix] Change parameters for legacy bdm * Set api\_version to 2.0 by default for v2.Client * Deprecate 'novaclient.client.get\_client\_class' * Restrict direct usage of novaclient.v2.client * Fix bugs with rackspace * Add unit tests for different help commands * Enable i18n with Babel * Fix nova --help needs authentication 2.28.1 ------ * [Bug-Fix] Update requests body for quota-update * Adds missing internationalization for help message 2.28.0 ------ * workaround for RAX repose configuration * fix novaclient functional tests for new devstack config * Don't assume oscomputeversions is correctly deployed * Update path to subunit2html in post\_test\_hook * Option to specify max servers for live evacuate 2.27.0 ------ * Allow to reboot multiple servers * Add method for better random name * Launch functional tests serially * Add mechanism to vm list to return all resources * Fixed typo Errors in comments * Updated from global requirements * Add "limit" option to servers list cli * Add 'marker' argument to server list cli * Fix a fault of request logging with no credentials * Add response message when the state of a server is reset * Fix versions.list for v2.1 Nova API * Add help message for floating ip bulk operation * Correct the files's description "overrwriter" * Updated from global requirements * Updated from global requirements * Add ability to use default major version * Copy cli arguments in api\_versions.wraps decorator * Change docstring of api\_versions.discover\_version * Allow display project-id for server groups * Remove \_discover\_extensions * Updated from global requirements * Support forcing service down * Adds support for x509 certificates as keypairs * Set "latest" as default compute api version * Add version discover and check in CLI * Specify NIC option for nova boot * Refactor and update test\_instances and test\_volumes\_api * Use keystoneclient's TCPKeepAliveAdapter * Add 'deleted' status check in \_poll\_for\_status * Add "get\_current" method to VersionManager * Implements 'microversions' api type - Part 2 * Fixes table when there are multiline in result data * rename root-password to set-password * Updated from global requirements * hypervisor command can't use cell format id to show hypervisor * Set iso8601 log level to WARNING * Updated from global requirements * Implements 'microversions' api type - Part 1 * Updated from global requirements * Updated from global requirements * Improve hypervisor-show print list * Updated from global requirements * Fix resolving image.id in servers.boot * Added marker functionality to flavours and images * Updated from global requirements * Updated from global requirements * Change future nova version number references based on new values * cleanup openstack-common.conf and sync updated files * Updated from global requirements * Revert "Allow admin user to get all tenant's floating IPs" * Add a sample clouds.yaml * Pass full path to pkgutil.iter\_modules() * Cache a new token when the existing token expires * Add help message for secgroup-add/del-default-rule * Cleanup various inaccuracies in the README.rst * Update weblinks * Adds support to set admin password from the cli * Adding missing nova read only CLI test * Updated from global requirements * Fix all doc warnings and gate on warnings * Add docs tox env 2.26.0 ------ * Remove unused novaclient.tests.unit.v2.utils module * Add documentation on command deprecation process * Deprecate volume/volume-type/volume-snapshot CRUD CLIs/APIs * Do not check requirements when loading entry points * Eliminate test comprehensions * Remove redundant check for version of \`requests\` * Use clouds.yaml for functional test credentials * pass credentials via config file instead of magic * server-group-list support 'all\_projects' parameter 2.25.0 ------ * Drop use of 'oslo' namespace package * Reuse uuidutils frim oslo\_utils * Sync latest code from oslo-incubator * Updated from global requirements * Make \_discover\_extensions public * Updated from global requirements * nova client now support limits subcommand * Don't use SessionClient for version-list API * Add min/max microversions to version-list cmd * Deprecate v1.1 and remove v3 2.24.1 ------ * fix FloatingIP repr * Don't lookup service url when bypass\_url is given * Revert "nova flavor-show command is inconsistent" * add ips to novaclient server manager * Update README to work with release tools * refactor functional test base class to no inherit from tempest\_lib 2.24.0 ------ * Remove all imports from oslo namespace * Fix typo on class Client sample * Uncap library requirements for liberty * Report better error message --ephemeral poor usage * Fix displaying of an unavailable flavor of a showing instance * Handle binary userdata files such as gzip * Add --all-tenants option to 'nova delete' * Combine test cases for checking nova limits response * Fix repr of FloatingIPBulk * Fix comments on metadata number limitation * Corrected help for nova boot when attaching block device * Removes reference to v3 nova api from novaclient docs * Don't record time when self.timing is False * Updated from global requirements * Revert 'Remove image to local block device mapping' 2.23.0 ------ * Ensure the use of volume endpoint in volumes apis * Add missing servers.create parameter documentation * Add Client object to documentation * Add a test for the TCPKeepAliveAdapter * Update help message for nova boot --file * Fix typo in socket attribute name * nova client cinder query param changed to display\_name * nova flavor-show command is inconsistent * Update help messages for default security group commands * Add functional testing README * Fix client usage in api example * Updated from global requirements * Cleanup in asserts in python-novaclient * Update version of novaclient in the docs * Cleanup in test\_images and image\_fakes 2.22.0 ------ * Fix description of parameters in nova-client functions * Enable check for E124 rule * Removed unused 'e' from 'try except' statements * allow --endpoint-type internal|admin|public * Fixed redeclared test\_names * Updated from global requirements * add pretty\_tox to nova functional tests * add functional test for nova volume-attach bug * Revert "Overhaul bash-completion to support non-UUID based IDs" 2.21.0 ------ * Change commands name from net-\* to tenant-network-\* * Updated from global requirements * Copy functional tests from tempest cli read only * Add all tenants search opt to del instnce by name * Updated from global requirements * Check 'auth\_url' is presented while authentication * Wrong help for nova usage command * Add support for keypair-add command reading public key from stdin * Compare dicts for POST data in test\_client\_reauth * Remove image to local block device mapping * Change the unsuitable item caching for completion * Moved set of asserts from post\_servers\_1234\_action methods * Change logic in find\_resource() to identify upper-case/lower-case name * Add first pass at post\_test\_hook for functional tests * Rename v1\_1 to v2 * Fix issue of quota-show and quota-defaults * Refer to the admin password consistently * Update volumes.get() docstring to correctly reflect functionality * First pass at tempest\_lib based functional testing * Add OS\_TEST\_PATH to testr * Move unit tests into unit test directory * whitelist find in testenv * Updated from global requirements * Use TCP Keep-Alive on the socket level * Updated from global requirements * In strings/comments change Ip/ip to IP * Improving the help of the lock command * Updated from global requirements * Move to hacking 0.10 * Updated from global requirements * Adds separate class for Hypervisor Stats * Directly using base64 encoding for injected files * Document unexpected need for --all-tenants when using --tenant * Add 'Id' column to floating-ip-list * Pass kwargs through to the adapter * Display tenant id with nova list --all-tenants * novaclient sort parameters support * Reject non existent mock assert calls * Updated from global requirements * pass id to ServerGroupsManager in ServerGroup.delete() * Workflow documentation is now in infra-manual * Updated from global requirements * Remove aliases for \`args\` and \`env\` in utils * Use \`arg\` and \`env\` from cliutils: v1\_1/shell * Remove code related to V3 * Enable check for E128 rule * Fix E128 failures in novaclient/tests * Fix E128 failures in novaclient/v3 * Fix E128 failures in novaclient/v1\_1/ * Fix E128 failures in novaclient/v1\_1/shell * Updated from global requirements * Curl statements to include globoff for IPv6 URLs * Support using the Keystone V3 API from the Nova CLI * Add limits to V3 and improve limits formatting in shell * Fix the help comment about metadata * Updated from global requirements * support OS\_ENDPOINT\_TYPE in nova client * Updated from global requirements * Enable check for E129 * Enable check for E127 * Enable check for E126 * Propose 'tox' as tests launcher * Update novaclient shell to use shared arguments from Session * Allow to start/stop multiple servers * Define helper to run an action on many resources * Add missing parameters for server rescue * Remove E12\* from list of deliberately ignored flake8 rules * no way to delete valid/invalid security rule * Sync latest code * Port to use oslo.i18n * Avoid "ambiguous option" when only current/deprecated forms match * Updated from global requirements * Clarify "nova scrub" help text * Updated from global requirements * Show 'state' and 'status' in hypervisor-list * return 130 for keyboard interrupt * Fix parameter description in create\_server * Enable check for E123 * Enable check for E122 * Enable check for E121 * Use oslo.serialization * Use common code instead of novaclient.utils 2.20.0 ------ * Updated from global requirements * Corrects typos "coearse," "proejct," and "unecrypts" * Add list by user to shell * Add retry\_after only to exceptions supporting it 2.19.0 ------ * secgroup-create description is not optional * Live migrate each instance from one host to other hosts * novaclient: Convert v3 boot command with v2.1 spec (bdm) * Stop using intersphinx * novaclient: Convert v3 boot command with v2.1 spec (user-data) * novaclient: Convert v3 boot command with v2.1 spec (security-groups) * delete python bytecode before every test run * Add support for the server group quotas * quota delete tenant\_id parameter should be required * Don't display duplicated security groups * Updated from global requirements * add new command get-serial-console * Make findall support server side filtering * Fix test mistake with requests-mock * Use oslo.utils * Use Token fixtures from keystoneclient * Update requirements.txt to include keystoneclient * Updated from global requirements * Updated from global requirements * Enhance network-list to allow --fields * Adding Nova Client support for auto find host APIv2 * Adding Nova Client support for auto find host APIv3 * Add filtering by service to hosts list command * Quickstart (README) doc should refer to nova * Updated from global requirements * Fix listing of flavor-list (V1\_1) to display swap value * Use adapter from keystoneclient * Fix the return code of the command "delete" * Fix variable error for nova --service-type * Convert to requests-mock * Enable several checks and do not check docs/source/conf.py * Updated from global requirements * Fix order of arguments in assertEquals * Enable check for E131 * Add support for security-group-default-rules * Fix rxtx\_factor name for creating a flavor * Allow selecting the network for doing the ssh with * fix host resource repr to use 'host' attribute * Enable H233 * Don't log sensitive auth data * Enabled hacking checks H305 and H307 * Edits on help strings * Add support for new fields in network create 2.18.1 ------ * Add "version-list" for listing REST API versions * Description is mandatory parameter when creating Security Group * Revert "Reuse exceptions from Oslo" * Updated from global requirements * Revert "Set default http-based exception as \`HttpError\`" 2.18.0 ------ * Adding multiple server support to nova reset-state * Update nova boot help * Convert Server Group Tests to httpretty * Convert security group tests to httpretty * Convert security group rules tests to httpretty * Convert Quota tests to httpretty * Fix the wrong dest of 'vlan' option and add new 'vlan\_start' option * Fixing flavor access \_\_repr\_\_ method * Allow us to use keystoneclient's session * Fix the section name in CONTRIBUTING.rst * Adds clarification note for project\_id vs tenant\_id * Filter endpoints by region whenever possible * Convert network tests to httpretty * Convert limit tests to httpretty * Convert keypair tests to httpretty * Convert image tests to httpretty * Convert Hypervisor tests to httpretty * Convert hosts tests to httpretty * Add missing parameters for server rebuild * Fixes typo in error message of do\_network\_create * Mention keystoneclient.Session use in docs * Fix using a variable which is not defined * Fix booting from volume when using api v3 * Sync apiclient from oslo-incubator * Convert server tests to httpretty * Convert floating IP pool tests to httpretty * add disk bus and device type to volume attach * Fix listing of Server in floating-ip-list * Sync Oslo's apiclient * Overhaul bash-completion to support non-UUID based IDs * Add some security group tests for the V1\_1 API * Updated from global requirements * Allow updating fixed\_ips quotas in quota-class-update (v2 shell only) * Look for all accessible flavors by default, not just public ones * Enable F841 * Updated from global requirements * Remove quota-class-\* commands from v3 shell * Updated from global requirements * "nova boot" should not log an error if subsidiary commands fail * Bump hacking to 0.9.x series * Add posargs support to flake8 call * Fixes wrong value description for volume-detach * adjust to {SHA1} convention for token * 'policy' should be required in server\_group\_create * Adding cornercases for set\_metadata * add tox target for python 3.4 * mask keystone token in debug output * Remove \_print\_volume from volume-update * Add way to specify key-name from environ * Convert Floating IP bulk tests to httpretty * Convert Floating IPs DNS tests to httpretty * Convert Floating IP tests to httpretty * Overwrite HelpFormatter constructur to extend argument column * Add swap measurement unit (MB) to CLI output * Change help message for volume-update * Add NOVACLIENT\_BYPASS\_URL env variable * Logical error in flavors unset\_keys method * Removed now unnecesary workaround for PyPy * Change help for --poll option in Nova commands * Revert "Remove quota-class subcommand" * Synced jsonutils from oslo-incubator * Convert fPing tests to httpretty * Convert Fixed IP tests to httpretty * Convert Cloud Pipe tests to httpretty * In Py3 decode the output of base64.decode * Convert certificate tests to httpretty * Set default http-based exception as \`HttpError\` * Convert Availability Zone testing to httpretty * Convert agent testing to httpretty * debug level logs should not be translated * Add missing dependent module gettextutils * Allow the default value of 1 to be set for boot multiple * Add extension-list command for v3 API * Add mailmap entry * Some Help Messages Missing Translation Support * Print message if instance is successfully deleted v3 * Enable delete multiple server groups in one request * Make port-id and net-id keys mutually exclusive * Remove duplicate test test\_boot\_multiple * Deprecate num-instances in favor of min/max count * Make help description of rescue/unrescue more useful * Print message if instance is successfully deleted * Updated from global requirements * Fix documentation for config\_drive boot parameter * Synced jsonutils from oslo-incubator * Fix for "nova help list" command * Fixed a typo in a comment * Reuse exceptions from Oslo * Updated from global requirements * Fix the incorrect return messages in keypair show and delete * Fix wrong fake return values for Nova V3 client tests * Add unit test for keypair's api * Fix the unlimited length of console-log * Fix mac address and task\_state in baremetal-node-list * Updated from global requirements * Convert aggregates testing to use httpretty * Fix name arg help for volume-type-create * Add service-delete subcommand to delete a service * Updated from global requirements * Fix session handling in novaclient * Print adminPass when rebuilding from shell * Remove py3kcompat * Split test\_rebuild() into two tests * Allow admin user to get all tenant's floating IPs * Fix for invalid literal ValueError parsing ipv6 url(s) * Remove unused arguments to \_boot() * typo in novaclient * Sync with Oslo-Incubator * Start using oslosphinx theme for docs * Fix error when run with no arguments on Python 3 * Avoid AttributeError in servers.Server.\_\_repr\_\_ * Raise exception when poll returns error state * Correct the help sting of volume-type-delete * Print a useful message for unknown server errors * Show Exception Name in Shell Output * Work around pypy testing issue * Support IPv6 when booting instances * Updated from global requirements * Do auth\_url.rstrip('/') only if auth\_url is set * Explain how to delete a metadata in aggregate-set-metadata * Nova CLI for server groups * Removes copy of output of 'nova help' from README * Fix authentication bug when booting an server in V3 2.17.0 ------ * Allow user ID for authentication * Add os-server-external-events support * Fix some spelling mistakes * Add classifiers for specific versions of Python * Remove quota-class subcommand * Re-add install\_venv\_common to openstack-common.conf * oslo sync apiclient and cliutils * Fix in in novaclient, to avoid excessive conns * Revert "'name' should as be optional param on aggregate-update" * Add service-list show \`id\` column * oslo-sync of low hanging fruit * Updated from global requirements * 'name' should as be optional param on aggregate-update 2.16.0 ------ * Fix typo in novaclient * Remove usage of module py3kcompat * Updated from global requirements * Invalid client version message unclear * Remove None for dict.get() * Replace assertEqual(None, \*) with assertIsNone in tests * Fix element miss in client request body * Fix i18n messages in novaclient, part II * Update broken command line reference link * Fix spelling miss of password\_func variable * Fix copy/paste errors in print messages * Remove invalid parameter of quota-update * Remove tox locale overrides * Fix python 3.3 unit test job * Adds support for the get\_rdp\_console API * Support building wheels (PEP-427) * Fixed polling after boot in shell * Update my mailmap * Fix Serivce class AttributeError * [UT] Fixed floating\_ip\_pools fake return to expected one * [UT] Removed duplicate key from dict in fake baremetal\_node * Fixed multi validation and wrong fail calls in unit tests * Fixed super constructor call for TestResponse class * Flavor ExtraSpecs containing '/' cannot be deleted * Removed undefined method in install\_env.py file * Fix i18n messages in novaclient, part I * Adds ability to boot a server via the Nova V3 API * Removes unsupported volume commands from V3 API support * Reuse Resource from oslo * Updates nova client to use the latest oslo files * Using common methods from oslo cliutils * Add tests for boot method of v3 shell * Replace basestring by six.string\_types * Removes use of timeutils.set\_time\_override * Fix logic for "nova flavor-show 0#" * Sync with global requirements * Don't call CS if a token + URL are provided * Sync cliutils from oslo * Sync apiclient from oslo * Fix QuotaClassSet and their tests * assertTrue(isinstance) replace by assertIsInstance * Remove the coverage extension code * shell: refactor boot to use \_print\_server * Don't slugify() None names * Adds volume support for the V3 API * Fixes ambiguous cli output between "None" and NoneType * Support list deleted servers for admin * Using floating-ip-{associate|disassociate} * Removes vim configuration headers * Adds quota usage support for the V3 API * Fix tab-completion of --flags under OS X * Remove class\_name parameter from quota\_class * Ensure that the diagnostics are user friendly * Code cleanup: use oslo's to\_slug() instead of slugify() * Added v3 interfaces in reference doc * Enable pep8 check for config.py in doc * Generate interfaces reference doc * Ensure that nova client prints dictionaries and arrays correctly * Replace some utils.bool\_from\_str with strutils * Allow empty response in service-list * Nova aggregate-details should be more human friendly * Removed duplicated import * Adding additional tests for novaclient ssh * Fix "device" as the optional para on volume-attach * Adds simple tenant usage support for the Nova V3 API * Adds keypairs support for the Nova V3 API * Adds certificates support for Nova V3 API * Adds aggregates support for Nova V3 API * Adds hypervisor support for Nova V3 API * Adds services support for Nova V3 API * Adds second part of quotas support for Nova V3 API * Adds first part of quotas support for Nova V3 API * Adds availability zone support for Nova V3 API * Adds basic servers support for the Nova V3 API * add support for nova ssh user@host * remove duplicate six import * Allow multiple volume delete from cli like Cinder * Fixed autodoc can't import/find class error * Expose the rebuild preserve-ephemeral extension * Stop using deprecated keyring backends * Adds images support for Nova V3 API * Remove commands not supported by Nova V3 API * Adds agent support for Nova V3 API * Adds flavor access support for Nova V3 API * Adds flavor support for Nova V3 API * Enables H403 pep8 rules * Allow graceful shutdown on Ctrl+C * Enables H306 pep8 rules * Enables E711,E721,E712 pep8 rules * Updates tox.ini to use new features * Updated from global requirements * Remove the release.rst file * Fix docstring on novaclient * add support for server set metadata item * Fix incorrect help message on flavor\_access action * Fix inappropriate comment for delete FloatingIP * Enable hacking check for Apache 2.0 license * Sets default service type for Nova V3 API * Fix the inappropriate comment for flavor * Adds a --show option to the image-create subcommand * Updates .gitignore * Allows users to retrieve ciphered VM passwords * Fix inappropriate comment for flavor create api * Fix typo in novaclient * Removes unnecessary pass * Updated from global requirements * Discrepancy between README.rst and nova help * nova security-group-\* should support uuid as input * Change "project" to "project\_id" in cloudpipe-create * Fix single H234 Bug to make Hacking 0.8 pass * Flatten hypervisor-show dictionary for printing * Revert "Nova aggregate-details should be more human friendly" * Update mailmap for Joe Gordon * Print security groups as a human readable list * Adds locking to completion caches * Nova aggregate-details should be more human friendly * Make 'nova ssh' automatically fall back to private address * Quote URL in curl output to handle query params * Add --insecure to curl output if required * Apply six for metaclass * Updated from global requirements * Remove deprecated NOVA\_RAX\_AUTH * Print dicts in alphabetical order * Make os-cache retry on an invalid token * Document and make OS\_CACHE work * Revert "Add-in some re-auth logic when using os\_cache" * Align mocking pattern for test case * py33: use six.StringIO() to mock stdout/stderr * py33: sort the files parameters of "--files" * py33: sort hosts while treeize AvailabilityZone * py33: unify the input of request to json format * py33: align the order of parameters for urlencode() * py33: sort dict for test\_add\_floating\_ip\_to\_fixed * py33: iteration order of dict is unpredictable * Updated from global requirements * py33: 'str' does not support the buffer interface * assertEquals is deprecated, use assertEqual * py33: align the order of parameters for urlencode() * Add shelve/unshelve/shelve-offload command * py33: uuid verification in find\_resource() * py33: don't encode security\_group * Add-in some re-auth logic when using os\_cache * if we have a valid auth token, use it instead of generating a new one * py33: safe\_encode() returns bytes in Python 3 * py33: unknown encoding: base64 Edit * Fix AttributeError in Keypair.\_add\_details() * Fixed several test failures on Python3 * Make nova CLI use term "server" where possible * py33: dict.keys() is not a list in python3 * Corrected several usage of keys() for Python 3 * py33: 'dict\_keys' object does not support indexing * Corrected usage of len(filter(...)) * Update pbr usage * Clean up a little cruft * Novaclient shell list command should support a minimal server list 2.15.0 ------ * Add v3 HostManager * Create v3 tests directory * Fix the print order of quota-show * assertEquals is deprecated, use assertEqual * Small bugfix for client v3 * Modify --num-instances flag description to clarify limit upper bound * Add a block device for the image when using BDMv2 * python3: Compatibility for iteritems differences * python3: Fix traceback while running unit tests * python3: Fix Traceback while running unit tests * Unittests added for client v1\_1 * Python3: Fix traceback while running unit tests * Python3: Use six.StringIO for io.Bytes() * Update oslo from oslo-incubator * Add delete method to Flavor class * New syntax to boot from a block device mapping * Allow name argument to flavor-access-add * Add support for os-assisted-volume-snapshots * Suport instance list pagination in novaclient, Part I * Add interface for listing security groups of an instance * Added support for running the tests under PyPy with tox * python3: Fix imports for py2/py3 * Upgrade to Hacking 0.7 * Sync py3kcompat from oslo * Update mailmap * Update mailmap * Added 'nova migration-list' command * Fix and gate on H501, no locals for string formatting * Update oslo * Allow name argument to flavor-access-add * python3: Fix traceback while running tests * Fix the help messages to specify image/flavor name * Clean up inaccurate docstrings of server list() method * Remove old references * Enable v3 api code * Begin adding v3 api support * change 'Host' object's 'host' attribute to 'host\_name' * Updated from global requirements * Do not restrict flavor to only ID and integers * Fix typo and grammar in docstring only 2.14.1 ------ * remove requests version max 2.14.0 ------ * Sync with global requirements * Add support for swap\_volume * FakeClient: fix the arguments of a string format * Support programmatic use of disk config extension * Check whether the security group id is integer * Fixing host-action on V2 * Add user quota client API support * Fix net-id metavar for interface-attach * make findall in novaclient/base.py more efficient * Fix the help text process and the generated wrong help * Remove python 2.4 and python 2.5 support * Enable force\_delete and restore instance via novaclient * Add name argument to aggregate commands * Add name argument to hypervisor commands * recognize 429 as an rate limiting status * Fix backwards-incompatible API change (method signature) * Fix and enable gating on H402 * Add AgregatesManager.get() * Skip setting volume\_size if not given * Fix interface-list got none mac address * Remove uncessary code related to nova start/stop * make v2\_auth and plugin\_auth explictly return their results * Sync install\_venv\_common from oslo * Clean up and make HACKING.rst point to openstack-dev/hacking * CLI for disable service reason * Allow tenant ID for authentication * Adds zsh completion * Bring stdout/stderr capturing in line w/ nova * Fixup trivial License Header mismatch * Remove Diablo compatibility options * python3: Fix print statements * python3: Compatibility for iteritems differences * python3: Fix unicode compatibility python2/python3 * Return Customer's Quota Usage through Admin API * Discard possibly expired token before re-authenticating * Support force update quota * Update help for --nic opt and make net-id or port-id required * Adds support for ExtendedFloatingIps APi extension * Remove explicit distribute depend * Cells Support * Set default value of flavorid to "auto" * Migrate each instances of a host to another * Set/Delete metadata on all instances of a host * The 'nova keypair-show key\_name' command added * Use Python 3.x compatible except: construct * Delete a quota through admin api * Exit w/ valid code when no servers are deleted * Evacuate each instance from one host to another * python3: Introduce py33 to tox.ini * Start using Hacking and PyFlakes * Add update method of security group name and description * Fix shell tests for older prettytable versions * Provide nova CLI man page * Improve error messages for invalid --nic / --file * 100% test coverage for security groups and rules * Add MethodNotAllowed and Conflict exception classes * Move tests into the novaclient package * Add CONTRIBUTING file * Rename requires files to standard names * Code cleanup in advance of flake8 * Migrate to flake8 * Revert "Support force update quota" * Only add logging handlers if there currently aren't any * Convert to more modern openstack-common.conf format * Cleanup unused local variables * Reuse oslo for is\_uuid\_like() implementation * Synchronize code from oslo * Migrate to pbr * Cleanup nova subcommands for security groups and rules * Make ManagerWithFind abstract and fix its descendants * Cleanup some flavor commands * Fix the default parameter in print\_list * Fix for --bridge-interface being ignore by nova network-create * Add setuptools\_git-\*.egg to .gitignore * Expose retry\_after attribute of OverLimit exception * Adds extended status fields to nova list * Clean up exceptions.from\_response * Allow deleting multiple images from shell * Synchronize code from oslo * Add 'flavor-list --all' admin switch * Fix nova instance-action-list output field and order * Make list flavor show extra specs optional * Use HTTP keep-alive feature in HTTPClient class * Cleanup unused import * Make --vlan option work in network-create in VLAN mode * Support force update quota * make sure .get() also updates \_info * Add coverage-reset command to reset Nova coverage data * Fixing shell command 'service-disable' description * Correct a unit test failure that crept into trunk * Fix problem with nova --version * Make "multi\_host" True when it is set to 'T' in network\_create * Fix IBM copyright strings * Allow for bypass\_url when using proxy\_token 2.13.0 ------ * Fix mispelt x-auth-token header * Remove actions command from servers * do not ignore --os-cache * Improve authentication plugins management * Skip security groups w/ no protocol * catch NoKeyringDaemonError from gnomekeyring * Ensure shell tests use isolated env variables set * Update to latest openstack.common.setup * setuptools: remove data\_files section * Use correct filter name for listing of instances 2.12.0 ------ * Don't check build/ for pep8 violations * Add support for retrieving instance-actions info * Split commands properly for bash completion test * Remove extraneous output during testing * Use setuptools-git to include files from the repo * Update tools/pip-requires for prettytable * Fix keypair-delete help documents * Add support for the new fixed\_ip quota * Set up debug level on root logger * Remove unused import * Fix Copyright Headers from LLC to Foundation * Removes tenant IDs checking for nova quota operations * Make os-services API extensions consistent with Nova * Revert API changes in "Unify Manager.\_update behaviour" * Use keyring for testing * Show Tenant\_ID for secgroup-list with all-tenant * Additional "Unify Manager.\_update behaviour" cleanup * Add wrap option to nova credentials for humans * Check if tenant flag is uuid\_like for all quota operations * Fix nova boot --num-instances option checking * Fix typo in error message * Extend test coverage for v1\_1/shell.py * Decodes input and encodes output * Fixed bug with password prompt, added tests * Make ip\_protocol parameter in security groups rules case insensitive * Fixes the output of 'os-evacuate' command * Update the docstring of cloudpipe-configure command * Accept 201 status code on POST * Fix how tests were failing due to missing attributes * Missing import for gnomekeyring * A minimum of Python3 fixes so that installation works without errors/warnings * Allows admins to reset-network of an instance * Remove prov\_vlan\_id from baremetal * Add support for os-attach-interfaces * Added limit to image-list in a preparatory step toward addressing bug 1001345 * Extend test coverage (shell, fping) 2.11.1 ------ * Issue when gnomekeyring is present but not the current backend * Avoid doing a deep copy on the availability zone manager * Allow extensions to provide a name when discovered on the python path * Fix IOError with gnomekeyring.find\_network\_password\_sync * Expand and improve baremetal API * Fix nova availability-zone-list for admin users * Make availability\_zone in aggregate\_create optional * Corrects 2nd argument type for logging 2.11.0 ------ * Add format options to 'nova coverage-report' * Update to requests >= 0.8 * Mask permissions on private key files * Fix run\_tests.sh --coverage * Support showing extra fields in server list * management\_url not set by authenticate method * Update .coveragerc * Show the summary or details of all availability zones of a region * Upgrade to pep8 1.3.3 * Fixed 7 pep8 errors * Live migration with an auto selection of dest * Add help about the id 'auto' for flavor-create * Fix default format of 'nova coverage-report' * Add usage command to show usage for single tenant * Store tenant\_id from keystone and use for quotas * Show the details of the added bare-metal resource * Fix the usage of password, keyrings, and tokens * Added homedir path expansion for keypair-add * Migrate from nose to testr * \_get\_secgroup returns first group even if multiple groups match * Fix bash completion on osx * Check tenant\_id's format in "nova quota-update" * ClientExceptions should include url and method * Adds baremetal nova API support * RateLimit does not have method attribute * make print\_dict split strings with newlines into multiple rows * Allow for image selection using the metadata properties * Add support for get\_spice\_console RPC API * Ensure list output function can support non-sorting printing * Allow request timeout to be specified * Implement get password for novaclient * Adds tenant network support to the client * Update functionality of coverage extension * Fix a couple of broken shell tests * Update hosts API action calls (startup etc.) * When logging request include request data * Add support for instance evacuate * Fix the help text of add-fixed-ip * Move from untitest2 to testtools * Update README.rst * Unify Manager.\_update behaviour * Fix some usage messages of 'nova volume-\*' * add num\_instances option for nova boot * Use requests module for HTTP/HTTPS * Fix find for alphanumeic flavor id/name * Make --tenant a required arg for quota-show * Add support for the coverage extension * Specify some arguments by name * Makes the OS\_NO\_CACHE env variable work again * Add optional argument to include reservations in os-used-limits * Add nova client support for nova-manage agent command * Adds --os-cache to replace old --no-cache * Adds support for security group/rules quotas * Adds nova client support for nova-manage network command * add host-update help info param * Fix argument checking method for 'nova list --flavor' command * Fix a wrong substition for '-h' in bash completion * Fixed nics param ignored when bdm is specified * Adds support for key\_pairs quota * Adds support for injected\_file\_path\_bytes quota * Adds nova client support for nova-manage floating command 2.10.0 ------ * Remove unnecessary casts in flavor create * Validate that rxtx\_factor is a float * Adds nova client support for nova-manage vpn command * Fix aggregate command help messages * Add nova client support for nova-manage account scrub command * Adds nova client support for nova-manage fixed command * Implement fping calls in nova client * Expand help message for 'migrate' to explain how the new host is selected * Improved quota output * Boot from volume without image supplied * Added --extra-opts to the nova ssh command * Cleans up the flavor creation code. Fixes bug 1080891 * Adding support to filter instances by tenant from the admin api * Make sure create\_image returns result * make tenant id optional for quota-defaults and quota-show * fix hypervisor-servers for hypervisors without servers * discover extensions via entry points * show help when calling without arguments * Add nova client support for nova-manage service command * Updated the help text for nova list command * Fixes setup compatibility issue on Windows * include projectid in the cache key * Fixes utils.findresource checking for integer * Allows deletion of multiple servers through CLI * Add ability of nova client to display availability zones when listing hosts * Validate that boolean parameters are boolean * Auto-Assign Flavor ID * Pull in latest openstack-common changes and fix a minor PEP8 issue * Add OpenStack trove classifier for PyPI * Exception handling for 'nova flavor-create' arguments * Add support for backup instance * Add simple os-api extension cli extension * Raises Exception on improper Auth Configuration * Do not prefer ALL\_TENANTS environment variable to command line arguments * Encode user data to utf-8 when creating a server * Add --all-tenants option to volume-list 2.9.0 ----- * Show volume and snapshot data on create * Fixes setup compatibility issue on Windows * allow empty network list to be requested * Work around httplib2 tunnelling bug * Add support for all-tenants search opt to secgroup-list * expose os-networks extension to CLI * Add support for Unicode secgroup names * Support flavor extra specs in nova client * Optionally faster 'nova show' * Makes handling of nic args more robust * Show instances built from deleted snapshots * Add ConnectionRefused exception 2.8.0 ----- * Fix usage-list date range to use UTC time * Show POST in debug with curl * Fixes doc string and string formatting * Add the image\_id arg to volume create * Make region case insensitive * Fix PEP8 issues * Add -X to DELETE and PUT in debug mode * Implement project specific flavors API, client bindings * Add missing port-id usage info * Change '\_' to '-' in options * Adding --version option * Added -nic port-id= support * Implement network calls in nova client * Add nosehtmloutput as a test dependency * split req and response logging this allows capture of timestamps prior to and after request for timing also did some pep8 1.3 cleanup while I was in there * Add availability\_zone support for volume creation * Adds support for autogenerated device on attach * Allow resources to use any field as 'name' * gitignore ChangeLog and add to MANIFEST.in * Allow different auth providers via plugin system * Better handling of stale tokens (no more 401's) * change image list and network list data to be sorted by name rather than UUID * Relex prettytable depend to match glanceclient 2.7.0 ----- * Add call to get hypervisor statistics * Fix image-create --poll race-condition * set admin password during instance creation * Clarify usage of --insecure flag * Fix resize polling * Add support for modification of instance Security Group * Add support for hypervisor-uptime * Install test-requires in development venv * 'endpoints' and 'credentials' work with token caching * This should fix a problem with overly aggressive token caching * Flavor-list sort by ID numerically * Bring back the output from client.http\_log() 2.6.10 ------ * Add hypervisor information extension * More friendly keyring support when token caching is off * Whoops, the last changes to keyring introduced some problems with v1.1 auth tests * Auth token caching on by default. --no\_cache to disable. Better bypass support too * Add host-list command * Indicate unused variables and other misc. house cleaning * don't bash-complete the '-h' option * Add read\_versioninfo method * Turn multiple hints with the same key into a list * Cleanup of setup.py usage of openstack-common * Implement post-tag versioning numbering * Small doc cleanup round * Update Contributing blurb in the docs * Update for blueprint general-host-aggregates 2.6.1 ----- * Admin action to reset states * Filter out v1.0 endpoints * option to bypass managment endpoint and timings support * Removes NOVACLIENT\_DEBUG from client code * Fix spelling errors in aggregates section * Move docs to doc * Lock prettytable dep at v0.6 * Removed generate\_authors.sh since it's no longer used * nova show cmd displays unique flavor and image id * Use openstack-common for AUTHORS generation * Add .tox to .gitignore * Add start and stop to server actions * Adds flavor-show support * doc: fix and clarify the --meta option help * Lock pep8 at v1.1 * Turn on verbose test output * Align tox.ini with standards * make nova bash-complete faster and more accurate * refactored --service\_name to only work with compute calls and added --volume\_service\_name for volume calls * removed int requirement for volume\_id on snaps * Updated to new prettytable api. Fixes bug 995818 * Allow server name to be specified for actions and diagnostics * Don't force volume id to int and allow search by name * Fix LP #990667 - Keypair \_\_repr\_\_ referencesuuid * really output the description of an exception * Limit hint/nic parsing to one split on '=' * update README.rst,add args "service\_type" when getting endpoints * Rename NOVA\_VERSION to OS\_COMPUTE\_API\_VERSION * Raise exception on all 4xx and 5xx responses * Update unittests to be Python 2.6 compatible * Display the request id on error response * Make '--help' argument more useful * Fixed the subcommand error message for nova shell * Request ID when name is ambiguous * Set resources as loaded on get * Miscellaneous code cleanup * add packages using find\_packages() * set 'compute' as default endpoint bug fix for #970808 * Add -i/--identity option to 'nova ssh' * Improve 'nova ssh' error message * Fix spelling of curent in list sec groups * Set up the log handler only once * Remove serverId lookup in volume attachments * Handle server\_id and serverId in volume list * Added cloudpipe support. Fixes bug 962286 * Proposed HACKING guidelines for string encoding * Add missing tools and tox.ini to tarball * Fixes bug #959262 - Prevent a failure to create the cache directory from causing an exception * Improve the error message from the nova shell * Adds NOVACLIENT\_INSECURE option * Implement quota classes * Open Folsom * Adding Console Log to CLI * Change CLIAuth arg names * Add suport for instance locking/unlocking * Add --poll for instance snapshots * Add human-friendly ID support * Fixes lp#948685 proxy\_token and proxy\_tenant\_id behavior * Separate UUID caches for different endpoints * Remove trailing whitespaces in regular file * Adds --ipv6 and --port to ssh convience command * Add --poll for long running actions * Add support for volume types * Makes novaclient use the volumes endpoint * Fix for backward compatibility with stable/diablo flavors * Add support for ephemeral\_gb to novaclient * allow '=' inside value of --meta=key=value * bug 932408: python-novaclient miss OSAPI host operations * Add ssh convenience command * Allow UUID\_CACHE\_DIR overriding via env variable * Removes zones * Fixes bug 925644: move dotfiles into dir * add support for --config-drive 'boot' command * shell: Hook --debug up to more stuff * Properly handle KeyErrors * adding credentials and endpoints output for debugging * Fixes bug 924588: Remove proto-keystone client from novaclient * Fix bug 904364: Consistiently handle trailing '/' on URLs * Adding describe-resource subcommand * Add Accept: applicaton/json header to all service requests. Fixes bug 904436 * Blueprint cli-auth: common cli args * Add --all\_tenants option to 'nova list' * Adding live migration subcommand * Handle Ambiguous Endpoints Correctly * Implementing Scheduling Hints * Remove non-working --key\_path argument on boot * Fix datetime issue with usage\_data * blueprint host-aggregates: client bindings * moves the "help" in the usage information of a wrong command to the correct position * Implementing client for new x509 support in nova * Add flavor create/delete support * Add a 'usage' module and 'usage-list' cli command * Implement virtual interfaces servers api * Print adminPass when rescuing an instance * do not require NOVA\_VERSION in env, default to 1.1 * Match create\_image on server object and manager * Catch novaclient up with renaming and other nova changes * Add server.get\_vnc\_console functionality to python-novaclient * Fix bad api call, 'migrate' is an action * Adding rebuild/resize hooks * Implementing Floating Ip Pools * Get ImpLoader from ImpImporter for Py2.6 * Discover extensions via Python's import paths * PEP8 python-novaclient cleanup * show 409 responses * Added command-line interfaces for the floating ip DNS api to nova * Fix Quota ant SecurityGroup resources refreshing * Clean FloatingIPDNS resource * Install a good version of pip in the venv * Add tox.ini file * Add missing returns and remove superfluous ones * Fix typo in endpoint\_name help string * Add the python api for floating IP DNS * Abstract Client building into novaclient.client * Remove unused imports and fix NameError on exc * Improve the test framework to handle urls with args * Simplifying get\_console\_output client interface * Removing cache-busting query param (fresh) * Adding return statement to get\_console\_output * python-novaclient missing pep8 in pip-requires * utils.find\_resource fixes + fix for volumes * Add list() method to ManagerWithFind * Extensions can now modify resources * more work towards standardize config * Allow to not specify image if block\_device\_mapping is set * Adding support for the os-getConsoleOutput server action * Add 'discover' command for Keystone discovery and version listing * User friendly help message * Do no depends on argparse for Python >= 2.7 * standardize environmental settings for cli auth * Removed v1.0 support * Making contrib a Python package * Adding extension framework * Fix typo in README * Accept 1 and 2 as version choices * Add support for RAX authentication * Align run\_tests.sh with nova * Switch versioning to common Nova versioning * Fix PEP8 error * Add MANIFEST.in and setup.cfg back * Adding 'absolute-limits' and 'rate-limits' * Fixing all remaining pep8 errors * Clean up image-list cli command * Clean up image-show * Updated README.rst * Converting rxtx\_cap and rxtx\_quota to rxtx\_factor * Gracefully handle failure to cache UUID's. Bug #897885 * Change 'zone\_blob' key to 'blob' in create server. bug 893183 * Fix spacing errors in authentication exceptions * Adding UUID cache for bash autocompletion * Revert api\_key change in novaclient Client argument * Adds bash completion support and cleans up setup.py * Rewriting admin-only calls as server actions * Add rfc.sh * Add .gitreview config file for gerrit * pep8 * fix tests * trunk merge * Add support for specifying VIF networks while booting * Use a try/finally to ensure state restoration * Follow redirects when calling out to Keystone * Modified as per code-review comments: - Renaned snapshot to volume-snapshot - Created a new file for volume snapshots * few missing references to api\_key * tests working * started * added --endpoint\_name support * Add back display of adminPass to boot * Boot now works with limited info returned from server * fixed missing line continuation characters in shell.py * PEP8 cleanups of utils, and the v1\_?/shell.py files * minor pep8 tweaks * corrected argument order and replaced tabs with spaces * resolved merge conflict * added a space after url * Added the option --insecure. This disables SSL certificate validation * Updated the novaclient shell to display the parent server id that the image came from * Fixed description for block\_device\_mapping parameter * minor fixes * Added support for boot from volume (or snapshot) * version update * minor tweaks and long overdue pep8 * new service catalog semantics * Added support for listing/creating/deleting snapshots of nova volumes. Also implemented the supporting CLI commands. Requires the OS API extension, 'os-snapshots' * Updated volume-create command to accept an optional attribute, snapshot\_id. This enables the user to create a volume from a snapshot * Fixes #133 -- Keystone Client fetches correct service type and endpoint * fix tests * typo * merged and fixed pshkitin's keypair work * doc improvements * Added support to specify more boot options * Updated volume attach/detach commands to accept server name (in addition to server id). Code review comments: https://github.com/rackspace/python-novaclient/pull/125/files#r169829 * Booting server with specific key is implemented * Added commands to work with keypairs * make description consistent * remove extra space * add ability to create source group rules * don't expose ids to end user * work on formatting for secgroup rules * display floating ip on create * Add CLI for security groups and rules * raise exception if floating\_ip is not found in floating-ip-delete * Add cli for floating ips * Added support to specify more boot options * Don't filter endpoints when filter\_value is non-truthy * Added the following CLI commands to access nova volumes: volume-attach Attach a volume to a server. volume-create Add a new volume. volume-delete Remove a volume. volume-detach Detach a volume from a server. volume-list List all the volumes. volume-show Show details about a volume * now uses tenantName vs. tenantId to auth * version bump * removed unicode casts * cleaned up exception handling * new service catalog implementation * change auth cred format for keystone * Added methods to get, attach and detach volumes to/from running instances * Added support to access nova-volume api (v1.1 extension) - Only the basic functionality (create, delete, list) is implemented * add todo to update doc strings so that they reflect extension/optional-ness * update doc strings * add key\_name to servers.create * Make sure flavor is a type of int * removed debugging * token support * fixed unknown service * properly uses keystone admin endpoint for token lookup * proxy token support - no tests * readme fix * service catalog with multiple endpoints per service * Add ability to force debugging via os environ * merge fixup * version bump * readme * service catalog as auth parameter * service name support * Extend lazy loading support to Weighting * Fix unittests breakage in test\_shell * Fix #109 (nova show name not working) * Add userdata support * Remove extra NOVA\_PROJECT\_ID * Fix unittests breakage from merge 3507905 * Add 'meta' command to allow set/delete of metadata items on servers. Added ability to run multiple assert\_called tests from one test function * add build, dist, python-novaclient.egg-info to .gitignore * fixes odd \_\_get\_attr\_\_ behavior in 2.6.5 * conflict fixed * catch misssing id * Add body in debugging * Fix test installation exclude * Add support for image metadata to be viewed, added, updated, and deleted * Bump the release version * fixed up zone-add * Reducing v1\_1.base to just booting manager * tests working * in progress - adding zone name * ensure we have auth\_url and project\_id for !1.0 * Updated error message as suggested by bcwaldon * take auth token param * Do not assume default for image and flavor * expanding on concept of 'loaded' * limiting resource lazyloading to a single query * Fix extra # char as noticed by jk0 * Add piston service\_catalog * Add anotherjesse keystone here * Fix loop properly * Make sure we can do a get on the base class * Client changes for username and password in zone add * fix for chmouel's comment, and tweaks to tests * support for floating\_ips + D4 * make \_\_repr\_\_ more useful with default behavior, rather than juse displaying id * a few tweaks to get the client talking to nova * more cleanup * progress on security groups * updating version * updating for new rebuild format * adding tests * cleaning up find\_resource method to support str/int ids, uuids, and integer-like names * Fix #85 * fixing the shell tests * Fixed 1.0 and unit tests * Added support for 1.0 and added unit tests * Updated rescue/unrescue to use public API * removing extra space * updating quotas and tests with the format which recently landed in nova * fixing up a few pep8 issues, and pointing client to the new endpoint * Properly make image\_id a requirement to be int * Make sure the image id is an integer * really fixed * accidentally deleted a comment when fixing conflict * pep8 issues * update readme to talk about keystone with CLI and use 1.1 api * Switch API path to match http://bazaar.launchpad.net/~tpatil/nova/os-security-groups/revision/1369 * Fix API path * fix display\_name references that should have been instance\_name * removed fixed\_ip from v1.1 shell. Use --ip instead. Fixed up rest of other search options from last commit * start add of --image, --flavor, --status, and --host options to 'list' command. also fix up differences with --name and --display\_name compared to how nova implementation turned out * Security groups cleanups * Added redirect tests, changed wrong status in test\_authenticate\_success * Added self.auth\_url updating, WrongResponse exception * add note about keystone / auth 2.0 * Clean up id handling and pass basic tests * Add security group rules * Eradicate TABs, make tests run * missed a conflict * merge master * Initial security groups code * adding unittest * removing extra newline * adding email to .mailmap * catching authorization failure (x-server-management-url KeyError) * bring up-to-date with lp:~cloudbuilders/nova/os-keypairs * keypair api * add license headers * add support for quotas * pep8, again * Recursion handling * Added .mailmap file for AUTHORS * Updated authors and fixed tools/generate\_authors.sh * Fixes copyright notice and adds script to gen AUTHORS * keypair api * pep8 * Status code 305 fix, ClientExceptions if we can not handle response * whitespace cleanups * pep8 cleanups after the rebase * Adds run\_tests.sh and virtualenv support * pep8 in tests * pep8 in novaclient * Add Hacking and Authors to bring this into accordance with OpenStack coding standards * redirect * Redirection handling * cleaning up boot output; upping version * Added documentation for NOVA\_VERSION * Make it possible to authenticate against keystone * Removed the bodies again * Corrected docs * off by one * Missed a conflict 2.6.0 ----- * manual merge * Accidently had a reference to ipgroup still * Merged v1.0 functionality into v1.1 so we don't lose any features by...upgrading? * Fix for failing tests because boot response now requests additional information * formatting updates * novaclient -> nova in some documentation as per feedback * Removed unneeded print * Change create-image back to image-create, and increased version to 2.6.0 * Updated --version to default to NOVA\_VERSION, quick fix * Updated --version to default to NOVA\_VERSION * osc -> novaclient * Cleaned up v1.0 and v1.1 test setup to remove globals and encapsulate custom asserts. Still duplicate code, but closer to being able to remove. Now tests set up OpenStackClient much closer to how users will do it, minus the stubbing of the client * Wrong client was getting loaded * Grrrr, bad import * Tests now run correctly for v1.1 and v1.0 * Updated the default version back to 1.0, as there are some quirks with 1.1 * Tests working again...merged in some work we did earlier * Split everything down the middle into v1\_0 and v1\_1, including tests * bumping version and updating README * updating server backup action; pep8 fixes * removed server dump after add/remove fixed-ip * version bump * fixed public private ip list * added various search options to list command. will need a version bump as i changed the 'list' api that nova uses. after version bump, my search nova branch will need pip-requires updated to match * docs * added add/remove fixed\_ip actions to servers * Clarify description so usage doesn't imply name is the only valid value * Added support for request timeouts * Added migration functionality * Refactored backup rotation * Review feedback * Fixed unit tests * Implemented backup with rotation * for creating 'x' instances, min\_count > max\_count check was reversed make max/min\_instances a little more sane by making them 'int' types fix issue where only specifying --min\_instances didn't work * Due to how novaclient works, it tends to do a 'get' first on whatever ID you pass on command line. Then it does the real command, re-using the ID found in the 'get' call, instead of the initial ID that you specified (which may have been a UUID) * Cleaned up the query\_string generation for 'nova list' Made --recurse\_zones not need an '=argument' * Added --recurse\_zones option to 'list' Added --fixed\_ip option to 'list' to find a particular instance by IP Fixed issue with 'show' when --recurse\_zones=1 and specifying UUID * fixup * release note update * tests working again for weight\_scale/weight\_offset * fixed up tests after trunk merge and bumped version * version bump * fixed project\_id tests * Merged my 'create-num-instances' branch which adds support for --min\_instances and --max\_instances to zone-boot * version bumped * trunk merge * Typo fix * Added the missing files * Added a method to create instances on behalf of an account via the admin API methods for openstack * changed docs about using project id * bumped version # after project\_id update * Don't restrict ids to int * fix errors * support for project id header * Now that I understand how to build extensions, I understand how this extension will be built, and can fully implement add\_fixed\_ip() and remove\_fixed\_ip() * Add the basic calls for add\_fixed\_ip/remove\_fixed\_ip * defaults back to no detail * tweaks * griddynamics better logging * reservation\_id optional parameter added to GET /servers (aka 'list') * works properly with zone-boot * Improving tests * Added parameter detailed to list * zone select support and version bump to 2.4.3 * fix to reviewer comment: add check if logging disabled * improve perfomance on string concat in logging * add logging for http request-response * zone select * add undetailed flavor list * zone\_blob support added to server.create * fixed flavor-list columns * added support for missing flavors/images * up'ed version to suit pypi distribution update * fixed software license * version 2.4 * Added Jacob Kaplan-Moss copyright notices on older/untouched files * renamed to novaclient and fixed flavor tests * missing docstring quote * tweaked release notes * removed copyright/license notices from files not significantly changed * renamed cmdline tool from novatools to nova. Changed version to 2.1. Changed license to Apache. Added copyright notices. Cleaner exception reporting in non-debug scnario * fixed setup * longer zone list * Added full flavor detail support * zone info works * get this zone status * removed Username from zone info * NOVA\_TOOLS\_\* -> NOVA\_\*, --debug * removed zone name, renamed auth\_url to api\_url, added username/password * zone shell cmds & tests added * zone tests pass * zone tests pass * tests pass again * zones * Renamed all CloudServers to OpenStack and python-cloudservers to python-novatools * installer fixup * README update and rename cloudservers to novatools * Starting on child zone support * Initial commit from fork