Rev pyrax to 1.9.3 and lower python-novaclient to 2.20.0 due to a

strange deprecation warning message that is not suppressable.
This commit is contained in:
Matthew Jones 2015-04-13 21:57:13 -04:00
parent 6525120c15
commit c350fafe22
131 changed files with 4896 additions and 3060 deletions

View File

@ -53,13 +53,13 @@ pexpect==3.1 (pexpect/*, excluded pxssh.py, fdpexpect.py, FSM.py, screen.py,
ANSI.py)
pip==1.5.4 (pip/*, excluded bin/pip*)
prettytable==0.7.2 (prettytable.py)
pyrax==1.9.0 (pyrax/*)
pyrax==1.9.3 (pyrax/*)
python-cinderclient==1.1.1 (cinderclient/*)
python-dateutil==2.4.0 (dateutil/*)
python-glanceclient==0.17.0 (glanceclient/*)
python-ironicclient==0.5.0 (ironicclient/*)
python-neutronclient==2.3.11 (neutronclient/*)
python-novaclient==2.23.0 (novaclient/*, excluded bin/nova)
python-novaclient==2.20.0 (novaclient/*, excluded bin/nova)
python-swiftclient==2.2.0 (swiftclient/*, excluded bin/swift)
python-troveclient==1.0.9 (troveclient/*)
pytz==2014.10 (pytz/*)

View File

@ -20,17 +20,13 @@ Base utilities to build API operation managers and objects on top of.
"""
import abc
import contextlib
import hashlib
import inspect
import os
import threading
import six
from novaclient import exceptions
from novaclient.openstack.common.apiclient import base
from novaclient.openstack.common import cliutils
from novaclient import utils
Resource = base.Resource
@ -46,17 +42,24 @@ def getid(obj):
return obj
class Manager(base.HookableMixin):
class Manager(utils.HookableMixin):
"""
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
def _write_object_to_completion_cache(self, obj):
if hasattr(self.api, 'write_object_to_completion_cache'):
self.api.write_object_to_completion_cache(obj)
def _clear_completion_cache_for_class(self, obj_class):
if hasattr(self.api, 'clear_completion_cache_for_class'):
self.api.clear_completion_cache_for_class(obj_class)
def _list(self, url, response_key, obj_class=None, body=None):
if body:
_resp, body = self.api.client.post(url, body=body)
@ -75,86 +78,22 @@ class Manager(base.HookableMixin):
except KeyError:
pass
with self.completion_cache('human_id', obj_class, mode="w"):
with self.completion_cache('uuid', obj_class, mode="w"):
return [obj_class(self, res, loaded=True)
for res in data if res]
self._clear_completion_cache_for_class(obj_class)
@contextlib.contextmanager
def alternate_service_type(self, service_type):
original_service_type = self.api.client.service_type
self.api.client.service_type = service_type
try:
yield
finally:
self.api.client.service_type = original_service_type
objs = []
for res in data:
if res:
obj = obj_class(self, res, loaded=True)
self._write_object_to_completion_cache(obj)
objs.append(obj)
@contextlib.contextmanager
def completion_cache(self, cache_type, obj_class, mode):
"""
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 = cliutils.env('NOVACLIENT_UUID_CACHE_DIR',
default="~/.novaclient")
# NOTE(sirp): Keep separate UUID caches for each username +
# endpoint pair
username = cliutils.env('OS_USERNAME', 'NOVA_USERNAME')
url = cliutils.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)
return objs
def _get(self, url, response_key):
_resp, body = self.api.client.get(url)
return self.resource_class(self, body[response_key], loaded=True)
obj = self.resource_class(self, body[response_key], loaded=True)
self._write_object_to_completion_cache(obj)
return obj
def _create(self, url, body, response_key, return_raw=False, **kwargs):
self.run_hooks('modify_body_for_create', body, **kwargs)
@ -162,9 +101,9 @@ class Manager(base.HookableMixin):
if return_raw:
return body[response_key]
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])
obj = self.resource_class(self, body[response_key])
self._write_object_to_completion_cache(obj)
return obj
def _delete(self, url):
_resp, _body = self.api.client.delete(url)
@ -241,10 +180,6 @@ class ManagerWithFind(Manager):
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']
listing = self.list(**list_kwargs)

View File

@ -21,15 +21,16 @@ OpenStack Client interface. Handles the REST calls and responses.
"""
import copy
import errno
import functools
import glob
import hashlib
import logging
import os
import re
import socket
import time
from keystoneclient import adapter
from oslo.utils import importutils
from oslo.utils import netutils
import requests
from requests import adapters
@ -42,19 +43,9 @@ except ImportError:
from six.moves.urllib import parse
from novaclient import exceptions
from novaclient.i18n import _
from novaclient.openstack.common.gettextutils import _
from novaclient import service_catalog
class TCPKeepAliveAdapter(adapters.HTTPAdapter):
"""The custom adapter used to set TCP Keep-Alive on all connections."""
def init_poolmanager(self, *args, **kwargs):
if requests.__version__ >= '2.4.1':
kwargs.setdefault('socket_options', [
(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
])
super(TCPKeepAliveAdapter, self).init_poolmanager(*args, **kwargs)
from novaclient import utils
class _ClientConnectionPool(object):
@ -67,42 +58,98 @@ class _ClientConnectionPool(object):
Store and reuse HTTP adapters per Service URL.
"""
if url not in self._adapters:
self._adapters[url] = TCPKeepAliveAdapter()
self._adapters[url] = adapters.HTTPAdapter()
return self._adapters[url]
class SessionClient(adapter.LegacyJsonAdapter):
class CompletionCache(object):
"""The completion cache is how we support tab-completion with novaclient.
def __init__(self, *args, **kwargs):
self.times = []
super(SessionClient, self).__init__(*args, **kwargs)
The `Manager` writes object IDs and Human-IDs to the completion-cache on
object-show, object-list, and object-create calls.
The `nova.bash_completion` script then uses these files to provide the
actual tab-completion.
The cache directory layout is:
~/.novaclient/
<hash-of-endpoint-and-username>/
<resource>-id-cache
<resource>-human-id-cache
"""
def __init__(self, username, auth_url, attributes=('id', 'human_id')):
self.directory = self._make_directory_name(username, auth_url)
self.attributes = attributes
def _make_directory_name(self, username, auth_url):
"""Creates a unique directory name based on the auth_url and username
of the current user.
"""
uniqifier = hashlib.md5(username.encode('utf-8') +
auth_url.encode('utf-8')).hexdigest()
base_dir = utils.env('NOVACLIENT_UUID_CACHE_DIR',
default="~/.novaclient")
return os.path.expanduser(os.path.join(base_dir, uniqifier))
def _prepare_directory(self):
try:
os.makedirs(self.directory, 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
def clear_class(self, obj_class):
self._prepare_directory()
resource = obj_class.__name__.lower()
resource_glob = os.path.join(self.directory, "%s-*-cache" % resource)
for filename in glob.iglob(resource_glob):
try:
os.unlink(filename)
except OSError as e:
if e.errno != errno.ENOENT:
raise
def _write_attribute(self, resource, attribute, value):
self._prepare_directory()
filename = "%s-%s-cache" % (resource, attribute.replace('_', '-'))
path = os.path.join(self.directory, filename)
with open(path, 'a') as f:
f.write("%s\n" % value)
def write_object(self, obj):
resource = obj.__class__.__name__.lower()
for attribute in self.attributes:
value = getattr(obj, attribute, None)
if value:
self._write_attribute(resource, attribute, value)
class SessionClient(adapter.LegacyJsonAdapter):
def request(self, url, method, **kwargs):
# NOTE(jamielennox): The standard call raises errors from
# keystoneclient, where we need to raise the novaclient errors.
raise_exc = kwargs.pop('raise_exc', True)
start_time = time.time()
resp, body = super(SessionClient, self).request(url,
method,
raise_exc=False,
**kwargs)
end_time = time.time()
self.times.append(('%s %s' % (method, url),
start_time, end_time))
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 = []
def _original_only(f):
"""Indicates and enforces that this function can only be used if we are
@ -145,7 +192,7 @@ class HTTPClient(object):
self.tenant_id = tenant_id
self._connection_pool = (_ClientConnectionPool()
if connection_pool else None)
if connection_pool else None)
# This will be called by #_get_password if self.password is None.
# EG if a password can only be obtained by prompting the user, but a
@ -212,11 +259,6 @@ class HTTPClient(object):
# otherwise we will get all the requests logging messages
rql.setLevel(logging.WARNING)
# NOTE(melwitt): Service catalog is only set if bypass_url isn't
# used. Otherwise, we can cache using services_url.
self.service_catalog = None
self.services_url = {}
def use_token_cache(self, use_it):
self.os_cache = use_it
@ -274,7 +316,7 @@ class HTTPClient(object):
if not self.http_log_debug:
return
string_parts = ['curl -g -i']
string_parts = ['curl -i']
if not kwargs.get('verify', True):
string_parts.append(' --insecure')
@ -335,10 +377,10 @@ class HTTPClient(object):
self._session.close()
self._current_url = service_url
self._logger.debug(
"New session created for: (%s)" % service_url)
"New session created for: (%s)" % service_url)
self._session = requests.Session()
self._session.mount(service_url,
self._connection_pool.get(service_url))
self._connection_pool.get(service_url))
return self._session
elif self._session:
return self._session
@ -378,7 +420,7 @@ class HTTPClient(object):
# or 'actively refused' in the body, so that's what we'll do.
if resp.status_code == 400:
if ('Connection refused' in resp.text or
'actively refused' in resp.text):
'actively refused' in resp.text):
raise exceptions.ConnectionRefused(resp.text)
try:
body = json.loads(resp.text)
@ -410,12 +452,7 @@ class HTTPClient(object):
path = re.sub(r'v[1-9]/[a-z0-9]+$', '', path)
url = parse.urlunsplit((scheme, netloc, path, None, None))
else:
if self.service_catalog:
url = self.get_service_url(self.service_type) + url
else:
# NOTE(melwitt): The service catalog is not available
# when bypass_url is used.
url = self.management_url + url
url = self.management_url + url
# Perform the request once. If we get a 401 back then it
# might be because the auth token expired, so try to
@ -458,19 +495,6 @@ class HTTPClient(object):
def delete(self, url, **kwargs):
return self._cs_request(url, 'DELETE', **kwargs)
def get_service_url(self, service_type):
if service_type not in self.services_url:
url = self.service_catalog.url_for(
attr='region',
filter_value=self.region_name,
endpoint_type=self.endpoint_type,
service_type=service_type,
service_name=self.service_name,
volume_service_name=self.volume_service_name,)
url = url.rstrip('/')
self.services_url[service_type] = url
return self.services_url[service_type]
def _extract_service_catalog(self, url, resp, body, extract_token=True):
"""See what the auth service told us and process the response.
We may get redirected to another site, fail or actually get
@ -487,7 +511,14 @@ class HTTPClient(object):
self.auth_token = self.service_catalog.get_token()
self.tenant_id = self.service_catalog.get_tenant_id()
self.management_url = self.get_service_url(self.service_type)
management_url = self.service_catalog.url_for(
attr='region',
filter_value=self.region_name,
endpoint_type=self.endpoint_type,
service_type=self.service_type,
service_name=self.service_name,
volume_service_name=self.volume_service_name,)
self.management_url = management_url.rstrip('/')
return None
except exceptions.AmbiguousEndpoints:
print(_("Found more than one valid endpoint. Use a more "
@ -527,10 +558,6 @@ class HTTPClient(object):
extract_token=False)
def authenticate(self):
if not self.auth_url:
msg = _("Authentication requires 'auth_url', which should be "
"specified in '%s'") % self.__class__.__name__
raise exceptions.AuthorizationFailure(msg)
magic_tuple = netutils.urlsplit(self.auth_url)
scheme, netloc, path, query, frag = magic_tuple
port = magic_tuple.port
@ -677,11 +704,11 @@ def _construct_http_client(username=None, password=None, project_id=None,
auth_token=None, cacert=None, tenant_id=None,
user_id=None, connection_pool=False, session=None,
auth=None, user_agent='python-novaclient',
interface=None, **kwargs):
**kwargs):
if session:
return SessionClient(session=session,
auth=auth,
interface=interface or endpoint_type,
interface=endpoint_type,
service_type=service_type,
region_name=region_name,
service_name=service_name,
@ -718,9 +745,9 @@ def _construct_http_client(username=None, password=None, project_id=None,
def get_client_class(version):
version_map = {
'1.1': 'novaclient.v2.client.Client',
'2': 'novaclient.v2.client.Client',
'3': 'novaclient.v2.client.Client',
'1.1': 'novaclient.v1_1.client.Client',
'2': 'novaclient.v1_1.client.Client',
'3': 'novaclient.v3.client.Client',
}
try:
client_path = version_map[str(version)]
@ -730,7 +757,7 @@ def get_client_class(version):
'keys': ', '.join(version_map.keys())}
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)
return utils.import_class(client_path)
def Client(version, *args, **kwargs):

View File

@ -14,11 +14,10 @@
# under the License.
from novaclient import base
from novaclient.openstack.common.apiclient import base as common_base
from novaclient import utils
class Extension(common_base.HookableMixin):
class Extension(utils.HookableMixin):
"""Extension descriptor."""
SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__')

View File

@ -0,0 +1,17 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))

View File

@ -26,12 +26,13 @@ Base utilities to build API operation managers and objects on top of.
import abc
import copy
from oslo.utils import strutils
import six
from six.moves.urllib import parse
from novaclient.openstack.common._i18n import _
from novaclient.openstack.common.apiclient import exceptions
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import strutils
from novaclient.openstack.common import uuidutils
def getid(obj):
@ -99,13 +100,12 @@ class BaseManager(HookableMixin):
super(BaseManager, self).__init__()
self.client = client
def _list(self, url, response_key=None, obj_class=None, json=None):
def _list(self, url, response_key, obj_class=None, json=None):
"""List the collection.
:param url: a partial URL, e.g., '/servers'
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'. If response_key is None - all response body
will be used.
e.g., 'servers'
:param obj_class: class for constructing the returned objects
(self.resource_class will be used by default)
:param json: data that will be encoded as JSON and passed in POST
@ -119,7 +119,7 @@ class BaseManager(HookableMixin):
if obj_class is None:
obj_class = self.resource_class
data = body[response_key] if response_key is not None else body
data = body[response_key]
# NOTE(ja): keystone returns values as list as {'values': [ ... ]}
# unlike other services which just return the list...
try:
@ -129,17 +129,15 @@ class BaseManager(HookableMixin):
return [obj_class(self, res, loaded=True) for res in data if res]
def _get(self, url, response_key=None):
def _get(self, url, response_key):
"""Get an object from collection.
:param url: a partial URL, e.g., '/servers'
:param response_key: the key to be looked up in response dictionary,
e.g., 'server'. If response_key is None - all response body
will be used.
e.g., 'server'
"""
body = self.client.get(url).json()
data = body[response_key] if response_key is not None else body
return self.resource_class(self, data, loaded=True)
return self.resource_class(self, body[response_key], loaded=True)
def _head(self, url):
"""Retrieve request headers for an object.
@ -149,23 +147,21 @@ class BaseManager(HookableMixin):
resp = self.client.head(url)
return resp.status_code == 204
def _post(self, url, json, response_key=None, return_raw=False):
def _post(self, url, json, response_key, return_raw=False):
"""Create an object.
:param url: a partial URL, e.g., '/servers'
:param json: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'server'. If response_key is None - all response body
will be used.
e.g., 'servers'
:param return_raw: flag to force returning raw JSON instead of
Python object of self.resource_class
"""
body = self.client.post(url, json=json).json()
data = body[response_key] if response_key is not None else body
if return_raw:
return data
return self.resource_class(self, data)
return body[response_key]
return self.resource_class(self, body[response_key])
def _put(self, url, json=None, response_key=None):
"""Update an object with PUT method.
@ -174,8 +170,7 @@ class BaseManager(HookableMixin):
:param json: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'. If response_key is None - all response body
will be used.
e.g., 'servers'
"""
resp = self.client.put(url, json=json)
# PUT requests may not return a body
@ -193,8 +188,7 @@ class BaseManager(HookableMixin):
:param json: data that will be encoded as JSON and passed in POST
request (GET will be sent by default)
:param response_key: the key to be looked up in response dictionary,
e.g., 'servers'. If response_key is None - all response body
will be used.
e.g., 'servers'
"""
body = self.client.patch(url, json=json).json()
if response_key is not None:
@ -443,6 +437,21 @@ class Resource(object):
self._info = info
self._add_details(info)
self._loaded = loaded
self._init_completion_cache()
def _init_completion_cache(self):
cache_write = getattr(self.manager, 'write_to_completion_cache', None)
if not cache_write:
return
# NOTE(sirp): ensure `id` is already present because if it isn't we'll
# enter an infinite loop of __getattr__ -> get -> __init__ ->
# __getattr__ -> ...
if 'id' in self.__dict__ and uuidutils.is_uuid_like(self.id):
cache_write('uuid', self.id)
if self.human_id:
cache_write('human_id', self.human_id)
def __repr__(self):
reprkeys = sorted(k

View File

@ -33,11 +33,11 @@ try:
except ImportError:
import json
from oslo.utils import importutils
import requests
from novaclient.openstack.common._i18n import _
from novaclient.openstack.common.apiclient import exceptions
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import importutils
_logger = logging.getLogger(__name__)
@ -104,7 +104,7 @@ class HTTPClient(object):
return
string_parts = [
"curl -g -i",
"curl -i",
"-X '%s'" % method,
"'%s'" % url,
]
@ -156,7 +156,7 @@ class HTTPClient(object):
requests.Session.request (such as `headers`) or `json`
that will be encoded as JSON and used as `data` argument
"""
kwargs.setdefault("headers", {})
kwargs.setdefault("headers", kwargs.get("headers", {}))
kwargs["headers"]["User-Agent"] = self.user_agent
if self.original_ip:
kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % (
@ -247,10 +247,6 @@ class HTTPClient(object):
raise
self.cached_token = None
client.cached_endpoint = None
if self.auth_plugin.opts.get('token'):
self.auth_plugin.opts['token'] = None
if self.auth_plugin.opts.get('endpoint'):
self.auth_plugin.opts['endpoint'] = None
self.authenticate()
try:
token, endpoint = self.auth_plugin.token_and_endpoint(
@ -361,7 +357,8 @@ class BaseClient(object):
"Must be one of: %(version_map)s") % {
'api_name': api_name,
'version': version,
'version_map': ', '.join(version_map.keys())}
'version_map': ', '.join(version_map.keys())
}
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)

View File

@ -25,7 +25,7 @@ import sys
import six
from novaclient.openstack.common._i18n import _
from novaclient.openstack.common.gettextutils import _
class ClientException(Exception):
@ -34,6 +34,14 @@ class ClientException(Exception):
pass
class MissingArgs(ClientException):
"""Supplied arguments are not sufficient for calling a function."""
def __init__(self, missing):
self.missing = missing
msg = _("Missing arguments: %s") % ", ".join(missing)
super(MissingArgs, self).__init__(msg)
class ValidationError(ClientException):
"""Error in validation on API client side."""
pass
@ -439,8 +447,8 @@ def from_response(response, method, url):
except ValueError:
pass
else:
if isinstance(body, dict) and isinstance(body.get("error"), dict):
error = body["error"]
if isinstance(body, dict):
error = list(body.values())[0]
kwargs["message"] = error.get("message")
kwargs["details"] = error.get("details")
elif content_type.startswith("text/"):

View File

@ -33,9 +33,7 @@ from six.moves.urllib import parse
from novaclient.openstack.common.apiclient import client
def assert_has_keys(dct, required=None, optional=None):
required = required or []
optional = optional or []
def assert_has_keys(dct, required=[], optional=[]):
for k in required:
try:
assert k in dct

View File

@ -24,21 +24,14 @@ import os
import sys
import textwrap
from oslo.utils import encodeutils
from oslo.utils import strutils
import prettytable
import six
from six import moves
from novaclient.openstack.common._i18n import _
class MissingArgs(Exception):
"""Supplied arguments are not sufficient for calling a function."""
def __init__(self, missing):
self.missing = missing
msg = _("Missing arguments: %s") % ", ".join(missing)
super(MissingArgs, self).__init__(msg)
from novaclient.openstack.common.apiclient import exceptions
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import strutils
from novaclient.openstack.common import uuidutils
def validate_args(fn, *args, **kwargs):
@ -63,7 +56,7 @@ def validate_args(fn, *args, **kwargs):
required_args = argspec.args[:len(argspec.args) - num_defaults]
def isbound(method):
return getattr(method, '__self__', None) is not None
return getattr(method, 'im_self', None) is not None
if isbound(fn):
required_args.pop(0)
@ -71,7 +64,7 @@ def validate_args(fn, *args, **kwargs):
missing = [arg for arg in required_args if arg not in kwargs]
missing = missing[len(args):]
if missing:
raise MissingArgs(missing)
raise exceptions.MissingArgs(missing)
def arg(*args, **kwargs):
@ -139,7 +132,7 @@ def isunauthenticated(func):
def print_list(objs, fields, formatters=None, sortby_index=0,
mixed_case_fields=None, field_labels=None):
mixed_case_fields=None):
"""Print a list or objects as a table, one row per object.
:param objs: iterable of :class:`Resource`
@ -148,22 +141,14 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
:param sortby_index: index of the field for sorting table rows
:param mixed_case_fields: fields corresponding to object attributes that
have mixed case names (e.g., 'serverId')
:param field_labels: Labels to use in the heading of the table, default to
fields.
"""
formatters = formatters or {}
mixed_case_fields = mixed_case_fields or []
field_labels = field_labels or fields
if len(field_labels) != len(fields):
raise ValueError(_("Field labels list %(labels)s has different number "
"of elements than fields list %(fields)s"),
{'labels': field_labels, 'fields': fields})
if sortby_index is None:
kwargs = {}
else:
kwargs = {'sortby': field_labels[sortby_index]}
pt = prettytable.PrettyTable(field_labels)
kwargs = {'sortby': fields[sortby_index]}
pt = prettytable.PrettyTable(fields, caching=False)
pt.align = 'l'
for o in objs:
@ -180,7 +165,7 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
row.append(data)
pt.add_row(row)
print(encodeutils.safe_encode(pt.get_string(**kwargs)))
print(strutils.safe_encode(pt.get_string(**kwargs)))
def print_dict(dct, dict_property="Property", wrap=0):
@ -190,7 +175,7 @@ def print_dict(dct, dict_property="Property", wrap=0):
:param dict_property: name of the first column
:param wrap: wrapping for the second column
"""
pt = prettytable.PrettyTable([dict_property, 'Value'])
pt = prettytable.PrettyTable([dict_property, 'Value'], caching=False)
pt.align = 'l'
for k, v in six.iteritems(dct):
# convert dict to str to check length
@ -208,7 +193,7 @@ def print_dict(dct, dict_property="Property", wrap=0):
col1 = ''
else:
pt.add_row([k, v])
print(encodeutils.safe_encode(pt.get_string()))
print(strutils.safe_encode(pt.get_string()))
def get_password(max_password_prompts=3):
@ -232,16 +217,76 @@ def get_password(max_password_prompts=3):
return pw
def find_resource(manager, name_or_id, **find_args):
"""Look for resource in a given manager.
Used as a helper for the _find_* methods.
Example:
def _find_hypervisor(cs, hypervisor):
#Get a hypervisor by name or ID.
return cliutils.find_resource(cs.hypervisors, hypervisor)
"""
# first try to get entity as integer id
try:
return manager.get(int(name_or_id))
except (TypeError, ValueError, exceptions.NotFound):
pass
# now try to get entity as uuid
try:
tmp_id = strutils.safe_encode(name_or_id)
if uuidutils.is_uuid_like(tmp_id):
return manager.get(tmp_id)
except (TypeError, ValueError, exceptions.NotFound):
pass
# for str id which is not uuid
if getattr(manager, 'is_alphanum_id_allowed', False):
try:
return manager.get(name_or_id)
except exceptions.NotFound:
pass
try:
try:
return manager.find(human_id=name_or_id, **find_args)
except exceptions.NotFound:
pass
# finally try to find entity by name
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:
msg = _("No %(name)s with a name or "
"ID of '%(name_or_id)s' exists.") % \
{
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id
}
raise exceptions.CommandError(msg)
except exceptions.NoUniqueMatch:
msg = _("Multiple %(name)s matches found for "
"'%(name_or_id)s', use an ID to be more specific.") % \
{
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id
}
raise exceptions.CommandError(msg)
def service_type(stype):
"""Adds 'service_type' attribute to decorated function.
Usage:
.. code-block:: python
@service_type('volume')
def mymethod(f):
...
@service_type('volume')
def mymethod(f):
...
"""
def inner(f):
f.service_type = stype

View File

@ -32,24 +32,113 @@ import os
from babel import localedata
import six
_localedir = os.environ.get('novaclient'.upper() + '_LOCALEDIR')
_t = gettext.translation('novaclient', localedir=_localedir, fallback=True)
# We use separate translation catalogs for each log level, so set up a
# mapping between the log level name and the translator. The domain
# for the log level is project_name + "-log-" + log_level so messages
# for each level end up in their own catalog.
_t_log_levels = dict(
(level, gettext.translation('novaclient' + '-log-' + level,
localedir=_localedir,
fallback=True))
for level in ['info', 'warning', 'error', 'critical']
)
_AVAILABLE_LANGUAGES = {}
# FIXME(dhellmann): Remove this when moving to oslo.i18n.
USE_LAZY = False
class TranslatorFactory(object):
"""Create translator functions
"""
def __init__(self, domain, lazy=False, localedir=None):
"""Establish a set of translation functions for the domain.
:param domain: Name of translation domain,
specifying a message catalog.
:type domain: str
:param lazy: Delays translation until a message is emitted.
Defaults to False.
:type lazy: Boolean
:param localedir: Directory with translation catalogs.
:type localedir: str
"""
self.domain = domain
self.lazy = lazy
if localedir is None:
localedir = os.environ.get(domain.upper() + '_LOCALEDIR')
self.localedir = localedir
def _make_translation_func(self, domain=None):
"""Return a new translation function ready for use.
Takes into account whether or not lazy translation is being
done.
The domain can be specified to override the default from the
factory, but the localedir from the factory is always used
because we assume the log-level translation catalogs are
installed in the same directory as the main application
catalog.
"""
if domain is None:
domain = self.domain
if self.lazy:
return functools.partial(Message, domain=domain)
t = gettext.translation(
domain,
localedir=self.localedir,
fallback=True,
)
if six.PY3:
return t.gettext
return t.ugettext
@property
def primary(self):
"The default translation function."
return self._make_translation_func()
def _make_log_translation_func(self, level):
return self._make_translation_func(self.domain + '-log-' + level)
@property
def log_info(self):
"Translate info-level log messages."
return self._make_log_translation_func('info')
@property
def log_warning(self):
"Translate warning-level log messages."
return self._make_log_translation_func('warning')
@property
def log_error(self):
"Translate error-level log messages."
return self._make_log_translation_func('error')
@property
def log_critical(self):
"Translate critical-level log messages."
return self._make_log_translation_func('critical')
# NOTE(dhellmann): When this module moves out of the incubator into
# oslo.i18n, these global variables can be moved to an integration
# module within each application.
# Create the global translation functions.
_translators = TranslatorFactory('novaclient')
# The primary translation function using the well-known name "_"
_ = _translators.primary
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
# NOTE(dhellmann): End of globals that will move to the application's
# integration module.
def enable_lazy():
"""Convenience function for configuring _() to use lazy gettext
@ -58,41 +147,18 @@ def enable_lazy():
your project is importing _ directly instead of using the
gettextutils.install() way of importing the _ function.
"""
global USE_LAZY
# FIXME(dhellmann): This function will be removed in oslo.i18n,
# because the TranslatorFactory makes it superfluous.
global _, _LI, _LW, _LE, _LC, USE_LAZY
tf = TranslatorFactory('novaclient', lazy=True)
_ = tf.primary
_LI = tf.log_info
_LW = tf.log_warning
_LE = tf.log_error
_LC = tf.log_critical
USE_LAZY = True
def _(msg):
if USE_LAZY:
return Message(msg, domain='novaclient')
else:
if six.PY3:
return _t.gettext(msg)
return _t.ugettext(msg)
def _log_translation(msg, level):
"""Build a single translation of a log message
"""
if USE_LAZY:
return Message(msg, domain='novaclient' + '-log-' + level)
else:
translator = _t_log_levels[level]
if six.PY3:
return translator.gettext(msg)
return translator.ugettext(msg)
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = functools.partial(_log_translation, level='info')
_LW = functools.partial(_log_translation, level='warning')
_LE = functools.partial(_log_translation, level='error')
_LC = functools.partial(_log_translation, level='critical')
def install(domain, lazy=False):
"""Install a _() function using the given translation domain.
@ -112,26 +178,9 @@ def install(domain, lazy=False):
any available locale.
"""
if lazy:
# NOTE(mrodden): Lazy gettext functionality.
#
# The following introduces a deferred way to do translations on
# messages in OpenStack. We override the standard _() function
# and % (format string) operation to build Message objects that can
# later be translated when we have more information.
def _lazy_gettext(msg):
"""Create and return a Message object.
Lazy gettext function for a given domain, it is a factory method
for a project/module to get a lazy gettext function for its own
translation domain (i.e. nova, glance, cinder, etc.)
Message encapsulates a string so that we can translate
it later when needed.
"""
return Message(msg, domain=domain)
from six import moves
moves.builtins.__dict__['_'] = _lazy_gettext
tf = TranslatorFactory(domain, lazy=True)
moves.builtins.__dict__['_'] = tf.primary
else:
localedir = '%s_LOCALEDIR' % domain.upper()
if six.PY3:
@ -274,13 +323,14 @@ class Message(six.text_type):
def __radd__(self, other):
return self.__add__(other)
def __str__(self):
# NOTE(luisg): Logging in python 2.6 tries to str() log records,
# and it expects specifically a UnicodeError in order to proceed.
msg = _('Message objects do not support str() because they may '
'contain non-ascii characters. '
'Please use unicode() or translate() instead.')
raise UnicodeError(msg)
if six.PY2:
def __str__(self):
# NOTE(luisg): Logging in python 2.6 tries to str() log records,
# and it expects specifically a UnicodeError in order to proceed.
msg = _('Message objects do not support str() because they may '
'contain non-ascii characters. '
'Please use unicode() or translate() instead.')
raise UnicodeError(msg)
def get_available_languages(domain):
@ -323,8 +373,8 @@ def get_available_languages(domain):
'zh_Hant_HK': 'zh_HK',
'zh_Hant': 'zh_TW',
'fil': 'tl_PH'}
for (locale, alias) in six.iteritems(aliases):
if locale in language_list and alias not in language_list:
for (locale_, alias) in six.iteritems(aliases):
if locale_ in language_list and alias not in language_list:
language_list.append(alias)
_AVAILABLE_LANGUAGES[domain] = language_list

View File

@ -24,10 +24,10 @@ import traceback
def import_class(import_str):
"""Returns a class from a string including module and class."""
mod_str, _sep, class_str = import_str.rpartition('.')
__import__(mod_str)
try:
__import__(mod_str)
return getattr(sys.modules[mod_str], class_str)
except (ValueError, AttributeError):
except AttributeError:
raise ImportError('Class %s cannot be found (%s)' %
(class_str,
traceback.format_exception(*sys.exc_info())))

View File

@ -32,8 +32,8 @@ class ServiceCatalog(object):
return self.catalog['access']['token']['tenant']['id']
def url_for(self, attr=None, filter_value=None,
service_type=None, endpoint_type='publicURL',
service_name=None, volume_service_name=None):
service_type=None, endpoint_type='publicURL',
service_name=None, volume_service_name=None):
"""Fetch the public URL from the Compute service for
a particular endpoint attribute. If none given, return
the first. See tests for sample service catalog.
@ -72,11 +72,11 @@ class ServiceCatalog(object):
endpoints = service['endpoints']
for endpoint in endpoints:
# Ignore 1.0 compute endpoints
if (service.get("type") == 'compute' and
endpoint.get('versionId', '2') not in ('1.1', '2')):
if service.get("type") == 'compute' and \
endpoint.get('versionId', '2') not in ('1.1', '2'):
continue
if (not filter_value or
endpoint.get(attr).lower() == filter_value.lower()):
if not filter_value or \
endpoint.get(attr).lower() == filter_value.lower():
endpoint["serviceName"] = service.get("name")
matching_endpoints.append(endpoint)
@ -84,6 +84,6 @@ class ServiceCatalog(object):
raise novaclient.exceptions.EndpointNotFound()
elif len(matching_endpoints) > 1:
raise novaclient.exceptions.AmbiguousEndpoints(
endpoints=matching_endpoints)
endpoints=matching_endpoints)
else:
return matching_endpoints[0][endpoint_type]

View File

@ -28,12 +28,7 @@ import logging
import os
import pkgutil
import sys
import time
from keystoneclient.auth.identity.generic import password
from keystoneclient.auth.identity.generic import token
from keystoneclient.auth.identity import v3 as identity
from keystoneclient import session as ksession
from oslo.utils import encodeutils
from oslo.utils import strutils
import pkg_resources
@ -52,12 +47,13 @@ import novaclient.auth_plugin
from novaclient import client
from novaclient import exceptions as exc
import novaclient.extension
from novaclient.i18n import _
from novaclient.openstack.common import cliutils
from novaclient.openstack.common.gettextutils import _
from novaclient import utils
from novaclient.v2 import shell as shell_v2
from novaclient.v1_1 import shell as shell_v1_1
from novaclient.v3 import shell as shell_v3
DEFAULT_OS_COMPUTE_API_VERSION = "2"
DEFAULT_OS_COMPUTE_API_VERSION = "1.1"
DEFAULT_NOVA_ENDPOINT_TYPE = 'publicURL'
# NOTE(cyeoh): Having the service type dependent on the API version
# is pretty ugly, but we have to do this because traditionally the
@ -139,7 +135,7 @@ class SecretsHelper(object):
if not HAS_KEYRING or not self.args.os_cache:
return
if (auth_token == self.auth_token and
management_url == self.management_url):
management_url == self.management_url):
# Nothing changed....
return
if not all([management_url, auth_token, tenant_id]):
@ -159,7 +155,7 @@ class SecretsHelper(object):
self._password = self.args.os_password
else:
verify_pass = strutils.bool_from_string(
cliutils.env("OS_VERIFY_PASSWORD", default=False), True)
utils.env("OS_VERIFY_PASSWORD", default=False), True)
self._password = self._prompt_password(verify_pass)
if not self._password:
raise exc.CommandError(
@ -228,47 +224,13 @@ class NovaClientArgumentParser(argparse.ArgumentParser):
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
" for more information.\n") %
{'errmsg': message.split(choose_from)[0],
'mainp': progparts[0],
'subp': progparts[2]})
class OpenStackComputeShell(object):
times = []
def _append_global_identity_args(self, parser):
# Register the CLI arguments that have moved to the session object.
ksession.Session.register_cli_options(parser)
parser.set_defaults(insecure=cliutils.env('NOVACLIENT_INSECURE',
default=False))
identity.Password.register_argparse_arguments(parser)
parser.set_defaults(os_username=cliutils.env('OS_USERNAME',
'NOVA_USERNAME'))
parser.set_defaults(os_password=cliutils.env('OS_PASSWORD',
'NOVA_PASSWORD'))
parser.set_defaults(os_auth_url=cliutils.env('OS_AUTH_URL',
'NOVA_URL'))
def get_base_parser(self):
parser = NovaClientArgumentParser(
@ -281,8 +243,7 @@ class OpenStackComputeShell(object):
)
# Global arguments
parser.add_argument(
'-h', '--help',
parser.add_argument('-h', '--help',
action='store_true',
help=argparse.SUPPRESS,
)
@ -291,118 +252,111 @@ class OpenStackComputeShell(object):
action='version',
version=novaclient.__version__)
parser.add_argument(
'--debug',
parser.add_argument('--debug',
default=False,
action='store_true',
help=_("Print debugging output"))
parser.add_argument(
'--os-cache',
parser.add_argument('--os-cache',
default=strutils.bool_from_string(
cliutils.env('OS_CACHE', default=False), True),
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',
parser.add_argument('--timings',
default=False,
action='store_true',
help=_("Print call timing info"))
parser.add_argument(
'--os-auth-token',
default=cliutils.env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN]')
parser.add_argument('--timeout',
default=600,
metavar='<seconds>',
type=positive_non_zero_float,
help=_("Set HTTP call timeout (in seconds)"))
parser.add_argument(
'--os_username',
parser.add_argument('--os-auth-token',
default=utils.env('OS_AUTH_TOKEN'),
help='Defaults to env[OS_AUTH_TOKEN]')
parser.add_argument('--os-username',
metavar='<auth-user-name>',
default=utils.env('OS_USERNAME', 'NOVA_USERNAME'),
help=_('Defaults to env[OS_USERNAME].'))
parser.add_argument('--os_username',
help=argparse.SUPPRESS)
parser.add_argument(
'--os_password',
parser.add_argument('--os-user-id',
metavar='<auth-user-id>',
default=utils.env('OS_USER_ID'),
help=_('Defaults to env[OS_USER_ID].'))
parser.add_argument('--os-password',
metavar='<auth-password>',
default=utils.env('OS_PASSWORD', 'NOVA_PASSWORD'),
help=_('Defaults to env[OS_PASSWORD].'))
parser.add_argument('--os_password',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-tenant-name',
parser.add_argument('--os-tenant-name',
metavar='<auth-tenant-name>',
default=cliutils.env('OS_TENANT_NAME', 'NOVA_PROJECT_ID'),
default=utils.env('OS_TENANT_NAME', 'NOVA_PROJECT_ID'),
help=_('Defaults to env[OS_TENANT_NAME].'))
parser.add_argument(
'--os_tenant_name',
parser.add_argument('--os_tenant_name',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-tenant-id',
parser.add_argument('--os-tenant-id',
metavar='<auth-tenant-id>',
default=cliutils.env('OS_TENANT_ID'),
default=utils.env('OS_TENANT_ID'),
help=_('Defaults to env[OS_TENANT_ID].'))
parser.add_argument(
'--os_auth_url',
parser.add_argument('--os-auth-url',
metavar='<auth-url>',
default=utils.env('OS_AUTH_URL', 'NOVA_URL'),
help=_('Defaults to env[OS_AUTH_URL].'))
parser.add_argument('--os_auth_url',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-region-name',
parser.add_argument('--os-region-name',
metavar='<region-name>',
default=cliutils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'),
default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'),
help=_('Defaults to env[OS_REGION_NAME].'))
parser.add_argument(
'--os_region_name',
parser.add_argument('--os_region_name',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-auth-system',
parser.add_argument('--os-auth-system',
metavar='<auth-system>',
default=cliutils.env('OS_AUTH_SYSTEM'),
default=utils.env('OS_AUTH_SYSTEM'),
help='Defaults to env[OS_AUTH_SYSTEM].')
parser.add_argument(
'--os_auth_system',
parser.add_argument('--os_auth_system',
help=argparse.SUPPRESS)
parser.add_argument(
'--service-type',
parser.add_argument('--service-type',
metavar='<service-type>',
help=_('Defaults to compute for most actions'))
parser.add_argument(
'--service_type',
parser.add_argument('--service_type',
help=argparse.SUPPRESS)
parser.add_argument(
'--service-name',
parser.add_argument('--service-name',
metavar='<service-name>',
default=cliutils.env('NOVA_SERVICE_NAME'),
default=utils.env('NOVA_SERVICE_NAME'),
help=_('Defaults to env[NOVA_SERVICE_NAME]'))
parser.add_argument(
'--service_name',
parser.add_argument('--service_name',
help=argparse.SUPPRESS)
parser.add_argument(
'--volume-service-name',
parser.add_argument('--volume-service-name',
metavar='<volume-service-name>',
default=cliutils.env('NOVA_VOLUME_SERVICE_NAME'),
default=utils.env('NOVA_VOLUME_SERVICE_NAME'),
help=_('Defaults to env[NOVA_VOLUME_SERVICE_NAME]'))
parser.add_argument(
'--volume_service_name',
parser.add_argument('--volume_service_name',
help=argparse.SUPPRESS)
parser.add_argument(
'--os-endpoint-type',
parser.add_argument('--endpoint-type',
metavar='<endpoint-type>',
dest='endpoint_type',
default=cliutils.env(
'NOVA_ENDPOINT_TYPE',
default=cliutils.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(
'--endpoint-type',
help=argparse.SUPPRESS)
default=utils.env('NOVA_ENDPOINT_TYPE',
default=DEFAULT_NOVA_ENDPOINT_TYPE),
help=_('Defaults to env[NOVA_ENDPOINT_TYPE] or ')
+ DEFAULT_NOVA_ENDPOINT_TYPE + '.')
# NOTE(dtroyer): We can't add --endpoint_type here due to argparse
# thinking usage-list --end is ambiguous; but it
# works fine with only --endpoint-type present
@ -410,32 +364,42 @@ class OpenStackComputeShell(object):
# parser.add_argument('--endpoint_type',
# help=argparse.SUPPRESS)
parser.add_argument(
'--os-compute-api-version',
parser.add_argument('--os-compute-api-version',
metavar='<compute-api-ver>',
default=cliutils.env('OS_COMPUTE_API_VERSION',
default=DEFAULT_OS_COMPUTE_API_VERSION),
default=utils.env('OS_COMPUTE_API_VERSION',
default=DEFAULT_OS_COMPUTE_API_VERSION),
help=_('Accepts 1.1 or 3, '
'defaults to env[OS_COMPUTE_API_VERSION].'))
parser.add_argument(
'--os_compute_api_version',
'defaults to env[OS_COMPUTE_API_VERSION].'))
parser.add_argument('--os_compute_api_version',
help=argparse.SUPPRESS)
parser.add_argument(
'--bypass-url',
parser.add_argument('--os-cacert',
metavar='<ca-certificate>',
default=utils.env('OS_CACERT', default=None),
help='Specify a CA bundle file to use in '
'verifying a TLS (https) server certificate. '
'Defaults to env[OS_CACERT]')
parser.add_argument('--insecure',
default=utils.env('NOVACLIENT_INSECURE', default=False),
action='store_true',
help=_("Explicitly allow novaclient to perform \"insecure\" "
"SSL (https) requests. The server's certificate will "
"not be verified against any certificate authorities. "
"This option should be used with caution."))
parser.add_argument('--bypass-url',
metavar='<bypass-url>',
dest='bypass_url',
default=cliutils.env('NOVACLIENT_BYPASS_URL'),
default=utils.env('NOVACLIENT_BYPASS_URL'),
help="Use this API endpoint instead of the Service Catalog. "
"Defaults to env[NOVACLIENT_BYPASS_URL]")
parser.add_argument('--bypass_url',
help=argparse.SUPPRESS)
help=argparse.SUPPRESS)
# The auth-system-plugins might require some extra options
novaclient.auth_plugin.load_auth_system_opts(parser)
self._append_global_identity_args(parser)
return parser
def get_subcommand_parser(self, version):
@ -446,12 +410,12 @@ class OpenStackComputeShell(object):
try:
actions_module = {
'1.1': shell_v2,
'2': shell_v2,
'3': shell_v2,
'1.1': shell_v1_1,
'2': shell_v1_1,
'3': shell_v3,
}[version]
except KeyError:
actions_module = shell_v2
actions_module = shell_v1_1
self._find_actions(subparsers, actions_module)
self._find_actions(subparsers, self)
@ -491,10 +455,6 @@ class OpenStackComputeShell(object):
def _discover_via_contrib_path(self, version):
module_path = os.path.dirname(os.path.abspath(__file__))
version_str = "v%s" % version.replace('.', '_')
# NOTE(akurilin): v1.1, v2 and v3 have one implementation, so
# we should discover contrib modules in one place.
if version_str in ["v1_1", "v3"]:
version_str = "v2"
ext_path = os.path.join(module_path, version_str, 'contrib')
ext_glob = os.path.join(ext_path, "*.py")
@ -515,8 +475,7 @@ class OpenStackComputeShell(object):
yield name, module
def _add_bash_completion_subparser(self, subparsers):
subparser = subparsers.add_parser(
'bash_completion',
subparser = subparsers.add_parser('bash_completion',
add_help=False,
formatter_class=OpenStackHelpFormatter
)
@ -532,14 +491,13 @@ class OpenStackComputeShell(object):
action_help = desc.strip()
arguments = getattr(callback, 'arguments', [])
subparser = subparsers.add_parser(
command,
subparser = subparsers.add_parser(command,
help=action_help,
description=desc,
add_help=False,
formatter_class=OpenStackHelpFormatter)
subparser.add_argument(
'-h', '--help',
formatter_class=OpenStackHelpFormatter
)
subparser.add_argument('-h', '--help',
action='help',
help=argparse.SUPPRESS,
)
@ -558,20 +516,6 @@ class OpenStackComputeShell(object):
logging.basicConfig(level=logging.DEBUG,
format=streamformat)
def _get_keystone_auth(self, session, auth_url, **kwargs):
auth_token = kwargs.pop('auth_token', None)
if auth_token:
return token.Token(auth_url, auth_token, **kwargs)
else:
return password.Password(
auth_url,
username=kwargs.pop('username'),
user_id=kwargs.pop('user_id'),
password=kwargs.pop('password'),
user_domain_id=kwargs.pop('user_domain_id'),
user_domain_name=kwargs.pop('user_domain_name'),
**kwargs)
def main(self, argv):
# Parse args once to find version and debug settings
parser = self.get_base_parser()
@ -583,7 +527,7 @@ class OpenStackComputeShell(object):
# build available subcommands based on version
self.extensions = self._discover_extensions(
options.os_compute_api_version)
options.os_compute_api_version)
self._run_extension_hooks('__pre_parse_args__')
# NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse
@ -595,7 +539,7 @@ class OpenStackComputeShell(object):
argv[spot] = '--endpoint-type'
subcommand_parser = self.get_subcommand_parser(
options.os_compute_api_version)
options.os_compute_api_version)
self.parser = subcommand_parser
if options.help or not argv:
@ -631,9 +575,6 @@ class OpenStackComputeShell(object):
cacert = args.os_cacert
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.
@ -651,11 +592,6 @@ class OpenStackComputeShell(object):
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:
os_compute_api_version = (options.os_compute_api_version or
DEFAULT_OS_COMPUTE_API_VERSION)
@ -665,20 +601,13 @@ class OpenStackComputeShell(object):
except KeyError:
service_type = DEFAULT_NOVA_SERVICE_TYPE_MAP[
DEFAULT_OS_COMPUTE_API_VERSION]
service_type = cliutils.get_service_type(args.func) or service_type
service_type = utils.get_service_type(args.func) or service_type
# If we have an auth token but no management_url, we must auth anyway.
# Expired tokens are handled by client.py:_cs_request
must_auth = not (cliutils.isunauthenticated(args.func)
or (auth_token and management_url))
# Do not use Keystone session for cases with no session support. The
# presence of auth_plugin means os_auth_system is present and is not
# keystone.
use_session = True
if auth_plugin or bypass_url or os_cache or volume_service_name:
use_session = False
# 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:
@ -687,86 +616,54 @@ class OpenStackComputeShell(object):
if not auth_plugin or not auth_plugin.opts:
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]"))
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([args.os_tenant_name, args.os_tenant_id,
args.os_project_id, args.os_project_name]):
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_tenant_name and not os_tenant_id:
raise exc.CommandError(_("You must provide a tenant name "
"or tenant id via --os-tenant-name, "
"--os-tenant-id, env[OS_TENANT_NAME] "
"or env[OS_TENANT_ID]"))
if not os_auth_url:
if os_auth_system and os_auth_system != 'keystone':
os_auth_url = auth_plugin.get_auth_url()
if not os_auth_url:
raise exc.CommandError(
_("You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL] "
"or specify an auth_system which defines a "
"default url with --os-auth-system "
"or env[OS_AUTH_SYSTEM]"))
project_id = args.os_project_id or args.os_tenant_id
project_name = args.os_project_name or args.os_tenant_name
if use_session:
# Not using Nova auth plugin, so use keystone
start_time = time.time()
keystone_session = ksession.Session.load_from_cli_options(args)
keystone_auth = self._get_keystone_auth(
keystone_session,
args.os_auth_url,
username=args.os_username,
user_id=args.os_user_id,
user_domain_id=args.os_user_domain_id,
user_domain_name=args.os_user_domain_name,
password=args.os_password,
auth_token=args.os_auth_token,
project_id=project_id,
project_name=project_name,
project_domain_id=args.os_project_domain_id,
project_domain_name=args.os_project_domain_name)
end_time = time.time()
self.times.append(
('%s %s' % ('auth_url', args.os_auth_url),
start_time, end_time))
raise exc.CommandError(_("You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL] "
"or specify an auth_system which defines a "
"default url with --os-auth-system "
"or env[OS_AUTH_SYSTEM]"))
if (options.os_compute_api_version and
options.os_compute_api_version != '1.0'):
if not any([args.os_tenant_id, args.os_tenant_name,
args.os_project_id, args.os_project_name]):
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_tenant_name and not os_tenant_id:
raise exc.CommandError(_("You must provide a tenant name "
"or tenant id via --os-tenant-name, "
"--os-tenant-id, env[OS_TENANT_NAME] "
"or env[OS_TENANT_ID]"))
if not os_auth_url:
raise exc.CommandError(
_("You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL]"))
raise exc.CommandError(_("You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL]"))
self.cs = client.Client(
options.os_compute_api_version,
os_username, os_password, os_tenant_name,
tenant_id=os_tenant_id, 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_system=os_auth_system,
auth_plugin=auth_plugin, auth_token=auth_token,
volume_service_name=volume_service_name,
timings=args.timings, bypass_url=bypass_url,
os_cache=os_cache, http_log_debug=options.debug,
cacert=cacert, timeout=timeout,
session=keystone_session, auth=keystone_auth)
completion_cache = client.CompletionCache(os_username, os_auth_url)
self.cs = client.Client(options.os_compute_api_version,
os_username, os_password, os_tenant_name,
tenant_id=os_tenant_id, 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_system=os_auth_system,
auth_plugin=auth_plugin, auth_token=auth_token,
volume_service_name=volume_service_name,
timings=args.timings, bypass_url=bypass_url,
os_cache=os_cache, http_log_debug=options.debug,
cacert=cacert, timeout=timeout,
completion_cache=completion_cache)
# Now check for the password/token of which pieces of the
# identifying keyring key can come from the underlying client
@ -799,11 +696,7 @@ class OpenStackComputeShell(object):
# This does a couple of bits which are useful even if we've
# got the token + service URL already. It exits fast in that case.
if not cliutils.isunauthenticated(args.func):
if not use_session:
# Only call authenticate() if Nova auth plugin is used.
# If keystone is used, authentication is handled as part
# of session.
self.cs.authenticate()
self.cs.authenticate()
except exc.Unauthorized:
raise exc.CommandError(_("Invalid OpenStack Nova credentials."))
except exc.AuthorizationFailure:
@ -832,13 +725,12 @@ class OpenStackComputeShell(object):
volume_service_name=volume_service_name,
timings=args.timings, bypass_url=bypass_url,
os_cache=os_cache, http_log_debug=options.debug,
session=keystone_session, auth=keystone_auth,
cacert=cacert, timeout=timeout)
args.func(self.cs, args)
if args.timings:
self._dump_timings(self.times + self.cs.get_timings())
self._dump_timings(self.cs.get_timings())
def _dump_timings(self, timings):
class Tyme(object):
@ -873,11 +765,8 @@ class OpenStackComputeShell(object):
commands.remove('bash_completion')
print(' '.join(commands | options))
@cliutils.arg(
'command',
metavar='<subcommand>',
nargs='?',
help='Display help for <subcommand>')
@utils.arg('command', metavar='<subcommand>', nargs='?',
help='Display help for <subcommand>')
def do_help(self, args):
"""
Display help about this program or one of its subcommands.
@ -897,7 +786,7 @@ 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)
max_help_position, width)
def start_section(self, heading):
# Title-case the headings
@ -917,9 +806,9 @@ def main():
print("ERROR (%(name)s): %(msg)s" % details,
file=sys.stderr)
sys.exit(1)
except KeyboardInterrupt:
print("... terminating nova client", file=sys.stderr)
sys.exit(130)
except KeyboardInterrupt as e:
print("Shutting down novaclient", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -35,9 +32,9 @@ class Fixture(base.Fixture):
}
}
httpretty.register_uri(httpretty.POST, self.url(),
body=jsonutils.dumps(post_os_agents),
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=post_os_agents,
headers=self.json_headers)
put_os_agents_1 = {
"agent": {
@ -48,10 +45,10 @@ class Fixture(base.Fixture):
}
}
httpretty.register_uri(httpretty.PUT, self.url(1),
body=jsonutils.dumps(put_os_agents_1),
content_type='application/json')
self.requests.register_uri('PUT', self.url(1),
json=put_os_agents_1,
headers=self.json_headers)
httpretty.register_uri(httpretty.DELETE, self.url(1),
content_type='application/json',
status=202)
self.requests.register_uri('DELETE', self.url(1),
headers=self.json_headers,
status_code=202)

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -32,21 +29,24 @@ class Fixture(base.Fixture):
'availability_zone': 'nova1'},
]}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_aggregates),
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json=get_os_aggregates,
headers=self.json_headers)
r = jsonutils.dumps({'aggregate': get_os_aggregates['aggregates'][0]})
get_aggregates_1 = {'aggregate': get_os_aggregates['aggregates'][0]}
httpretty.register_uri(httpretty.POST, self.url(), body=r,
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=get_aggregates_1,
headers=self.json_headers)
for agg_id in (1, 2):
for method in (httpretty.GET, httpretty.PUT):
httpretty.register_uri(method, self.url(agg_id), body=r,
content_type='application/json')
for method in ('GET', 'PUT'):
self.requests.register_uri(method, self.url(agg_id),
json=get_aggregates_1,
headers=self.json_headers)
httpretty.register_uri(httpretty.POST, self.url(agg_id, 'action'),
body=r, content_type='application/json')
self.requests.register_uri('POST', self.url(agg_id, 'action'),
json=get_aggregates_1,
headers=self.json_headers)
httpretty.register_uri(httpretty.DELETE, self.url(1), status=202)
self.requests.register_uri('DELETE', self.url(1), status_code=202)

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -41,9 +38,10 @@ class V1(base.Fixture):
}
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_availability_zone),
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json=get_os_availability_zone,
headers=self.json_headers)
get_os_zone_detail = {
self.zone_info_key: [
@ -88,9 +86,9 @@ class V1(base.Fixture):
]
}
httpretty.register_uri(httpretty.GET, self.url('detail'),
body=jsonutils.dumps(get_os_zone_detail),
content_type='application/json')
self.requests.register_uri('GET', self.url('detail'),
json=get_os_zone_detail,
headers=self.json_headers)
class V3(V1):

View File

@ -19,9 +19,11 @@ COMPUTE_URL = 'http://compute.host'
class Fixture(fixtures.Fixture):
base_url = None
json_headers = {'Content-Type': 'application/json'}
def __init__(self, compute_url=COMPUTE_URL):
def __init__(self, requests, compute_url=COMPUTE_URL):
super(Fixture, self).__init__()
self.requests = requests
self.compute_url = compute_url
def url(self, *args, **kwargs):

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -43,9 +40,9 @@ class Fixture(base.Fixture):
'data': 'foo'
}
}
httpretty.register_uri(httpretty.GET, self.url('root'),
body=jsonutils.dumps(get_os_certificate),
content_type='application/json')
self.requests.register_uri('GET', self.url('root'),
json=get_os_certificate,
headers=self.json_headers)
post_os_certificates = {
'certificate': {
@ -53,6 +50,6 @@ class Fixture(base.Fixture):
'data': 'bar'
}
}
httpretty.register_uri(httpretty.POST, self.url(),
body=jsonutils.dumps(post_os_certificates),
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=post_os_certificates,
headers=self.json_headers)

View File

@ -11,11 +11,10 @@
# under the License.
import fixtures
import httpretty
from keystoneclient.auth.identity import v2
from keystoneclient import fixture
from keystoneclient import session
from novaclient.openstack.common import jsonutils
from novaclient.v1_1 import client as v1_1client
from novaclient.v3 import client as v3client
@ -25,74 +24,31 @@ COMPUTE_URL = 'http://compute.host'
class V1(fixtures.Fixture):
def __init__(self, compute_url=COMPUTE_URL, identity_url=IDENTITY_URL):
def __init__(self, requests,
compute_url=COMPUTE_URL, identity_url=IDENTITY_URL):
super(V1, self).__init__()
self.identity_url = identity_url
self.compute_url = compute_url
self.client = None
self.requests = requests
self.token = {
'access': {
"token": {
"id": "ab48a9efdfedb23ty3494",
"expires": "2010-11-01T03:32:15-05:00",
"tenant": {
"id": "345",
"name": "My Project"
}
},
"user": {
"id": "123",
"name": "jqsmith",
"roles": [
{
"id": "234",
"name": "compute:admin",
},
{
"id": "235",
"name": "object-store:admin",
"tenantId": "1",
}
],
"roles_links": [],
},
"serviceCatalog": [
{
"name": "Cloud Servers",
"type": "compute",
"endpoints": [
{
"publicURL": self.compute_url,
"internalURL": "https://compute1.host/v1/1",
},
],
"endpoints_links": [],
},
{
"name": "Cloud Servers",
"type": "computev3",
"endpoints": [
{
"publicURL": self.compute_url,
"internalURL": "https://compute1.host/v1/1",
},
],
"endpoints_links": [],
},
],
}
}
self.token = fixture.V2Token()
self.token.set_scope()
s = self.token.add_service('compute')
s.add_endpoint(self.compute_url)
s = self.token.add_service('computev3')
s.add_endpoint(self.compute_url)
def setUp(self):
super(V1, self).setUp()
httpretty.enable()
self.addCleanup(httpretty.disable)
auth_url = '%s/tokens' % self.identity_url
httpretty.register_uri(httpretty.POST, auth_url,
body=jsonutils.dumps(self.token),
content_type='application/json')
headers = {'X-Content-Type': 'application/json'}
self.requests.register_uri('POST', auth_url,
json=self.token,
headers=headers)
self.client = self.new_client()
def new_client(self):

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -24,17 +21,17 @@ class Fixture(base.Fixture):
super(Fixture, self).setUp()
get_os_cloudpipe = {'cloudpipes': [{'project_id': 1}]}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_cloudpipe),
content_type='application/json')
self.requests.register_uri('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}
httpretty.register_uri(httpretty.POST, self.url(),
body=jsonutils.dumps(post_os_cloudpipe),
content_type='application/json',
status=202)
self.requests.register_uri('POST', self.url(),
json=post_os_cloudpipe,
headers=self.json_headers,
status_code=202)
httpretty.register_uri(httpretty.PUT, self.url('configure-project'),
content_type='application/json',
status=202)
self.requests.register_uri('PUT', self.url('configure-project'),
headers=self.json_headers,
status_code=202)

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -31,11 +28,12 @@ class Fixture(base.Fixture):
'host': 'bar'
}
}
httpretty.register_uri(httpretty.GET, self.url('192.168.1.1'),
body=jsonutils.dumps(get_os_fixed_ips),
content_type='application/json')
httpretty.register_uri(httpretty.POST,
self.url('192.168.1.1', 'action'),
content_type='application/json',
status=202)
self.requests.register_uri('GET', self.url('192.168.1.1'),
json=get_os_fixed_ips,
headers=self.json_headers)
self.requests.register_uri('POST',
self.url('192.168.1.1', 'action'),
headers=self.json_headers,
status_code=202)

View File

@ -10,8 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests import fakes
from novaclient.tests.fixture_data import base
@ -28,29 +26,28 @@ class FloatingFixture(base.Fixture):
{'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'}]
get_os_floating_ips = {'floating_ips': floating_ips}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_floating_ips),
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json=get_os_floating_ips,
headers=self.json_headers)
for ip in floating_ips:
get_os_floating_ip = {'floating_ip': ip}
httpretty.register_uri(httpretty.GET, self.url(ip['id']),
body=jsonutils.dumps(get_os_floating_ip),
content_type='application/json')
self.requests.register_uri('GET', self.url(ip['id']),
json=get_os_floating_ip,
headers=self.json_headers)
httpretty.register_uri(httpretty.DELETE, self.url(ip['id']),
content_type='application/json',
status=204)
self.requests.register_uri('DELETE', self.url(ip['id']),
headers=self.json_headers,
status_code=204)
def post_os_floating_ips(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def post_os_floating_ips(request, context):
body = jsonutils.loads(request.body)
ip = floating_ips[0].copy()
ip['pool'] = body.get('pool')
ip = jsonutils.dumps({'floating_ip': ip})
return 200, headers, ip
httpretty.register_uri(httpretty.POST, self.url(),
body=post_os_floating_ips,
content_type='application/json')
return {'floating_ip': ip}
self.requests.register_uri('POST', self.url(),
json=post_os_floating_ips,
headers=self.json_headers)
class DNSFixture(base.Fixture):
@ -66,10 +63,10 @@ class DNSFixture(base.Fixture):
{'domain': 'example.com'}
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_floating_ip_dns),
content_type='application/json',
status=205)
self.requests.register_uri('GET', self.url(),
json=get_os_floating_ip_dns,
headers=self.json_headers,
status_code=205)
get_dns_testdomain_entries_testname = {
'dns_entry': {
@ -80,31 +77,30 @@ class DNSFixture(base.Fixture):
}
}
url = self.url('testdomain', 'entries', 'testname')
body = jsonutils.dumps(get_dns_testdomain_entries_testname)
httpretty.register_uri(httpretty.GET, url,
body=body,
content_type='application/json',
status=205)
self.requests.register_uri('GET', url,
json=get_dns_testdomain_entries_testname,
headers=self.json_headers,
status_code=205)
httpretty.register_uri(httpretty.DELETE, self.url('testdomain'),
status=200)
self.requests.register_uri('DELETE', self.url('testdomain'))
url = self.url('testdomain', 'entries', 'testname')
httpretty.register_uri(httpretty.DELETE, url, status=200)
self.requests.register_uri('DELETE', url)
def put_dns_testdomain_entries_testname(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def put_dns_testdomain_entries_testname(request, context):
body = jsonutils.loads(request.body)
fakes.assert_has_keys(body['dns_entry'],
required=['ip', 'dns_type'])
return 205, headers, request.body
httpretty.register_uri(httpretty.PUT, url,
body=put_dns_testdomain_entries_testname,
content_type='application/json')
context.status_code = 205
return request.body
self.requests.register_uri('PUT', url,
text=put_dns_testdomain_entries_testname,
headers=self.json_headers)
url = self.url('testdomain', 'entries')
httpretty.register_uri(httpretty.GET, url, status=404)
self.requests.register_uri('GET', url, status_code=404)
get_os_floating_ip_dns_testdomain_entries = {
get_os_floating_ip_dns_testdomain = {
'dns_entries': [
{
'dns_entry': {
@ -124,14 +120,13 @@ class DNSFixture(base.Fixture):
},
]
}
body = jsonutils.dumps(get_os_floating_ip_dns_testdomain_entries)
httpretty.register_uri(httpretty.GET, url + '?ip=1.2.3.4',
body=body,
status=205,
content_type='application/json')
self.requests.register_uri('GET', url + '?ip=1.2.3.4',
json=get_os_floating_ip_dns_testdomain,
status_code=205,
headers=self.json_headers)
def put_os_floating_ip_dns_testdomain(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def put_os_floating_ip_dns_testdomain(request, context):
body = jsonutils.loads(request.body)
if body['domain_entry']['scope'] == 'private':
fakes.assert_has_keys(body['domain_entry'],
required=['availability_zone', 'scope'])
@ -142,11 +137,12 @@ class DNSFixture(base.Fixture):
fakes.assert_has_keys(body['domain_entry'],
required=['project', 'scope'])
headers['Content-Type'] = 'application/json'
return (205, headers, request.body)
return request.body
httpretty.register_uri(httpretty.PUT, self.url('testdomain'),
body=put_os_floating_ip_dns_testdomain)
self.requests.register_uri('PUT', self.url('testdomain'),
text=put_os_floating_ip_dns_testdomain,
status_code=205,
headers=self.json_headers)
class BulkFixture(base.Fixture):
@ -162,40 +158,38 @@ class BulkFixture(base.Fixture):
{'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'},
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_floating_ips_bulk),
content_type='application/json')
httpretty.register_uri(httpretty.GET, self.url('testHost'),
body=jsonutils.dumps(get_os_floating_ips_bulk),
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json=get_os_floating_ips_bulk,
headers=self.json_headers)
self.requests.register_uri('GET', self.url('testHost'),
json=get_os_floating_ips_bulk,
headers=self.json_headers)
def put_os_floating_ips_bulk_delete(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def put_os_floating_ips_bulk_delete(request, context):
body = jsonutils.loads(request.body)
ip_range = body.get('ip_range')
data = {'floating_ips_bulk_delete': ip_range}
return 200, headers, jsonutils.dumps(data)
return {'floating_ips_bulk_delete': ip_range}
httpretty.register_uri(httpretty.PUT, self.url('delete'),
body=put_os_floating_ips_bulk_delete,
content_type='application/json')
self.requests.register_uri('PUT', self.url('delete'),
json=put_os_floating_ips_bulk_delete,
headers=self.json_headers)
def post_os_floating_ips_bulk(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def post_os_floating_ips_bulk(request, context):
body = jsonutils.loads(request.body)
params = body.get('floating_ips_bulk_create')
pool = params.get('pool', 'defaultPool')
interface = params.get('interface', 'defaultInterface')
data = {
return {
'floating_ips_bulk_create': {
'ip_range': '192.168.1.0/30',
'pool': pool,
'interface': interface
}
}
return 200, headers, jsonutils.dumps(data)
httpretty.register_uri(httpretty.POST, self.url(),
body=post_os_floating_ips_bulk,
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=post_os_floating_ips_bulk,
headers=self.json_headers)
class PoolsFixture(base.Fixture):
@ -211,6 +205,6 @@ class PoolsFixture(base.Fixture):
{'name': 'bar'}
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_floating_ip_pools),
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json=get_os_floating_ip_pools,
headers=self.json_headers)

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -30,9 +27,9 @@ class Fixture(base.Fixture):
"alive": True,
}
}
httpretty.register_uri(httpretty.GET, self.url(1),
body=jsonutils.dumps(get_os_fping_1),
content_type='application/json')
self.requests.register_uri('GET', self.url(1),
json=get_os_fping_1,
headers=self.json_headers)
get_os_fping = {
'servers': [
@ -44,6 +41,6 @@ class Fixture(base.Fixture):
},
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_fping),
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json=get_os_fping,
headers=self.json_headers)

View File

@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from six.moves.urllib import parse
from novaclient.openstack.common import jsonutils
@ -36,13 +35,17 @@ class BaseFixture(base.Fixture):
'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}}
]
}
httpretty.register_uri(httpretty.GET, self.url('host'),
body=jsonutils.dumps(get_os_hosts_host),
content_type='application/json')
def get_os_hosts(request, url, headers):
host, query = parse.splitquery(url)
headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.url('host'),
json=get_os_hosts_host,
headers=headers)
def get_os_hosts(request, context):
host, query = parse.splitquery(request.url)
zone = 'nova1'
service = None
if query:
qs = parse.parse_qs(query)
@ -51,70 +54,71 @@ class BaseFixture(base.Fixture):
except Exception:
pass
data = {
try:
service = qs['service'][0]
except Exception:
pass
return {
'hosts': [
{
'host': 'host1',
'service': 'nova-compute',
'service': service or 'nova-compute',
'zone': zone
},
{
'host': 'host1',
'service': 'nova-cert',
'service': service or 'nova-cert',
'zone': zone
}
]
}
return 200, headers, jsonutils.dumps(data)
httpretty.register_uri(httpretty.GET, self.url(),
body=get_os_hosts,
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json=get_os_hosts,
headers=headers)
get_os_hosts_sample_host = {
'host': [
{'resource': {'host': 'sample_host'}}
],
}
httpretty.register_uri(httpretty.GET, self.url('sample_host'),
body=jsonutils.dumps(get_os_hosts_sample_host),
content_type='application/json')
self.requests.register_uri('GET', self.url('sample_host'),
json=get_os_hosts_sample_host,
headers=headers)
httpretty.register_uri(httpretty.PUT, self.url('sample_host', 1),
body=jsonutils.dumps(self.put_host_1()),
content_type='application/json')
self.requests.register_uri('PUT', self.url('sample_host', 1),
json=self.put_host_1(),
headers=headers)
httpretty.register_uri(httpretty.PUT, self.url('sample_host', 2),
body=jsonutils.dumps(self.put_host_2()),
content_type='application/json')
self.requests.register_uri('PUT', self.url('sample_host', 2),
json=self.put_host_2(),
headers=headers)
httpretty.register_uri(httpretty.PUT, self.url('sample_host', 3),
body=jsonutils.dumps(self.put_host_3()),
content_type='application/json')
self.requests.register_uri('PUT', self.url('sample_host', 3),
json=self.put_host_3(),
headers=headers)
url = self.url('sample_host', 'reboot')
httpretty.register_uri(httpretty.GET, url,
body=jsonutils.dumps(self.get_host_reboot()),
content_type='application/json')
self.requests.register_uri('GET', self.url('sample_host', 'reboot'),
json=self.get_host_reboot(),
headers=headers)
url = self.url('sample_host', 'startup')
httpretty.register_uri(httpretty.GET, url,
body=jsonutils.dumps(self.get_host_startup()),
content_type='application/json')
self.requests.register_uri('GET', self.url('sample_host', 'startup'),
json=self.get_host_startup(),
headers=headers)
url = self.url('sample_host', 'shutdown')
httpretty.register_uri(httpretty.GET, url,
body=jsonutils.dumps(self.get_host_shutdown()),
content_type='application/json')
self.requests.register_uri('GET', self.url('sample_host', 'shutdown'),
json=self.get_host_shutdown(),
headers=headers)
def put_os_hosts_sample_host(request, url, headers):
def put_os_hosts_sample_host(request, context):
result = {'host': 'dummy'}
result.update(jsonutils.loads(request.body.decode('utf-8')))
return 200, headers, jsonutils.dumps(result)
result.update(jsonutils.loads(request.body))
return result
httpretty.register_uri(httpretty.PUT, self.url('sample_host'),
body=put_os_hosts_sample_host,
content_type='application/json')
self.requests.register_uri('PUT', self.url('sample_host'),
json=put_os_hosts_sample_host,
headers=headers)
class V1(BaseFixture):

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -30,15 +27,20 @@ class V1(base.Fixture):
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_hypervisors),
content_type='application/json')
self.headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.url(),
json=get_os_hypervisors,
headers=self.headers)
get_os_hypervisors_detail = {
'hypervisors': [
{
'id': 1234,
'service': {'id': 1, 'host': 'compute1'},
'service': {
'id': 1,
'host': 'compute1',
},
'vcpus': 4,
'memory_mb': 10 * 1024,
'local_gb': 250,
@ -57,29 +59,32 @@ class V1(base.Fixture):
},
{
'id': 2,
'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
'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
}
]
}
httpretty.register_uri(httpretty.GET, self.url('detail'),
body=jsonutils.dumps(get_os_hypervisors_detail),
content_type='application/json')
self.requests.register_uri('GET', self.url('detail'),
json=get_os_hypervisors_detail,
headers=self.headers)
get_os_hypervisors_stats = {
'hypervisor_statistics': {
@ -98,9 +103,9 @@ class V1(base.Fixture):
}
}
httpretty.register_uri(httpretty.GET, self.url('statistics'),
body=jsonutils.dumps(get_os_hypervisors_stats),
content_type='application/json')
self.requests.register_uri('GET', self.url('statistics'),
json=get_os_hypervisors_stats,
headers=self.headers)
get_os_hypervisors_search = {
'hypervisors': [
@ -109,9 +114,9 @@ class V1(base.Fixture):
]
}
httpretty.register_uri(httpretty.GET, self.url('hyper', 'search'),
body=jsonutils.dumps(get_os_hypervisors_search),
content_type='application/json')
self.requests.register_uri('GET', self.url('hyper', 'search'),
json=get_os_hypervisors_search,
headers=self.headers)
get_hyper_server = {
'hypervisors': [
@ -134,9 +139,9 @@ class V1(base.Fixture):
]
}
httpretty.register_uri(httpretty.GET, self.url('hyper', 'servers'),
body=jsonutils.dumps(get_hyper_server),
content_type='application/json')
self.requests.register_uri('GET', self.url('hyper', 'servers'),
json=get_hyper_server,
headers=self.headers)
get_os_hypervisors_1234 = {
'hypervisor': {
@ -160,9 +165,9 @@ class V1(base.Fixture):
}
}
httpretty.register_uri(httpretty.GET, self.url(1234),
body=jsonutils.dumps(get_os_hypervisors_1234),
content_type='application/json')
self.requests.register_uri('GET', self.url(1234),
json=get_os_hypervisors_1234,
headers=self.headers)
get_os_hypervisors_uptime = {
'hypervisor': {
@ -172,9 +177,9 @@ class V1(base.Fixture):
}
}
httpretty.register_uri(httpretty.GET, self.url(1234, 'uptime'),
body=jsonutils.dumps(get_os_hypervisors_uptime),
content_type='application/json')
self.requests.register_uri('GET', self.url(1234, 'uptime'),
json=get_os_hypervisors_uptime,
headers=self.headers)
class V3(V1):
@ -189,10 +194,10 @@ class V3(V1):
]
}
httpretty.register_uri(httpretty.GET,
self.url('search', query='hyper'),
body=jsonutils.dumps(get_os_hypervisors_search),
content_type='application/json')
self.requests.register_uri('GET',
self.url('search', query='hyper'),
json=get_os_hypervisors_search,
headers=self.headers)
get_1234_servers = {
'hypervisor': {
@ -205,6 +210,6 @@ class V3(V1):
},
}
httpretty.register_uri(httpretty.GET, self.url(1234, 'servers'),
body=jsonutils.dumps(get_1234_servers),
content_type='application/json')
self.requests.register_uri('GET', self.url(1234, 'servers'),
json=get_1234_servers,
headers=self.headers)

View File

@ -10,8 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests import fakes
from novaclient.tests.fixture_data import base
@ -31,9 +29,11 @@ class V1(base.Fixture):
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_images),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.url(),
json=get_images,
headers=headers)
image_1 = {
'id': 1,
@ -58,60 +58,53 @@ class V1(base.Fixture):
"links": {},
}
get_images_detail = {'images': [image_1, image_2]}
self.requests.register_uri('GET', self.url('detail'),
json={'images': [image_1, image_2]},
headers=headers)
httpretty.register_uri(httpretty.GET, self.url('detail'),
body=jsonutils.dumps(get_images_detail),
content_type='application/json')
self.requests.register_uri('GET', self.url(1),
json={'image': image_1},
headers=headers)
get_images_1 = {'image': image_1}
self.requests.register_uri('GET', self.url(2),
json={'image': image_2},
headers=headers)
httpretty.register_uri(httpretty.GET, self.url(1),
body=jsonutils.dumps(get_images_1),
content_type='application/json')
self.requests.register_uri('GET', self.url(456),
json={'image': image_2},
headers=headers)
get_images_2 = {'image': image_2}
httpretty.register_uri(httpretty.GET, self.url(2),
body=jsonutils.dumps(get_images_2),
content_type='application/json')
httpretty.register_uri(httpretty.GET, self.url(456),
body=jsonutils.dumps(get_images_2),
content_type='application/json')
def post_images(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def post_images(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['image']
fakes.assert_has_keys(body['image'], required=['serverId', 'name'])
return 202, headers, jsonutils.dumps(images_1)
return images_1
httpretty.register_uri(httpretty.POST, self.url(),
body=post_images,
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=post_images,
headers=headers,
status_code=202)
def post_images_1_metadata(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def post_images_1_metadata(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['metadata']
fakes.assert_has_keys(body['metadata'], required=['test_key'])
data = jsonutils.dumps({'metadata': image_1['metadata']})
return 200, headers, data
return {'metadata': image_1['metadata']}
httpretty.register_uri(httpretty.POST, self.url(1, 'metadata'),
body=post_images_1_metadata,
content_type='application/json')
self.requests.register_uri('POST', self.url(1, 'metadata'),
json=post_images_1_metadata,
headers=headers)
for u in (1, 2, '1/metadata/test_key'):
httpretty.register_uri(httpretty.DELETE, self.url(u),
status=204)
self.requests.register_uri('DELETE', self.url(u), status_code=204)
httpretty.register_uri(httpretty.HEAD, self.url(1), status=200,
x_image_meta_id=1,
x_image_meta_name='CentOS 5.2',
x_image_meta_updated='2010-10-10T12:00:00Z',
x_image_meta_created='2010-10-10T12:00:00Z',
x_image_meta_status='ACTIVE',
x_image_meta_property_test_key='test_value')
image_headers = {'x-image-meta-id': '1',
'x-image-meta-name': 'CentOS 5.2',
'x-image-meta-updated': '2010-10-10T12:00:00Z',
'x-image-meta-created': '2010-10-10T12:00:00Z',
'x-image-meta-status': 'ACTIVE',
'x-image-meta-property-test-key': 'test_value'}
self.requests.register_uri('HEAD', self.url(1), headers=image_headers)
class V3(V1):

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests import fakes
from novaclient.tests.fixture_data import base
@ -26,25 +23,27 @@ class V1(base.Fixture):
super(V1, self).setUp()
keypair = {'fingerprint': 'FAKE_KEYPAIR', 'name': 'test'}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps({'keypairs': [keypair]}),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
httpretty.register_uri(httpretty.GET, self.url('test'),
body=jsonutils.dumps({'keypair': keypair}),
content_type='application/json')
self.requests.register_uri('GET', self.url(),
json={'keypairs': [keypair]},
headers=headers)
httpretty.register_uri(httpretty.DELETE, self.url('test'), status=202)
self.requests.register_uri('GET', self.url('test'),
json={'keypair': keypair},
headers=headers)
def post_os_keypairs(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
self.requests.register_uri('DELETE', self.url('test'), status_code=202)
def post_os_keypairs(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['keypair']
fakes.assert_has_keys(body['keypair'], required=['name'])
return 202, headers, jsonutils.dumps({'keypair': keypair})
return {'keypair': keypair}
httpretty.register_uri(httpretty.POST, self.url(),
body=post_os_keypairs,
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=post_os_keypairs,
headers=headers)
class V3(V1):

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -77,6 +74,7 @@ class Fixture(base.Fixture):
},
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_limits),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.url(),
json=get_limits,
headers=headers)

View File

@ -10,8 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -34,29 +32,30 @@ class Fixture(base.Fixture):
]
}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_os_networks),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
def post_os_networks(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
data = jsonutils.dumps({'network': body})
return 202, headers, data
self.requests.register_uri('GET', self.url(),
json=get_os_networks,
headers=headers)
httpretty.register_uri(httpretty.POST, self.url(),
body=post_os_networks,
content_type='application/json')
def post_os_networks(request, context):
body = jsonutils.loads(request.body)
return {'network': body}
self.requests.register_uri("POST", self.url(),
json=post_os_networks,
headers=headers)
get_os_networks_1 = {'network': {"label": "1", "cidr": "10.0.0.0/24"}}
httpretty.register_uri(httpretty.GET, self.url(1),
body=jsonutils.dumps(get_os_networks_1),
content_type='application/json')
self.requests.register_uri('GET', self.url(1),
json=get_os_networks_1,
headers=headers)
httpretty.register_uri(httpretty.DELETE,
self.url('networkdelete'),
stauts=202)
self.requests.register_uri('DELETE',
self.url('networkdelete'),
status_code=202)
for u in ('add', 'networkdisassociate/action', 'networktest/action',
'1/action', '2/action'):
httpretty.register_uri(httpretty.POST, self.url(u), stauts=202)
self.requests.register_uri('POST', self.url(u), status_code=202)

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -25,32 +22,32 @@ class V1(base.Fixture):
uuid = '97f4c221-bff4-4578-b030-0df4ef119353'
uuid2 = '97f4c221bff44578b0300df4ef119353'
test_json = jsonutils.dumps({'quota_set': self.test_quota('test')})
test_json = {'quota_set': self.test_quota('test')}
self.headers = {'Content-Type': 'application/json'}
for u in ('test', 'tenant-id', 'tenant-id/defaults',
'%s/defaults' % uuid2):
httpretty.register_uri(httpretty.GET, self.url(u),
body=test_json,
content_type='application/json')
self.requests.register_uri('GET', self.url(u),
json=test_json,
headers=self.headers)
quota_json = jsonutils.dumps({'quota_set': self.test_quota(uuid)})
httpretty.register_uri(httpretty.PUT, self.url(uuid),
body=quota_json,
content_type='application/json')
httpretty.register_uri(httpretty.GET, self.url(uuid),
body=quota_json,
content_type='application/json')
self.requests.register_uri('PUT', self.url(uuid),
json={'quota_set': self.test_quota(uuid)},
headers=self.headers)
quota_json2 = jsonutils.dumps({'quota_set': self.test_quota(uuid2)})
httpretty.register_uri(httpretty.PUT, self.url(uuid2),
body=quota_json2,
content_type='application/json')
httpretty.register_uri(httpretty.GET, self.url(uuid2),
body=quota_json2,
content_type='application/json')
self.requests.register_uri('GET', self.url(uuid),
json={'quota_set': self.test_quota(uuid)},
headers=self.headers)
self.requests.register_uri('PUT', self.url(uuid2),
json={'quota_set': self.test_quota(uuid2)},
headers=self.headers)
self.requests.register_uri('GET', self.url(uuid2),
json={'quota_set': self.test_quota(uuid2)},
headers=self.headers)
for u in ('test', uuid2):
httpretty.register_uri(httpretty.DELETE, self.url(u), status=202)
self.requests.register_uri('DELETE', self.url(u), status_code=202)
def test_quota(self, tenant_id='test'):
return {
@ -82,6 +79,6 @@ class V3(V1):
}
}
httpretty.register_uri(httpretty.GET, self.url('test', 'detail'),
body=jsonutils.dumps(get_detail),
content_type='application/json')
self.requests.register_uri('GET', self.url('test', 'detail'),
json=get_detail,
headers=self.headers)

View File

@ -10,8 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests import fakes
from novaclient.tests.fixture_data import base
@ -34,24 +32,26 @@ class Fixture(base.Fixture):
'cidr': '10.0.0.0/8'
}
get_rules = {'security_group_rules': [rule]}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_rules),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.url(),
json={'security_group_rules': [rule]},
headers=headers)
for u in (1, 11, 12):
httpretty.register_uri(httpretty.DELETE, self.url(u), status=202)
self.requests.register_uri('DELETE', self.url(u), status_code=202)
def post_rules(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def post_rules(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['security_group_rule']
fakes.assert_has_keys(body['security_group_rule'],
required=['parent_group_id'],
optional=['group_id', 'ip_protocol',
'from_port', 'to_port', 'cidr'])
return 202, headers, jsonutils.dumps({'security_group_rule': rule})
return {'security_group_rule': rule}
httpretty.register_uri(httpretty.POST, self.url(),
body=post_rules,
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=post_rules,
headers=headers,
status_code=202)

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests import fakes
from novaclient.tests.fixture_data import base
@ -64,36 +61,39 @@ class Fixture(base.Fixture):
}
get_groups = {'security_groups': [security_group_1, security_group_2]}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_groups),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.url(),
json=get_groups,
headers=headers)
get_group_1 = {'security_group': security_group_1}
httpretty.register_uri(httpretty.GET, self.url(1),
body=jsonutils.dumps(get_group_1),
content_type='application/json')
self.requests.register_uri('GET', self.url(1),
json=get_group_1,
headers=headers)
httpretty.register_uri(httpretty.DELETE, self.url(1), status=202)
self.requests.register_uri('DELETE', self.url(1), status_code=202)
def post_os_security_groups(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def post_os_security_groups(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['security_group']
fakes.assert_has_keys(body['security_group'],
required=['name', 'description'])
r = jsonutils.dumps({'security_group': security_group_1})
return 202, headers, r
return {'security_group': security_group_1}
httpretty.register_uri(httpretty.POST, self.url(),
body=post_os_security_groups,
content_type='application/json')
self.requests.register_uri('POST', self.url(),
json=post_os_security_groups,
headers=headers,
status_code=202)
def put_os_security_groups_1(request, url, headers):
body = jsonutils.loads(request.body.decode('utf-8'))
def put_os_security_groups_1(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['security_group']
fakes.assert_has_keys(body['security_group'],
required=['name', 'description'])
return 205, headers, request.body
return body
httpretty.register_uri(httpretty.PUT, self.url(1),
body=put_os_security_groups_1,
content_type='application/json')
self.requests.register_uri('PUT', self.url(1),
json=put_os_security_groups_1,
headers=headers,
status_code=205)

View File

@ -10,9 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import base
@ -54,22 +51,23 @@ class Fixture(base.Fixture):
}
]
get_server_groups = {'server_groups': server_groups}
httpretty.register_uri(httpretty.GET, self.url(),
body=jsonutils.dumps(get_server_groups),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.url(),
json={'server_groups': server_groups},
headers=headers)
server = server_groups[0]
server_json = jsonutils.dumps({'server_group': server})
server_j = jsonutils.dumps({'server_group': server})
def _register(method, *args):
httpretty.register_uri(method, self.url(*args), body=server_json)
self.requests.register_uri(method, self.url(*args), text=server_j)
_register(httpretty.POST)
_register(httpretty.POST, server['id'])
_register(httpretty.GET, server['id'])
_register(httpretty.PUT, server['id'])
_register(httpretty.POST, server['id'], '/action')
_register('POST')
_register('POST', server['id'])
_register('GET', server['id'])
_register('PUT', server['id'])
_register('POST', server['id'], '/action')
httpretty.register_uri(httpretty.DELETE, self.url(server['id']),
status=202)
self.requests.register_uri('DELETE', self.url(server['id']),
status_code=202)

View File

@ -0,0 +1,612 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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.openstack.common import jsonutils
from novaclient.tests import fakes
from novaclient.tests.fixture_data import base
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.register_uri('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.register_uri('GET', self.url('detail'),
json=get_servers_detail,
headers=self.json_headers)
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.register_uri('GET', self.url(s['id']),
json={'server': s},
headers=self.json_headers)
for s in (1234, 5678):
self.requests.register_uri('DELETE', self.url(s), status_code=202)
for k in ('test_key', 'key1', 'key2'):
self.requests.register_uri('DELETE',
self.url(1234, 'metadata', k),
status_code=204)
metadata1 = {'metadata': {'test_key': 'test_value'}}
self.requests.register_uri('POST', self.url(1234, 'metadata'),
json=metadata1,
headers=self.json_headers)
self.requests.register_uri('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.register_uri('POST', self.url(u, 'metadata'),
json=metadata2, status_code=204)
self.requests.register_uri('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.register_uri('GET',
self.url('1234', 'os-security-groups'),
json=get_security_groups)
self.requests.register_uri('POST', self.url(),
json=self.post_servers,
headers=self.json_headers)
self.requests.register_uri('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.register_uri('GET',
self.url('1234', 'os-interface'),
json=get_os_interface,
headers=self.json_headers)
interface_data = {'interfaceAttachment': {}}
self.requests.register_uri('POST',
self.url('1234', 'os-interface'),
json=interface_data,
headers=self.json_headers)
def put_servers_1234(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['server']
fakes.assert_has_keys(body['server'],
optional=['name', 'adminPass'])
return request.body
self.requests.register_uri('PUT', self.url(1234),
text=put_servers_1234,
status_code=204,
headers=self.json_headers)
def post_os_volumes_boot(request, context):
body = jsonutils.loads(request.body)
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.register_uri('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.register_uri('DELETE',
self.url(1234, 'os-server-password'),
status_code=202)
class V1(Base):
def setUp(self):
super(V1, self).setUp()
#
# Server Addresses
#
add = self.server_1234['addresses']
self.requests.register_uri('GET', self.url(1234, 'ips'),
json={'addresses': add},
headers=self.json_headers)
self.requests.register_uri('GET', self.url(1234, 'ips', 'public'),
json={'public': add['public']},
headers=self.json_headers)
self.requests.register_uri('GET', self.url(1234, 'ips', 'private'),
json={'private': add['private']},
headers=self.json_headers)
self.requests.register_uri('DELETE',
self.url(1234, 'ips', 'public', '1.2.3.4'),
status_code=202)
self.requests.register_uri('GET',
self.url('1234', 'diagnostics'),
json=self.diagnostic)
self.requests.register_uri('DELETE',
self.url('1234', 'os-interface', 'port-id'))
# Testing with the following password and key
#
# Clear password: FooBar123
#
# RSA Private Key: novaclient/tests/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.register_uri('GET',
self.url(1234, 'os-server-password'),
json=get_server_password)
def post_servers(self, request, context):
body = jsonutils.loads(request.body)
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_action(self, request, context):
_body = ''
body = jsonutils.loads(request.body)
context.status_code = 202
assert len(body.keys()) == 1
action = list(body)[0]
if action == 'reboot':
assert list(body[action]) == ['type']
assert body[action]['type'] in ['HARD', 'SOFT']
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 == 'resize':
keys = body[action].keys()
assert 'flavorRef' in keys
elif action == 'confirmResize':
assert body[action] is None
# This one method returns a different response code
context.status_code = 204
return None
elif action == 'revertResize':
assert body[action] is None
elif action == 'migrate':
assert body[action] is None
elif action == 'os-stop':
assert body[action] is None
elif action == 'os-start':
assert body[action] is None
elif action == 'forceDelete':
assert body[action] is None
elif action == 'restore':
assert body[action] is None
elif action == 'pause':
assert body[action] is None
elif action == 'unpause':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'rescue':
assert body[action] is None
_body = {'Password': 'RescuePassword'}
elif action == 'unrescue':
assert body[action] is None
elif action == 'resume':
assert body[action] is None
elif action == 'suspend':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'shelve':
assert body[action] is None
elif action == 'shelveOffload':
assert body[action] is None
elif action == 'unshelve':
assert body[action] is None
elif action == 'addFixedIp':
assert list(body[action]) == ['networkId']
elif action == 'removeFixedIp':
assert list(body[action]) == ['address']
elif action == 'addFloatingIp':
assert (list(body[action]) == ['address'] or
sorted(list(body[action])) == ['address',
'fixed_address'])
elif action == 'removeFloatingIp':
assert list(body[action]) == ['address']
elif action == 'createImage':
assert set(body[action].keys()) == set(['name', 'metadata'])
context.headers['location'] = "http://blah/images/456"
elif action == 'changePassword':
assert list(body[action]) == ['adminPass']
elif action == 'os-getConsoleOutput':
assert list(body[action]) == ['length']
context.status_code = 202
return {'output': 'foo'}
elif action == 'os-getVNCConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSPICEConsole':
assert list(body[action]) == ['type']
elif action == 'os-getRDPConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSerialConsole':
assert list(body[action]) == ['type']
elif action == 'os-migrateLive':
assert set(body[action].keys()) == set(['host',
'block_migration',
'disk_over_commit'])
elif action == 'os-resetState':
assert list(body[action]) == ['state']
elif action == 'resetNetwork':
assert body[action] is None
elif action == 'addSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'removeSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'createBackup':
assert set(body[action]) == set(['name',
'backup_type',
'rotation'])
elif action == 'evacuate':
keys = list(body[action])
if 'adminPass' in keys:
keys.remove('adminPass')
assert set(keys) == set(['host', 'onSharedStorage'])
else:
raise AssertionError("Unexpected server action: %s" % action)
return {'server': _body}
class V3(Base):
def setUp(self):
super(V3, self).setUp()
get_interfaces = {
"interface_attachments": [
{
"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.register_uri('GET',
self.url('1234', 'os-attach-interfaces'),
json=get_interfaces,
headers=self.json_headers)
attach_body = {'interface_attachment': {}}
self.requests.register_uri('POST',
self.url('1234', 'os-attach-interfaces'),
json=attach_body,
headers=self.json_headers)
self.requests.register_uri('GET',
self.url('1234', 'os-server-diagnostics'),
json=self.diagnostic)
url = self.url('1234', 'os-attach-interfaces', 'port-id')
self.requests.register_uri('DELETE', url)
self.requests.register_uri('GET',
self.url(1234, 'os-server-password'),
json={'password': ''})
def post_servers(self, request, context):
body = jsonutils.loads(request.body)
assert set(body.keys()) <= set(['server'])
fakes.assert_has_keys(body['server'],
required=['name', 'image_ref', 'flavor_ref'],
optional=['metadata', 'personality',
'os-scheduler-hints:scheduler_hints'])
if body['server']['name'] == 'some-bad-server':
body = self.server_1235
else:
body = self.server_1234
context.status_code = 202
return {'server': body}
def post_servers_1234_action(self, request, context):
context.status_code = 202
body_is_none_list = [
'revert_resize', 'migrate', 'stop', 'start', 'force_delete',
'restore', 'pause', 'unpause', 'lock', 'unlock', 'unrescue',
'resume', 'suspend', 'lock', 'unlock', 'shelve', 'shelve_offload',
'unshelve', 'reset_network', 'rescue', 'confirm_resize']
body_return_map = {
'rescue': {'admin_password': 'RescuePassword'},
'get_console_output': {'output': 'foo'},
'rebuild': {'server': self.server_1234},
}
body_param_check_exists = {
'rebuild': 'image_ref',
'resize': 'flavor_ref'}
body_params_check_exact = {
'reboot': ['type'],
'add_fixed_ip': ['network_id'],
'evacuate': ['host', 'on_shared_storage'],
'remove_fixed_ip': ['address'],
'change_password': ['admin_password'],
'get_console_output': ['length'],
'get_vnc_console': ['type'],
'get_spice_console': ['type'],
'get_serial_console': ['type'],
'reset_state': ['state'],
'create_image': ['name', 'metadata'],
'migrate_live': ['host', 'block_migration', 'disk_over_commit'],
'create_backup': ['name', 'backup_type', 'rotation'],
'attach': ['volume_id', 'device'],
'detach': ['volume_id'],
'swap_volume_attachment': ['old_volume_id', 'new_volume_id']}
body = jsonutils.loads(request.body)
assert len(body.keys()) == 1
action = list(body)[0]
_body = body_return_map.get(action, '')
if action in body_is_none_list:
assert body[action] is None
if action in body_param_check_exists:
assert body_param_check_exists[action] in body[action]
if action == 'evacuate':
body[action].pop('admin_password', None)
if action in body_params_check_exact:
assert set(body[action]) == set(body_params_check_exact[action])
if action == 'reboot':
assert body[action]['type'] in ['HARD', 'SOFT']
elif action == 'confirm_resize':
# This one method returns a different response code
context.status_code = 204
elif action == 'create_image':
context.headers['location'] = "http://blah/images/456"
if action not in set.union(set(body_is_none_list),
set(body_params_check_exact.keys()),
set(body_param_check_exists.keys())):
raise AssertionError("Unexpected server action: %s" % action)
return _body

View File

@ -14,6 +14,8 @@
# under the License.
import argparse
from keystoneclient import fixture
import mock
import pkg_resources
import requests
@ -32,30 +34,10 @@ from novaclient.v1_1 import client
def mock_http_request(resp=None):
"""Mock an HTTP Request."""
if not resp:
resp = {
"access": {
"token": {
"expires": "12345",
"id": "FAKE_ID",
"tenant": {
"id": "FAKE_TENANT_ID",
}
},
"serviceCatalog": [
{
"type": "compute",
"endpoints": [
{
"region": "RegionOne",
"adminURL": "http://localhost:8774/v1.1",
"internalURL": "http://localhost:8774/v1.1",
"publicURL": "http://localhost:8774/v1.1/",
},
],
},
],
},
}
resp = fixture.V2Token()
resp.set_scope()
s = resp.add_service('compute')
s.add_endpoint("http://localhost:8774/v1.1", region='RegionOne')
auth_response = utils.TestResponse({
"status_code": 200,
@ -171,7 +153,7 @@ class DeprecatedAuthPluginTest(utils.TestCase):
auth_system="fakewithauthurl",
auth_plugin=plugin)
cs.client.authenticate()
self.assertEqual(cs.client.auth_url, "http://faked/v2.0")
self.assertEqual("http://faked/v2.0", cs.client.auth_url)
test_auth_call()
@ -301,7 +283,7 @@ class AuthPluginTest(utils.TestCase):
cs = client.Client("username", "password", "project_id",
auth_system="fakewithauthurl",
auth_plugin=plugin)
self.assertEqual(cs.client.auth_url, "http://faked/v2.0")
self.assertEqual("http://faked/v2.0", cs.client.auth_url)
@mock.patch.object(pkg_resources, "iter_entry_points")
def test_exception_if_no_authenticate(self, mock_iter_entry_points):

View File

@ -25,18 +25,18 @@ class BaseTest(utils.TestCase):
def test_resource_repr(self):
r = base.Resource(None, dict(foo="bar", baz="spam"))
self.assertEqual(repr(r), "<Resource baz=spam, foo=bar>")
self.assertEqual("<Resource baz=spam, foo=bar>", repr(r))
def test_getid(self):
self.assertEqual(base.getid(4), 4)
self.assertEqual(4, base.getid(4))
class TmpObject(object):
id = 4
self.assertEqual(base.getid(TmpObject), 4)
self.assertEqual(4, base.getid(TmpObject))
def test_resource_lazy_getattr(self):
f = flavors.Flavor(cs.flavors, {'id': 1})
self.assertEqual(f.name, '256 MB Server')
self.assertEqual('256 MB Server', f.name)
cs.assert_called('GET', '/flavors/1')
# Missing stuff still fails after a second get

View File

@ -14,11 +14,12 @@
# under the License.
import json
import logging
import mock
import requests
import fixtures
import mock
import requests
import novaclient.client
import novaclient.extension
@ -27,8 +28,6 @@ from novaclient.tests import utils
import novaclient.v1_1.client
import novaclient.v3.client
import json
class ClientConnectionPoolTest(utils.TestCase):
@ -48,7 +47,7 @@ class ClientTest(utils.TestCase):
projectid='project',
timeout=2,
auth_url="http://www.blah.com")
self.assertEqual(instance.timeout, 2)
self.assertEqual(2, instance.timeout)
mock_request = mock.Mock()
mock_request.return_value = requests.Response()
mock_request.return_value.status_code = 200
@ -107,7 +106,38 @@ class ClientTest(utils.TestCase):
allow_redirects=mock.ANY,
data=json.dumps(data),
verify=mock.ANY)]
self.assertEqual(mock_request.call_args_list, expected)
self.assertEqual(expected, mock_request.call_args_list)
@mock.patch.object(novaclient.client.HTTPClient, 'request',
return_value=(200, "{'versions':[]}"))
def _check_version_url(self, management_url, version_url, mock_request):
projectid = '25e469aa1848471b875e68cde6531bc5'
instance = novaclient.client.HTTPClient(user='user',
password='password',
projectid=projectid,
auth_url="http://www.blah.com")
instance.auth_token = 'foobar'
instance.management_url = management_url % projectid
instance.version = 'v2.0'
# If passing None as the part of url, a client accesses the url which
# doesn't include "v2/<projectid>" for getting API version info.
instance.get(None)
mock_request.assert_called_once_with(version_url, 'GET',
headers=mock.ANY)
mock_request.reset_mock()
# Otherwise, a client accesses the url which includes "v2/<projectid>".
instance.get('servers')
url = instance.management_url + 'servers'
mock_request.assert_called_once_with(url, 'GET', headers=mock.ANY)
def test_client_version_url(self):
self._check_version_url('http://foo.com/v2/%s', 'http://foo.com/')
def test_client_version_url_with_project_name(self):
self._check_version_url('http://foo.com/nova/v2/%s',
'http://foo.com/nova/')
def test_get_client_class_v3(self):
output = novaclient.client.get_client_class('3')
@ -233,7 +263,7 @@ class ClientTest(utils.TestCase):
def test_get_password_simple(self):
cs = novaclient.client.HTTPClient("user", "password", "", "")
cs.password_func = mock.Mock()
self.assertEqual(cs._get_password(), "password")
self.assertEqual("password", cs._get_password())
self.assertFalse(cs.password_func.called)
def test_get_password_none(self):
@ -243,26 +273,26 @@ class ClientTest(utils.TestCase):
def test_get_password_func(self):
cs = novaclient.client.HTTPClient("user", None, "", "")
cs.password_func = mock.Mock(return_value="password")
self.assertEqual(cs._get_password(), "password")
self.assertEqual("password", cs._get_password())
cs.password_func.assert_called_once_with()
cs.password_func = mock.Mock()
self.assertEqual(cs._get_password(), "password")
self.assertEqual("password", cs._get_password())
self.assertFalse(cs.password_func.called)
def test_auth_url_rstrip_slash(self):
cs = novaclient.client.HTTPClient("user", "password", "project_id",
auth_url="foo/v2/")
self.assertEqual(cs.auth_url, "foo/v2")
self.assertEqual("foo/v2", cs.auth_url)
def test_token_and_bypass_url(self):
cs = novaclient.client.HTTPClient(None, None, None,
auth_token="12345",
bypass_url="compute/v100/")
self.assertIsNone(cs.auth_url)
self.assertEqual(cs.auth_token, "12345")
self.assertEqual(cs.bypass_url, "compute/v100")
self.assertEqual(cs.management_url, "compute/v100")
self.assertEqual("12345", cs.auth_token)
self.assertEqual("compute/v100", cs.bypass_url)
self.assertEqual("compute/v100", cs.management_url)
@mock.patch("novaclient.client.requests.Session")
def test_session(self, mock_session):
@ -343,6 +373,9 @@ class ClientTest(utils.TestCase):
{'X-Foo': 'bar',
'X-Auth-Token': 'totally_bogus'}
})
cs.http_log_req('GET', '/foo', {'headers': {},
'data': '{"auth": {"passwordCredentials": '
'{"password": "zhaoqin"}}}'})
output = self.logger.output.split('\n')
@ -356,3 +389,32 @@ class ClientTest(utils.TestCase):
'"X-Auth-Token: {SHA1}b42162b6ffdbd7c3c37b7c95b7ba9f51dda0236d"'
' -H "X-Foo: bar"',
output)
self.assertIn(
"REQ: curl -i '/foo' -X GET -d "
'\'{"auth": {"passwordCredentials": {"password":'
' "{SHA1}4fc49c6a671ce889078ff6b250f7066cf6d2ada2"}}}\'',
output)
def test_log_resp(self):
self.logger = self.useFixture(
fixtures.FakeLogger(
format="%(message)s",
level=logging.DEBUG,
nuke_handlers=True
)
)
cs = novaclient.client.HTTPClient("user", None, "",
connection_pool=True)
cs.http_log_debug = True
text = ('{"access": {"token": {"id": "zhaoqin"}}}')
resp = utils.TestResponse({'status_code': 200, 'headers': {},
'text': text})
cs.http_log_resp(resp)
output = self.logger.output.split('\n')
self.assertIn('RESP: [200] {}', output)
self.assertIn('RESP BODY: {"access": {"token": {"id":'
' "{SHA1}4fc49c6a671ce889078ff6b250f7066cf6d2ada2"}}}',
output)

View File

@ -40,7 +40,7 @@ class DiscoverTest(utils.TestCase):
def test():
shell = novaclient.shell.OpenStackComputeShell()
for name, module in shell._discover_via_entry_points():
self.assertEqual(name, 'foo')
self.assertEqual('foo', name)
self.assertTrue(inspect.ismodule(module))
test()
@ -68,7 +68,7 @@ class DiscoverTest(utils.TestCase):
def test():
shell = novaclient.shell.OpenStackComputeShell()
extensions = shell._discover_extensions('1.1')
self.assertEqual(len(extensions), 3)
self.assertEqual(3, len(extensions))
names = sorted(['foo', 'bar', 'baz'])
sorted_extensions = sorted(extensions, key=lambda ext: ext.name)
for i in range(len(names)):

View File

@ -44,6 +44,32 @@ unknown_error_response = utils.TestResponse({
})
unknown_error_mock_request = mock.Mock(return_value=unknown_error_response)
retry_after_response = utils.TestResponse({
"status_code": 413,
"text": '',
"headers": {
"retry-after": "5"
},
})
retry_after_mock_request = mock.Mock(return_value=retry_after_response)
retry_after_no_headers_response = utils.TestResponse({
"status_code": 413,
"text": '',
})
retry_after_no_headers_mock_request = mock.Mock(
return_value=retry_after_no_headers_response)
retry_after_non_supporting_response = utils.TestResponse({
"status_code": 403,
"text": '',
"headers": {
"retry-after": "5"
},
})
retry_after_non_supporting_mock_request = mock.Mock(
return_value=retry_after_non_supporting_response)
def get_client():
cl = client.HTTPClient("username", "password",
@ -79,7 +105,7 @@ class ClientTest(utils.TestCase):
headers=headers,
**self.TEST_REQUEST_BASE)
# Automatic JSON parsing
self.assertEqual(body, {"hi": "there"})
self.assertEqual({"hi": "there"}, body)
test_get_call()
@ -136,11 +162,11 @@ class ClientTest(utils.TestCase):
def test_client_logger(self):
cl1 = client.HTTPClient("username", "password", "project_id",
"auth_test", http_log_debug=True)
self.assertEqual(len(cl1._logger.handlers), 1)
self.assertEqual(1, len(cl1._logger.handlers))
cl2 = client.HTTPClient("username", "password", "project_id",
"auth_test", http_log_debug=True)
self.assertEqual(len(cl2._logger.handlers), 1)
self.assertEqual(1, len(cl2._logger.handlers))
@mock.patch.object(requests, 'request', unknown_error_mock_request)
def test_unknown_server_error(self):
@ -154,3 +180,33 @@ class ClientTest(utils.TestCase):
self.assertIn('Unknown Error', six.text_type(exc))
else:
self.fail('Expected exceptions.ClientException')
@mock.patch.object(requests, "request", retry_after_mock_request)
def test_retry_after_request(self):
cl = get_client()
try:
cl.get("/hi")
except exceptions.OverLimit as exc:
self.assertEqual(5, exc.retry_after)
else:
self.fail('Expected exceptions.OverLimit')
@mock.patch.object(requests, "request",
retry_after_no_headers_mock_request)
def test_retry_after_request_no_headers(self):
cl = get_client()
try:
cl.get("/hi")
except exceptions.OverLimit as exc:
self.assertEqual(0, exc.retry_after)
else:
self.fail('Expected exceptions.OverLimit')
@mock.patch.object(requests, "request",
retry_after_non_supporting_mock_request)
def test_retry_after_request_non_supporting_exc(self):
cl = get_client()
self.assertRaises(exceptions.Forbidden, cl.get, "/hi")

View File

@ -11,116 +11,32 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient import fixture
from novaclient import exceptions
from novaclient import service_catalog
from novaclient.tests import utils
# Taken directly from keystone/content/common/samples/auth.json
# Do not edit this structure. Instead, grab the latest from there.
SERVICE_CATALOG = fixture.V2Token()
SERVICE_CATALOG.set_scope()
SERVICE_CATALOG = {
"access": {
"token": {
"id": "ab48a9efdfedb23ty3494",
"expires": "2010-11-01T03:32:15-05:00",
"tenant": {
"id": "345",
"name": "My Project"
}
},
"user": {
"id": "123",
"name": "jqsmith",
"roles": [
{
"id": "234",
"name": "compute:admin",
},
{
"id": "235",
"name": "object-store:admin",
"tenantId": "1",
}
],
"roles_links": [],
},
"serviceCatalog": [
{
"name": "Cloud Servers",
"type": "compute",
"endpoints": [
{
# Tenant 1, no region, v1.0
"tenantId": "1",
"publicURL": "https://compute1.host/v1/1",
"internalURL": "https://compute1.host/v1/1",
"versionId": "1.0",
"versionInfo": "https://compute1.host/v1.0/",
"versionList": "https://compute1.host/"
},
{
# Tenant 2, with region, v1.1
"tenantId": "2",
"publicURL": "https://compute1.host/v1.1/2",
"internalURL": "https://compute1.host/v1.1/2",
"region": "North",
"versionId": "1.1",
"versionInfo": "https://compute1.host/v1.1/",
"versionList": "https://compute1.host/"
},
{
# Tenant 1, with region, v2.0
"tenantId": "1",
"publicURL": "https://compute1.host/v2/1",
"internalURL": "https://compute1.host/v2/1",
"region": "North",
"versionId": "2",
"versionInfo": "https://compute1.host/v2/",
"versionList": "https://compute1.host/"
},
],
"endpoints_links": [],
},
{
"name": "Nova Volumes",
"type": "volume",
"endpoints": [
{
"tenantId": "1",
"publicURL": "https://volume1.host/v1/1",
"internalURL": "https://volume1.host/v1/1",
"region": "South",
"versionId": "1.0",
"versionInfo": "uri",
"versionList": "uri"
},
{
"tenantId": "2",
"publicURL": "https://volume1.host/v1.1/2",
"internalURL": "https://volume1.host/v1.1/2",
"region": "South",
"versionId": "1.1",
"versionInfo": "https://volume1.host/v1.1/",
"versionList": "https://volume1.host/"
},
],
"endpoints_links": [
{
"rel": "next",
"href": "https://identity1.host/v2.0/endpoints"
},
],
},
],
"serviceCatalog_links": [
{
"rel": "next",
"href": "https://identity.host/v2.0/endpoints?session=2hfh8Ar",
},
],
},
}
_s = SERVICE_CATALOG.add_service('compute')
_e = _s.add_endpoint("https://compute1.host/v1/1")
_e["tenantId"] = "1"
_e["versionId"] = "1.0"
_e = _s.add_endpoint("https://compute1.host/v1.1/2", region="North")
_e["tenantId"] = "2"
_e["versionId"] = "1.1"
_e = _s.add_endpoint("https://compute1.host/v2/1", region="North")
_e["tenantId"] = "1"
_e["versionId"] = "2"
_s = SERVICE_CATALOG.add_service('volume')
_e = _s.add_endpoint("https://volume1.host/v1/1", region="South")
_e["tenantId"] = "1"
_e = _s.add_endpoint("https://volume1.host/v1.1/2", region="South")
_e["tenantId"] = "2"
class ServiceCatalogTest(utils.TestCase):
@ -129,10 +45,10 @@ class ServiceCatalogTest(utils.TestCase):
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
service_type='compute')
self.assertEqual(sc.url_for('tenantId', '1', service_type='compute'),
"https://compute1.host/v2/1")
self.assertEqual(sc.url_for('tenantId', '2', service_type='compute'),
"https://compute1.host/v1.1/2")
self.assertEqual("https://compute1.host/v2/1",
sc.url_for('tenantId', '1', service_type='compute'))
self.assertEqual("https://compute1.host/v1.1/2",
sc.url_for('tenantId', '2', service_type='compute'))
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
"region", "South", service_type='compute')
@ -148,10 +64,10 @@ class ServiceCatalogTest(utils.TestCase):
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
service_type='volume')
self.assertEqual(sc.url_for('tenantId', '1', service_type='volume'),
"https://volume1.host/v1/1")
self.assertEqual(sc.url_for('tenantId', '2', service_type='volume'),
"https://volume1.host/v1.1/2")
self.assertEqual("https://volume1.host/v1/1",
sc.url_for('tenantId', '1', service_type='volume'))
self.assertEqual("https://volume1.host/v1.1/2",
sc.url_for('tenantId', '2', service_type='volume'))
self.assertRaises(exceptions.EndpointNotFound, sc.url_for,
"region", "North", service_type='volume')

View File

@ -11,15 +11,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import prettytable
import distutils.version as dist_version
import re
import six
import sys
from distutils.version import StrictVersion
import fixtures
import mock
import prettytable
import six
from testtools import matchers
import novaclient.client
@ -201,7 +200,8 @@ class ShellTest(utils.TestCase):
# default output of empty tables differs depending between prettytable
# versions
if (hasattr(prettytable, '__version__') and
StrictVersion(prettytable.__version__) < StrictVersion('0.7.2')):
dist_version.StrictVersion(prettytable.__version__) <
dist_version.StrictVersion('0.7.2')):
ex = '\n'
else:
ex = (
@ -232,7 +232,8 @@ class ShellTest(utils.TestCase):
if version is None:
cmd = 'list'
else:
cmd = '--os-compute-api-version %s list' % version
cmd = ('--service_type %s --os-compute-api-version %s list' %
(service_type, version))
self.make_env()
self.shell(cmd)
_, client_kwargs = mock_client.call_args_list[0]

View File

@ -140,26 +140,26 @@ class PrintResultTestCase(test_utils.TestCase):
def test_print_dict(self):
dict = {'key': 'value'}
utils.print_dict(dict)
self.assertEqual(sys.stdout.getvalue(),
'+----------+-------+\n'
self.assertEqual('+----------+-------+\n'
'| Property | Value |\n'
'+----------+-------+\n'
'| key | value |\n'
'+----------+-------+\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(sys.stdout.getvalue(),
'+----------+--------------+\n'
self.assertEqual('+----------+--------------+\n'
'| Property | Value |\n'
'+----------+--------------+\n'
'| key1 | not wrapped |\n'
'| key2 | this will be |\n'
'| | wrapped |\n'
'+----------+--------------+\n')
'+----------+--------------+\n',
sys.stdout.getvalue())
@mock.patch('sys.stdout', six.StringIO())
def test_print_list_sort_by_str(self):
@ -169,14 +169,14 @@ class PrintResultTestCase(test_utils.TestCase):
utils.print_list(objs, ["Name", "Value"], sortby_index=0)
self.assertEqual(sys.stdout.getvalue(),
'+------+-------+\n'
self.assertEqual('+------+-------+\n'
'| Name | Value |\n'
'+------+-------+\n'
'| k1 | 1 |\n'
'| k2 | 3 |\n'
'| k3 | 2 |\n'
'+------+-------+\n')
'+------+-------+\n',
sys.stdout.getvalue())
@mock.patch('sys.stdout', six.StringIO())
def test_print_list_sort_by_integer(self):
@ -186,14 +186,14 @@ class PrintResultTestCase(test_utils.TestCase):
utils.print_list(objs, ["Name", "Value"], sortby_index=1)
self.assertEqual(sys.stdout.getvalue(),
'+------+-------+\n'
self.assertEqual('+------+-------+\n'
'| Name | Value |\n'
'+------+-------+\n'
'| k1 | 1 |\n'
'| k3 | 2 |\n'
'| k2 | 3 |\n'
'+------+-------+\n')
'+------+-------+\n',
sys.stdout.getvalue())
# without sorting
@mock.patch('sys.stdout', six.StringIO())
@ -204,47 +204,47 @@ class PrintResultTestCase(test_utils.TestCase):
utils.print_list(objs, ["Name", "Value"], sortby_index=None)
self.assertEqual(sys.stdout.getvalue(),
'+------+-------+\n'
self.assertEqual('+------+-------+\n'
'| Name | Value |\n'
'+------+-------+\n'
'| k1 | 1 |\n'
'| k3 | 3 |\n'
'| k2 | 2 |\n'
'+------+-------+\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(sys.stdout.getvalue(),
'+----------+----------------+\n'
self.assertEqual('+----------+----------------+\n'
'| Property | Value |\n'
'+----------+----------------+\n'
'| k | {"foo": "bar"} |\n'
'+----------+----------------+\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(sys.stdout.getvalue(),
'+----------+------------------+\n'
self.assertEqual('+----------+------------------+\n'
'| Property | Value |\n'
'+----------+------------------+\n'
'| k | [{"foo": "bar"}] |\n'
'+----------+------------------+\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(sys.stdout.getvalue(),
'+----------+----------------+\n'
self.assertEqual('+----------+----------------+\n'
'| Property | Value |\n'
'+----------+----------------+\n'
'| k | ["foo", "bar"] |\n'
'+----------+----------------+\n')
'+----------+----------------+\n',
sys.stdout.getvalue())
class FlattenTestCase(test_utils.TestCase):
@ -270,22 +270,22 @@ class FlattenTestCase(test_utils.TestCase):
def test_pretty_choice_list(self):
l = []
r = utils.pretty_choice_list(l)
self.assertEqual(r, "")
self.assertEqual("", r)
l = ["v1", "v2", "v3"]
r = utils.pretty_choice_list(l)
self.assertEqual(r, "'v1', 'v2', 'v3'")
self.assertEqual("'v1', 'v2', 'v3'", r)
def test_pretty_choice_dict(self):
d = {}
r = utils.pretty_choice_dict(d)
self.assertEqual(r, "")
self.assertEqual("", r)
d = {"k1": "v1",
"k2": "v2",
"k3": "v3"}
r = utils.pretty_choice_dict(d)
self.assertEqual(r, "'k1=v1', 'k2=v2', 'k3=v3'")
self.assertEqual("'k1=v1', 'k2=v2', 'k3=v3'", r)
class ValidationsTestCase(test_utils.TestCase):

View File

@ -67,6 +67,25 @@ class V1(base.Fixture):
json={'image': image_1},
headers=headers)
self.requests.register_uri('GET', self.url(2),
json={'image': image_2},
headers=headers)
self.requests.register_uri('GET', self.url(456),
json={'image': image_2},
headers=headers)
def post_images(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['image']
fakes.assert_has_keys(body['image'], required=['serverId', 'name'])
return images_1
self.requests.register_uri('POST', self.url(),
json=post_images,
headers=headers,
status_code=202)
def post_images_1_metadata(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['metadata']
@ -77,9 +96,17 @@ class V1(base.Fixture):
json=post_images_1_metadata,
headers=headers)
for u in (1, '1/metadata/test_key'):
for u in (1, 2, '1/metadata/test_key'):
self.requests.register_uri('DELETE', self.url(u), status_code=204)
image_headers = {'x-image-meta-id': '1',
'x-image-meta-name': 'CentOS 5.2',
'x-image-meta-updated': '2010-10-10T12:00:00Z',
'x-image-meta-created': '2010-10-10T12:00:00Z',
'x-image-meta-status': 'ACTIVE',
'x-image-meta-property-test-key': 'test_value'}
self.requests.register_uri('HEAD', self.url(1), headers=image_headers)
class V3(V1):

View File

@ -14,7 +14,6 @@ from oslo.serialization import jsonutils
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):
@ -383,24 +382,43 @@ class V1(Base):
context.status_code = 202
assert len(body.keys()) == 1
action = list(body)[0]
if v2_fakes.FakeHTTPClient.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
if action == 'reboot':
assert list(body[action]) == ['type']
assert body[action]['type'] in ['HARD', 'SOFT']
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 == 'resize':
keys = body[action].keys()
assert 'flavorRef' in keys
elif action == 'confirmResize':
assert body[action] is None
# This one method returns a different response code
context.status_code = 204
return None
elif action == 'revertResize':
assert body[action] is None
elif action == 'migrate':
assert body[action] is None
elif action == 'os-stop':
assert body[action] is None
elif action == 'os-start':
assert body[action] is None
elif action == 'forceDelete':
assert body[action] is None
elif action == 'restore':
assert body[action] is None
elif action == 'pause':
assert body[action] is None
elif action == 'unpause':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'rescue':
if body[action]:
keys = set(body[action].keys())
@ -408,15 +426,65 @@ class V1(Base):
else:
assert body[action] is None
_body = {'adminPass': 'RescuePassword'}
elif action == 'unrescue':
assert body[action] is None
elif action == 'resume':
assert body[action] is None
elif action == 'suspend':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'shelve':
assert body[action] is None
elif action == 'shelveOffload':
assert body[action] is None
elif action == 'unshelve':
assert body[action] is None
elif action == 'addFixedIp':
assert list(body[action]) == ['networkId']
elif action == 'removeFixedIp':
assert list(body[action]) == ['address']
elif action == 'addFloatingIp':
assert (list(body[action]) == ['address'] or
sorted(list(body[action])) == ['address',
'fixed_address'])
elif action == 'removeFloatingIp':
assert list(body[action]) == ['address']
elif action == 'createImage':
assert set(body[action].keys()) == set(['name', 'metadata'])
context.headers['location'] = "http://blah/images/456"
elif action == 'changePassword':
assert list(body[action]) == ['adminPass']
elif action == 'os-getConsoleOutput':
assert list(body[action]) == ['length']
context.status_code = 202
return {'output': 'foo'}
elif action == 'os-getVNCConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSPICEConsole':
assert list(body[action]) == ['type']
elif action == 'os-getRDPConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSerialConsole':
assert list(body[action]) == ['type']
elif action == 'os-migrateLive':
assert set(body[action].keys()) == set(['host',
'block_migration',
'disk_over_commit'])
elif action == 'os-resetState':
assert list(body[action]) == ['state']
elif action == 'resetNetwork':
assert body[action] is None
elif action == 'addSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'removeSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'createBackup':
assert set(body[action]) == set(['name',
'backup_type',
'rotation'])
elif action == 'evacuate':
keys = list(body[action])
if 'adminPass' in keys:

View File

@ -36,7 +36,7 @@ class BaseTest(utils.TestCase):
def test_resource_lazy_getattr(self):
f = flavors.Flavor(cs.flavors, {'id': 1})
self.assertEqual('256 mb server', f.name)
self.assertEqual('256 MB Server', f.name)
cs.assert_called('GET', '/flavors/1')
# Missing stuff still fails after a second get

View File

@ -16,7 +16,6 @@
import json
import logging
import socket
import fixtures
import mock
@ -28,24 +27,6 @@ from novaclient.tests.unit import utils
import novaclient.v2.client
class TCPKeepAliveAdapterTest(utils.TestCase):
@mock.patch.object(requests.adapters.HTTPAdapter, 'init_poolmanager')
def test_init_poolmanager(self, mock_init_poolmgr):
adapter = novaclient.client.TCPKeepAliveAdapter()
kwargs = {}
adapter.init_poolmanager(**kwargs)
if requests.__version__ >= '2.4.1':
kwargs.setdefault('socket_options', [
(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
])
# NOTE(melwitt): This is called twice because
# HTTPAdapter.__init__ calls it first.
self.assertEqual(2, mock_init_poolmgr.call_count)
mock_init_poolmgr.assert_called_with(**kwargs)
class ClientConnectionPoolTest(utils.TestCase):
@mock.patch("novaclient.client.TCPKeepAliveAdapter")
@ -86,7 +67,6 @@ class ClientTest(utils.TestCase):
auth_url="http://www.blah.com")
instance.auth_token = 'foobar'
instance.management_url = 'http://example.com'
instance.get_service_url = mock.Mock(return_value='http://example.com')
instance.version = 'v2.0'
mock_request = mock.Mock()
mock_request.side_effect = novaclient.exceptions.Unauthorized(401)
@ -137,8 +117,6 @@ class ClientTest(utils.TestCase):
auth_url="http://www.blah.com")
instance.auth_token = 'foobar'
instance.management_url = management_url % projectid
mock_get_service_url = mock.Mock(return_value=instance.management_url)
instance.get_service_url = mock_get_service_url
instance.version = 'v2.0'
# If passing None as the part of url, a client accesses the url which
@ -183,26 +161,26 @@ class ClientTest(utils.TestCase):
def test_client_with_os_cache_enabled(self):
cs = novaclient.v2.client.Client("user", "password", "project_id",
auth_url="foo/v2", os_cache=True)
self.assertTrue(cs.os_cache)
self.assertTrue(cs.client.os_cache)
self.assertEqual(True, cs.os_cache)
self.assertEqual(True, cs.client.os_cache)
def test_client_with_os_cache_disabled(self):
cs = novaclient.v2.client.Client("user", "password", "project_id",
auth_url="foo/v2", os_cache=False)
self.assertFalse(cs.os_cache)
self.assertFalse(cs.client.os_cache)
self.assertEqual(False, cs.os_cache)
self.assertEqual(False, cs.client.os_cache)
def test_client_with_no_cache_enabled(self):
cs = novaclient.v2.client.Client("user", "password", "project_id",
auth_url="foo/v2", no_cache=True)
self.assertFalse(cs.os_cache)
self.assertFalse(cs.client.os_cache)
self.assertEqual(False, cs.os_cache)
self.assertEqual(False, cs.client.os_cache)
def test_client_with_no_cache_disabled(self):
cs = novaclient.v2.client.Client("user", "password", "project_id",
auth_url="foo/v2", no_cache=False)
self.assertTrue(cs.os_cache)
self.assertTrue(cs.client.os_cache)
self.assertEqual(True, cs.os_cache)
self.assertEqual(True, cs.client.os_cache)
def test_client_set_management_url_v1_1(self):
cs = novaclient.v2.client.Client("user", "password", "project_id",

View File

@ -82,7 +82,6 @@ def get_authed_client():
cl = get_client()
cl.management_url = "http://example.com"
cl.auth_token = "token"
cl.get_service_url = mock.Mock(return_value="http://example.com")
return cl

View File

@ -311,7 +311,7 @@ class ValidationsTestCase(test_utils.TestCase):
utils.validate_flavor_metadata_keys([key])
self.fail("Invalid key passed validation: %s" % key)
except exceptions.CommandError as ce:
self.assertIn(key, str(ce))
self.assertTrue(key in str(ce))
class ResourceManagerExtraKwargsHookTestCase(test_utils.TestCase):

View File

@ -258,7 +258,7 @@ class FakeHTTPClient(base_client.HTTPClient):
},
"flavor": {
"id": 1,
"name": "256 mb server",
"name": "256 MB Server",
},
"hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
"status": "BUILD",
@ -299,7 +299,7 @@ class FakeHTTPClient(base_client.HTTPClient):
},
"flavor": {
"id": 1,
"name": "256 mb server",
"name": "256 MB Server",
},
"hostId": "9e107d9d372bb6826bd81d3542a419d6",
"status": "ACTIVE",
@ -340,7 +340,7 @@ class FakeHTTPClient(base_client.HTTPClient):
"image": "",
"flavor": {
"id": 1,
"name": "256 mb server",
"name": "256 MB Server",
},
"hostId": "9e107d9d372bb6826bd81d3542a419d6",
"status": "ACTIVE",
@ -542,73 +542,48 @@ class FakeHTTPClient(base_client.HTTPClient):
# 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-migrateLive':
assert set(body[action].keys()) == set(['host', 'block_migration',
'disk_over_commit'])
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 == 'createBackup':
assert set(body[action]) == set(['name', 'backup_type',
'rotation'])
else:
return False
return True
def post_servers_1234_action(self, body, **kw):
_headers = None
_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
if action == 'reboot':
assert list(body[action]) == ['type']
assert body[action]['type'] in ['HARD', 'SOFT']
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 == 'resize':
keys = body[action].keys()
assert 'flavorRef' in keys
elif action == 'confirmResize':
assert body[action] is None
# This one method returns a different response code
return (204, {}, None)
elif action == 'revertResize':
assert body[action] is None
elif action == 'migrate':
assert body[action] is None
elif action == 'os-stop':
assert body[action] is None
elif action == 'os-start':
assert body[action] is None
elif action == 'forceDelete':
assert body[action] is None
elif action == 'restore':
assert body[action] is None
elif action == 'pause':
assert body[action] is None
elif action == 'unpause':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'rescue':
if body[action]:
keys = set(body[action].keys())
@ -616,12 +591,62 @@ class FakeHTTPClient(base_client.HTTPClient):
else:
assert body[action] is None
_body = {'adminPass': 'RescuePassword'}
elif action == 'unrescue':
assert body[action] is None
elif action == 'resume':
assert body[action] is None
elif action == 'suspend':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'shelve':
assert body[action] is None
elif action == 'shelveOffload':
assert body[action] is None
elif action == 'unshelve':
assert body[action] is None
elif action == 'addFixedIp':
assert list(body[action]) == ['networkId']
elif action == 'removeFixedIp':
assert list(body[action]) == ['address']
elif action == 'addFloatingIp':
assert (list(body[action]) == ['address'] or
sorted(list(body[action])) == ['address',
'fixed_address'])
elif action == 'removeFloatingIp':
assert list(body[action]) == ['address']
elif action == 'createImage':
assert set(body[action].keys()) == set(['name', 'metadata'])
_headers = dict(location="http://blah/images/456")
elif action == 'changePassword':
assert list(body[action]) == ['adminPass']
elif action == 'os-getConsoleOutput':
assert list(body[action]) == ['length']
return (202, {}, {'output': 'foo'})
elif action == 'os-getVNCConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSPICEConsole':
assert list(body[action]) == ['type']
elif action == 'os-getRDPConsole':
assert list(body[action]) == ['type']
elif action == 'os-migrateLive':
assert set(body[action].keys()) == set(['host',
'block_migration',
'disk_over_commit'])
elif action == 'os-resetState':
assert list(body[action]) == ['state']
elif action == 'resetNetwork':
assert body[action] is None
elif action == 'addSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'removeSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'createBackup':
assert set(body[action]) == set(['name',
'backup_type',
'rotation'])
elif action == 'evacuate':
keys = list(body[action])
if 'adminPass' in keys:
@ -672,19 +697,19 @@ class FakeHTTPClient(base_client.HTTPClient):
def get_flavors_detail(self, **kw):
flavors = {'flavors': [
{'id': 1, 'name': '256 mb server', 'ram': 256, 'disk': 10,
{'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,
{'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,
{'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,
{'id': 'aa1', 'name': '128 MB Server', 'ram': 128, 'disk': 0,
'OS-FLV-EXT-DATA:ephemeral': 0,
'os-flavor-access:is_public': True,
'links': {}}
@ -736,16 +761,16 @@ class FakeHTTPClient(base_client.HTTPClient):
{},
{'flavor': {
'id': 3,
'name': '256 mb server',
'name': '256 MB Server',
'ram': 256,
'disk': 10,
}},
)
def get_flavors_512_mb_server(self, **kw):
def get_flavors_512_MB_Server(self, **kw):
raise exceptions.NotFound('404')
def get_flavors_128_mb_server(self, **kw):
def get_flavors_128_MB_Server(self, **kw):
raise exceptions.NotFound('404')
def get_flavors_aa1(self, **kw):
@ -2196,7 +2221,6 @@ class FakeSessionMockClient(base_client.SessionClient, FakeHTTPClient):
self.callstack = []
self.auth = mock.Mock()
self.session = mock.Mock()
self.service_type = 'service_type'
self.auth.get_auth_ref.return_value.project_id = 'tenant_id'

View File

@ -363,7 +363,6 @@ class AuthenticationTests(utils.TestCase):
"project_id", utils.AUTH_URL)
http_client = cs.client
http_client.management_url = ''
http_client.get_service_url = mock.Mock(return_value='')
mock_request = mock.Mock(return_value=(None, None))
@mock.patch.object(http_client, 'request', mock_request)

View File

@ -70,7 +70,7 @@ class FlavorsTest(utils.TestCase):
self.assertEqual(256, f.ram)
self.assertEqual(10, f.disk)
self.assertEqual(10, f.ephemeral)
self.assertTrue(f.is_public)
self.assertEqual(True, f.is_public)
def test_get_flavor_details_alphanum_id(self):
f = self.cs.flavors.get('aa1')
@ -79,7 +79,7 @@ class FlavorsTest(utils.TestCase):
self.assertEqual(128, f.ram)
self.assertEqual(0, f.disk)
self.assertEqual(0, f.ephemeral)
self.assertTrue(f.is_public)
self.assertEqual(True, f.is_public)
def test_get_flavor_details_diablo(self):
f = self.cs.flavors.get(3)
@ -93,10 +93,10 @@ class FlavorsTest(utils.TestCase):
def test_find(self):
f = self.cs.flavors.find(ram=256)
self.cs.assert_called('GET', '/flavors/detail')
self.assertEqual('256 mb server', f.name)
self.assertEqual('256 MB Server', f.name)
f = self.cs.flavors.find(disk=0)
self.assertEqual('128 mb server', f.name)
self.assertEqual('128 MB Server', f.name)
self.assertRaises(exceptions.NotFound, self.cs.flavors.find,
disk=12345)

View File

@ -34,7 +34,7 @@ class FpingTest(utils.FixturedTestCase):
for f in fl:
self.assertIsInstance(f, fping.Fping)
self.assertEqual("fake-project", f.project_id)
self.assertTrue(f.alive)
self.assertEqual(True, f.alive)
def test_list_fpings_all_tenants(self):
fl = self.cs.fping.list(all_tenants=True)
@ -59,4 +59,4 @@ class FpingTest(utils.FixturedTestCase):
self.assert_called('GET', '/os-fping/1')
self.assertIsInstance(f, fping.Fping)
self.assertEqual("fake-project", f.project_id)
self.assertTrue(f.alive)
self.assertEqual(True, f.alive)

View File

@ -26,7 +26,6 @@ class ImagesTest(utils.FixturedTestCase):
il = self.cs.images.list()
self.assert_called('GET', '/images/detail')
[self.assertIsInstance(i, images.Image) for i in il]
self.assertEqual(2, len(il))
def test_list_images_undetailed(self):
il = self.cs.images.list(detailed=False)

View File

@ -47,7 +47,7 @@ class LimitsTest(utils.FixturedTestCase):
self.assertEqual(len(abs_limits), len(expected))
for limit in abs_limits:
self.assertIn(limit, expected)
self.assertTrue(limit in expected)
def test_absolute_limits_reserved(self):
obj = self.cs.limits.get(reserved=True)
@ -65,7 +65,7 @@ class LimitsTest(utils.FixturedTestCase):
self.assertEqual(len(abs_limits), len(expected))
for limit in abs_limits:
self.assertIn(limit, expected)
self.assertTrue(limit in expected)
def test_rate_limits(self):
obj = self.cs.limits.get()
@ -85,4 +85,4 @@ class LimitsTest(utils.FixturedTestCase):
self.assertEqual(len(rate_limits), len(expected))
for limit in rate_limits:
self.assertIn(limit, expected)
self.assertTrue(limit in expected)

View File

@ -212,7 +212,7 @@ class ServersTest(utils.FixturedTestCase):
# verify disk config param was used in the request:
body = jsonutils.loads(self.requests.last_request.body)
server = body['server']
self.assertIn('OS-DCF:diskConfig', server)
self.assertTrue('OS-DCF:diskConfig' in server)
self.assertEqual(disk_config, server['OS-DCF:diskConfig'])
def test_create_server_disk_config_auto(self):
@ -302,7 +302,7 @@ class ServersTest(utils.FixturedTestCase):
body = jsonutils.loads(self.requests.last_request.body)
d = body[operation]
self.assertIn('OS-DCF:diskConfig', d)
self.assertTrue('OS-DCF:diskConfig' in d)
self.assertEqual(disk_config, d['OS-DCF:diskConfig'])
def test_rebuild_server_disk_config_auto(self):
@ -318,7 +318,7 @@ class ServersTest(utils.FixturedTestCase):
body = jsonutils.loads(self.requests.last_request.body)
d = body['rebuild']
self.assertIn('preserve_ephemeral', d)
self.assertTrue(d['preserve_ephemeral'])
self.assertEqual(True, d['preserve_ephemeral'])
def test_rebuild_server_name_meta_files(self):
files = {'/etc/passwd': 'some data'}
@ -560,11 +560,11 @@ class ServersTest(utils.FixturedTestCase):
def test_get_server_diagnostics(self):
s = self.cs.servers.get(1234)
diagnostics = s.diagnostics()
self.assertIsNotNone(diagnostics)
self.assertTrue(diagnostics is not None)
self.assert_called('GET', '/servers/1234/diagnostics')
diagnostics_from_manager = self.cs.servers.diagnostics(1234)
self.assertIsNotNone(diagnostics_from_manager)
self.assertTrue(diagnostics_from_manager is not None)
self.assert_called('GET', '/servers/1234/diagnostics')
self.assertEqual(diagnostics[1], diagnostics_from_manager[1])

View File

@ -661,10 +661,10 @@ class ShellTest(utils.TestCase):
def test_boot_named_flavor(self):
self.run_command(["boot", "--image", "1",
"--flavor", "512 mb server",
"--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/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(
@ -701,15 +701,15 @@ class ShellTest(utils.TestCase):
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.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.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)
@ -746,7 +746,7 @@ class ShellTest(utils.TestCase):
{'addTenantAccess': {'tenant': 'proj2'}})
def test_flavor_access_add_by_name(self):
self.run_command(['flavor-access-add', '512 mb server', 'proj2'])
self.run_command(['flavor-access-add', '512 MB Server', 'proj2'])
self.assert_called('POST', '/flavors/2/action',
{'addTenantAccess': {'tenant': 'proj2'}})
@ -756,7 +756,7 @@ class ShellTest(utils.TestCase):
{'removeTenantAccess': {'tenant': 'proj2'}})
def test_flavor_access_remove_by_name(self):
self.run_command(['flavor-access-remove', '512 mb server', 'proj2'])
self.run_command(['flavor-access-remove', '512 MB Server', 'proj2'])
self.assert_called('POST', '/flavors/2/action',
{'removeTenantAccess': {'tenant': 'proj2'}})
@ -2145,7 +2145,7 @@ class ShellTest(utils.TestCase):
def test_volume_show(self):
self.run_command('volume-show Work')
self.assert_called('GET', '/volumes?display_name=Work', pos=-2)
self.assert_called('GET', '/volumes?name=Work', pos=-2)
self.assert_called(
'GET',
'/volumes/15e59938-07d5-11e1-90e3-e3dffe0c5983',

View File

@ -14,8 +14,8 @@
import os
import fixtures
import httpretty
import requests
from requests_mock.contrib import fixture as requests_mock_fixture
import six
import testscenarios
import testtools
@ -52,24 +52,26 @@ class FixturedTestCase(testscenarios.TestWithScenarios, TestCase):
def setUp(self):
super(FixturedTestCase, self).setUp()
httpretty.reset()
self.requests = self.useFixture(requests_mock_fixture.Fixture())
self.data_fixture = None
self.client_fixture = None
self.cs = None
if self.client_fixture_class:
self.client_fixture = self.useFixture(self.client_fixture_class())
fix = self.client_fixture_class(self.requests)
self.client_fixture = self.useFixture(fix)
self.cs = self.client_fixture.client
if self.data_fixture_class:
self.data_fixture = self.useFixture(self.data_fixture_class())
fix = self.data_fixture_class(self.requests)
self.data_fixture = self.useFixture(fix)
def assert_called(self, method, path, body=None):
self.assertEqual(httpretty.last_request().method, method)
self.assertEqual(httpretty.last_request().path, path)
self.assertEqual(self.requests.last_request.method, method)
self.assertEqual(self.requests.last_request.path_url, path)
if body:
req_data = httpretty.last_request().body
req_data = self.requests.last_request.body
if isinstance(req_data, six.binary_type):
req_data = req_data.decode('utf-8')
if not isinstance(body, six.string_types):

View File

@ -15,10 +15,9 @@
from novaclient import extension
from novaclient.v1_1.contrib import baremetal
from novaclient.tests import utils
from novaclient.tests.v1_1.contrib import fakes
from novaclient.v1_1.contrib import baremetal
extensions = [

View File

@ -12,11 +12,11 @@
# 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 extension
from novaclient.v1_1.contrib import instance_action
from novaclient import extension
from novaclient.tests import utils
from novaclient.tests.v1_1.contrib import fakes
from novaclient.v1_1.contrib import instance_action
extensions = [

View File

@ -12,10 +12,9 @@
# under the License.
from novaclient import extension
from novaclient.v1_1.contrib import list_extensions
from novaclient.tests import utils
from novaclient.tests.v1_1 import fakes
from novaclient.v1_1.contrib import list_extensions
extensions = [

View File

@ -14,10 +14,9 @@
# under the License.
from novaclient import extension
from novaclient.v1_1.contrib import tenant_networks
from novaclient.tests import utils
from novaclient.tests.v1_1.contrib import fakes
from novaclient.v1_1.contrib import tenant_networks
extensions = [

View File

@ -14,14 +14,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime
import datetime
from oslo.utils import strutils
import six
from six.moves.urllib import parse
from novaclient import client as base_client
from novaclient import exceptions
from novaclient.openstack.common import strutils
from novaclient.tests import fakes
from novaclient.tests import utils
from novaclient.v1_1 import client
@ -636,7 +636,9 @@ class FakeHTTPClient(base_client.HTTPClient):
keys = list(body[action])
if 'adminPass' in keys:
keys.remove('adminPass')
assert set(keys) == set(['host', 'onSharedStorage'])
if 'host' in keys:
keys.remove('host')
assert set(keys) == set(['onSharedStorage'])
else:
raise AssertionError("Unexpected server action: %s" % action)
return (resp, _headers, _body)
@ -710,14 +712,14 @@ class FakeHTTPClient(base_client.HTTPClient):
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']
]
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']
]
v for v in flavors['flavors']
if not v['os-flavor-access:is_public']
]
return (200, {}, flavors)
@ -1311,7 +1313,7 @@ class FakeHTTPClient(base_client.HTTPClient):
def get_os_security_group_rules(self, **kw):
return (200, {}, {"security_group_rules": [
{'id': 1, 'parent_group_id': 1, 'group_id': 2,
'ip_protocol': 'TCP', 'from_port': '22', 'to_port': 22,
'ip_protocol': 'TCP', 'from_port': 22, 'to_port': 22,
'cidr': '10.0.0.0/8'}
]})
@ -1334,6 +1336,34 @@ class FakeHTTPClient(base_client.HTTPClient):
self.get_os_security_group_rules()[2]['security_group_rules'][0]}
return (202, {}, r)
#
# Security Group Default Rules
#
def get_os_security_group_default_rules(self, **kw):
return (200, {}, {"security_group_default_rules": [
{'id': 1, 'ip_protocol': 'TCP', 'from_port': 22,
'to_port': 22, 'cidr': '10.0.0.0/8'}
]})
def delete_os_security_group_default_rules_1(self, **kw):
return (202, {}, None)
def delete_os_security_group_default_rules_11(self, **kw):
return (202, {}, None)
def delete_os_security_group_default_rules_12(self, **kw):
return (202, {}, None)
def post_os_security_group_default_rules(self, body, **kw):
assert list(body) == ['security_group_default_rule']
fakes.assert_has_keys(body['security_group_default_rule'],
optional=['ip_protocol', 'from_port',
'to_port', 'cidr'])
rules = self.get_os_security_group_default_rules()
r = {'security_group_default_rule':
rules[2]['security_group_default_rules'][0]}
return (202, {}, r)
#
# Tenant Usage
#
@ -1344,7 +1374,7 @@ class FakeHTTPClient(base_client.HTTPClient):
six.u('total_vcpus_usage'): 49.71047423333333,
six.u('total_hours'): 49.71047423333333,
six.u('tenant_id'):
six.u('7b0a1d73f8fb41718f3343c207597869'),
six.u('7b0a1d73f8fb41718f3343c207597869'),
six.u('stop'): six.u('2012-01-22 19:48:41.750722'),
six.u('server_usages'): [{
six.u('hours'): 49.71047423333333,
@ -1353,7 +1383,7 @@ class FakeHTTPClient(base_client.HTTPClient):
six.u('ended_at'): None,
six.u('name'): six.u('f15image1'),
six.u('tenant_id'):
six.u('7b0a1d73f8fb41718f3343c207597869'),
six.u('7b0a1d73f8fb41718f3343c207597869'),
six.u('vcpus'): 1,
six.u('memory_mb'): 512,
six.u('state'): six.u('active'),
@ -1370,7 +1400,7 @@ class FakeHTTPClient(base_client.HTTPClient):
six.u('total_vcpus_usage'): 49.71047423333333,
six.u('total_hours'): 49.71047423333333,
six.u('tenant_id'):
six.u('7b0a1d73f8fb41718f3343c207597869'),
six.u('7b0a1d73f8fb41718f3343c207597869'),
six.u('stop'): six.u('2012-01-22 19:48:41.750722'),
six.u('server_usages'): [{
six.u('hours'): 49.71047423333333,
@ -1456,15 +1486,25 @@ class FakeHTTPClient(base_client.HTTPClient):
{'id': '2',
'name': 'test2',
'availability_zone': 'nova1'},
{'id': '3',
'name': 'test3',
'metadata': {'test': "dup", "none_key": "Nine"}},
]})
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()
@ -1474,12 +1514,18 @@ class FakeHTTPClient(base_client.HTTPClient):
def put_os_aggregates_2(self, body, **kw):
return self._return_aggregate()
def put_os_aggregates_3(self, body, **kw):
return self._return_aggregate_3()
def post_os_aggregates_1_action(self, body, **kw):
return self._return_aggregate()
def post_os_aggregates_2_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)
@ -1495,13 +1541,17 @@ class FakeHTTPClient(base_client.HTTPClient):
'zone': 'nova',
'status': 'enabled',
'state': 'up',
'updated_at': datetime(2012, 10, 29, 13, 42, 2)},
'updated_at': datetime.datetime(
2012, 10, 29, 13, 42, 2
)},
{'binary': binary,
'host': host,
'zone': 'nova',
'status': 'disabled',
'state': 'down',
'updated_at': datetime(2012, 9, 18, 8, 3, 38)},
'updated_at': datetime.datetime(
2012, 9, 18, 8, 3, 38
)},
]})
def put_os_services_enable(self, body, **kw):
@ -1708,13 +1758,17 @@ class FakeHTTPClient(base_client.HTTPClient):
def get_os_networks(self, **kw):
return (200, {}, {'networks': [{"label": "1", "cidr": "10.0.0.0/24",
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
'id': '1'}]})
'id': '1', 'vlan': '1234'}]})
def delete_os_networks_1(self, **kw):
return (202, {}, None)
def post_os_networks(self, **kw):
return (202, {}, {'network': kw})
def get_os_networks_1(self, **kw):
return (200, {}, {'network': {"label": "1", "cidr": "10.0.0.0/24"}})
return (200, {}, {'network': {"label": "1", "cidr": "10.0.0.0/24",
"id": "1"}})
def delete_os_networks_networkdelete(self, **kw):
return (202, {}, None)
@ -1781,7 +1835,9 @@ class FakeHTTPClient(base_client.HTTPClient):
"nova-compute": {"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 25, 0)}}}},
datetime.datetime(
2012, 12, 26, 14, 45, 25, 0
)}}}},
{"zoneName": "internal",
"zoneState": {"available": True},
"hosts": {
@ -1790,13 +1846,17 @@ class FakeHTTPClient(base_client.HTTPClient):
"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 25, 0)}},
datetime.datetime(
2012, 12, 26, 14, 45, 25, 0
)}},
"fake_host-2": {
"nova-network": {
"active": True,
"available": False,
"updated_at":
datetime(2012, 12, 26, 14, 45, 24, 0)}}}},
datetime.datetime(
2012, 12, 26, 14, 45, 24, 0
)}}}},
{"zoneName": "zone-2",
"zoneState": {"available": False},
"hosts": None}]})

View File

@ -13,9 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import agents as data
from novaclient.tests.fixture_data import client
from novaclient.tests import utils
@ -53,9 +50,10 @@ class AgentsTest(utils.FixturedTestCase):
]
}
httpretty.register_uri(httpretty.GET, self.data_fixture.url(),
body=jsonutils.dumps(get_os_agents),
content_type='application/json')
headers = {'Content-Type': 'application/json'}
self.requests.register_uri('GET', self.data_fixture.url(),
json=get_os_agents,
headers=headers)
def test_list_agents(self):
self.stub_hypervisors()
@ -63,7 +61,7 @@ class AgentsTest(utils.FixturedTestCase):
self.assert_called('GET', '/os-agents')
for a in ags:
self.assertIsInstance(a, agents.Agent)
self.assertEqual(a.hypervisor, 'kvm')
self.assertEqual('kvm', a.hypervisor)
def test_list_agents_with_hypervisor(self):
self.stub_hypervisors('xen')
@ -71,7 +69,7 @@ class AgentsTest(utils.FixturedTestCase):
self.assert_called('GET', '/os-agents?hypervisor=xen')
for a in ags:
self.assertIsInstance(a, agents.Agent)
self.assertEqual(a.hypervisor, 'xen')
self.assertEqual('xen', a.hypervisor)
def test_agents_create(self):
ag = self.cs.agents.create('win', 'x86', '7.0',

View File

@ -14,6 +14,7 @@
import copy
import json
from keystoneclient import fixture
import mock
import requests
@ -23,33 +24,21 @@ from novaclient.v1_1 import client
class AuthenticateAgainstKeystoneTests(utils.TestCase):
def get_token(self, **kwargs):
resp = fixture.V2Token(**kwargs)
resp.set_scope()
s = resp.add_service('compute')
s.add_endpoint('http://localhost:8774/v1.1', region='RegionOne')
return resp
def test_authenticate_success(self):
cs = client.Client("username", "password", "project_id",
utils.AUTH_URL_V2, service_type='compute')
resp = {
"access": {
"token": {
"expires": "12345",
"id": "FAKE_ID",
"tenant": {
"id": "FAKE_TENANT_ID",
}
},
"serviceCatalog": [
{
"type": "compute",
"endpoints": [
{
"region": "RegionOne",
"adminURL": "http://localhost:8774/v1.1",
"internalURL": "http://localhost:8774/v1.1",
"publicURL": "http://localhost:8774/v1.1/",
},
],
},
],
},
}
resp = self.get_token()
auth_response = utils.TestResponse({
"status_code": 200,
"text": json.dumps(resp),
@ -112,30 +101,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
def test_v1_auth_redirect(self):
cs = client.Client("username", "password", "project_id",
utils.AUTH_URL_V1, service_type='compute')
dict_correct_response = {
"access": {
"token": {
"expires": "12345",
"id": "FAKE_ID",
"tenant": {
"id": "FAKE_TENANT_ID",
}
},
"serviceCatalog": [
{
"type": "compute",
"endpoints": [
{
"adminURL": "http://localhost:8774/v1.1",
"region": "RegionOne",
"internalURL": "http://localhost:8774/v1.1",
"publicURL": "http://localhost:8774/v1.1/",
},
],
},
],
},
}
dict_correct_response = self.get_token()
correct_response = json.dumps(dict_correct_response)
dict_responses = [
{"headers": {'location': 'http://127.0.0.1:5001'},
@ -173,9 +139,9 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
'passwordCredentials': {
'username': cs.client.user,
'password': cs.client.password,
},
'tenantName': cs.client.projectid,
},
},
'tenantName': cs.client.projectid,
},
}
token_url = cs.client.auth_url + "/tokens"
@ -200,30 +166,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
def test_v2_auth_redirect(self):
cs = client.Client("username", "password", "project_id",
utils.AUTH_URL_V2, service_type='compute')
dict_correct_response = {
"access": {
"token": {
"expires": "12345",
"id": "FAKE_ID",
"tenant": {
"id": "FAKE_TENANT_ID",
}
},
"serviceCatalog": [
{
"type": "compute",
"endpoints": [
{
"adminURL": "http://localhost:8774/v1.1",
"region": "RegionOne",
"internalURL": "http://localhost:8774/v1.1",
"publicURL": "http://localhost:8774/v1.1/",
},
],
},
],
},
}
dict_correct_response = self.get_token()
correct_response = json.dumps(dict_correct_response)
dict_responses = [
{"headers": {'location': 'http://127.0.0.1:5001'},
@ -261,9 +204,9 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
'passwordCredentials': {
'username': cs.client.user,
'password': cs.client.password,
},
'tenantName': cs.client.projectid,
},
},
'tenantName': cs.client.projectid,
},
}
token_url = cs.client.auth_url + "/tokens"
@ -288,42 +231,12 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
def test_ambiguous_endpoints(self):
cs = client.Client("username", "password", "project_id",
utils.AUTH_URL_V2, service_type='compute')
resp = {
"access": {
"token": {
"expires": "12345",
"id": "FAKE_ID",
"tenant": {
"id": "FAKE_TENANT_ID",
}
},
"serviceCatalog": [
{
"adminURL": "http://localhost:8774/v1.1",
"type": "compute",
"name": "Compute CLoud",
"endpoints": [
{
"region": "RegionOne",
"internalURL": "http://localhost:8774/v1.1",
"publicURL": "http://localhost:8774/v1.1/",
},
],
},
{
"adminURL": "http://localhost:8774/v1.1",
"type": "compute",
"name": "Hyper-compute Cloud",
"endpoints": [
{
"internalURL": "http://localhost:8774/v1.1",
"publicURL": "http://localhost:8774/v1.1/",
},
],
},
],
},
}
resp = self.get_token()
# duplicate existing service
s = resp.add_service('compute')
s.add_endpoint('http://localhost:8774/v1.1', region='RegionOne')
auth_response = utils.TestResponse({
"status_code": 200,
"text": json.dumps(resp),
@ -342,30 +255,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
cs = client.Client("username", None, "project_id",
utils.AUTH_URL_V2, service_type='compute')
cs.client.auth_token = "FAKE_ID"
resp = {
"access": {
"token": {
"expires": "12345",
"id": "FAKE_ID",
"tenant": {
"id": "FAKE_TENANT_ID",
}
},
"serviceCatalog": [
{
"type": "compute",
"endpoints": [
{
"region": "RegionOne",
"adminURL": "http://localhost:8774/v1.1",
"internalURL": "http://localhost:8774/v1.1",
"publicURL": "http://localhost:8774/v1.1/",
},
],
},
],
},
}
resp = self.get_token(token_id="FAKE_ID")
auth_response = utils.TestResponse({
"status_code": 200,
"text": json.dumps(resp),

View File

@ -58,7 +58,7 @@ class AvailabilityZoneTest(utils.FixturedTestCase):
z0 = self.shell._treeizeAvailabilityZone(zones[0])
z1 = self.shell._treeizeAvailabilityZone(zones[1])
self.assertEqual((len(z0), len(z1)), (1, 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])
@ -89,7 +89,7 @@ class AvailabilityZoneTest(utils.FixturedTestCase):
z1 = self.shell._treeizeAvailabilityZone(zones[1])
z2 = self.shell._treeizeAvailabilityZone(zones[2])
self.assertEqual((len(z0), len(z1), len(z2)), (3, 5, 1))
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])

View File

@ -28,10 +28,10 @@ class FixedIpsTest(utils.FixturedTestCase):
def test_get_fixed_ip(self):
info = self.cs.fixed_ips.get(fixed_ip='192.168.1.1')
self.assert_called('GET', '/os-fixed-ips/192.168.1.1')
self.assertEqual(info.cidr, '192.168.1.0/24')
self.assertEqual(info.address, '192.168.1.1')
self.assertEqual(info.hostname, 'foo')
self.assertEqual(info.host, 'bar')
self.assertEqual('192.168.1.0/24', info.cidr)
self.assertEqual('192.168.1.1', info.address)
self.assertEqual('foo', info.hostname)
self.assertEqual('bar', info.host)
def test_reserve_fixed_ip(self):
body = {"reserve": None}

View File

@ -67,36 +67,36 @@ class FlavorsTest(utils.TestCase):
f = self.cs.flavors.get(1)
self.cs.assert_called('GET', '/flavors/1')
self.assertIsInstance(f, self.flavor_type)
self.assertEqual(f.ram, 256)
self.assertEqual(f.disk, 10)
self.assertEqual(f.ephemeral, 10)
self.assertEqual(f.is_public, True)
self.assertEqual(256, f.ram)
self.assertEqual(10, f.disk)
self.assertEqual(10, f.ephemeral)
self.assertEqual(True, f.is_public)
def test_get_flavor_details_alphanum_id(self):
f = self.cs.flavors.get('aa1')
self.cs.assert_called('GET', '/flavors/aa1')
self.assertIsInstance(f, self.flavor_type)
self.assertEqual(f.ram, 128)
self.assertEqual(f.disk, 0)
self.assertEqual(f.ephemeral, 0)
self.assertEqual(f.is_public, True)
self.assertEqual(128, f.ram)
self.assertEqual(0, f.disk)
self.assertEqual(0, f.ephemeral)
self.assertEqual(True, f.is_public)
def test_get_flavor_details_diablo(self):
f = self.cs.flavors.get(3)
self.cs.assert_called('GET', '/flavors/3')
self.assertIsInstance(f, self.flavor_type)
self.assertEqual(f.ram, 256)
self.assertEqual(f.disk, 10)
self.assertEqual(f.ephemeral, 'N/A')
self.assertEqual(f.is_public, 'N/A')
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.cs.assert_called('GET', '/flavors/detail')
self.assertEqual(f.name, '256 MB Server')
self.assertEqual('256 MB Server', f.name)
f = self.cs.flavors.find(disk=0)
self.assertEqual(f.name, '128 MB Server')
self.assertEqual('128 MB Server', f.name)
self.assertRaises(exceptions.NotFound, self.cs.flavors.find,
disk=12345)

View File

@ -25,13 +25,13 @@ class FloatingIPDNSDomainTest(utils.FixturedTestCase):
def test_dns_domains(self):
domainlist = self.cs.dns_domains.domains()
self.assertEqual(len(domainlist), 2)
self.assertEqual(2, len(domainlist))
for entry in domainlist:
self.assertIsInstance(entry,
floating_ip_dns.FloatingIPDNSDomain)
self.assertEqual(domainlist[1].domain, 'example.com')
self.assertEqual('example.com', domainlist[1].domain)
def test_create_private_domain(self):
self.cs.dns_domains.create_private(self.testdomain, 'test_avzone')
@ -61,13 +61,13 @@ class FloatingIPDNSEntryTest(utils.FixturedTestCase):
def test_get_dns_entries_by_ip(self):
entries = self.cs.dns_entries.get_for_ip(self.testdomain,
ip=self.testip)
self.assertEqual(len(entries), 2)
self.assertEqual(2, len(entries))
for entry in entries:
self.assertIsInstance(entry,
floating_ip_dns.FloatingIPDNSEntry)
self.assertEqual(entries[1].dns_entry['name'], 'host2')
self.assertEqual('host2', entries[1].dns_entry['name'])
self.assertEqual(entries[1].dns_entry['ip'], self.testip)
def test_get_dns_entry_by_name(self):

View File

@ -55,5 +55,5 @@ class FloatingIPsTest(utils.FixturedTestCase):
def test_create_floating_ip_with_pool(self):
fl = self.cs.floating_ips.create('nova')
self.assert_called('POST', '/os-floating-ips')
self.assertEqual(fl.pool, 'nova')
self.assertEqual('nova', fl.pool)
self.assertIsInstance(fl, floating_ips.FloatingIP)

View File

@ -26,15 +26,15 @@ class FpingTest(utils.FixturedTestCase):
def test_fping_repr(self):
r = self.cs.fping.get(1)
self.assertEqual(repr(r), "<Fping: 1>")
self.assertEqual("<Fping: 1>", repr(r))
def test_list_fpings(self):
fl = self.cs.fping.list()
self.assert_called('GET', '/os-fping')
for f in fl:
self.assertIsInstance(f, fping.Fping)
self.assertEqual(f.project_id, "fake-project")
self.assertEqual(f.alive, True)
self.assertEqual("fake-project", f.project_id)
self.assertEqual(True, f.alive)
def test_list_fpings_all_tenants(self):
fl = self.cs.fping.list(all_tenants=True)
@ -58,5 +58,5 @@ class FpingTest(utils.FixturedTestCase):
f = self.cs.fping.get(1)
self.assert_called('GET', '/os-fping/1')
self.assertIsInstance(f, fping.Fping)
self.assertEqual(f.project_id, "fake-project")
self.assertEqual(f.alive, True)
self.assertEqual("fake-project", f.project_id)
self.assertEqual(True, f.alive)

View File

@ -78,3 +78,7 @@ class HostsTest(utils.FixturedTestCase):
host.shutdown()
self.assert_called(
'GET', '/os-hosts/sample_host/shutdown')
def test_hosts_repr(self):
hs = self.cs.hosts.get('host')
self.assertEqual('<Host: dummy>', repr(hs[0]))

View File

@ -40,8 +40,8 @@ class ImagesTest(utils.FixturedTestCase):
i = self.cs.images.get(1)
self.assert_called('GET', '/images/1')
self.assertIsInstance(i, images.Image)
self.assertEqual(i.id, 1)
self.assertEqual(i.name, 'CentOS 5.2')
self.assertEqual(1, i.id)
self.assertEqual('CentOS 5.2', i.name)
def test_delete_image(self):
self.cs.images.delete(1)
@ -58,9 +58,9 @@ class ImagesTest(utils.FixturedTestCase):
def test_find(self):
i = self.cs.images.find(name="CentOS 5.2")
self.assertEqual(i.id, 1)
self.assertEqual(1, i.id)
self.assert_called('GET', '/images/1')
iml = self.cs.images.findall(status='SAVING')
self.assertEqual(len(iml), 1)
self.assertEqual(iml[0].name, 'My Server Backup')
self.assertEqual(1, len(iml))
self.assertEqual('My Server Backup', iml[0].name)

View File

@ -37,7 +37,7 @@ class KeypairsTest(utils.FixturedTestCase):
kp = self.cs.keypairs.get('test')
self.assert_called('GET', '/%s/test' % self.keypair_prefix)
self.assertIsInstance(kp, keypairs.Keypair)
self.assertEqual(kp.name, 'test')
self.assertEqual('test', kp.name)
def test_list_keypairs(self):
kps = self.cs.keypairs.list()

View File

@ -59,7 +59,13 @@ class NetworksTest(utils.FixturedTestCase):
'project_id': '1',
'vlan': 5,
'vlan_start': 1,
'vpn_start': 1
'vpn_start': 1,
'mtu': 1500,
'enable_dhcp': 'T',
'dhcp_server': '1920.2.2',
'share_address': 'T',
'allowed_start': '192.0.2.10',
'allowed_end': '192.0.2.20',
}
f = self.cs.networks.create(**params)

View File

@ -16,48 +16,55 @@ import mock
import six
from novaclient import exceptions
from novaclient.openstack.common import jsonutils
from novaclient.tests.fixture_data import client
from novaclient.tests.fixture_data import floatingips
from novaclient.tests.fixture_data import servers as data
from novaclient.tests import utils
from novaclient.tests.v1_1 import fakes
from novaclient.v1_1 import servers
cs = fakes.FakeClient()
class ServersTest(utils.FixturedTestCase):
client_fixture_class = client.V1
data_fixture_class = data.V1
class ServersTest(utils.TestCase):
def setUp(self):
super(ServersTest, self).setUp()
self.useFixture(floatingips.FloatingFixture(self.requests))
def test_list_servers(self):
sl = cs.servers.list()
cs.assert_called('GET', '/servers/detail')
sl = self.cs.servers.list()
self.assert_called('GET', '/servers/detail')
[self.assertIsInstance(s, servers.Server) for s in sl]
def test_list_servers_undetailed(self):
sl = cs.servers.list(detailed=False)
cs.assert_called('GET', '/servers')
sl = self.cs.servers.list(detailed=False)
self.assert_called('GET', '/servers')
[self.assertIsInstance(s, servers.Server) for s in sl]
def test_list_servers_with_marker_limit(self):
sl = cs.servers.list(marker=1234, limit=2)
cs.assert_called('GET', '/servers/detail?limit=2&marker=1234')
sl = self.cs.servers.list(marker=1234, limit=2)
self.assert_called('GET', '/servers/detail?limit=2&marker=1234')
for s in sl:
self.assertIsInstance(s, servers.Server)
def test_get_server_details(self):
s = cs.servers.get(1234)
cs.assert_called('GET', '/servers/1234')
s = self.cs.servers.get(1234)
self.assert_called('GET', '/servers/1234')
self.assertIsInstance(s, servers.Server)
self.assertEqual(s.id, 1234)
self.assertEqual(s.status, 'BUILD')
self.assertEqual(1234, s.id)
self.assertEqual('BUILD', s.status)
def test_get_server_promote_details(self):
s1 = cs.servers.list(detailed=False)[0]
s2 = cs.servers.list(detailed=True)[0]
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 = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -69,11 +76,11 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
}
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_boot_from_volume_with_nics(self):
old_boot = cs.servers._boot
old_boot = self.cs.servers._boot
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
'v4-fixed-ip': '10.0.0.7'}]
@ -87,9 +94,9 @@ class ServersTest(utils.TestCase):
self.assertEqual(boot_kwargs['nics'], nics)
return old_boot(url, key, *boot_args, **boot_kwargs)
@mock.patch.object(cs.servers, '_boot', wrapped_boot)
@mock.patch.object(self.cs.servers, '_boot', wrapped_boot)
def test_create_server_from_volume():
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -99,13 +106,13 @@ class ServersTest(utils.TestCase):
block_device_mapping=bdm,
nics=nics
)
cs.assert_called('POST', '/os-volumes_boot')
self.assert_called('POST', '/os-volumes_boot')
self.assertIsInstance(s, servers.Server)
test_create_server_from_volume()
def test_create_server_boot_with_nics_ipv6(self):
old_boot = cs.servers._boot
old_boot = self.cs.servers._boot
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
'v6-fixed-ip': '2001:db9:0:1::10'}]
@ -113,8 +120,8 @@ class ServersTest(utils.TestCase):
self.assertEqual(boot_kwargs['nics'], nics)
return old_boot(url, key, *boot_args, **boot_kwargs)
with mock.patch.object(cs.servers, '_boot', wrapped_boot):
s = cs.servers.create(
with mock.patch.object(self.cs.servers, '_boot', wrapped_boot):
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -123,11 +130,11 @@ class ServersTest(utils.TestCase):
key_name="fakekey",
nics=nics
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_userdata_file_object(self):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -138,11 +145,11 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
},
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_userdata_unicode(self):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -154,11 +161,11 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
},
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_userdata_utf8(self):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -170,22 +177,21 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
},
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def _create_disk_config(self, disk_config):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
disk_config=disk_config
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
# verify disk config param was used in the request:
last_request = cs.client.callstack[-1]
body = last_request[-1]
body = jsonutils.loads(self.requests.last_request.body)
server = body['server']
self.assertTrue('OS-DCF:diskConfig' in server)
self.assertEqual(disk_config, server['OS-DCF:diskConfig'])
@ -197,86 +203,84 @@ class ServersTest(utils.TestCase):
self._create_disk_config('MANUAL')
def test_update_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
# Update via instance
s.update(name='hi')
cs.assert_called('PUT', '/servers/1234')
self.assert_called('PUT', '/servers/1234')
s.update(name='hi')
cs.assert_called('PUT', '/servers/1234')
self.assert_called('PUT', '/servers/1234')
# Silly, but not an error
s.update()
# Update via manager
cs.servers.update(s, name='hi')
cs.assert_called('PUT', '/servers/1234')
self.cs.servers.update(s, name='hi')
self.assert_called('PUT', '/servers/1234')
def test_delete_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.delete()
cs.assert_called('DELETE', '/servers/1234')
cs.servers.delete(1234)
cs.assert_called('DELETE', '/servers/1234')
cs.servers.delete(s)
cs.assert_called('DELETE', '/servers/1234')
self.assert_called('DELETE', '/servers/1234')
self.cs.servers.delete(1234)
self.assert_called('DELETE', '/servers/1234')
self.cs.servers.delete(s)
self.assert_called('DELETE', '/servers/1234')
def test_delete_server_meta(self):
cs.servers.delete_meta(1234, ['test_key'])
cs.assert_called('DELETE', '/servers/1234/metadata/test_key')
self.cs.servers.delete_meta(1234, ['test_key'])
self.assert_called('DELETE', '/servers/1234/metadata/test_key')
def test_set_server_meta(self):
cs.servers.set_meta(1234, {'test_key': 'test_value'})
cs.assert_called('POST', '/servers/1234/metadata',
{'metadata': {'test_key': 'test_value'}})
self.cs.servers.set_meta(1234, {'test_key': 'test_value'})
self.assert_called('POST', '/servers/1234/metadata',
{'metadata': {'test_key': 'test_value'}})
def test_set_server_meta_item(self):
cs.servers.set_meta_item(1234, 'test_key', 'test_value')
cs.assert_called('PUT', '/servers/1234/metadata/test_key',
{'meta': {'test_key': 'test_value'}})
self.cs.servers.set_meta_item(1234, 'test_key', 'test_value')
self.assert_called('PUT', '/servers/1234/metadata/test_key',
{'meta': {'test_key': 'test_value'}})
def test_find(self):
server = cs.servers.find(name='sample-server')
cs.assert_called('GET', '/servers', pos=-2)
cs.assert_called('GET', '/servers/1234', pos=-1)
self.assertEqual(server.name, 'sample-server')
server = self.cs.servers.find(name='sample-server')
self.assert_called('GET', '/servers/1234')
self.assertEqual('sample-server', server.name)
self.assertRaises(exceptions.NoUniqueMatch, cs.servers.find,
self.assertRaises(exceptions.NoUniqueMatch, self.cs.servers.find,
flavor={"id": 1, "name": "256 MB Server"})
sl = cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"})
self.assertEqual([s.id for s in sl], [1234, 5678, 9012])
sl = self.cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"})
self.assertEqual([1234, 5678, 9012], [s.id for s in sl])
def test_reboot_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.reboot()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.reboot(s, reboot_type='HARD')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.reboot(s, reboot_type='HARD')
self.assert_called('POST', '/servers/1234/action')
def test_rebuild_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.rebuild(image=1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.rebuild(s, image=1)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.rebuild(s, image=1)
self.assert_called('POST', '/servers/1234/action')
s.rebuild(image=1, password='5678')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.rebuild(s, image=1, password='5678')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.rebuild(s, image=1, password='5678')
self.assert_called('POST', '/servers/1234/action')
def _rebuild_resize_disk_config(self, disk_config, operation="rebuild"):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
if operation == "rebuild":
s.rebuild(image=1, disk_config=disk_config)
elif operation == "resize":
s.resize(flavor=1, disk_config=disk_config)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
# verify disk config param was used in the request:
last_request = cs.client.callstack[-1]
body = last_request[-1]
body = jsonutils.loads(self.requests.last_request.body)
d = body[operation]
self.assertTrue('OS-DCF:diskConfig' in d)
@ -289,20 +293,31 @@ class ServersTest(utils.TestCase):
self._rebuild_resize_disk_config('MANUAL')
def test_rebuild_server_preserve_ephemeral(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.rebuild(image=1, preserve_ephemeral=True)
cs.assert_called('POST', '/servers/1234/action')
body = cs.client.callstack[-1][-1]
self.assert_called('POST', '/servers/1234/action')
body = jsonutils.loads(self.requests.last_request.body)
d = body['rebuild']
self.assertIn('preserve_ephemeral', d)
self.assertEqual(d['preserve_ephemeral'], True)
self.assertEqual(True, d['preserve_ephemeral'])
def test_rebuild_server_name_meta_files(self):
files = {'/etc/passwd': 'some data'}
s = self.cs.servers.get(1234)
s.rebuild(image=1, name='new', meta={'foo': 'bar'}, files=files)
body = jsonutils.loads(self.requests.last_request.body)
d = body['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 = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.resize(flavor=1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.resize(s, flavor=1)
cs.assert_called('POST', '/servers/1234/action')
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')
@ -311,162 +326,163 @@ class ServersTest(utils.TestCase):
self._rebuild_resize_disk_config('MANUAL', 'resize')
def test_confirm_resized_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.confirm_resize()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.confirm_resize(s)
cs.assert_called('POST', '/servers/1234/action')
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 = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.revert_resize()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.revert_resize(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.revert_resize(s)
self.assert_called('POST', '/servers/1234/action')
def test_migrate_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.migrate()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.migrate(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.migrate(s)
self.assert_called('POST', '/servers/1234/action')
def test_add_fixed_ip(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.add_fixed_ip(1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.add_fixed_ip(s, 1)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.add_fixed_ip(s, 1)
self.assert_called('POST', '/servers/1234/action')
def test_remove_fixed_ip(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.remove_fixed_ip('10.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.remove_fixed_ip(s, '10.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.remove_fixed_ip(s, '10.0.0.1')
self.assert_called('POST', '/servers/1234/action')
def test_add_floating_ip(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.add_floating_ip('11.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.add_floating_ip(s, '11.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
f = cs.floating_ips.list()[0]
cs.servers.add_floating_ip(s, f)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.add_floating_ip(s, '11.0.0.1')
self.assert_called('POST', '/servers/1234/action')
f = self.cs.floating_ips.list()[0]
self.cs.servers.add_floating_ip(s, f)
self.assert_called('POST', '/servers/1234/action')
s.add_floating_ip(f)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
def test_add_floating_ip_to_fixed(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.add_floating_ip('11.0.0.1', fixed_address='12.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.add_floating_ip(s, '11.0.0.1',
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.add_floating_ip(s, '11.0.0.1',
fixed_address='12.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
f = cs.floating_ips.list()[0]
cs.servers.add_floating_ip(s, f)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
f = self.cs.floating_ips.list()[0]
self.cs.servers.add_floating_ip(s, f)
self.assert_called('POST', '/servers/1234/action')
s.add_floating_ip(f)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
def test_remove_floating_ip(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.remove_floating_ip('11.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.remove_floating_ip(s, '11.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
f = cs.floating_ips.list()[0]
cs.servers.remove_floating_ip(s, f)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.remove_floating_ip(s, '11.0.0.1')
self.assert_called('POST', '/servers/1234/action')
f = self.cs.floating_ips.list()[0]
self.cs.servers.remove_floating_ip(s, f)
self.assert_called('POST', '/servers/1234/action')
s.remove_floating_ip(f)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
def test_stop(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.stop()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.stop(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.stop(s)
self.assert_called('POST', '/servers/1234/action')
def test_force_delete(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.force_delete()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.force_delete(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.force_delete(s)
self.assert_called('POST', '/servers/1234/action')
def test_restore(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.restore()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.restore(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.restore(s)
self.assert_called('POST', '/servers/1234/action')
def test_start(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.start()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.start(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.start(s)
self.assert_called('POST', '/servers/1234/action')
def test_rescue(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.rescue()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.rescue(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.rescue(s)
self.assert_called('POST', '/servers/1234/action')
def test_unrescue(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.unrescue()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.unrescue(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.unrescue(s)
self.assert_called('POST', '/servers/1234/action')
def test_lock(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.lock()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.lock(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.lock(s)
self.assert_called('POST', '/servers/1234/action')
def test_unlock(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.unlock()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.unlock(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.unlock(s)
self.assert_called('POST', '/servers/1234/action')
def test_backup(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.backup('back1', 'daily', 1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.backup(s, 'back1', 'daily', 2)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.backup(s, 'back1', 'daily', 2)
self.assert_called('POST', '/servers/1234/action')
def test_get_console_output_without_length(self):
success = 'foo'
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_console_output()
self.assertEqual(s.get_console_output(), success)
cs.assert_called('POST', '/servers/1234/action')
self.assertEqual(success, s.get_console_output())
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_console_output(s)
self.assertEqual(cs.servers.get_console_output(s), success)
cs.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_console_output(s)
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 = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_console_output(length=50)
self.assertEqual(s.get_console_output(length=50), success)
cs.assert_called('POST', '/servers/1234/action')
self.assertEqual(success, s.get_console_output(length=50))
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_console_output(s, length=50)
self.assertEqual(cs.servers.get_console_output(s, length=50), success)
cs.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_console_output(s, length=50)
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
#
@ -483,126 +499,135 @@ class ServersTest(utils.TestCase):
# Hi/fmZZNQQqj1Ijq0caOIw==
def test_get_password(self):
s = cs.servers.get(1234)
self.assertEqual(s.get_password('novaclient/tests/idfake.pem'),
b'FooBar123')
cs.assert_called('GET', '/servers/1234/os-server-password')
s = self.cs.servers.get(1234)
self.assertEqual(b'FooBar123',
s.get_password('novaclient/tests/idfake.pem'))
self.assert_called('GET', '/servers/1234/os-server-password')
def test_get_password_without_key(self):
s = cs.servers.get(1234)
self.assertEqual(s.get_password(),
s = self.cs.servers.get(1234)
self.assertEqual(
'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r'
'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho'
'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw'
'/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N'
'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk'
'Hi/fmZZNQQqj1Ijq0caOIw==')
cs.assert_called('GET', '/servers/1234/os-server-password')
'Hi/fmZZNQQqj1Ijq0caOIw==', s.get_password())
self.assert_called('GET', '/servers/1234/os-server-password')
def test_clear_password(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.clear_password()
cs.assert_called('DELETE', '/servers/1234/os-server-password')
self.assert_called('DELETE', '/servers/1234/os-server-password')
def test_get_server_diagnostics(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
diagnostics = s.diagnostics()
self.assertTrue(diagnostics is not None)
cs.assert_called('GET', '/servers/1234/diagnostics')
self.assert_called('GET', '/servers/1234/diagnostics')
diagnostics_from_manager = cs.servers.diagnostics(1234)
diagnostics_from_manager = self.cs.servers.diagnostics(1234)
self.assertTrue(diagnostics_from_manager is not None)
cs.assert_called('GET', '/servers/1234/diagnostics')
self.assert_called('GET', '/servers/1234/diagnostics')
self.assertEqual(diagnostics, diagnostics_from_manager)
self.assertEqual(diagnostics[1], diagnostics_from_manager[1])
def test_get_vnc_console(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_vnc_console('fake')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_vnc_console(s, 'fake')
cs.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_vnc_console(s, 'fake')
self.assert_called('POST', '/servers/1234/action')
def test_get_spice_console(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_spice_console('fake')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_spice_console(s, 'fake')
cs.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_spice_console(s, 'fake')
self.assert_called('POST', '/servers/1234/action')
def test_get_serial_console(self):
s = self.cs.servers.get(1234)
s.get_serial_console('fake')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_serial_console(s, 'fake')
self.assert_called('POST', '/servers/1234/action')
def test_get_rdp_console(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_rdp_console('fake')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_rdp_console(s, 'fake')
cs.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_rdp_console(s, 'fake')
self.assert_called('POST', '/servers/1234/action')
def test_create_image(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.create_image('123')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
s.create_image('123', {})
cs.assert_called('POST', '/servers/1234/action')
cs.servers.create_image(s, '123')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.create_image(s, '123', {})
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.create_image(s, '123')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.create_image(s, '123', {})
def test_live_migrate_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.live_migrate(host='hostname', block_migration=False,
disk_over_commit=False)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.live_migrate(s, host='hostname', block_migration=False,
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.live_migrate(s, host='hostname', block_migration=False,
disk_over_commit=False)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
def test_reset_state(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.reset_state('newstate')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.reset_state(s, 'newstate')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.reset_state(s, 'newstate')
self.assert_called('POST', '/servers/1234/action')
def test_reset_network(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.reset_network()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.reset_network(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.reset_network(s)
self.assert_called('POST', '/servers/1234/action')
def test_add_security_group(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.add_security_group('newsg')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.add_security_group(s, 'newsg')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.add_security_group(s, 'newsg')
self.assert_called('POST', '/servers/1234/action')
def test_remove_security_group(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.remove_security_group('oldsg')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.remove_security_group(s, 'oldsg')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.remove_security_group(s, 'oldsg')
self.assert_called('POST', '/servers/1234/action')
def test_list_security_group(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.list_security_group()
cs.assert_called('GET', '/servers/1234/os-security-groups')
self.assert_called('GET', '/servers/1234/os-security-groups')
def test_evacuate(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.evacuate('fake_target_host', 'True')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.evacuate(s, 'fake_target_host', 'False', 'NewAdminPassword')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.evacuate(s, 'fake_target_host',
'False', 'NewAdminPassword')
self.assert_called('POST', '/servers/1234/action')
def test_interface_list(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.interface_list()
cs.assert_called('GET', '/servers/1234/os-interface')
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."""
@ -631,11 +656,11 @@ class ServersTest(utils.TestCase):
self.assertEqual('<Server: unknown-name>', '%r' % s)
def test_interface_attach(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.interface_attach(None, None, None)
cs.assert_called('POST', '/servers/1234/os-interface')
self.assert_called('POST', '/servers/1234/os-interface')
def test_interface_detach(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.interface_detach('port-id')
cs.assert_called('DELETE', '/servers/1234/os-interface/port-id')
self.assert_called('DELETE', '/servers/1234/os-interface/port-id')

View File

@ -35,8 +35,8 @@ class ServicesTest(utils.TestCase):
self.cs.assert_called('GET', '/os-services')
for s in svs:
self.assertIsInstance(s, self._get_service_type())
self.assertEqual(s.binary, 'nova-compute')
self.assertEqual(s.host, 'host1')
self.assertEqual('nova-compute', s.binary)
self.assertEqual('host1', s.host)
self.assertTrue(str(s).startswith('<Service: '))
def test_list_services_with_hostname(self):
@ -44,16 +44,16 @@ class ServicesTest(utils.TestCase):
self.cs.assert_called('GET', '/os-services?host=host2')
for s in svs:
self.assertIsInstance(s, self._get_service_type())
self.assertEqual(s.binary, 'nova-compute')
self.assertEqual(s.host, 'host2')
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.cs.assert_called('GET', '/os-services?binary=nova-cert')
for s in svs:
self.assertIsInstance(s, self._get_service_type())
self.assertEqual(s.binary, 'nova-cert')
self.assertEqual(s.host, 'host1')
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')
@ -61,8 +61,8 @@ class ServicesTest(utils.TestCase):
'/os-services?host=host2&binary=nova-cert')
for s in svs:
self.assertIsInstance(s, self._get_service_type())
self.assertEqual(s.binary, 'nova-cert')
self.assertEqual(s.host, 'host2')
self.assertEqual('nova-cert', s.binary)
self.assertEqual('host2', s.host)
def _update_body(self, host, binary, disabled_reason=None):
body = {"host": host,
@ -76,7 +76,7 @@ class ServicesTest(utils.TestCase):
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(service.status, 'enabled')
self.assertEqual('enabled', service.status)
def test_services_delete(self):
self.cs.services.delete('1')
@ -87,7 +87,7 @@ class ServicesTest(utils.TestCase):
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(service.status, 'disabled')
self.assertEqual('disabled', service.status)
def test_services_disable_log_reason(self):
service = self.cs.services.disable_log_reason(
@ -96,4 +96,4 @@ class ServicesTest(utils.TestCase):
"disable bad host")
self.cs.assert_called('PUT', '/os-services/disable-log-reason', values)
self.assertIsInstance(service, self._get_service_type())
self.assertEqual(service.status, 'disabled')
self.assertEqual('disabled', service.status)

View File

@ -22,6 +22,7 @@ import os
import fixtures
import mock
from oslo.utils import timeutils
import six
from six.moves import builtins
@ -266,7 +267,10 @@ class ShellTest(utils.TestCase):
{
'volume_id': 'blah',
'delete_on_termination': '0',
'device_name': 'vda'
'device_name': 'vda',
'uuid': 'blah',
'boot_index': 0,
'source_type': ''
}
],
'imageRef': '',
@ -652,7 +656,7 @@ class ShellTest(utils.TestCase):
'max_count': 1,
}},
)
self.assertEqual(poll_method.call_count, 1)
self.assertEqual(1, poll_method.call_count)
poll_method.assert_has_calls(
[mock.call(self.shell.cs.servers.get, 1234, 'building',
['active'])])
@ -853,7 +857,7 @@ class ShellTest(utils.TestCase):
def test_rebuild(self):
output = self.run_command('rebuild sample-server 1')
self.assert_called('GET', '/servers', pos=-6)
self.assert_called('GET', '/servers?name=sample-server', pos=-6)
self.assert_called('GET', '/servers/1234', pos=-5)
self.assert_called('GET', '/images/1', pos=-4)
self.assert_called('POST', '/servers/1234/action',
@ -865,7 +869,7 @@ class ShellTest(utils.TestCase):
def test_rebuild_password(self):
output = self.run_command('rebuild sample-server 1'
' --rebuild-password asdf')
self.assert_called('GET', '/servers', pos=-6)
self.assert_called('GET', '/servers?name=sample-server', pos=-6)
self.assert_called('GET', '/servers/1234', pos=-5)
self.assert_called('GET', '/images/1', pos=-4)
self.assert_called('POST', '/servers/1234/action',
@ -877,7 +881,7 @@ class ShellTest(utils.TestCase):
def test_rebuild_preserve_ephemeral(self):
self.run_command('rebuild sample-server 1 --preserve-ephemeral')
self.assert_called('GET', '/servers', pos=-6)
self.assert_called('GET', '/servers?name=sample-server', pos=-6)
self.assert_called('GET', '/servers/1234', pos=-5)
self.assert_called('GET', '/images/1', pos=-4)
self.assert_called('POST', '/servers/1234/action',
@ -886,6 +890,19 @@ class ShellTest(utils.TestCase):
self.assert_called('GET', '/flavors/1', pos=-2)
self.assert_called('GET', '/images/2')
def test_rebuild_name_meta(self):
self.run_command('rebuild sample-server 1 --name asdf --meta '
'foo=bar')
self.assert_called('GET', '/servers?name=sample-server', pos=-6)
self.assert_called('GET', '/servers/1234', pos=-5)
self.assert_called('GET', '/images/1', pos=-4)
self.assert_called('POST', '/servers/1234/action',
{'rebuild': {'imageRef': 1,
'name': 'asdf',
'metadata': {'foo': 'bar'}}}, pos=-3)
self.assert_called('GET', '/flavors/1', pos=-2)
self.assert_called('GET', '/images/2')
def test_start(self):
self.run_command('start sample-server')
self.assert_called('POST', '/servers/1234/action', {'os-start': None})
@ -1026,17 +1043,19 @@ class ShellTest(utils.TestCase):
self.assert_called('DELETE', '/servers/1234', pos=-3)
self.assert_called('DELETE', '/servers/5678', pos=-1)
self.run_command('delete sample-server sample-server2')
self.assert_called('GET', '/servers', pos=-6)
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', pos=-3)
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_one_nonexistent(self):
self.run_command('delete 1234 123456789')
cmd = 'delete 1234 123456789'
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
self.assert_called_anytime('DELETE', '/servers/1234')
self.run_command('delete sample-server nonexistentserver')
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):
@ -1214,9 +1233,9 @@ class ShellTest(utils.TestCase):
'end=2005-02-01T00:00:00&' +
'detailed=1')
@mock.patch('novaclient.openstack.common.timeutils.utcnow')
def test_usage_list_no_args(self, mock_utcnow):
mock_utcnow.return_value = datetime.datetime(2005, 2, 1, 0, 0)
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?' +
@ -1293,17 +1312,29 @@ class ShellTest(utils.TestCase):
self.assert_called('PUT', '/os-aggregates/1', body, pos=-2)
self.assert_called('GET', '/os-aggregates/1', pos=-1)
def test_aggregate_set_metadata_by_id(self):
self.run_command('aggregate-set-metadata 1 foo=bar delete_key')
body = {"set_metadata": {"metadata": {"foo": "bar",
"delete_key": None}}}
self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2)
self.assert_called('GET', '/os-aggregates/1', pos=-1)
def test_aggregate_set_metadata_add_by_id(self):
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)
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 delete_key')
body = {"set_metadata": {"metadata": {"foo": "bar",
"delete_key": None}}}
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)
@ -1511,17 +1542,13 @@ class ShellTest(utils.TestCase):
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': {'host': None,
'onSharedStorage': True}}, pos=1)
{'evacuate': {'onSharedStorage': True}}, pos=1)
self.assert_called('POST', '/servers/uuid2/action',
{'evacuate': {'host': None,
'onSharedStorage': True}}, pos=2)
{'evacuate': {'onSharedStorage': True}}, pos=2)
self.assert_called('POST', '/servers/uuid3/action',
{'evacuate': {'host': None,
'onSharedStorage': True}}, pos=3)
{'evacuate': {'onSharedStorage': True}}, pos=3)
self.assert_called('POST', '/servers/uuid4/action',
{'evacuate': {'host': None,
'onSharedStorage': True}}, pos=4)
{'evacuate': {'onSharedStorage': True}}, pos=4)
def test_host_servers_migrate(self):
self.run_command('host-servers-migrate hyper')
@ -1656,7 +1683,8 @@ class ShellTest(utils.TestCase):
'--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'
'--key-pairs', '--security-groups', '--security-group-rules',
'--server-groups', '--server-group-members'
)
for arg in args:
self.run_command('quota-class-update '
@ -1672,6 +1700,13 @@ class ShellTest(utils.TestCase):
self.run_command('network-list')
self.assert_called('GET', '/os-networks')
def test_network_list_fields(self):
output = self.run_command('network-list --fields '
'vlan,project_id')
self.assert_called('GET', '/os-networks')
self.assertIn('1234', output)
self.assertIn('4ffc664c198e435e9853f2538fbcd7a7', output)
def test_network_show(self):
self.run_command('network-show 1')
self.assert_called('GET', '/os-networks/1')
@ -1757,16 +1792,32 @@ class ShellTest(utils.TestCase):
self.run_command('network-create --fixed-range-v4 192.168.0.0/24'
' --vlan=200 new_network')
body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network',
'vlan': '200'}}
'vlan': 200}}
self.assert_called('POST', '/os-networks', body)
def test_network_create_vlan_start(self):
self.run_command('network-create --fixed-range-v4 192.168.0.0/24'
' --vlan-start=100 new_network')
body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network',
'vlan_start': '100'}}
'vlan_start': 100}}
self.assert_called('POST', '/os-networks', body)
def test_network_create_extra_args(self):
self.run_command('network-create --fixed-range-v4 192.168.0.0/24'
' --enable-dhcp F --dhcp-server 192.168.0.2'
' --share-address T --allowed-start 192.168.0.10'
' --allowed-end 192.168.0.20 --mtu 9000 new_network')
body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network',
'enable_dhcp': False, 'dhcp_server': '192.168.0.2',
'share_address': True, 'mtu': 9000,
'allowed_start': '192.168.0.10',
'allowed_end': '192.168.0.20'}}
self.assert_called('POST', '/os-networks', body)
def test_network_delete(self):
self.run_command('network-delete 1')
self.assert_called('DELETE', '/os-networks/1')
def test_add_fixed_ip(self):
self.run_command('add-fixed-ip sample-server 1')
self.assert_called('POST', '/servers/1234/action',
@ -1810,16 +1861,24 @@ class ShellTest(utils.TestCase):
{'evacuate': {'host': 'new_host',
'onSharedStorage': False,
'adminPass': 'NewAdminPass'}})
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 '
'--on-shared-storage')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'onSharedStorage': 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')
@ -1937,7 +1996,7 @@ class ShellTest(utils.TestCase):
def test_volume_show(self):
self.run_command('volume-show Work')
self.assert_called('GET', '/volumes', pos=-2)
self.assert_called('GET', '/volumes?name=Work', pos=-2)
self.assert_called(
'GET',
'/volumes/15e59938-07d5-11e1-90e3-e3dffe0c5983',
@ -2031,10 +2090,15 @@ class ShellTest(utils.TestCase):
def test_ssh(self, mock_system, mock_find_server):
class FakeResources(object):
addresses = {
"private": [{'version': 4, 'addr': "1.1.1.1"},
{'version': 6, 'addr': "2607:f0d0:1002::4"}],
"public": [{'version': 4, 'addr': "2.2.2.2"},
{'version': 6, 'addr': "7612:a1b2:2004::6"}]
"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()
@ -2069,6 +2133,33 @@ class ShellTest(utils.TestCase):
mock_system.assert_called_with("ssh -6 -p22 "
"root@2607:f0d0:1002::4 -1")
@mock.patch('novaclient.v1_1.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 test_keypair_add(self):
self.run_command('keypair-add test')
self.assert_called('POST', '/os-keypairs',
@ -2116,7 +2207,7 @@ class GetSecgroupTest(utils.TestCase):
'security_groups.list.return_value': [],
})
result = novaclient.v1_1.shell._get_secgroup(cs, '1')
self.assertEqual(result, 'sec_group')
self.assertEqual('sec_group', result)
cs.security_groups.get.assert_called_once_with('1')
def test_with_uuid(self):
@ -2126,7 +2217,7 @@ class GetSecgroupTest(utils.TestCase):
})
result = novaclient.v1_1.shell._get_secgroup(
cs, 'c0c32459-dc5f-44dc-9a0a-473b28bac831')
self.assertEqual(result, 'sec_group')
self.assertEqual('sec_group', result)
cs.security_groups.get.assert_called_once_with(
'c0c32459-dc5f-44dc-9a0a-473b28bac831')
@ -2151,3 +2242,44 @@ class GetSecgroupTest(utils.TestCase):
novaclient.v1_1.shell._get_secgroup,
cs,
'group_one')
class GetFirstEndpointTest(utils.TestCase):
def test_only_one_endpoint(self):
"""If there is only one endpoint, it is returned."""
endpoint = {"url": "test"}
result = novaclient.v1_1.shell._get_first_endpoint([endpoint], "XYZ")
self.assertEqual(endpoint, result)
def test_multiple_endpoints(self):
"""If there are multiple endpoints, the first one of the appropriate
region is returned.
"""
endpoints = [
{"region": "XYZ"},
{"region": "ORD", "number": 1},
{"region": "ORD", "number": 2}
]
result = novaclient.v1_1.shell._get_first_endpoint(endpoints, "ORD")
self.assertEqual(endpoints[1], result)
def test_multiple_endpoints_but_none_suitable(self):
"""If there are multiple endpoints but none of them are suitable, an
exception is raised.
"""
endpoints = [
{"region": "XYZ"},
{"region": "PQR"},
{"region": "STU"}
]
self.assertRaises(LookupError,
novaclient.v1_1.shell._get_first_endpoint,
endpoints, "ORD")
def test_no_endpoints(self):
"""If there are no endpoints available, an exception is raised."""
self.assertRaises(LookupError,
novaclient.v1_1.shell._get_first_endpoint,
[], "ORD")

View File

@ -14,9 +14,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime
import datetime
from oslo.utils import strutils
from novaclient.openstack.common import strutils
from novaclient.tests import fakes
from novaclient.tests.v1_1 import fakes as fakes_v1_1
from novaclient.v3 import client
@ -104,14 +105,14 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
if filter_is_public is not None:
if filter_is_public:
flavors['flavors'] = [
v for v in flavors['flavors']
if v['flavor-access:is_public']
]
v for v in flavors['flavors']
if v['flavor-access:is_public']
]
else:
flavors['flavors'] = [
v for v in flavors['flavors']
if not v['flavor-access:is_public']
]
v for v in flavors['flavors']
if not v['flavor-access:is_public']
]
return (200, {}, flavors)
@ -197,11 +198,11 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
}
body_param_check_exists = {
'rebuild': 'image_ref',
'resize': 'flavor_ref'}
'resize': 'flavor_ref',
'evacuate': 'on_shared_storage'}
body_params_check_exact = {
'reboot': ['type'],
'add_fixed_ip': ['network_id'],
'evacuate': ['host', 'on_shared_storage'],
'remove_fixed_ip': ['address'],
'change_password': ['admin_password'],
'get_console_output': ['length'],
@ -226,9 +227,6 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
if action in body_param_check_exists:
assert body_param_check_exists[action] in body[action]
if action == 'evacuate':
body[action].pop('admin_password', None)
if action in body_params_check_exact:
assert set(body[action]) == set(body_params_check_exact[action])
@ -282,7 +280,9 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
"nova-compute": {"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 25, 0)}}}},
datetime.datetime(
2012, 12, 26, 14, 45, 25, 0
)}}}},
{"zone_name": "internal",
"zone_state": {"available": True},
"hosts": {
@ -291,13 +291,17 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
"active": True,
"available": True,
"updated_at":
datetime(2012, 12, 26, 14, 45, 25, 0)}},
datetime.datetime(
2012, 12, 26, 14, 45, 25, 0
)}},
"fake_host-2": {
"nova-network": {
"active": True,
"available": False,
"updated_at":
datetime(2012, 12, 26, 14, 45, 24, 0)}}}},
datetime.datetime(
2012, 12, 26, 14, 45, 24, 0
)}}}},
{"zone_name": "zone-2",
"zone_state": {"available": False},
"hosts": None}]})
@ -319,7 +323,9 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
'cores': 1,
'keypairs': 1,
'security_groups': 1,
'security_group_rules': 1}})
'security_group_rules': 1,
'server_groups': 1,
'server_group_members': 1}})
def get_os_quota_sets_test_detail(self, **kw):
return (200, {}, {'quota_set': {

View File

@ -38,7 +38,7 @@ class FlavorsTest(test_flavors.FlavorsTest):
"ephemeral": ephemeral,
"id": id,
"swap": swap,
"rxtx_factor": rxtx_factor,
"os-flavor-rxtx:rxtx_factor": rxtx_factor,
"flavor-access:is_public": is_public,
}
}

View File

@ -34,14 +34,29 @@ class HostsTest(utils.FixturedTestCase):
self.assert_called('GET', '/os-hosts')
for h in hs:
self.assertIsInstance(h, hosts.Host)
self.assertEqual(h.zone, 'nova1')
self.assertEqual('nova1', h.zone)
def test_list_host_with_zone(self):
hs = self.cs.hosts.list('nova')
self.assert_called('GET', '/os-hosts?zone=nova')
for h in hs:
self.assertIsInstance(h, hosts.Host)
self.assertEqual('nova', h.zone)
def test_list_host_with_service(self):
hs = self.cs.hosts.list(service='nova-compute')
self.assert_called('GET', '/os-hosts?service=nova-compute')
for h in hs:
self.assertIsInstance(h, hosts.Host)
self.assertEqual(h.service, 'nova-compute')
def test_list_host_with_zone_and_service(self):
hs = self.cs.hosts.list(service='nova-compute', zone='nova')
self.assert_called('GET', '/os-hosts?zone=nova&service=nova-compute')
for h in hs:
self.assertIsInstance(h, hosts.Host)
self.assertEqual(h.zone, 'nova')
self.assertEqual(h.service, 'nova-compute')
def test_update_enable(self):
host = self.cs.hosts.get('sample_host')[0]

View File

@ -44,14 +44,14 @@ class ImagesTest(utils.FixturedTestCase):
i = self.cs.images.get(1)
self.assert_called('HEAD', '/v1/images/1')
self.assertIsInstance(i, images.Image)
self.assertEqual(i.id, '1')
self.assertEqual(i.name, 'CentOS 5.2')
self.assertEqual('1', i.id)
self.assertEqual('CentOS 5.2', i.name)
def test_find(self):
i = self.cs.images.find(name="CentOS 5.2")
self.assertEqual(i.id, '1')
self.assertEqual('1', i.id)
self.assert_called('HEAD', '/v1/images/1')
iml = self.cs.images.findall(status='SAVING')
self.assertEqual(len(iml), 1)
self.assertEqual(iml[0].name, 'My Server Backup')
self.assertEqual(1, len(iml))
self.assertEqual('My Server Backup', iml[0].name)

View File

@ -17,50 +17,51 @@ import mock
import six
from novaclient import exceptions
from novaclient.tests.fixture_data import client
from novaclient.tests.fixture_data import servers as data
from novaclient.tests import utils
from novaclient.tests.v3 import fakes
from novaclient.v3 import servers
cs = fakes.FakeClient()
class ServersTest(utils.FixturedTestCase):
class ServersTest(utils.TestCase):
client_fixture_class = client.V3
data_fixture_class = data.V3
def test_list_servers(self):
sl = cs.servers.list()
cs.assert_called('GET', '/servers/detail')
sl = self.cs.servers.list()
self.assert_called('GET', '/servers/detail')
for s in sl:
self.assertIsInstance(s, servers.Server)
def test_list_servers_undetailed(self):
sl = cs.servers.list(detailed=False)
cs.assert_called('GET', '/servers')
sl = self.cs.servers.list(detailed=False)
self.assert_called('GET', '/servers')
for s in sl:
self.assertIsInstance(s, servers.Server)
def test_list_servers_with_marker_limit(self):
sl = cs.servers.list(marker=1234, limit=2)
cs.assert_called('GET', '/servers/detail?limit=2&marker=1234')
sl = self.cs.servers.list(marker=1234, limit=2)
self.assert_called('GET', '/servers/detail?limit=2&marker=1234')
for s in sl:
self.assertIsInstance(s, servers.Server)
def test_get_server_details(self):
s = cs.servers.get(1234)
cs.assert_called('GET', '/servers/1234')
s = self.cs.servers.get(1234)
self.assert_called('GET', '/servers/1234')
self.assertIsInstance(s, servers.Server)
self.assertEqual(s.id, 1234)
self.assertEqual(s.status, 'BUILD')
self.assertEqual(1234, s.id)
self.assertEqual('BUILD', s.status)
def test_get_server_promote_details(self):
s1 = cs.servers.list(detailed=False)[0]
s2 = cs.servers.list(detailed=True)[0]
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 = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -72,11 +73,11 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
}
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_boot_with_nics_ipv4(self):
old_boot = cs.servers._boot
old_boot = self.cs.servers._boot
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
'v4-fixed-ip': '10.10.0.7'}]
@ -84,8 +85,8 @@ class ServersTest(utils.TestCase):
self.assertEqual(boot_kwargs['nics'], nics)
return old_boot(url, key, *boot_args, **boot_kwargs)
with mock.patch.object(cs.servers, '_boot', wrapped_boot):
s = cs.servers.create(
with mock.patch.object(self.cs.servers, '_boot', wrapped_boot):
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -94,20 +95,20 @@ class ServersTest(utils.TestCase):
key_name="fakekey",
nics=nics
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_boot_with_nics_ipv6(self):
old_boot = cs.servers._boot
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)
self.assertEqual(nics, boot_kwargs['nics'])
return old_boot(url, key, *boot_args, **boot_kwargs)
with mock.patch.object(cs.servers, '_boot', wrapped_boot):
s = cs.servers.create(
with mock.patch.object(self.cs.servers, '_boot', wrapped_boot):
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -116,11 +117,11 @@ class ServersTest(utils.TestCase):
key_name="fakekey",
nics=nics
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_userdata_file_object(self):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -131,11 +132,11 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
},
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_userdata_unicode(self):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -147,11 +148,11 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
},
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_userdata_utf8(self):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -163,11 +164,11 @@ class ServersTest(utils.TestCase):
'/tmp/foo.txt': six.StringIO('data'), # a stream
},
)
cs.assert_called('POST', '/servers')
self.assert_called('POST', '/servers')
self.assertIsInstance(s, servers.Server)
def test_create_server_return_reservation_id(self):
s = cs.servers.create(
s = self.cs.servers.create(
name="My server",
image=1,
flavor=1,
@ -183,294 +184,295 @@ class ServersTest(utils.TestCase):
'os-multiple-create:return_reservation_id': True,
}
}
cs.assert_called('POST', '/servers', expected_body)
self.assert_called('POST', '/servers', expected_body)
self.assertIsInstance(s, servers.Server)
def test_update_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
# Update via instance
s.update(name='hi')
cs.assert_called('PUT', '/servers/1234')
self.assert_called('PUT', '/servers/1234')
s.update(name='hi')
cs.assert_called('PUT', '/servers/1234')
self.assert_called('PUT', '/servers/1234')
# Silly, but not an error
s.update()
# Update via manager
cs.servers.update(s, name='hi')
cs.assert_called('PUT', '/servers/1234')
self.cs.servers.update(s, name='hi')
self.assert_called('PUT', '/servers/1234')
def test_delete_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.delete()
cs.assert_called('DELETE', '/servers/1234')
cs.servers.delete(1234)
cs.assert_called('DELETE', '/servers/1234')
cs.servers.delete(s)
cs.assert_called('DELETE', '/servers/1234')
self.assert_called('DELETE', '/servers/1234')
self.cs.servers.delete(1234)
self.assert_called('DELETE', '/servers/1234')
self.cs.servers.delete(s)
self.assert_called('DELETE', '/servers/1234')
def test_delete_server_meta(self):
cs.servers.delete_meta(1234, ['test_key'])
cs.assert_called('DELETE', '/servers/1234/metadata/test_key')
self.cs.servers.delete_meta(1234, ['test_key'])
self.assert_called('DELETE', '/servers/1234/metadata/test_key')
def test_set_server_meta(self):
cs.servers.set_meta(1234, {'test_key': 'test_value'})
cs.assert_called('POST', '/servers/1234/metadata',
{'metadata': {'test_key': 'test_value'}})
self.cs.servers.set_meta(1234, {'test_key': 'test_value'})
self.assert_called('POST', '/servers/1234/metadata',
{'metadata': {'test_key': 'test_value'}})
def test_find(self):
server = cs.servers.find(name='sample-server')
cs.assert_called('GET', '/servers', pos=-2)
cs.assert_called('GET', '/servers/1234', pos=-1)
self.assertEqual(server.name, 'sample-server')
server = self.cs.servers.find(name='sample-server')
self.assert_called('GET', '/servers/1234')
self.assertEqual('sample-server', server.name)
self.assertRaises(exceptions.NoUniqueMatch, cs.servers.find,
self.assertRaises(exceptions.NoUniqueMatch, self.cs.servers.find,
flavor={"id": 1, "name": "256 MB Server"})
sl = cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"})
self.assertEqual([s.id for s in sl], [1234, 5678, 9012])
sl = self.cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"})
self.assertEqual([1234, 5678, 9012], [s.id for s in sl])
def test_reboot_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.reboot()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.reboot(s, reboot_type='HARD')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.reboot(s, reboot_type='HARD')
self.assert_called('POST', '/servers/1234/action')
def test_rebuild_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.rebuild(image=1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.rebuild(s, image=1)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.rebuild(s, image=1)
self.assert_called('POST', '/servers/1234/action')
s.rebuild(image=1, password='5678')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.rebuild(s, image=1, password='5678')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.rebuild(s, image=1, password='5678')
self.assert_called('POST', '/servers/1234/action')
def test_resize_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.resize(flavor=1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.resize(s, flavor=1)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.resize(s, flavor=1)
self.assert_called('POST', '/servers/1234/action')
def test_confirm_resized_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.confirm_resize()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.confirm_resize(s)
cs.assert_called('POST', '/servers/1234/action')
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 = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.revert_resize()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.revert_resize(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.revert_resize(s)
self.assert_called('POST', '/servers/1234/action')
def test_migrate_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.migrate()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.migrate(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.migrate(s)
self.assert_called('POST', '/servers/1234/action')
def test_add_fixed_ip(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.add_fixed_ip(1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.add_fixed_ip(s, 1)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.add_fixed_ip(s, 1)
self.assert_called('POST', '/servers/1234/action')
def test_remove_fixed_ip(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.remove_fixed_ip('10.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.remove_fixed_ip(s, '10.0.0.1')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.remove_fixed_ip(s, '10.0.0.1')
self.assert_called('POST', '/servers/1234/action')
def test_stop(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.stop()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.stop(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.stop(s)
self.assert_called('POST', '/servers/1234/action')
def test_force_delete(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.force_delete()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.force_delete(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.force_delete(s)
self.assert_called('POST', '/servers/1234/action')
def test_restore(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.restore()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.restore(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.restore(s)
self.assert_called('POST', '/servers/1234/action')
def test_start(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.start()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.start(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.start(s)
self.assert_called('POST', '/servers/1234/action')
def test_rescue(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.rescue()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.rescue(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.rescue(s)
self.assert_called('POST', '/servers/1234/action')
def test_unrescue(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.unrescue()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.unrescue(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.unrescue(s)
self.assert_called('POST', '/servers/1234/action')
def test_lock(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.lock()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.lock(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.lock(s)
self.assert_called('POST', '/servers/1234/action')
def test_unlock(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.unlock()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.unlock(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.unlock(s)
self.assert_called('POST', '/servers/1234/action')
def test_backup(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.backup('back1', 'daily', 1)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.backup(s, 'back1', 'daily', 2)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.backup(s, 'back1', 'daily', 2)
self.assert_called('POST', '/servers/1234/action')
def test_get_console_output_without_length(self):
success = 'foo'
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_console_output()
self.assertEqual(s.get_console_output(), success)
cs.assert_called('POST', '/servers/1234/action')
self.assertEqual(success, s.get_console_output())
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_console_output(s)
self.assertEqual(cs.servers.get_console_output(s), success)
cs.assert_called('POST', '/servers/1234/action',
{'get_console_output': {'length': -1}})
self.cs.servers.get_console_output(s)
self.assertEqual(success, self.cs.servers.get_console_output(s))
self.assert_called('POST', '/servers/1234/action',
{'get_console_output': {'length': -1}})
def test_get_console_output_with_length(self):
success = 'foo'
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_console_output(length=50)
self.assertEqual(s.get_console_output(length=50), success)
cs.assert_called('POST', '/servers/1234/action',
{'get_console_output': {'length': 50}})
self.assertEqual(success, s.get_console_output(length=50))
self.assert_called('POST', '/servers/1234/action',
{'get_console_output': {'length': 50}})
cs.servers.get_console_output(s, length=50)
self.assertEqual(cs.servers.get_console_output(s, length=50), success)
cs.assert_called('POST', '/servers/1234/action',
{'get_console_output': {'length': 50}})
self.cs.servers.get_console_output(s, length=50)
self.assertEqual(success,
self.cs.servers.get_console_output(s, length=50))
self.assert_called('POST', '/servers/1234/action',
{'get_console_output': {'length': 50}})
def test_get_password(self):
s = cs.servers.get(1234)
self.assertEqual(s.get_password('/foo/id_rsa'), '')
cs.assert_called('GET', '/servers/1234/os-server-password')
s = self.cs.servers.get(1234)
self.assertEqual('', s.get_password('/foo/id_rsa'))
self.assert_called('GET', '/servers/1234/os-server-password')
def test_clear_password(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.clear_password()
cs.assert_called('DELETE', '/servers/1234/os-server-password')
self.assert_called('DELETE', '/servers/1234/os-server-password')
def test_get_server_diagnostics(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
diagnostics = s.diagnostics()
self.assertTrue(diagnostics is not None)
cs.assert_called('GET', '/servers/1234/os-server-diagnostics')
self.assert_called('GET', '/servers/1234/os-server-diagnostics')
diagnostics_from_manager = cs.servers.diagnostics(1234)
diagnostics_from_manager = self.cs.servers.diagnostics(1234)
self.assertTrue(diagnostics_from_manager is not None)
cs.assert_called('GET', '/servers/1234/os-server-diagnostics')
self.assert_called('GET', '/servers/1234/os-server-diagnostics')
self.assertEqual(diagnostics, diagnostics_from_manager)
self.assertEqual(diagnostics_from_manager[1], diagnostics[1])
def test_get_vnc_console(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_vnc_console('fake')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_vnc_console(s, 'fake')
cs.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_vnc_console(s, 'fake')
self.assert_called('POST', '/servers/1234/action')
def test_get_spice_console(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.get_spice_console('fake')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
cs.servers.get_spice_console(s, 'fake')
cs.assert_called('POST', '/servers/1234/action')
self.cs.servers.get_spice_console(s, 'fake')
self.assert_called('POST', '/servers/1234/action')
def test_create_image(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.create_image('123')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
s.create_image('123', {})
cs.assert_called('POST', '/servers/1234/action')
cs.servers.create_image(s, '123')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.create_image(s, '123', {})
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.create_image(s, '123')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.create_image(s, '123', {})
def test_live_migrate_server(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.live_migrate(host='hostname', block_migration=False,
disk_over_commit=False)
cs.assert_called('POST', '/servers/1234/action')
cs.servers.live_migrate(s, host='hostname', block_migration=False,
disk_over_commit=False)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.live_migrate(s, host='hostname', block_migration=False,
disk_over_commit=False)
self.assert_called('POST', '/servers/1234/action')
def test_reset_state(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.reset_state('newstate')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.reset_state(s, 'newstate')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.reset_state(s, 'newstate')
self.assert_called('POST', '/servers/1234/action')
def test_reset_network(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.reset_network()
cs.assert_called('POST', '/servers/1234/action')
cs.servers.reset_network(s)
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.reset_network(s)
self.assert_called('POST', '/servers/1234/action')
def test_evacuate(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.evacuate('fake_target_host', 'True')
cs.assert_called('POST', '/servers/1234/action')
cs.servers.evacuate(s, 'fake_target_host', 'False', 'NewAdminPassword')
cs.assert_called('POST', '/servers/1234/action')
self.assert_called('POST', '/servers/1234/action')
self.cs.servers.evacuate(s, 'fake_target_host',
'False', 'NewAdminPassword')
self.assert_called('POST', '/servers/1234/action')
def test_interface_list(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.interface_list()
cs.assert_called('GET', '/servers/1234/os-attach-interfaces')
self.assert_called('GET', '/servers/1234/os-attach-interfaces')
def test_interface_attach(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.interface_attach(None, None, None)
cs.assert_called('POST', '/servers/1234/os-attach-interfaces')
self.assert_called('POST', '/servers/1234/os-attach-interfaces')
def test_interface_detach(self):
s = cs.servers.get(1234)
s = self.cs.servers.get(1234)
s.interface_detach('port-id')
cs.assert_called('DELETE',
'/servers/1234/os-attach-interfaces/port-id')
self.assert_called('DELETE',
'/servers/1234/os-attach-interfaces/port-id')

View File

@ -127,17 +127,29 @@ class ShellTest(utils.TestCase):
self.assert_called('PUT', '/os-aggregates/1', body, pos=-2)
self.assert_called('GET', '/os-aggregates/1', pos=-1)
def test_aggregate_set_metadata_by_id(self):
self.run_command('aggregate-set-metadata 1 foo=bar delete_key')
body = {"set_metadata": {"metadata": {"foo": "bar",
"delete_key": None}}}
self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2)
self.assert_called('GET', '/os-aggregates/1', pos=-1)
def test_aggregate_set_metadata_add_by_id(self):
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)
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 delete_key')
body = {"set_metadata": {"metadata": {"foo": "bar",
"delete_key": None}}}
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)
@ -235,7 +247,7 @@ class ShellTest(utils.TestCase):
'image_ref': '1',
'os-multiple-create:min_count': 1,
'os-multiple-create:max_count': 1,
'os-user-data:user_data': user_data}},
'user_data': user_data}},
)
def test_boot_avzone(self):
@ -261,8 +273,8 @@ class ShellTest(utils.TestCase):
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'os-security-groups:security_groups': [{'name': 'secgroup1'},
{'name': 'secgroup2'}],
'security_groups': [{'name': 'secgroup1'},
{'name': 'secgroup2'}],
'flavor_ref': '1',
'name': 'some-server',
'image_ref': '1',
@ -325,11 +337,14 @@ class ShellTest(utils.TestCase):
{'server': {
'flavor_ref': '1',
'name': 'some-server',
'os-block-device-mapping:block_device_mapping': [
'block_device_mapping': [
{
'volume_id': 'blah',
'delete_on_termination': '0',
'device_name': 'vda'
'device_name': 'vda',
'boot_index': 0,
'uuid': 'blah',
'source_type': ''
}
],
'image_ref': '',
@ -344,15 +359,16 @@ class ShellTest(utils.TestCase):
'source=volume,dest=volume,device=vda,size=1,format=ext4,'
'type=disk,shutdown=preserve some-server'
)
id = ('fake-id,source=volume,dest=volume,device=vda,size=1,'
'format=ext4,type=disk,shutdown=preserve')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavor_ref': '1',
'name': 'some-server',
'os-block-device-mapping:block_device_mapping': [
{'device_name': 'id', 'volume_id':
'fake-id,source=volume,dest=volume,device=vda,size=1,'
'format=ext4,type=disk,shutdown=preserve'}],
'block_device_mapping': [
{'device_name': 'id', 'volume_id': id,
'source_type': 'volume', 'boot_index': 0, 'uuid': id}],
'image_ref': '1',
'os-multiple-create:min_count': 1,
'os-multiple-create:max_count': 1,
@ -550,7 +566,7 @@ class ShellTest(utils.TestCase):
'os-multiple-create:max_count': 1,
}},
)
self.assertEqual(poll_method.call_count, 1)
self.assertEqual(1, poll_method.call_count)
poll_method.assert_has_calls(
[mock.call(self.shell.cs.servers.get, 1234, 'building',
['active'])])
@ -559,6 +575,39 @@ class ShellTest(utils.TestCase):
self.assertRaises(exceptions.InstanceInErrorState, self.run_command,
'boot --flavor 1 --image 1 some-bad-server --poll')
def test_evacuate(self):
self.run_command('evacuate sample-server new_host')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'on_shared_storage': False}})
self.run_command('evacuate sample-server new_host '
'--password NewAdminPass')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'on_shared_storage': False,
'admin_password': 'NewAdminPass'}})
self.run_command('evacuate sample-server new_host')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'on_shared_storage': False}})
self.run_command('evacuate sample-server new_host '
'--on-shared-storage')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'host': 'new_host',
'on_shared_storage': True}})
def test_evacuate_with_no_target_host(self):
self.run_command('evacuate sample-server')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'on_shared_storage': False}})
self.run_command('evacuate sample-server --password NewAdminPass')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'on_shared_storage': False,
'admin_password': 'NewAdminPass'}})
self.run_command('evacuate sample-server --on-shared-storage')
self.assert_called('POST', '/servers/1234/action',
{'evacuate': {'on_shared_storage': True}})
def test_boot_named_flavor(self):
self.run_command(["boot", "--image", "1",
"--flavor", "512 MB Server",
@ -594,3 +643,82 @@ class ShellTest(utils.TestCase):
self.assert_called('GET', '/flavors?is_public=None', pos=2)
self.assert_called('GET', '/flavors/2', pos=3)
self.assert_called('GET', '/flavors/2/flavor-extra-specs', pos=4)
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_delete_two_with_two_existent(self):
self.run_command('delete 1234 5678')
self.assert_called('DELETE', '/servers/1234', pos=-3)
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_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)
class GetFirstEndpointTest(utils.TestCase):
def test_only_one_endpoint(self):
"""If there is only one endpoint, it is returned."""
endpoint = {"url": "test"}
result = novaclient.v3.shell._get_first_endpoint([endpoint], "XYZ")
self.assertEqual(endpoint, result)
def test_multiple_endpoints(self):
"""If there are multiple endpoints, the first one of the appropriate
region is returned.
"""
endpoints = [
{"region": "XYZ"},
{"region": "ORD", "number": 1},
{"region": "ORD", "number": 2}
]
result = novaclient.v3.shell._get_first_endpoint(endpoints, "ORD")
self.assertEqual(endpoints[1], result)
def test_multiple_endpoints_but_none_suitable(self):
"""If there are multiple endpoints but none of them are suitable, an
exception is raised.
"""
endpoints = [
{"region": "XYZ"},
{"region": "PQR"},
{"region": "STU"}
]
self.assertRaises(LookupError,
novaclient.v3.shell._get_first_endpoint,
endpoints, "ORD")
def test_no_endpoints(self):
"""If there are no endpoints available, an exception is raised."""
self.assertRaises(LookupError,
novaclient.v3.shell._get_first_endpoint,
[], "ORD")

View File

@ -13,20 +13,24 @@
import json
import re
import sys
import textwrap
import uuid
from oslo.serialization import jsonutils
from oslo.utils import encodeutils
import pkg_resources
import prettytable
import six
from novaclient import exceptions
from novaclient.i18n import _
from novaclient.openstack.common import cliutils
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import jsonutils
arg = cliutils.arg
env = cliutils.env
VALID_KEY_REGEX = re.compile(r"[\w\.\- :]+$", re.UNICODE)
@ -67,10 +71,34 @@ def get_resource_manager_extra_kwargs(f, args, allow_conflicts=False):
return extra_kwargs
def service_type(stype):
"""
Adds 'service_type' attribute to decorated function.
Usage:
@service_type('volume')
def mymethod(f):
...
"""
def inner(f):
f.service_type = stype
return f
return inner
def get_service_type(f):
"""
Retrieves service type from function
"""
return getattr(f, 'service_type', None)
def pretty_choice_list(l):
return ', '.join("'%s'" % i for i in l)
def pretty_choice_dict(d):
"""Returns a formatted dict as 'key=value'."""
return cliutils.pretty_choice_list(
['%s=%s' % (k, d[k]) for k in sorted(d.keys())])
return pretty_choice_list(['%s=%s' % (k, d[k]) for k in sorted(d.keys())])
def print_list(objs, fields, formatters={}, sortby_index=None):
@ -208,6 +236,12 @@ def find_resource(manager, name_or_id, **find_args):
pass
try:
try:
return manager.find(human_id=name_or_id, **find_args)
except exceptions.NotFound:
pass
# finally try to find entity by name
try:
resource = getattr(manager, 'resource_class', None)
name_attr = resource.NAME_ATTR if resource else 'name'
@ -215,15 +249,9 @@ def find_resource(manager, name_or_id, **find_args):
kwargs.update(find_args)
return manager.find(**kwargs)
except exceptions.NotFound:
pass
# finally try to find entity by human_id
try:
return manager.find(human_id=name_or_id, **find_args)
except 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})
msg = _("No %(class)s with a name or ID of '%(name)s' exists.") % \
{'class': manager.resource_class.__name__.lower(),
'name': name_or_id}
raise exceptions.CommandError(msg)
except exceptions.NoUniqueMatch:
msg = (_("Multiple %(class)s matches found for '%(name)s', use an ID "
@ -285,6 +313,24 @@ def _make_field_formatter(attr, filters=None):
return name, formatter
class HookableMixin(object):
"""Mixin so classes can register and run hooks."""
_hooks_map = {}
@classmethod
def add_hook(cls, hook_type, hook_func):
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):
hook_funcs = cls._hooks_map.get(hook_type) or []
for hook_func in hook_funcs:
hook_func(*args, **kwargs)
def safe_issubclass(*args):
"""Like issubclass, but will just return False if not a class."""
@ -297,20 +343,11 @@ def safe_issubclass(*args):
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(e)
if failure_flag:
raise exceptions.CommandError(error_msg)
def import_class(import_str):
"""Returns a class from a string including module and class."""
mod_str, _sep, class_str = import_str.rpartition('.')
__import__(mod_str)
return getattr(sys.modules[mod_str], class_str)
def _load_entry_point(ep_name, name=None):

View File

@ -1,3 +1,4 @@
# Copyright (c) 2012 OpenStack Foundation
#
# All Rights Reserved.
#
@ -13,31 +14,4 @@
# License for the specific language governing permissions and limitations
# under the License.
# NOTE(akurilin): This module is left for backward compatibility. Feel free to
# remove it, when openstack project will use correct way to
# obtain novaclient object.
# Known problems:
# * python-openstackclient -
# https://bugs.launchpad.net/python-openstackclient/+bug/1418024
# * neutron - https://bugs.launchpad.net/neutron/+bug/1418017
import sys
import warnings
from novaclient import v2
warnings.warn("Module novaclient.v1_1 is deprecated (taken as a basis for "
"novaclient.v2). "
"The preferable way to get client class or object you can find "
"in novaclient.client module.")
class MovedModule(object):
def __init__(self, new_module):
self.new_module = new_module
def __getattr__(self, attr):
return getattr(self.new_module, attr)
sys.modules["novaclient.v1_1"] = MovedModule(v2)
from novaclient.v1_1.client import Client # noqa

View File

@ -35,12 +35,14 @@ from novaclient.v1_1 import limits
from novaclient.v1_1 import networks
from novaclient.v1_1 import quota_classes
from novaclient.v1_1 import quotas
from novaclient.v1_1 import security_group_default_rules
from novaclient.v1_1 import security_group_rules
from novaclient.v1_1 import security_groups
from novaclient.v1_1 import server_groups
from novaclient.v1_1 import servers
from novaclient.v1_1 import services
from novaclient.v1_1 import usage
from novaclient.v1_1 import versions
from novaclient.v1_1 import virtual_interfaces
from novaclient.v1_1 import volume_snapshots
from novaclient.v1_1 import volume_types
@ -55,6 +57,19 @@ class Client(object):
>>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL)
Or, alternatively, you can create a client instance using the
keystoneclient.session API::
>>> from keystoneclient.auth.identity import v2
>>> from keystoneclient import session
>>> from novaclient.client import Client
>>> auth = v2.Password(auth_url=AUTH_URL,
username=USERNAME,
password=PASSWORD,
tenant_name=PROJECT_ID)
>>> sess = session.Session(auth=auth)
>>> nova = client.Client(VERSION, session=sess)
Then call methods on its managers::
>>> client.servers.list()
@ -107,6 +122,7 @@ class Client(object):
self.images = images.ImageManager(self)
self.limits = limits.LimitsManager(self)
self.servers = servers.ServerManager(self)
self.versions = versions.VersionManager(self)
# extensions
self.agents = agents.AgentsManager(self)
@ -127,6 +143,8 @@ class Client(object):
self.security_groups = security_groups.SecurityGroupManager(self)
self.security_group_rules = \
security_group_rules.SecurityGroupRuleManager(self)
self.security_group_default_rules = \
security_group_default_rules.SecurityGroupDefaultRuleManager(self)
self.usage = usage.UsageManager(self)
self.virtual_interfaces = \
virtual_interfaces.VirtualInterfaceManager(self)

View File

@ -19,7 +19,7 @@ from novaclient import base
class Cloudpipe(base.Resource):
"""A cloudpipe instance is a VPN attached to a proejct's VLAN."""
"""A cloudpipe instance is a VPN attached to a project's VLAN."""
def __repr__(self):
return "<Cloudpipe: %s>" % self.project_id

View File

@ -26,8 +26,8 @@ def _server_evacuate(cs, server, args):
success = True
error_message = ""
try:
cs.servers.evacuate(server['uuid'], args.target_host,
args.on_shared_storage)
cs.servers.evacuate(server=server['uuid'], host=args.target_host,
on_shared_storage=args.on_shared_storage)
except Exception as e:
success = False
error_message = _("Error while evacuating instance: %s") % e
@ -41,7 +41,9 @@ def _server_evacuate(cs, server, args):
@utils.arg('--target_host',
metavar='<target_host>',
default=None,
help=_('Name of target host.'))
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",
@ -49,7 +51,7 @@ def _server_evacuate(cs, server, args):
help=_('Specifies whether all instances files are on shared '
' storage'))
def do_host_evacuate(cs, args):
"""Evacuate all instances from failed host to specified one."""
"""Evacuate all instances from failed host."""
hypervisors = cs.hypervisors.search(args.host, servers=True)
response = []
for hyper in hypervisors:

View File

@ -16,12 +16,12 @@
Flavor interface.
"""
from oslo.utils import strutils
from six.moves.urllib import parse
from novaclient import base
from novaclient import exceptions
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import strutils
from novaclient import utils

View File

@ -21,7 +21,7 @@ from novaclient import base
class Host(base.Resource):
def __repr__(self):
return "<Host: %s>" % self.host_name
return "<Host: %s>" % self.host
def _add_details(self, info):
dico = 'resource' in info and info['resource'] or info

View File

@ -26,7 +26,7 @@ class Network(base.Resource):
"""
A network.
"""
HUMAN_ID = False
HUMAN_ID = True
NAME_ATTR = "label"
def __repr__(self):
@ -89,8 +89,14 @@ class NetworkManager(base.ManagerWithFind):
:param vlan: int
:param vlan_start: int
:param vpn_start: int
:param mtu: int
:param enable_dhcp: int
:param dhcp_server: str
:param share_address: int
:param allowed_start: str
:param allowed_end: str
:rtype: list of :class:`Network`
:rtype: object of :class:`Network`
"""
body = {"network": kwargs}
return self._create('/os-networks', body, 'network')

View File

@ -0,0 +1,81 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Security group default rules interface.
"""
from novaclient import base
from novaclient import exceptions
from novaclient.openstack.common.gettextutils import _
class SecurityGroupDefaultRule(base.Resource):
def __str__(self):
return str(self.id)
def delete(self):
self.manager.delete(self)
class SecurityGroupDefaultRuleManager(base.Manager):
resource_class = SecurityGroupDefaultRule
def create(self, ip_protocol=None, from_port=None, to_port=None,
cidr=None):
"""
Create a security group default rule
:param ip_protocol: IP protocol, one of 'tcp', 'udp' or 'icmp'
:param from_port: Source port
:param to_port: Destination port
:param cidr: Destination IP address(es) in CIDR notation
"""
try:
from_port = int(from_port)
except (TypeError, ValueError):
raise exceptions.CommandError(_("From port must be an integer."))
try:
to_port = int(to_port)
except (TypeError, ValueError):
raise exceptions.CommandError(_("To port must be an integer."))
if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']:
raise exceptions.CommandError(_("Ip protocol must be 'tcp', 'udp'"
", or 'icmp'."))
body = {"security_group_default_rule": {
"ip_protocol": ip_protocol,
"from_port": from_port,
"to_port": to_port,
"cidr": cidr}}
return self._create('/os-security-group-default-rules', body,
'security_group_default_rule')
def delete(self, rule):
"""
Delete a security group default rule
:param rule: The security group default rule to delete (ID or Class)
"""
self._delete('/os-security-group-default-rules/%s' % base.getid(rule))
def list(self):
"""
Get a list of all security group default rules
:rtype: list of :class:`SecurityGroupDefaultRule`
"""
return self._list('/os-security-group-default-rules',
'security_group_default_rules')

View File

@ -21,14 +21,15 @@ Server interface.
import base64
from oslo.utils import encodeutils
import six
from six.moves.urllib import parse
from novaclient import base
from novaclient import crypto
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import strutils
from novaclient.v1_1.security_groups import SecurityGroup
from novaclient.v1_1 import security_groups
REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD'
@ -85,6 +86,14 @@ class Server(base.Resource):
"""
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_password(self, private_key=None):
"""
Get password for a Server.
@ -364,7 +373,7 @@ class Server(base.Resource):
"""
return self.manager.list_security_group(self)
def evacuate(self, host, on_shared_storage, password=None):
def evacuate(self, host=None, on_shared_storage=True, password=None):
"""
Evacuate an instance from failed host to specified host.
@ -419,7 +428,7 @@ class ServerManager(base.BootingManagerWithFind):
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 return_raw: If True, don't try to coearse the result into
:param return_raw: If True, don't try to coerce the result into
a Resource object.
:param security_groups: list of security group names
:param key_name: (optional extension) name of keypair to inject into
@ -453,7 +462,7 @@ class ServerManager(base.BootingManagerWithFind):
if six.PY3:
userdata = userdata.encode("utf-8")
else:
userdata = strutils.safe_encode(userdata)
userdata = encodeutils.safe_encode(userdata)
userdata_b64 = base64.b64encode(userdata).decode('utf-8')
body["server"]["user_data"] = userdata_b64
@ -674,6 +683,17 @@ class ServerManager(base.BootingManagerWithFind):
return self._action('os-getRDPConsole', server,
{'type': console_type})[1]
def get_serial_console(self, server, console_type):
"""
Get a serial console for an instance
:param server: The :class:`Server` (or its ID) to add an IP to.
:param console_type: Type of serial console to get ('serial')
"""
return self._action('os-getSerialConsole', server,
{'type': console_type})[1]
def get_password(self, server, private_key=None):
"""
Get password for an instance
@ -922,7 +942,8 @@ class ServerManager(base.BootingManagerWithFind):
self._action('reboot', server, {'type': reboot_type})
def rebuild(self, server, image, password=None, disk_config=None,
preserve_ephemeral=False, **kwargs):
preserve_ephemeral=False, name=None, meta=None, files=None,
**kwargs):
"""
Rebuild -- shut down and then re-image -- a server.
@ -933,6 +954,15 @@ class ServerManager(base.BootingManagerWithFind):
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. A maximum of five entries is allowed, and both
keys and values must be 255 characters or less.
: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.
"""
body = {'imageRef': base.getid(image)}
if password is not None:
@ -941,6 +971,24 @@ class ServerManager(base.BootingManagerWithFind):
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 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('rebuild', server, body, **kwargs)
return Server(self, body['server'])
@ -1119,9 +1167,11 @@ class ServerManager(base.BootingManagerWithFind):
"""
return self._list('/servers/%s/os-security-groups' %
base.getid(server), 'security_groups', SecurityGroup)
base.getid(server), 'security_groups',
security_groups.SecurityGroup)
def evacuate(self, server, host, on_shared_storage, password=None):
def evacuate(self, server, host=None, on_shared_storage=True,
password=None):
"""
Evacuate a server instance.
@ -1131,10 +1181,10 @@ class ServerManager(base.BootingManagerWithFind):
on shared storage
:param password: string to set as password on the evacuated server.
"""
body = {
'host': host,
'onSharedStorage': on_shared_storage,
}
body = {'onSharedStorage': on_shared_storage}
if host is not None:
body['host'] = host
if password is not None:
body['adminPass'] = password

View File

@ -23,16 +23,18 @@ import copy
import datetime
import getpass
import locale
import logging
import os
import sys
import time
from oslo.utils import encodeutils
from oslo.utils import strutils
from oslo.utils import timeutils
import six
from novaclient import exceptions
from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import strutils
from novaclient.openstack.common import timeutils
from novaclient.openstack.common import uuidutils
from novaclient import utils
from novaclient.v1_1 import availability_zones
@ -40,6 +42,9 @@ from novaclient.v1_1 import quotas
from novaclient.v1_1 import servers
logger = logging.getLogger(__name__)
CLIENT_BDM2_KEYS = {
'id': 'uuid',
'source': 'source_type',
@ -356,6 +361,7 @@ def _boot(cs, args):
help=_("Store arbitrary files from <src-path> locally to <dst-path> "
"on the new server. You may store up to 5 files."))
@utils.arg('--key-name',
default=os.environ.get('NOVACLIENT_DEFAULT_KEY_NAME'),
metavar='<key-name>',
help=_("Key name of keypair that should be created earlier with \
the command keypair-add"))
@ -506,7 +512,7 @@ def _poll_for_status(poll_fn, obj_id, action, final_ok_states,
sys.stdout.flush()
if not silent:
print
print()
while True:
obj = poll_fn(obj_id)
@ -586,7 +592,7 @@ def _print_flavor_list(flavors, show_extra_specs=False):
'Memory_MB',
'Disk',
'Ephemeral',
'Swap_MB',
'Swap',
'VCPUs',
'RXTX_Factor',
'Is_Public',
@ -778,10 +784,25 @@ def do_scrub(cs, args):
cs.security_groups.delete(group)
def do_network_list(cs, _args):
@utils.arg('--fields',
default=None,
metavar='<fields>',
help='Comma-separated list of fields to display. '
'Use the show command to see which fields are available.')
def do_network_list(cs, args):
"""Print a list of available networks."""
network_list = cs.networks.list()
columns = ['ID', 'Label', 'Cidr']
formatters = {}
field_titles = []
if args.fields:
for field in args.fields.split(','):
field_title, formatter = utils._make_field_formatter(field, {})
field_titles.append(field_title)
formatters[field_title] = formatter
columns = columns + field_titles
utils.print_list(network_list, columns)
@ -794,6 +815,15 @@ def do_network_show(cs, args):
utils.print_dict(network._info)
@utils.arg('network',
metavar='<network>',
help=_("uuid or label of network"))
def do_network_delete(cs, args):
"""Delete network by label or id."""
network = utils.find_resource(cs.networks, args.network)
network.delete()
@utils.arg('--host-only',
dest='host_only',
metavar='<0|1>',
@ -844,7 +874,8 @@ def _filter_network_create_options(args):
valid_args = ['label', 'cidr', 'vlan_start', 'vpn_start', 'cidr_v6',
'gateway', 'gateway_v6', 'bridge', 'bridge_interface',
'multi_host', 'dns1', 'dns2', 'uuid', 'fixed_cidr',
'project_id', 'priority', 'vlan']
'project_id', 'priority', 'vlan', 'mtu', 'dhcp_server',
'allowed_start', 'allowed_end']
kwargs = {}
for k, v in args.__dict__.items():
if k in valid_args and v is not None:
@ -865,15 +896,18 @@ def _filter_network_create_options(args):
help=_('IPv6 subnet (ex: fe80::/64'))
@utils.arg('--vlan',
dest='vlan',
type=int,
metavar='<vlan id>',
help=_("vlan id to be assigned to project"))
help=_("The vlan ID to be assigned to the project."))
@utils.arg('--vlan-start',
dest='vlan_start',
type=int,
metavar='<vlan start>',
help=_('First vlan ID to be assigned to project. Subsequent vlan'
' IDs will be assigned incrementally'))
help=_('First vlan ID to be assigned to the project. Subsequent vlan '
'IDs will be assigned incrementally.'))
@utils.arg('--vpn',
dest='vpn_start',
type=int,
metavar='<vpn start>',
help=_("vpn start"))
@utils.arg('--gateway',
@ -881,15 +915,15 @@ def _filter_network_create_options(args):
help=_('gateway'))
@utils.arg('--gateway-v6',
dest="gateway_v6",
help=_('ipv6 gateway'))
help=_('IPv6 gateway'))
@utils.arg('--bridge',
dest="bridge",
metavar='<bridge>',
help=_('VIFs on this network are connected to this bridge'))
help=_('VIFs on this network are connected to this bridge.'))
@utils.arg('--bridge-interface',
dest="bridge_interface",
metavar='<bridge interface>',
help=_('the bridge is connected to this interface'))
help=_('The bridge is connected to this interface.'))
@utils.arg('--multi-host',
dest="multi_host",
metavar="<'T'|'F'>",
@ -908,25 +942,52 @@ def _filter_network_create_options(args):
@utils.arg('--fixed-cidr',
dest="fixed_cidr",
metavar='<x.x.x.x/yy>',
help=_('IPv4 subnet for fixed IPS (ex: 10.20.0.0/16)'))
help=_('IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)'))
@utils.arg('--project-id',
dest="project_id",
metavar="<project id>",
help=_('Project id'))
help=_('Project ID'))
@utils.arg('--priority',
dest="priority",
metavar="<number>",
help=_('Network interface priority'))
@utils.arg('--mtu',
dest="mtu",
type=int,
help=_('MTU for network'))
@utils.arg('--enable-dhcp',
dest="enable_dhcp",
metavar="<'T'|'F'>",
help=_('Enable dhcp'))
@utils.arg('--dhcp-server',
dest="dhcp_server",
help=_('Dhcp-server (defaults to gateway address)'))
@utils.arg('--share-address',
dest="share_address",
metavar="<'T'|'F'>",
help=_('Share address'))
@utils.arg('--allowed-start',
dest="allowed_start",
help=_('Start of allowed addresses for instances'))
@utils.arg('--allowed-end',
dest="allowed_end",
help=_('End of allowed addresses for instances'))
def do_network_create(cs, args):
"""Create a network."""
if not (args.cidr or args.cidr_v6):
raise exceptions.CommandError(
_("Must specify eith fixed_range_v4 or fixed_range_v6"))
_("Must specify either fixed_range_v4 or fixed_range_v6"))
kwargs = _filter_network_create_options(args)
if args.multi_host is not None:
kwargs['multi_host'] = bool(args.multi_host == 'T' or
strutils.bool_from_string(args.multi_host))
if args.enable_dhcp is not None:
kwargs['enable_dhcp'] = bool(args.enable_dhcp == 'T' or
strutils.bool_from_string(args.enable_dhcp))
if args.share_address is not None:
kwargs['share_address'] = bool(args.share_address == 'T' or
strutils.bool_from_string(args.share_address))
cs.networks.create(**kwargs)
@ -934,7 +995,7 @@ def do_network_create(cs, args):
@utils.arg('--limit',
dest="limit",
metavar="<limit>",
help=_('number of images to return per request'))
help=_('Number of images to return per request.'))
def do_image_list(cs, _args):
"""Print a list of available images to boot from."""
limit = _args.limit
@ -1110,7 +1171,7 @@ def do_image_delete(cs, args):
const=1,
help=argparse.SUPPRESS)
@utils.arg('--tenant',
#nova db searches by project_id
# nova db searches by project_id
dest='tenant',
metavar='<tenant>',
nargs='?',
@ -1242,6 +1303,23 @@ def do_reboot(cs, args):
action="store_true",
default=False,
help='Preserve the default ephemeral storage partition on rebuild.')
@utils.arg('--name',
metavar='<name>',
default=None,
help=_('Name for the new server'))
@utils.arg('--meta',
metavar="<key=value>",
action='append',
default=[],
help=_("Record arbitrary key/value metadata to /meta.js "
"on the new server. Can be specified multiple times."))
@utils.arg('--file',
metavar="<dst-path=src-path>",
action='append',
dest='files',
default=[],
help=_("Store arbitrary files from <src-path> locally to <dst-path> "
"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)
@ -1254,6 +1332,25 @@ def do_rebuild(cs, args):
kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args)
kwargs['preserve_ephemeral'] = args.preserve_ephemeral
kwargs['name'] = args.name
meta = dict(v.split('=', 1) for v in 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 as e:
raise exceptions.CommandError(_("Invalid file argument '%s'. "
"File arguments must be of the "
"form '--file "
"<dst-path=src-path>'") % f)
kwargs['files'] = files
server = server.rebuild(image, _password, **kwargs)
_print_server(cs, args, server)
@ -1529,8 +1626,11 @@ def _print_server(cs, args, server=None):
flavor_id)
if 'security_groups' in info:
info['security_groups'] = \
', '.join(group['name'] for group in info['security_groups'])
# 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:
@ -1567,19 +1667,19 @@ def do_show(cs, args):
help=_('Name or ID of server(s).'))
def do_delete(cs, args):
"""Immediately shut down and delete specified server(s)."""
failure_count = 0
failure_flag = False
for server in args.server:
try:
_find_server(cs, server).delete()
print(_("Request to delete server %s has been accepted.") % server)
except Exception as e:
failure_count += 1
failure_flag = True
print(e)
if failure_count == len(args.server):
raise exceptions.CommandError(_("Unable to delete any of the "
"specified servers."))
if failure_flag:
raise exceptions.CommandError(_("Unable to delete the "
"specified server(s)."))
def _find_server(cs, server):
@ -1940,6 +2040,27 @@ def do_get_rdp_console(cs, args):
utils.print_list([RDPConsole(data['console'])], ['Type', 'Url'])
@utils.arg('server', metavar='<server>', 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)
class SerialConsole:
def __init__(self, console_dict):
self.type = console_dict['type']
self.url = console_dict['url']
utils.print_list([SerialConsole(data['console'])], ['Type', 'Url'])
@utils.arg('server', metavar='<server>', help='Name or ID of server.')
@utils.arg('private_key',
metavar='<private-key>',
@ -2194,7 +2315,7 @@ def do_dns_create_public_domain(cs, args):
args.project)
def _print_secgroup_rules(rules):
def _print_secgroup_rules(rules, show_source_group=True):
class FormattedRule:
def __init__(self, obj):
items = (obj if isinstance(obj, dict) else obj._info).items()
@ -2210,8 +2331,10 @@ def _print_secgroup_rules(rules):
setattr(self, k, v)
rules = [FormattedRule(rule) for rule in rules]
utils.print_list(rules, ['IP Protocol', 'From Port', 'To Port',
'IP Range', 'Source Group'])
headers = ['IP Protocol', 'From Port', 'To Port', 'IP Range']
if show_source_group:
headers.append('Source Group')
utils.print_list(rules, headers)
def _print_secgroups(secgroups):
@ -2220,7 +2343,7 @@ def _print_secgroups(secgroups):
def _get_secgroup(cs, secgroup):
# Check secgroup is an ID (nova-network) or UUID (neutron)
if (utils.is_integer_like(strutils.safe_encode(secgroup))
if (utils.is_integer_like(encodeutils.safe_encode(secgroup))
or uuidutils.is_uuid_like(secgroup)):
try:
return cs.security_groups.get(secgroup)
@ -2504,7 +2627,7 @@ def _find_keypair(cs, keypair):
@utils.arg('--tenant',
#nova db searches by project_id
# nova db searches by project_id
dest='tenant',
metavar='<tenant>',
nargs='?',
@ -2786,6 +2909,14 @@ 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)
@ -3102,9 +3233,41 @@ def ensure_service_catalog_present(cs):
def do_endpoints(cs, _args):
"""Discover endpoints that get returned from the authenticate services."""
ensure_service_catalog_present(cs)
catalog = cs.client.service_catalog.catalog
for e in catalog['access']['serviceCatalog']:
utils.print_dict(e['endpoints'][0], e['name'])
region = cs.client.region_name
for service in catalog['access']['serviceCatalog']:
name, endpoints = service["name"], service["endpoints"]
try:
endpoint = _get_first_endpoint(endpoints, region)
utils.print_dict(endpoint, name)
except LookupError:
print(_("WARNING: %(service)s has no endpoint in %(region)s! "
"Available endpoints for this service:") %
{'service': name, 'region': region})
for other_endpoint in endpoints:
utils.print_dict(other_endpoint, name)
def _get_first_endpoint(endpoints, region):
"""Find the first suitable endpoint in endpoints.
If there is only one endpoint, return it. If there is more than
one endpoint, return the first one with the given region. If there
are no endpoints, or there is more than one endpoint but none of
them match the given region, raise KeyError.
"""
if len(endpoints) == 1:
return endpoints[0]
else:
for candidate_endpoint in endpoints:
if candidate_endpoint["region"] == region:
return candidate_endpoint
raise LookupError("No suitable endpoint found")
@utils.arg('--wrap', dest='wrap', metavar='<integer>', default=64,
@ -3130,9 +3293,16 @@ def do_credentials(cs, _args):
dest='private',
action='store_true',
default=False,
help=_('Optional flag to indicate whether to only use private address '
'attached to an instance. (Default=False). If no public address is '
'found try private address'))
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='<network>',
help=_('Network to use for the ssh.'), default=None)
@utils.arg('--ipv6',
dest='ipv6',
action='store_true',
@ -3157,42 +3327,65 @@ def do_ssh(cs, args):
args.server = server
addresses = _find_server(cs, args.server).addresses
address_type = "private" if args.private else "public"
address_type = "fixed" if args.private else args.address_type
version = 6 if args.ipv6 else 4
pretty_version = 'IPv%d' % version
if (address_type == "public" and address_type not in addresses and
"private" in addresses):
address_type = "private"
# 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]
if address_type not in addresses:
print(_("ERROR: No %(addr_type)s addresses found for '%(server)s'.") %
{'addr_type': address_type, 'server': args.server})
return
ip_address = None
for address in addresses[address_type]:
if address['version'] == version:
ip_address = address['addr']
break
# 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 ''
if ip_address:
os.system("ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity,
args.login, ip_address,
args.extra))
else:
pretty_version = "IPv%d" % version
print(_("ERROR: No %(addr_type)s %(pretty_version)s address found.") %
{'addr_type': address_type, 'pretty_version': pretty_version})
return
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)
_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']
'security_groups', 'security_group_rules',
'server_groups', 'server_group_members']
def _quota_show(quotas):
@ -3337,6 +3530,16 @@ def do_quota_defaults(cs, args):
type=int,
default=None,
help=_('New value for the "security-group-rules" quota.'))
@utils.arg('--server-groups',
metavar='<server-groups>',
type=int,
default=None,
help=_('New value for the "server-groups" quota.'))
@utils.arg('--server-group-members',
metavar='<server-group-members>',
type=int,
default=None,
help=_('New value for the "server-group-members" quota.'))
@utils.arg('--force',
dest='force',
action="store_true",
@ -3351,6 +3554,7 @@ def do_quota_update(cs, args):
@utils.arg('--tenant',
metavar='<tenant-id>',
required=True,
help=_('ID of tenant to delete quota for.'))
@utils.arg('--user',
metavar='<user-id>',
@ -3444,6 +3648,16 @@ def do_quota_class_show(cs, args):
type=int,
default=None,
help=_('New value for the "security-group-rules" quota.'))
@utils.arg('--server-groups',
metavar='<server-groups>',
type=int,
default=None,
help=_('New value for the "server-groups" quota.'))
@utils.arg('--server-group-members',
metavar='<server-group-members>',
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."""
@ -3451,11 +3665,12 @@ def do_quota_class_update(cs, args):
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('host', metavar='<host>', help=_('Name or ID of target host.'))
@utils.arg('host', metavar='<host>', 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='<password>',
default=None,
help=_("Set the provided password on the evacuated server. Not applicable "
"with on-shared-storage flag"))
@utils.arg('--on-shared-storage',
@ -3464,7 +3679,8 @@ def do_quota_class_update(cs, args):
default=False,
help=_('Specifies whether server files are located on shared storage'))
def do_evacuate(cs, args):
"""Evacuate server from failed host to specified one."""
"""Evacuate server from failed host."""
server = _find_server(cs, args.server)
res = server.evacuate(args.host, args.on_shared_storage, args.password)[1]
@ -3597,6 +3813,55 @@ def do_server_group_list(cs, args):
_print_server_group_details(server_groups)
def do_secgroup_list_default_rules(cs, args):
"""List rules for the default security group."""
_print_secgroup_rules(cs.security_group_default_rules.list(),
show_source_group=False)
@utils.arg('ip_proto',
metavar='<ip-proto>',
help=_('IP protocol (icmp, tcp, udp).'))
@utils.arg('from_port',
metavar='<from-port>',
help=_('Port at start of range.'))
@utils.arg('to_port',
metavar='<to-port>',
help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
def do_secgroup_add_default_rule(cs, args):
"""Add a rule to the default security group."""
rule = cs.security_group_default_rules.create(args.ip_proto,
args.from_port,
args.to_port,
args.cidr)
_print_secgroup_rules([rule], show_source_group=False)
@utils.arg('ip_proto',
metavar='<ip-proto>',
help=_('IP protocol (icmp, tcp, udp).'))
@utils.arg('from_port',
metavar='<from-port>',
help=_('Port at start of range.'))
@utils.arg('to_port',
metavar='<to-port>',
help=_('Port at end of range.'))
@utils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
def do_secgroup_delete_default_rule(cs, args):
"""Delete a rule from the default security group."""
for rule in cs.security_group_default_rules.list():
if (rule.ip_protocol and
rule.ip_protocol.upper() == args.ip_proto.upper() and
rule.from_port == int(args.from_port) and
rule.to_port == int(args.to_port) and
rule.ip_range['cidr'] == args.cidr):
_print_secgroup_rules([rule], show_source_group=False)
return cs.security_group_default_rules.delete(rule.id)
raise exceptions.CommandError(_("Rule not found"))
@utils.arg('name', metavar='<name>', help='Server group name.')
# NOTE(wingwj): The '--policy' way is still reserved here for preserving
# the backwards compatibility of CLI, even if a user won't get this usage
@ -3654,3 +3919,10 @@ def do_server_group_get(cs, args):
"""Get a specific server group."""
server_group = cs.server_groups.get(args.id)
_print_server_group_details([server_group])
def do_version_list(cs, args):
"""List all API versions."""
result = cs.versions.list()
columns = ["Id", "Status", "Updated"]
utils.print_list(result, columns)

View File

@ -0,0 +1,35 @@
# 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 novaclient import base
class Version(base.Resource):
"""
Compute REST API information
"""
def __repr__(self):
return "<Version>"
class VersionManager(base.ManagerWithFind):
resource_class = Version
def list(self):
"""List all versions."""
return self._list(None, "versions")

View File

@ -104,36 +104,6 @@ class Client(object):
cacert=None, tenant_id=None, user_id=None,
connection_pool=False, session=None, auth=None,
**kwargs):
"""
:param str username: Username
:param str api_key: API Key
:param str project_id: Project ID
:param str auth_url: Auth URL
:param bool insecure: Allow insecure
:param float timeout: API timeout, None or 0 disables
:param str proxy_tenant_id: Tenant ID
:param str proxy_token: Proxy Token
:param str region_name: Region Name
:param str endpoint_type: Endpoint Type
:param str extensions: Exensions
:param str service_type: Service Type
:param str service_name: Service Name
:param str volume_service_name: Volume Service Name
:param bool timings: Timings
:param str bypass_url: Bypass URL
:param bool os_cache: OS cache
:param bool no_cache: No cache
:param bool http_log_debug: Enable debugging for HTTP connections
:param str auth_system: Auth system
:param str auth_plugin: Auth plugin
:param str auth_token: Auth token
:param str cacert: cacert
:param str tenant_id: Tenant ID
:param str user_id: User ID
:param bool connection_pool: Use a connection pool
:param str session: Session
:param str auth: Auth
"""
# FIXME(comstud): Rename the api_key argument above when we
# know it's not being used as keyword argument

View File

@ -423,6 +423,40 @@ class ServerManager(base.BootingManagerWithFind):
config_drive=None, admin_pass=None, disk_config=None, **kwargs):
"""
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. A maximum of five entries is allowed, and both
keys and values must be 255 characters or less.
: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 return_raw: If True, don't try to coerce the result into
a Resource object.
:param security_groups: list of security group names
:param key_name: (optional extension) name of keypair to inject into
the instance
:param availability_zone: Name of the availability zone for instance
placement.
:param block_device_mapping: A dict of block device mappings for this
server.
:param block_device_mapping_v2: A dict of block device mappings V2 for
this server.
:param nics: (optional extension) an ordered list of nics to be
added to this server, with information about
connected networks, fixed IPs, etc.
:param scheduler_hints: (optional extension) arbitrary key-value pairs
specified by the client to help boot an instance.
:param config_drive: (optional extension) If True, enable config drive
on the server.
:param admin_pass: admin password for the server.
:param disk_config: (optional extension) control how the disk is
partitioned when the server is created.
"""
body = {"server": {
"name": name,
@ -839,15 +873,10 @@ class ServerManager(base.BootingManagerWithFind):
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 reservation_id: a UUID for the set of servers being requested.
: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

View File

@ -372,7 +372,7 @@ def _boot(cs, args):
dest='files',
default=[],
help=_("Store arbitrary files from <src-path> locally to <dst-path> "
"on the new server. Limited by the injected_files quota value."))
"on the new server. You may store up to 5 files."))
@cliutils.arg(
'--key-name',
default=os.environ.get('NOVACLIENT_DEFAULT_KEY_NAME'),
@ -1897,17 +1897,6 @@ def _find_image(cs, image):
def _find_flavor(cs, flavor):
"""Get a flavor by name, ID, or RAM size."""
try:
# isinstance() is being used to check if flavor is an instance of
# integer. It will help us to check if the user has entered flavor
# name or flavorid. If flavor name has been entered it is being
# converted to lowercase using lower(). Incase it is an ID the user
# has passed it will not go through the "flavor = flavor.lower()"
# code.The reason for checking if it is a flavor name or flavorid is
# that int has no lower() so it will give an error.
if isinstance(flavor, six.integer_types):
pass
else:
flavor = flavor.lower()
return utils.find_resource(cs.flavors, flavor, is_public=None)
except exceptions.NotFound:
return cs.flavors.find(ram=flavor)
@ -1983,6 +1972,7 @@ def _translate_availability_zone_keys(collection):
type=int,
const=1,
help=argparse.SUPPRESS)
@cliutils.service_type('volume')
def do_volume_list(cs, args):
"""List all the volumes."""
search_opts = {'all_tenants': args.all_tenants}
@ -2001,6 +1991,7 @@ def do_volume_list(cs, args):
'volume',
metavar='<volume>',
help=_('Name or ID of the volume.'))
@cliutils.service_type('volume')
def do_volume_show(cs, args):
"""Show details about a volume."""
volume = _find_volume(cs, args.volume)
@ -2053,6 +2044,7 @@ def do_volume_show(cs, args):
'--availability-zone', metavar='<availability-zone>',
help=_('Optional Availability Zone for volume. (Default=None)'),
default=None)
@cliutils.service_type('volume')
def do_volume_create(cs, args):
"""Add a new volume."""
volume = cs.volumes.create(args.size,
@ -2069,6 +2061,7 @@ def do_volume_create(cs, args):
'volume',
metavar='<volume>', nargs='+',
help=_('Name or ID of the volume(s) to delete.'))
@cliutils.service_type('volume')
def do_volume_delete(cs, args):
"""Remove volume(s)."""
for volume in args.volume:
@ -2135,6 +2128,7 @@ def do_volume_detach(cs, args):
args.attachment_id)
@cliutils.service_type('volume')
def do_volume_snapshot_list(cs, _args):
"""List all the snapshots."""
snapshots = cs.volume_snapshots.list()
@ -2147,6 +2141,7 @@ def do_volume_snapshot_list(cs, _args):
'snapshot',
metavar='<snapshot>',
help=_('Name or ID of the snapshot.'))
@cliutils.service_type('volume')
def do_volume_snapshot_show(cs, args):
"""Show details about a snapshot."""
snapshot = _find_volume_snapshot(cs, args.snapshot)
@ -2179,6 +2174,7 @@ def do_volume_snapshot_show(cs, args):
@cliutils.arg(
'--display_description',
help=argparse.SUPPRESS)
@cliutils.service_type('volume')
def do_volume_snapshot_create(cs, args):
"""Add a new snapshot."""
snapshot = cs.volume_snapshots.create(args.volume_id,
@ -2192,6 +2188,7 @@ def do_volume_snapshot_create(cs, args):
'snapshot',
metavar='<snapshot>',
help=_('Name or ID of the snapshot to delete.'))
@cliutils.service_type('volume')
def do_volume_snapshot_delete(cs, args):
"""Remove a snapshot."""
snapshot = _find_volume_snapshot(cs, args.snapshot)
@ -2202,6 +2199,7 @@ def _print_volume_type_list(vtypes):
utils.print_list(vtypes, ['ID', 'Name'])
@cliutils.service_type('volume')
def do_volume_type_list(cs, args):
"""Print a list of available 'volume types'."""
vtypes = cs.volume_types.list()
@ -2212,6 +2210,7 @@ def do_volume_type_list(cs, args):
'name',
metavar='<name>',
help=_("Name of the new volume type"))
@cliutils.service_type('volume')
def do_volume_type_create(cs, args):
"""Create a new volume type."""
vtype = cs.volume_types.create(args.name)
@ -2222,6 +2221,7 @@ def do_volume_type_create(cs, args):
'id',
metavar='<id>',
help=_("Unique ID of the volume type to delete"))
@cliutils.service_type('volume')
def do_volume_type_delete(cs, args):
"""Delete a specific volume type."""
cs.volume_types.delete(args.id)
@ -4326,9 +4326,7 @@ def do_server_group_list(cs, args):
def do_secgroup_list_default_rules(cs, args):
"""List rules that will be added to the 'default' security group for
new tenants.
"""
"""List rules for the default security group."""
_print_secgroup_rules(cs.security_group_default_rules.list(),
show_source_group=False)
@ -4347,9 +4345,7 @@ def do_secgroup_list_default_rules(cs, args):
help=_('Port at end of range.'))
@cliutils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
def do_secgroup_add_default_rule(cs, args):
"""Add a rule to the set of rules that will be added to the 'default'
security group for new tenants.
"""
"""Add a rule to the default security group."""
rule = cs.security_group_default_rules.create(args.ip_proto,
args.from_port,
args.to_port,
@ -4371,9 +4367,7 @@ def do_secgroup_add_default_rule(cs, args):
help=_('Port at end of range.'))
@cliutils.arg('cidr', metavar='<cidr>', help=_('CIDR for address range.'))
def do_secgroup_delete_default_rule(cs, args):
"""Delete a rule from the set of rules that will be added to the
'default' security group for new tenants.
"""
"""Delete a rule from the default security group."""
for rule in cs.security_group_default_rules.list():
if (rule.ip_protocol and
rule.ip_protocol.upper() == args.ip_proto.upper() and

View File

@ -55,12 +55,11 @@ class SnapshotManager(base.ManagerWithFind):
:param display_description: Description of the snapshot
:rtype: :class:`Snapshot`
"""
with self.alternate_service_type('volume'):
body = {'snapshot': {'volume_id': volume_id,
'force': force,
'display_name': display_name,
'display_description': display_description}}
return self._create('/snapshots', body, 'snapshot')
body = {'snapshot': {'volume_id': volume_id,
'force': force,
'display_name': display_name,
'display_description': display_description}}
return self._create('/snapshots', body, 'snapshot')
def get(self, snapshot_id):
"""
@ -69,8 +68,7 @@ class SnapshotManager(base.ManagerWithFind):
:param snapshot_id: The ID of the snapshot to get.
:rtype: :class:`Snapshot`
"""
with self.alternate_service_type('volume'):
return self._get("/snapshots/%s" % snapshot_id, "snapshot")
return self._get("/snapshots/%s" % snapshot_id, "snapshot")
def list(self, detailed=True):
"""
@ -78,11 +76,10 @@ class SnapshotManager(base.ManagerWithFind):
:rtype: list of :class:`Snapshot`
"""
with self.alternate_service_type('volume'):
if detailed is True:
return self._list("/snapshots/detail", "snapshots")
else:
return self._list("/snapshots", "snapshots")
if detailed is True:
return self._list("/snapshots/detail", "snapshots")
else:
return self._list("/snapshots", "snapshots")
def delete(self, snapshot):
"""
@ -90,5 +87,4 @@ class SnapshotManager(base.ManagerWithFind):
:param snapshot: The :class:`Snapshot` to delete.
"""
with self.alternate_service_type('volume'):
self._delete("/snapshots/%s" % base.getid(snapshot))
self._delete("/snapshots/%s" % base.getid(snapshot))

Some files were not shown because too many files have changed in this diff Show More